Merge pull request #8640 from randomnetcat/function-call-kind

Encapsulate kind in FunctionCallAnnotation
This commit is contained in:
chriseth 2020-08-17 11:51:43 +02:00 committed by GitHub
commit 999f158917
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 130 additions and 35 deletions

View File

@ -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;

View File

@ -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))

View File

@ -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, "");

View File

@ -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(), "");

View File

@ -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();

View File

@ -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;
}; };

View File

@ -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;

View File

@ -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());

View File

@ -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());

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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
View 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;
};
}