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/SourceLocation.h>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
|
||||
using namespace std;
|
||||
|
@ -277,7 +277,7 @@ struct EventOutsideEmitChecker: public PostTypeChecker::Checker
|
||||
|
||||
bool visit(FunctionCall const& _functionCall) override
|
||||
{
|
||||
if (_functionCall.annotation().kind != FunctionCallKind::FunctionCall)
|
||||
if (*_functionCall.annotation().kind != FunctionCallKind::FunctionCall)
|
||||
return true;
|
||||
|
||||
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)
|
||||
{
|
||||
if (_functionCall.annotation().kind == FunctionCallKind::FunctionCall)
|
||||
if (*_functionCall.annotation().kind == FunctionCallKind::FunctionCall)
|
||||
{
|
||||
auto functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type);
|
||||
solAssert(functionType, "");
|
||||
|
@ -885,7 +885,7 @@ bool TypeChecker::visit(IfStatement const& _ifStatement)
|
||||
void TypeChecker::endVisit(TryStatement const& _tryStatement)
|
||||
{
|
||||
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(
|
||||
5347_error,
|
||||
@ -1095,7 +1095,7 @@ void TypeChecker::endVisit(Return const& _return)
|
||||
void TypeChecker::endVisit(EmitStatement const& _emit)
|
||||
{
|
||||
if (
|
||||
_emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall ||
|
||||
*_emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall ||
|
||||
type(_emit.eventCall().expression())->category() != Type::Category::Function ||
|
||||
dynamic_cast<FunctionType const&>(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event
|
||||
)
|
||||
@ -1591,7 +1591,7 @@ TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType(
|
||||
FunctionCall const& _functionCall
|
||||
)
|
||||
{
|
||||
solAssert(_functionCall.annotation().kind == FunctionCallKind::TypeConversion, "");
|
||||
solAssert(*_functionCall.annotation().kind == FunctionCallKind::TypeConversion, "");
|
||||
TypePointer const& expressionType = type(_functionCall.expression());
|
||||
|
||||
vector<ASTPointer<Expression const>> const& arguments = _functionCall.arguments();
|
||||
@ -1956,8 +1956,10 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
||||
bool const isPositionalCall = _functionCall.names().empty();
|
||||
bool const isVariadic = _functionType->takesArbitraryParameters();
|
||||
|
||||
auto functionCallKind = *_functionCall.annotation().kind;
|
||||
|
||||
solAssert(
|
||||
!isVariadic || _functionCall.annotation().kind == FunctionCallKind::FunctionCall,
|
||||
!isVariadic || functionCallKind == FunctionCallKind::FunctionCall,
|
||||
"Struct constructor calls cannot be variadic."
|
||||
);
|
||||
|
||||
@ -1972,7 +1974,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
||||
)
|
||||
{
|
||||
bool const isStructConstructorCall =
|
||||
_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall;
|
||||
functionCallKind == FunctionCallKind::StructConstructorCall;
|
||||
|
||||
auto [errorId, description] = [&]() -> tuple<ErrorId, string> {
|
||||
string msg = isVariadic ?
|
||||
@ -2235,13 +2237,14 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
||||
|
||||
default:
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
||||
// Determine return types
|
||||
switch (funcCallAnno.kind)
|
||||
switch (*funcCallAnno.kind)
|
||||
{
|
||||
case FunctionCallKind::TypeConversion:
|
||||
funcCallAnno.type = typeCheckTypeConversionAndRetrieveReturnType(_functionCall);
|
||||
@ -2291,7 +2294,6 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
||||
break;
|
||||
}
|
||||
|
||||
case FunctionCallKind::Unset: // fall-through
|
||||
default:
|
||||
// for non-callables, ensure error reported and annotate node to void function
|
||||
solAssert(m_errorReporter.hasErrors(), "");
|
||||
|
@ -309,7 +309,7 @@ void ViewPureChecker::reportMutability(
|
||||
|
||||
void ViewPureChecker::endVisit(FunctionCall const& _functionCall)
|
||||
{
|
||||
if (_functionCall.annotation().kind != FunctionCallKind::FunctionCall)
|
||||
if (*_functionCall.annotation().kind != FunctionCallKind::FunctionCall)
|
||||
return;
|
||||
|
||||
StateMutability mutability = dynamic_cast<FunctionType const&>(*_functionCall.expression().annotation().type).stateMutability();
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include <libsolidity/ast/ASTEnums.h>
|
||||
#include <libsolidity/ast/ExperimentalFeatures.h>
|
||||
|
||||
#include <libsolutil/SetOnce.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@ -285,7 +287,6 @@ struct BinaryOperationAnnotation: ExpressionAnnotation
|
||||
|
||||
enum class FunctionCallKind
|
||||
{
|
||||
Unset,
|
||||
FunctionCall,
|
||||
TypeConversion,
|
||||
StructConstructorCall
|
||||
@ -293,7 +294,7 @@ enum class FunctionCallKind
|
||||
|
||||
struct FunctionCallAnnotation: ExpressionAnnotation
|
||||
{
|
||||
FunctionCallKind kind = FunctionCallKind::Unset;
|
||||
util::SetOnce<FunctionCallKind> kind;
|
||||
/// If true, this is the external call of a try statement.
|
||||
bool tryCall = false;
|
||||
};
|
||||
|
@ -726,13 +726,16 @@ bool ASTJsonConverter::visit(FunctionCall const& _node)
|
||||
make_pair("arguments", toJson(_node.arguments())),
|
||||
make_pair("tryCall", _node.annotation().tryCall)
|
||||
};
|
||||
|
||||
FunctionCallKind nodeKind = *_node.annotation().kind;
|
||||
|
||||
if (m_legacy)
|
||||
{
|
||||
attributes.emplace_back("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall);
|
||||
attributes.emplace_back("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion);
|
||||
attributes.emplace_back("isStructConstructorCall", nodeKind == FunctionCallKind::StructConstructorCall);
|
||||
attributes.emplace_back("type_conversion", nodeKind == FunctionCallKind::TypeConversion);
|
||||
}
|
||||
else
|
||||
attributes.emplace_back("kind", functionCallKind(_node.annotation().kind));
|
||||
attributes.emplace_back("kind", functionCallKind(nodeKind));
|
||||
appendExpressionAttributes(attributes, _node.annotation());
|
||||
setJsonNode(_node, "FunctionCall", std::move(attributes));
|
||||
return false;
|
||||
|
@ -492,8 +492,10 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
|
||||
|
||||
bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
{
|
||||
auto functionCallKind = *_functionCall.annotation().kind;
|
||||
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _functionCall);
|
||||
if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion)
|
||||
if (functionCallKind == FunctionCallKind::TypeConversion)
|
||||
{
|
||||
solAssert(_functionCall.arguments().size() == 1, "");
|
||||
solAssert(_functionCall.names().empty(), "");
|
||||
@ -517,7 +519,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
}
|
||||
|
||||
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& structType = dynamic_cast<StructType const&>(*type.actualType());
|
||||
@ -548,7 +550,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
solAssert(found, "");
|
||||
}
|
||||
|
||||
if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall)
|
||||
if (functionCallKind == FunctionCallKind::StructConstructorCall)
|
||||
{
|
||||
TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
|
||||
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)
|
||||
{
|
||||
solUnimplementedAssert(
|
||||
_functionCall.annotation().kind != FunctionCallKind::Unset,
|
||||
"This type of function call is not yet implemented"
|
||||
);
|
||||
auto functionCallKind = *_functionCall.annotation().kind;
|
||||
|
||||
if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion)
|
||||
if (functionCallKind == FunctionCallKind::TypeConversion)
|
||||
{
|
||||
solAssert(
|
||||
_functionCall.expression().annotation().type->category() == Type::Category::TypeType,
|
||||
@ -641,7 +638,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
}
|
||||
|
||||
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& 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))]);
|
||||
}
|
||||
|
||||
if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall)
|
||||
if (functionCallKind == FunctionCallKind::StructConstructorCall)
|
||||
{
|
||||
TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
|
||||
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)
|
||||
{
|
||||
solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, "");
|
||||
if (_funCall.annotation().kind != FunctionCallKind::FunctionCall)
|
||||
auto functionCallKind = *_funCall.annotation().kind;
|
||||
|
||||
if (functionCallKind != FunctionCallKind::FunctionCall)
|
||||
{
|
||||
SMTEncoder::endVisit(_funCall);
|
||||
return;
|
||||
|
@ -424,9 +424,9 @@ bool CHC::visit(ForStatement const& _for)
|
||||
|
||||
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);
|
||||
return;
|
||||
|
@ -581,9 +581,10 @@ void SMTEncoder::endVisit(BinaryOperation const& _op)
|
||||
|
||||
void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
||||
{
|
||||
solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, "");
|
||||
auto functionCallKind = *_funCall.annotation().kind;
|
||||
|
||||
createExpr(_funCall);
|
||||
if (_funCall.annotation().kind == FunctionCallKind::StructConstructorCall)
|
||||
if (functionCallKind == FunctionCallKind::StructConstructorCall)
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
4639_error,
|
||||
@ -593,7 +594,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
||||
return;
|
||||
}
|
||||
|
||||
if (_funCall.annotation().kind == FunctionCallKind::TypeConversion)
|
||||
if (functionCallKind == FunctionCallKind::TypeConversion)
|
||||
{
|
||||
visitTypeConversion(_funCall);
|
||||
return;
|
||||
@ -753,7 +754,7 @@ void SMTEncoder::endVisit(ElementaryTypeNameExpression const& _typeName)
|
||||
|
||||
void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall)
|
||||
{
|
||||
solAssert(_funCall.annotation().kind == FunctionCallKind::TypeConversion, "");
|
||||
solAssert(*_funCall.annotation().kind == FunctionCallKind::TypeConversion, "");
|
||||
solAssert(_funCall.arguments().size() == 1, "");
|
||||
auto argument = _funCall.arguments().front();
|
||||
unsigned argSize = argument->annotation().type->storageBytes();
|
||||
@ -1948,7 +1949,7 @@ string SMTEncoder::extraComment()
|
||||
|
||||
FunctionDefinition const* SMTEncoder::functionCallToDefinition(FunctionCall const& _funCall)
|
||||
{
|
||||
if (_funCall.annotation().kind != FunctionCallKind::FunctionCall)
|
||||
if (*_funCall.annotation().kind != FunctionCallKind::FunctionCall)
|
||||
return nullptr;
|
||||
|
||||
FunctionDefinition const* funDef = nullptr;
|
||||
|
@ -22,6 +22,7 @@ set(sources
|
||||
LazyInit.h
|
||||
picosha2.h
|
||||
Result.h
|
||||
SetOnce.h
|
||||
StringUtils.cpp
|
||||
StringUtils.h
|
||||
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