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/SourceLocation.h>
#include <cstdlib>
#include <memory>
using namespace std;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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