diff options
author | Brad King <brad.king@kitware.com> | 2020-10-23 13:05:05 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2020-10-23 13:05:13 (GMT) |
commit | 31848e35a7fa4f7fc8db6f824db86bb7e4809e71 (patch) | |
tree | 608155a6214033b551b8650270dcfd919dec2fe4 | |
parent | b91dd2c639ba095a3fca006c1e00f6f3aa33a995 (diff) | |
parent | 12f6e37eb79ad66c30269a3f19dfc28a9cb834e2 (diff) | |
download | CMake-31848e35a7fa4f7fc8db6f824db86bb7e4809e71.zip CMake-31848e35a7fa4f7fc8db6f824db86bb7e4809e71.tar.gz CMake-31848e35a7fa4f7fc8db6f824db86bb7e4809e71.tar.bz2 |
Merge topic 'proper-command-nesting'
12f6e37eb7 cmListFileCache: Enforce proper nesting of flow control statements
67383725bd cm::optional: Add constructor delegation to nullopt_t constructor
0668120398 cm::optional: Fix move assignment
Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !5401
22 files changed, 261 insertions, 114 deletions
diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx index 70ef5af..3658d11 100644 --- a/Source/cmListFileCache.cxx +++ b/Source/cmListFileCache.cxx @@ -30,6 +30,7 @@ struct cmListFileParser bool ParseFunction(const char* name, long line); bool AddArgument(cmListFileLexer_Token* token, cmListFileArgument::Delimiter delim); + cm::optional<cmListFileContext> CheckNesting(); cmListFile* ListFile; cmListFileBacktrace Backtrace; cmMessenger* Messenger; @@ -158,6 +159,17 @@ bool cmListFileParser::Parse() return false; } } + + // Check if all functions are nested properly. + if (auto badNesting = this->CheckNesting()) { + this->Messenger->IssueMessage( + MessageType::FATAL_ERROR, + "Flow control statements are not properly nested.", + this->Backtrace.Push(*badNesting)); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + return true; } @@ -317,6 +329,112 @@ bool cmListFileParser::AddArgument(cmListFileLexer_Token* token, return true; } +namespace { +enum class NestingStateEnum +{ + If, + Else, + While, + Foreach, + Function, + Macro, +}; + +struct NestingState +{ + NestingStateEnum State; + cmListFileContext Context; +}; + +bool TopIs(std::vector<NestingState>& stack, NestingStateEnum state) +{ + return !stack.empty() && stack.back().State == state; +} +} + +cm::optional<cmListFileContext> cmListFileParser::CheckNesting() +{ + std::vector<NestingState> stack; + + for (auto const& func : this->ListFile->Functions) { + auto const& name = func.LowerCaseName(); + if (name == "if") { + stack.push_back({ + NestingStateEnum::If, + cmListFileContext::FromCommandContext(func, this->FileName), + }); + } else if (name == "elseif") { + if (!TopIs(stack, NestingStateEnum::If)) { + return cmListFileContext::FromCommandContext(func, this->FileName); + } + stack.back() = { + NestingStateEnum::If, + cmListFileContext::FromCommandContext(func, this->FileName), + }; + } else if (name == "else") { + if (!TopIs(stack, NestingStateEnum::If)) { + return cmListFileContext::FromCommandContext(func, this->FileName); + } + stack.back() = { + NestingStateEnum::Else, + cmListFileContext::FromCommandContext(func, this->FileName), + }; + } else if (name == "endif") { + if (!TopIs(stack, NestingStateEnum::If) && + !TopIs(stack, NestingStateEnum::Else)) { + return cmListFileContext::FromCommandContext(func, this->FileName); + } + stack.pop_back(); + } else if (name == "while") { + stack.push_back({ + NestingStateEnum::While, + cmListFileContext::FromCommandContext(func, this->FileName), + }); + } else if (name == "endwhile") { + if (!TopIs(stack, NestingStateEnum::While)) { + return cmListFileContext::FromCommandContext(func, this->FileName); + } + stack.pop_back(); + } else if (name == "foreach") { + stack.push_back({ + NestingStateEnum::Foreach, + cmListFileContext::FromCommandContext(func, this->FileName), + }); + } else if (name == "endforeach") { + if (!TopIs(stack, NestingStateEnum::Foreach)) { + return cmListFileContext::FromCommandContext(func, this->FileName); + } + stack.pop_back(); + } else if (name == "function") { + stack.push_back({ + NestingStateEnum::Function, + cmListFileContext::FromCommandContext(func, this->FileName), + }); + } else if (name == "endfunction") { + if (!TopIs(stack, NestingStateEnum::Function)) { + return cmListFileContext::FromCommandContext(func, this->FileName); + } + stack.pop_back(); + } else if (name == "macro") { + stack.push_back({ + NestingStateEnum::Macro, + cmListFileContext::FromCommandContext(func, this->FileName), + }); + } else if (name == "endmacro") { + if (!TopIs(stack, NestingStateEnum::Macro)) { + return cmListFileContext::FromCommandContext(func, this->FileName); + } + stack.pop_back(); + } + } + + if (!stack.empty()) { + return stack.back().Context; + } + + return cm::nullopt; +} + // We hold either the bottom scope of a directory or a call/file context. // Discriminate these cases via the parent pointer. struct cmListFileBacktrace::Entry diff --git a/Source/cmListFileCache.h b/Source/cmListFileCache.h index 727fc60..ed45c07 100644 --- a/Source/cmListFileCache.h +++ b/Source/cmListFileCache.h @@ -89,6 +89,14 @@ public: { } +#if __cplusplus < 201703L && (!defined(_MSVC_LANG) || _MSVC_LANG < 201703L) + cmListFileContext(const cmListFileContext& /*other*/) = default; + cmListFileContext(cmListFileContext&& /*other*/) = default; + + cmListFileContext& operator=(const cmListFileContext& /*other*/) = default; + cmListFileContext& operator=(cmListFileContext&& /*other*/) = delete; +#endif + static cmListFileContext FromCommandContext( cmCommandContext const& lfcc, std::string const& fileName, cm::optional<std::string> deferId = {}) diff --git a/Tests/CMakeLib/testOptional.cxx b/Tests/CMakeLib/testOptional.cxx index de09c0f..2d7dd7c 100644 --- a/Tests/CMakeLib/testOptional.cxx +++ b/Tests/CMakeLib/testOptional.cxx @@ -82,6 +82,18 @@ public: int Value = 0; }; +class NoMoveAssignEventLogger : public EventLogger +{ +public: + using EventLogger::EventLogger; + + NoMoveAssignEventLogger(const NoMoveAssignEventLogger&) = default; + NoMoveAssignEventLogger(NoMoveAssignEventLogger&&) = default; + + NoMoveAssignEventLogger& operator=(const NoMoveAssignEventLogger&) = default; + NoMoveAssignEventLogger& operator=(NoMoveAssignEventLogger&&) = delete; +}; + #define ASSERT_TRUE(x) \ do { \ if (!(x)) { \ @@ -328,12 +340,28 @@ static bool testCopyAssign(std::vector<Event>& expected) o1 = o4; // Intentionally duplicated to test assigning an empty optional to // an empty optional + cm::optional<NoMoveAssignEventLogger> o5{ 1 }; + auto const* v5 = &*o5; + const cm::optional<NoMoveAssignEventLogger> o6{ 2 }; + auto const* v6 = &*o6; + o5 = std::move(o6); + const NoMoveAssignEventLogger e7{ 3 }; + o5 = std::move(e7); + expected = { { Event::VALUE_CONSTRUCT, v2, nullptr, 4 }, { Event::COPY_CONSTRUCT, v1, v2, 4 }, { Event::VALUE_CONSTRUCT, v3, nullptr, 5 }, { Event::COPY_ASSIGN, v1, v3, 5 }, { Event::DESTRUCT, v1, nullptr, 5 }, + { Event::VALUE_CONSTRUCT, v5, nullptr, 1 }, + { Event::VALUE_CONSTRUCT, v6, nullptr, 2 }, + { Event::COPY_ASSIGN, v5, v6, 2 }, + { Event::VALUE_CONSTRUCT, &e7, nullptr, 3 }, + { Event::COPY_ASSIGN, v5, &e7, 3 }, + { Event::DESTRUCT, &e7, nullptr, 3 }, + { Event::DESTRUCT, v6, nullptr, 2 }, + { Event::DESTRUCT, v5, nullptr, 3 }, { Event::DESTRUCT, v3, nullptr, 5 }, { Event::DESTRUCT, v2, nullptr, 4 }, }; diff --git a/Tests/CMakeTests/EndStuffTestScript.cmake b/Tests/CMakeTests/EndStuffTestScript.cmake index 9f40818..6a6b162 100644 --- a/Tests/CMakeTests/EndStuffTestScript.cmake +++ b/Tests/CMakeTests/EndStuffTestScript.cmake @@ -1,68 +1,40 @@ message(STATUS "testname='${testname}'") -if(testname STREQUAL bad_else) # fail - file(WRITE "${dir}/${testname}.cmake" -"else() -") +function(do_end content) + file(WRITE "${dir}/${testname}.cmake" "${content}") execute_process(COMMAND ${CMAKE_COMMAND} -P "${dir}/${testname}.cmake" RESULT_VARIABLE rv) if(NOT rv EQUAL 0) message(FATAL_ERROR "${testname} failed") endif() +endfunction() + +if(testname STREQUAL bad_else) # fail + do_end("else()\n") elseif(testname STREQUAL bad_elseif) # fail - file(WRITE "${dir}/${testname}.cmake" -"elseif() -") - execute_process(COMMAND ${CMAKE_COMMAND} -P "${dir}/${testname}.cmake" - RESULT_VARIABLE rv) - if(NOT rv EQUAL 0) - message(FATAL_ERROR "${testname} failed") - endif() + do_end("elseif()\n") elseif(testname STREQUAL bad_endforeach) # fail - endforeach() + do_end("endforeach()\n") elseif(testname STREQUAL bad_endfunction) # fail - endfunction() + do_end("endfunction()\n") elseif(testname STREQUAL bad_endif) # fail - file(WRITE "${dir}/${testname}.cmake" -"cmake_minimum_required(VERSION 2.8) -endif() -") - execute_process(COMMAND ${CMAKE_COMMAND} -P "${dir}/${testname}.cmake" - RESULT_VARIABLE rv) - if(NOT rv EQUAL 0) - message(FATAL_ERROR "${testname} failed") - endif() + do_end("cmake_minimum_required(VERSION 2.8)\nendif()\n") -elseif(testname STREQUAL endif_low_min_version) # pass - file(WRITE "${dir}/${testname}.cmake" -"cmake_minimum_required(VERSION 1.2) -endif() -") - execute_process(COMMAND ${CMAKE_COMMAND} -P "${dir}/${testname}.cmake" - RESULT_VARIABLE rv) - if(NOT rv EQUAL 0) - message(FATAL_ERROR "${testname} failed") - endif() +elseif(testname STREQUAL endif_low_min_version) # fail + do_end("cmake_minimum_required(VERSION 1.2)\nendif()\n") -elseif(testname STREQUAL endif_no_min_version) # pass - file(WRITE "${dir}/${testname}.cmake" -"endif() -") - execute_process(COMMAND ${CMAKE_COMMAND} -P "${dir}/${testname}.cmake" - RESULT_VARIABLE rv) - if(NOT rv EQUAL 0) - message(FATAL_ERROR "${testname} failed") - endif() +elseif(testname STREQUAL endif_no_min_version) # fail + do_end("endif()\n") elseif(testname STREQUAL bad_endmacro) # fail - endmacro() + do_end("endmacro()\n") elseif(testname STREQUAL bad_endwhile) # fail - endwhile() + do_end("endwhile()\n") else() # fail message(FATAL_ERROR "testname='${testname}' - error: no such test in '${CMAKE_CURRENT_LIST_FILE}'") diff --git a/Tests/RunCMake/Syntax/FunctionUnmatched-stderr.txt b/Tests/RunCMake/Syntax/FunctionUnmatched-stderr.txt index 306c255..87fa746 100644 --- a/Tests/RunCMake/Syntax/FunctionUnmatched-stderr.txt +++ b/Tests/RunCMake/Syntax/FunctionUnmatched-stderr.txt @@ -1,8 +1,4 @@ -^CMake Error in FunctionUnmatched.cmake: - A logical block opening on the line - - .*/Tests/RunCMake/Syntax/FunctionUnmatched.cmake:[0-9]+ \(function\) - - is not closed. +^CMake Error at FunctionUnmatched\.cmake:[0-9]+ \(function\): + Flow control statements are not properly nested\. Call Stack \(most recent call first\): - CMakeLists.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/Syntax/FunctionUnmatchedForeach-stderr.txt b/Tests/RunCMake/Syntax/FunctionUnmatchedForeach-stderr.txt index f4ff709..7904b87 100644 --- a/Tests/RunCMake/Syntax/FunctionUnmatchedForeach-stderr.txt +++ b/Tests/RunCMake/Syntax/FunctionUnmatchedForeach-stderr.txt @@ -1,8 +1,4 @@ -^CMake Error at FunctionUnmatchedForeach.cmake:[0-9]+ \(f\): - A logical block opening on the line - - .*/Tests/RunCMake/Syntax/FunctionUnmatchedForeach.cmake:[0-9]+ \(foreach\) - - is not closed. +^CMake Error at FunctionUnmatchedForeach\.cmake:[0-9]+ \(endfunction\): + Flow control statements are not properly nested\. Call Stack \(most recent call first\): - CMakeLists.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/Syntax/ImproperNesting-result.txt b/Tests/RunCMake/Syntax/ImproperNesting-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/Syntax/ImproperNesting-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/Syntax/ImproperNesting-stderr.txt b/Tests/RunCMake/Syntax/ImproperNesting-stderr.txt new file mode 100644 index 0000000..a209ef6 --- /dev/null +++ b/Tests/RunCMake/Syntax/ImproperNesting-stderr.txt @@ -0,0 +1,4 @@ +^CMake Error at ImproperNesting\.cmake:[0-9]+ \(endforeach\): + Flow control statements are not properly nested\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/Syntax/ImproperNesting.cmake b/Tests/RunCMake/Syntax/ImproperNesting.cmake new file mode 100644 index 0000000..47ff9f9 --- /dev/null +++ b/Tests/RunCMake/Syntax/ImproperNesting.cmake @@ -0,0 +1,7 @@ +message(FATAL_ERROR "This should not happen") + +foreach(i 1 2) + if(1) +endforeach() +endif() +endif() diff --git a/Tests/RunCMake/Syntax/MacroUnmatched-stderr.txt b/Tests/RunCMake/Syntax/MacroUnmatched-stderr.txt index 440d863..a7af590 100644 --- a/Tests/RunCMake/Syntax/MacroUnmatched-stderr.txt +++ b/Tests/RunCMake/Syntax/MacroUnmatched-stderr.txt @@ -1,8 +1,4 @@ -^CMake Error in MacroUnmatched.cmake: - A logical block opening on the line - - .*/Tests/RunCMake/Syntax/MacroUnmatched.cmake:[0-9]+ \(macro\) - - is not closed. +^CMake Error at MacroUnmatched\.cmake:[0-9]+ \(macro\): + Flow control statements are not properly nested\. Call Stack \(most recent call first\): - CMakeLists.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/Syntax/MacroUnmatchedForeach-stderr.txt b/Tests/RunCMake/Syntax/MacroUnmatchedForeach-stderr.txt index 820cd2e..30c4a4c 100644 --- a/Tests/RunCMake/Syntax/MacroUnmatchedForeach-stderr.txt +++ b/Tests/RunCMake/Syntax/MacroUnmatchedForeach-stderr.txt @@ -1,8 +1,4 @@ -^CMake Error at MacroUnmatchedForeach.cmake:[0-9]+ \(m\): - A logical block opening on the line - - .*/Tests/RunCMake/Syntax/MacroUnmatchedForeach.cmake:[0-9]+ \(foreach\) - - is not closed. +^CMake Error at MacroUnmatchedForeach\.cmake:[0-9]+ \(endmacro\): + Flow control statements are not properly nested\. Call Stack \(most recent call first\): - CMakeLists.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/Syntax/RunCMakeTest.cmake b/Tests/RunCMake/Syntax/RunCMakeTest.cmake index 8d74dc1..34885b8 100644 --- a/Tests/RunCMake/Syntax/RunCMakeTest.cmake +++ b/Tests/RunCMake/Syntax/RunCMakeTest.cmake @@ -72,6 +72,7 @@ run_cmake(UnterminatedBrace2) run_cmake(UnterminatedBracket0) run_cmake(UnterminatedBracket1) run_cmake(UnterminatedBracketComment) +run_cmake(ImproperNesting) # Variable expansion tests run_cmake(ExpandInAt) diff --git a/Tests/RunCMake/if/duplicate-deep-else-stderr.txt b/Tests/RunCMake/if/duplicate-deep-else-stderr.txt index ac2335c..ee886e0 100644 --- a/Tests/RunCMake/if/duplicate-deep-else-stderr.txt +++ b/Tests/RunCMake/if/duplicate-deep-else-stderr.txt @@ -1,4 +1,4 @@ -CMake Error at duplicate-deep-else.cmake:[0-9]+ \(else\): - A duplicate ELSE command was found inside an IF block. +CMake Error at duplicate-deep-else\.cmake:[0-9]+ \(else\): + Flow control statements are not properly nested\. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/if/duplicate-else-after-elseif-stderr.txt b/Tests/RunCMake/if/duplicate-else-after-elseif-stderr.txt index ba6765c..60c8484 100644 --- a/Tests/RunCMake/if/duplicate-else-after-elseif-stderr.txt +++ b/Tests/RunCMake/if/duplicate-else-after-elseif-stderr.txt @@ -1,4 +1,4 @@ -CMake Error at duplicate-else-after-elseif.cmake:[0-9]+ \(else\): - A duplicate ELSE command was found inside an IF block. +CMake Error at duplicate-else-after-elseif\.cmake:[0-9]+ \(else\): + Flow control statements are not properly nested\. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/if/duplicate-else-stderr.txt b/Tests/RunCMake/if/duplicate-else-stderr.txt index e0dd01f..518c43f 100644 --- a/Tests/RunCMake/if/duplicate-else-stderr.txt +++ b/Tests/RunCMake/if/duplicate-else-stderr.txt @@ -1,4 +1,4 @@ -CMake Error at duplicate-else.cmake:[0-9]+ \(else\): - A duplicate ELSE command was found inside an IF block. +CMake Error at duplicate-else\.cmake:[0-9]+ \(else\): + Flow control statements are not properly nested\. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/if/misplaced-elseif-stderr.txt b/Tests/RunCMake/if/misplaced-elseif-stderr.txt index c4b0266..5138f11 100644 --- a/Tests/RunCMake/if/misplaced-elseif-stderr.txt +++ b/Tests/RunCMake/if/misplaced-elseif-stderr.txt @@ -1,4 +1,4 @@ -CMake Error at misplaced-elseif.cmake:[0-9]+ \(elseif\): - An ELSEIF command was found after an ELSE command. +CMake Error at misplaced-elseif\.cmake:[0-9]+ \(elseif\): + Flow control statements are not properly nested\. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/while/EndAlone-stderr.txt b/Tests/RunCMake/while/EndAlone-stderr.txt index 5fe6655..3195fa0 100644 --- a/Tests/RunCMake/while/EndAlone-stderr.txt +++ b/Tests/RunCMake/while/EndAlone-stderr.txt @@ -1,5 +1,4 @@ -^CMake Error at EndAlone.cmake:1 \(endwhile\): - endwhile An ENDWHILE command was found outside of a proper WHILE ENDWHILE - structure. Or its arguments did not match the opening WHILE command. +^CMake Error at EndAlone\.cmake:[0-9]+ \(endwhile\): + Flow control statements are not properly nested\. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/while/EndAloneArgs-stderr.txt b/Tests/RunCMake/while/EndAloneArgs-stderr.txt index a8c043d..1634e3b 100644 --- a/Tests/RunCMake/while/EndAloneArgs-stderr.txt +++ b/Tests/RunCMake/while/EndAloneArgs-stderr.txt @@ -1,5 +1,4 @@ -^CMake Error at EndAloneArgs.cmake:1 \(endwhile\): - endwhile An ENDWHILE command was found outside of a proper WHILE ENDWHILE - structure. Or its arguments did not match the opening WHILE command. +^CMake Error at EndAloneArgs\.cmake:[0-9]+ \(endwhile\): + Flow control statements are not properly nested\. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/while/EndMissing-stderr.txt b/Tests/RunCMake/while/EndMissing-stderr.txt index 964792f..1e3be4d 100644 --- a/Tests/RunCMake/while/EndMissing-stderr.txt +++ b/Tests/RunCMake/while/EndMissing-stderr.txt @@ -1,8 +1,4 @@ -^CMake Error in EndMissing.cmake: - A logical block opening on the line - - .*/Tests/RunCMake/while/EndMissing.cmake:1 \(while\) - - is not closed. +^CMake Error at EndMissing\.cmake:[0-9]+ \(while\): + Flow control statements are not properly nested\. Call Stack \(most recent call first\): - CMakeLists.txt:[0-9]+ \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/while/MissingArgument-stderr.txt b/Tests/RunCMake/while/MissingArgument-stderr.txt index 7ff0971..59e8ee3 100644 --- a/Tests/RunCMake/while/MissingArgument-stderr.txt +++ b/Tests/RunCMake/while/MissingArgument-stderr.txt @@ -1,4 +1,11 @@ -^CMake Error at MissingArgument.cmake:1 \(while\): +^CMake Error at MissingArgument\.cmake:[0-9]+ \(while\): while called with incorrect number of arguments Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\)$ + CMakeLists\.txt:[0-9]+ \(include\) + + +CMake Error at MissingArgument\.cmake:[0-9]+ \(endwhile\): + endwhile An ENDWHILE command was found outside of a proper WHILE ENDWHILE + structure\. Or its arguments did not match the opening WHILE command\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/while/MissingArgument.cmake b/Tests/RunCMake/while/MissingArgument.cmake index 32eaa26..3fe2d97 100644 --- a/Tests/RunCMake/while/MissingArgument.cmake +++ b/Tests/RunCMake/while/MissingArgument.cmake @@ -1 +1,2 @@ while() +endwhile() diff --git a/Utilities/std/cm/optional b/Utilities/std/cm/optional index 9a5d840..0defae1 100644 --- a/Utilities/std/cm/optional +++ b/Utilities/std/cm/optional @@ -68,16 +68,22 @@ public: optional& operator=(nullopt_t) noexcept; optional& operator=(const optional& other); - optional& operator=(optional&& other) noexcept; - template < - typename U = T, - typename = typename std::enable_if< - !std::is_same<typename std::decay<U>::type, cm::optional<T>>::value && - std::is_constructible<T, U>::value && std::is_assignable<T&, U>::value && + template <typename U = T> + typename std::enable_if<std::is_constructible<T, U&&>::value && + std::is_assignable<T&, U&&>::value, + optional&>::type + operator=(optional<U>&& other) noexcept; + + template <typename U = T> + typename std::enable_if< + !std::is_same<typename std::decay<U>::type, cm::optional<T>>::value && + std::is_constructible<T, U&&>::value && + std::is_assignable<T&, U&&>::value && (!std::is_scalar<T>::value || - !std::is_same<typename std::decay<U>::type, T>::value)>::type> - optional& operator=(U&& v); + !std::is_same<typename std::decay<U>::type, T>::value), + optional&>::type + operator=(U&& v); const T* operator->() const; T* operator->(); @@ -134,19 +140,24 @@ optional<T> make_optional(Args&&... args) template <typename T> optional<T>::optional(nullopt_t) noexcept + : optional() { } template <typename T> optional<T>::optional(const optional& other) { - *this = other; + if (other.has_value()) { + this->emplace(*other); + } } template <typename T> optional<T>::optional(optional&& other) noexcept { - *this = std::move(other); + if (other.has_value()) { + this->emplace(std::move(*other)); + } } template <typename T> @@ -192,7 +203,11 @@ optional<T>& optional<T>::operator=(const optional& other) } template <typename T> -optional<T>& optional<T>::operator=(optional&& other) noexcept +template <typename U> +typename std::enable_if<std::is_constructible<T, U&&>::value && + std::is_assignable<T&, U&&>::value, + optional<T>&>::type +optional<T>::operator=(optional<U>&& other) noexcept { if (other.has_value()) { if (this->has_value()) { @@ -207,8 +222,15 @@ optional<T>& optional<T>::operator=(optional&& other) noexcept } template <typename T> -template <typename U, typename> -optional<T>& optional<T>::operator=(U&& v) +template <typename U> +typename std::enable_if< + !std::is_same<typename std::decay<U>::type, cm::optional<T>>::value && + std::is_constructible<T, U&&>::value && + std::is_assignable<T&, U&&>::value && + (!std::is_scalar<T>::value || + !std::is_same<typename std::decay<U>::type, T>::value), + optional<T>&>::type +optional<T>::operator=(U&& v) { if (this->has_value()) { this->value() = v; |