mirror of
https://github.com/Kitware/CMake.git
synced 2026-04-21 13:48:33 -05:00
Merge topic 'proper-command-nesting'
12f6e37eb7cmListFileCache: Enforce proper nesting of flow control statements67383725bdcm::optional: Add constructor delegation to nullopt_t constructor0668120398cm::optional: Fix move assignment Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !5401
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 = {})
|
||||
|
||||
@@ -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 },
|
||||
};
|
||||
|
||||
@@ -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}'")
|
||||
|
||||
@@ -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\)$
|
||||
|
||||
@@ -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\)$
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
@@ -0,0 +1,7 @@
|
||||
message(FATAL_ERROR "This should not happen")
|
||||
|
||||
foreach(i 1 2)
|
||||
if(1)
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
@@ -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\)$
|
||||
|
||||
@@ -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\)$
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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\)
|
||||
|
||||
@@ -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\)
|
||||
|
||||
@@ -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\)
|
||||
|
||||
@@ -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\)
|
||||
|
||||
@@ -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\)$
|
||||
|
||||
@@ -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\)$
|
||||
|
||||
@@ -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\)$
|
||||
|
||||
@@ -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\)$
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
while()
|
||||
endwhile()
|
||||
|
||||
+35
-13
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user