mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8640 from randomnetcat/function-call-kind
Encapsulate kind in FunctionCallAnnotation
This commit is contained in:
commit
999f158917
@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
#include <liblangutil/SourceLocation.h>
|
#include <liblangutil/SourceLocation.h>
|
||||||
|
#include <cstdlib>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -277,7 +277,7 @@ struct EventOutsideEmitChecker: public PostTypeChecker::Checker
|
|||||||
|
|
||||||
bool visit(FunctionCall const& _functionCall) override
|
bool visit(FunctionCall const& _functionCall) override
|
||||||
{
|
{
|
||||||
if (_functionCall.annotation().kind != FunctionCallKind::FunctionCall)
|
if (*_functionCall.annotation().kind != FunctionCallKind::FunctionCall)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (FunctionTypePointer const functionType = dynamic_cast<FunctionTypePointer const>(_functionCall.expression().annotation().type))
|
if (FunctionTypePointer const functionType = dynamic_cast<FunctionTypePointer const>(_functionCall.expression().annotation().type))
|
||||||
|
@ -292,7 +292,7 @@ bool StaticAnalyzer::visit(BinaryOperation const& _operation)
|
|||||||
|
|
||||||
bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
|
bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
|
||||||
{
|
{
|
||||||
if (_functionCall.annotation().kind == FunctionCallKind::FunctionCall)
|
if (*_functionCall.annotation().kind == FunctionCallKind::FunctionCall)
|
||||||
{
|
{
|
||||||
auto functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type);
|
auto functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type);
|
||||||
solAssert(functionType, "");
|
solAssert(functionType, "");
|
||||||
|
@ -885,7 +885,7 @@ bool TypeChecker::visit(IfStatement const& _ifStatement)
|
|||||||
void TypeChecker::endVisit(TryStatement const& _tryStatement)
|
void TypeChecker::endVisit(TryStatement const& _tryStatement)
|
||||||
{
|
{
|
||||||
FunctionCall const* externalCall = dynamic_cast<FunctionCall const*>(&_tryStatement.externalCall());
|
FunctionCall const* externalCall = dynamic_cast<FunctionCall const*>(&_tryStatement.externalCall());
|
||||||
if (!externalCall || externalCall->annotation().kind != FunctionCallKind::FunctionCall)
|
if (!externalCall || *externalCall->annotation().kind != FunctionCallKind::FunctionCall)
|
||||||
{
|
{
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
5347_error,
|
5347_error,
|
||||||
@ -1095,7 +1095,7 @@ void TypeChecker::endVisit(Return const& _return)
|
|||||||
void TypeChecker::endVisit(EmitStatement const& _emit)
|
void TypeChecker::endVisit(EmitStatement const& _emit)
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
_emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall ||
|
*_emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall ||
|
||||||
type(_emit.eventCall().expression())->category() != Type::Category::Function ||
|
type(_emit.eventCall().expression())->category() != Type::Category::Function ||
|
||||||
dynamic_cast<FunctionType const&>(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event
|
dynamic_cast<FunctionType const&>(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event
|
||||||
)
|
)
|
||||||
@ -1591,7 +1591,7 @@ TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType(
|
|||||||
FunctionCall const& _functionCall
|
FunctionCall const& _functionCall
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
solAssert(_functionCall.annotation().kind == FunctionCallKind::TypeConversion, "");
|
solAssert(*_functionCall.annotation().kind == FunctionCallKind::TypeConversion, "");
|
||||||
TypePointer const& expressionType = type(_functionCall.expression());
|
TypePointer const& expressionType = type(_functionCall.expression());
|
||||||
|
|
||||||
vector<ASTPointer<Expression const>> const& arguments = _functionCall.arguments();
|
vector<ASTPointer<Expression const>> const& arguments = _functionCall.arguments();
|
||||||
@ -1956,8 +1956,10 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
|||||||
bool const isPositionalCall = _functionCall.names().empty();
|
bool const isPositionalCall = _functionCall.names().empty();
|
||||||
bool const isVariadic = _functionType->takesArbitraryParameters();
|
bool const isVariadic = _functionType->takesArbitraryParameters();
|
||||||
|
|
||||||
|
auto functionCallKind = *_functionCall.annotation().kind;
|
||||||
|
|
||||||
solAssert(
|
solAssert(
|
||||||
!isVariadic || _functionCall.annotation().kind == FunctionCallKind::FunctionCall,
|
!isVariadic || functionCallKind == FunctionCallKind::FunctionCall,
|
||||||
"Struct constructor calls cannot be variadic."
|
"Struct constructor calls cannot be variadic."
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1972,7 +1974,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
bool const isStructConstructorCall =
|
bool const isStructConstructorCall =
|
||||||
_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall;
|
functionCallKind == FunctionCallKind::StructConstructorCall;
|
||||||
|
|
||||||
auto [errorId, description] = [&]() -> tuple<ErrorId, string> {
|
auto [errorId, description] = [&]() -> tuple<ErrorId, string> {
|
||||||
string msg = isVariadic ?
|
string msg = isVariadic ?
|
||||||
@ -2235,13 +2237,14 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
m_errorReporter.fatalTypeError(5704_error, _functionCall.location(), "Type is not callable");
|
m_errorReporter.fatalTypeError(5704_error, _functionCall.location(), "Type is not callable");
|
||||||
funcCallAnno.kind = FunctionCallKind::Unset;
|
// Unreachable, because fatalTypeError throws. We don't set kind, but that's okay because the switch below
|
||||||
|
// is never reached. And, even if it was, SetOnce would trigger an assertion violation and not UB.
|
||||||
funcCallAnno.isPure = argumentsArePure;
|
funcCallAnno.isPure = argumentsArePure;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine return types
|
// Determine return types
|
||||||
switch (funcCallAnno.kind)
|
switch (*funcCallAnno.kind)
|
||||||
{
|
{
|
||||||
case FunctionCallKind::TypeConversion:
|
case FunctionCallKind::TypeConversion:
|
||||||
funcCallAnno.type = typeCheckTypeConversionAndRetrieveReturnType(_functionCall);
|
funcCallAnno.type = typeCheckTypeConversionAndRetrieveReturnType(_functionCall);
|
||||||
@ -2291,7 +2294,6 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case FunctionCallKind::Unset: // fall-through
|
|
||||||
default:
|
default:
|
||||||
// for non-callables, ensure error reported and annotate node to void function
|
// for non-callables, ensure error reported and annotate node to void function
|
||||||
solAssert(m_errorReporter.hasErrors(), "");
|
solAssert(m_errorReporter.hasErrors(), "");
|
||||||
|
@ -309,7 +309,7 @@ void ViewPureChecker::reportMutability(
|
|||||||
|
|
||||||
void ViewPureChecker::endVisit(FunctionCall const& _functionCall)
|
void ViewPureChecker::endVisit(FunctionCall const& _functionCall)
|
||||||
{
|
{
|
||||||
if (_functionCall.annotation().kind != FunctionCallKind::FunctionCall)
|
if (*_functionCall.annotation().kind != FunctionCallKind::FunctionCall)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
StateMutability mutability = dynamic_cast<FunctionType const&>(*_functionCall.expression().annotation().type).stateMutability();
|
StateMutability mutability = dynamic_cast<FunctionType const&>(*_functionCall.expression().annotation().type).stateMutability();
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
#include <libsolidity/ast/ASTEnums.h>
|
#include <libsolidity/ast/ASTEnums.h>
|
||||||
#include <libsolidity/ast/ExperimentalFeatures.h>
|
#include <libsolidity/ast/ExperimentalFeatures.h>
|
||||||
|
|
||||||
|
#include <libsolutil/SetOnce.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@ -285,7 +287,6 @@ struct BinaryOperationAnnotation: ExpressionAnnotation
|
|||||||
|
|
||||||
enum class FunctionCallKind
|
enum class FunctionCallKind
|
||||||
{
|
{
|
||||||
Unset,
|
|
||||||
FunctionCall,
|
FunctionCall,
|
||||||
TypeConversion,
|
TypeConversion,
|
||||||
StructConstructorCall
|
StructConstructorCall
|
||||||
@ -293,7 +294,7 @@ enum class FunctionCallKind
|
|||||||
|
|
||||||
struct FunctionCallAnnotation: ExpressionAnnotation
|
struct FunctionCallAnnotation: ExpressionAnnotation
|
||||||
{
|
{
|
||||||
FunctionCallKind kind = FunctionCallKind::Unset;
|
util::SetOnce<FunctionCallKind> kind;
|
||||||
/// If true, this is the external call of a try statement.
|
/// If true, this is the external call of a try statement.
|
||||||
bool tryCall = false;
|
bool tryCall = false;
|
||||||
};
|
};
|
||||||
|
@ -726,13 +726,16 @@ bool ASTJsonConverter::visit(FunctionCall const& _node)
|
|||||||
make_pair("arguments", toJson(_node.arguments())),
|
make_pair("arguments", toJson(_node.arguments())),
|
||||||
make_pair("tryCall", _node.annotation().tryCall)
|
make_pair("tryCall", _node.annotation().tryCall)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
FunctionCallKind nodeKind = *_node.annotation().kind;
|
||||||
|
|
||||||
if (m_legacy)
|
if (m_legacy)
|
||||||
{
|
{
|
||||||
attributes.emplace_back("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall);
|
attributes.emplace_back("isStructConstructorCall", nodeKind == FunctionCallKind::StructConstructorCall);
|
||||||
attributes.emplace_back("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion);
|
attributes.emplace_back("type_conversion", nodeKind == FunctionCallKind::TypeConversion);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
attributes.emplace_back("kind", functionCallKind(_node.annotation().kind));
|
attributes.emplace_back("kind", functionCallKind(nodeKind));
|
||||||
appendExpressionAttributes(attributes, _node.annotation());
|
appendExpressionAttributes(attributes, _node.annotation());
|
||||||
setJsonNode(_node, "FunctionCall", std::move(attributes));
|
setJsonNode(_node, "FunctionCall", std::move(attributes));
|
||||||
return false;
|
return false;
|
||||||
|
@ -492,8 +492,10 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
|
|||||||
|
|
||||||
bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||||
{
|
{
|
||||||
|
auto functionCallKind = *_functionCall.annotation().kind;
|
||||||
|
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, _functionCall);
|
CompilerContext::LocationSetter locationSetter(m_context, _functionCall);
|
||||||
if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion)
|
if (functionCallKind == FunctionCallKind::TypeConversion)
|
||||||
{
|
{
|
||||||
solAssert(_functionCall.arguments().size() == 1, "");
|
solAssert(_functionCall.arguments().size() == 1, "");
|
||||||
solAssert(_functionCall.names().empty(), "");
|
solAssert(_functionCall.names().empty(), "");
|
||||||
@ -517,7 +519,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
|
|
||||||
FunctionTypePointer functionType;
|
FunctionTypePointer functionType;
|
||||||
if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall)
|
if (functionCallKind == FunctionCallKind::StructConstructorCall)
|
||||||
{
|
{
|
||||||
auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
|
auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
|
||||||
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
|
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
|
||||||
@ -548,7 +550,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
solAssert(found, "");
|
solAssert(found, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall)
|
if (functionCallKind == FunctionCallKind::StructConstructorCall)
|
||||||
{
|
{
|
||||||
TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
|
TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
|
||||||
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
|
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
|
||||||
|
@ -624,12 +624,9 @@ bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
|
|||||||
|
|
||||||
void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||||
{
|
{
|
||||||
solUnimplementedAssert(
|
auto functionCallKind = *_functionCall.annotation().kind;
|
||||||
_functionCall.annotation().kind != FunctionCallKind::Unset,
|
|
||||||
"This type of function call is not yet implemented"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion)
|
if (functionCallKind == FunctionCallKind::TypeConversion)
|
||||||
{
|
{
|
||||||
solAssert(
|
solAssert(
|
||||||
_functionCall.expression().annotation().type->category() == Type::Category::TypeType,
|
_functionCall.expression().annotation().type->category() == Type::Category::TypeType,
|
||||||
@ -641,7 +638,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
|
|
||||||
FunctionTypePointer functionType = nullptr;
|
FunctionTypePointer functionType = nullptr;
|
||||||
if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall)
|
if (functionCallKind == FunctionCallKind::StructConstructorCall)
|
||||||
{
|
{
|
||||||
auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
|
auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
|
||||||
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
|
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
|
||||||
@ -672,7 +669,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
arguments.push_back(callArguments[static_cast<size_t>(std::distance(callArgumentNames.begin(), it))]);
|
arguments.push_back(callArguments[static_cast<size_t>(std::distance(callArgumentNames.begin(), it))]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall)
|
if (functionCallKind == FunctionCallKind::StructConstructorCall)
|
||||||
{
|
{
|
||||||
TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
|
TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
|
||||||
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
|
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
|
||||||
|
@ -337,8 +337,9 @@ void BMC::endVisit(UnaryOperation const& _op)
|
|||||||
|
|
||||||
void BMC::endVisit(FunctionCall const& _funCall)
|
void BMC::endVisit(FunctionCall const& _funCall)
|
||||||
{
|
{
|
||||||
solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, "");
|
auto functionCallKind = *_funCall.annotation().kind;
|
||||||
if (_funCall.annotation().kind != FunctionCallKind::FunctionCall)
|
|
||||||
|
if (functionCallKind != FunctionCallKind::FunctionCall)
|
||||||
{
|
{
|
||||||
SMTEncoder::endVisit(_funCall);
|
SMTEncoder::endVisit(_funCall);
|
||||||
return;
|
return;
|
||||||
|
@ -424,9 +424,9 @@ bool CHC::visit(ForStatement const& _for)
|
|||||||
|
|
||||||
void CHC::endVisit(FunctionCall const& _funCall)
|
void CHC::endVisit(FunctionCall const& _funCall)
|
||||||
{
|
{
|
||||||
solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, "");
|
auto functionCallKind = *_funCall.annotation().kind;
|
||||||
|
|
||||||
if (_funCall.annotation().kind != FunctionCallKind::FunctionCall)
|
if (functionCallKind != FunctionCallKind::FunctionCall)
|
||||||
{
|
{
|
||||||
SMTEncoder::endVisit(_funCall);
|
SMTEncoder::endVisit(_funCall);
|
||||||
return;
|
return;
|
||||||
|
@ -581,9 +581,10 @@ void SMTEncoder::endVisit(BinaryOperation const& _op)
|
|||||||
|
|
||||||
void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
||||||
{
|
{
|
||||||
solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, "");
|
auto functionCallKind = *_funCall.annotation().kind;
|
||||||
|
|
||||||
createExpr(_funCall);
|
createExpr(_funCall);
|
||||||
if (_funCall.annotation().kind == FunctionCallKind::StructConstructorCall)
|
if (functionCallKind == FunctionCallKind::StructConstructorCall)
|
||||||
{
|
{
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
4639_error,
|
4639_error,
|
||||||
@ -593,7 +594,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_funCall.annotation().kind == FunctionCallKind::TypeConversion)
|
if (functionCallKind == FunctionCallKind::TypeConversion)
|
||||||
{
|
{
|
||||||
visitTypeConversion(_funCall);
|
visitTypeConversion(_funCall);
|
||||||
return;
|
return;
|
||||||
@ -753,7 +754,7 @@ void SMTEncoder::endVisit(ElementaryTypeNameExpression const& _typeName)
|
|||||||
|
|
||||||
void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall)
|
void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall)
|
||||||
{
|
{
|
||||||
solAssert(_funCall.annotation().kind == FunctionCallKind::TypeConversion, "");
|
solAssert(*_funCall.annotation().kind == FunctionCallKind::TypeConversion, "");
|
||||||
solAssert(_funCall.arguments().size() == 1, "");
|
solAssert(_funCall.arguments().size() == 1, "");
|
||||||
auto argument = _funCall.arguments().front();
|
auto argument = _funCall.arguments().front();
|
||||||
unsigned argSize = argument->annotation().type->storageBytes();
|
unsigned argSize = argument->annotation().type->storageBytes();
|
||||||
@ -1948,7 +1949,7 @@ string SMTEncoder::extraComment()
|
|||||||
|
|
||||||
FunctionDefinition const* SMTEncoder::functionCallToDefinition(FunctionCall const& _funCall)
|
FunctionDefinition const* SMTEncoder::functionCallToDefinition(FunctionCall const& _funCall)
|
||||||
{
|
{
|
||||||
if (_funCall.annotation().kind != FunctionCallKind::FunctionCall)
|
if (*_funCall.annotation().kind != FunctionCallKind::FunctionCall)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
FunctionDefinition const* funDef = nullptr;
|
FunctionDefinition const* funDef = nullptr;
|
||||||
|
@ -22,6 +22,7 @@ set(sources
|
|||||||
LazyInit.h
|
LazyInit.h
|
||||||
picosha2.h
|
picosha2.h
|
||||||
Result.h
|
Result.h
|
||||||
|
SetOnce.h
|
||||||
StringUtils.cpp
|
StringUtils.cpp
|
||||||
StringUtils.h
|
StringUtils.h
|
||||||
SwarmHash.cpp
|
SwarmHash.cpp
|
||||||
|
86
libsolutil/SetOnce.h
Normal file
86
libsolutil/SetOnce.h
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolutil/Assertions.h>
|
||||||
|
#include <libsolutil/Exceptions.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace solidity::util
|
||||||
|
{
|
||||||
|
|
||||||
|
DEV_SIMPLE_EXCEPTION(BadSetOnceReassignment);
|
||||||
|
DEV_SIMPLE_EXCEPTION(BadSetOnceAccess);
|
||||||
|
|
||||||
|
/// A class that stores a value that can only be set once
|
||||||
|
/// \tparam T the type of the stored value
|
||||||
|
template<typename T>
|
||||||
|
class SetOnce
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Initializes the class to have no stored value.
|
||||||
|
SetOnce() = default;
|
||||||
|
|
||||||
|
// Not copiable
|
||||||
|
SetOnce(SetOnce const&) = delete;
|
||||||
|
SetOnce(SetOnce&&) = delete;
|
||||||
|
|
||||||
|
// Not movable
|
||||||
|
SetOnce& operator=(SetOnce const&) = delete;
|
||||||
|
SetOnce& operator=(SetOnce&&) = delete;
|
||||||
|
|
||||||
|
/// @brief Sets the stored value to \p _newValue
|
||||||
|
/// @throws BadSetOnceReassignment when the stored value has already been set
|
||||||
|
/// @return `*this`
|
||||||
|
constexpr SetOnce& operator=(T _newValue) &
|
||||||
|
{
|
||||||
|
assertThrow(
|
||||||
|
!m_value.has_value(),
|
||||||
|
BadSetOnceReassignment,
|
||||||
|
"Attempt to reassign to a SetOnce that already has a value."
|
||||||
|
);
|
||||||
|
|
||||||
|
m_value.emplace(std::move(_newValue));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @return A reference to the stored value. The returned reference has the same lifetime as `*this`.
|
||||||
|
/// @throws BadSetOnceAccess when the stored value has not yet been set
|
||||||
|
T const& operator*() const
|
||||||
|
{
|
||||||
|
assertThrow(
|
||||||
|
m_value.has_value(),
|
||||||
|
BadSetOnceAccess,
|
||||||
|
"Attempt to access the value of a SetOnce that does not have a value."
|
||||||
|
);
|
||||||
|
|
||||||
|
return m_value.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @return A reference to the stored value. The referent of the returned pointer has the same lifetime as `*this`.
|
||||||
|
/// @throws BadSetOnceAccess when the stored value has not yet been set
|
||||||
|
T const* operator->() const { return std::addressof(**this); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<T> m_value = std::nullopt;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user