Add annotation field `requiresVirtualLookup`

This commit is contained in:
Mathias Baumann 2020-09-16 11:00:07 +02:00
parent 858b4507e2
commit 8584c98b6a
9 changed files with 57 additions and 8 deletions

View File

@ -125,7 +125,7 @@ bool ImmutableValidator::visit(WhileStatement const& _whileStatement)
void ImmutableValidator::endVisit(Identifier const& _identifier)
{
if (auto const callableDef = dynamic_cast<CallableDeclaration const*>(_identifier.annotation().referencedDeclaration))
visitCallableIfNew(callableDef->resolveVirtual(m_currentContract));
visitCallableIfNew(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual ? callableDef->resolveVirtual(m_currentContract) : *callableDef);
if (auto const varDecl = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
analyseVariableReference(*varDecl, _identifier);
}

View File

@ -2544,8 +2544,10 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
TypePointer exprType = type(_memberAccess.expression());
ASTString const& memberName = _memberAccess.memberName();
auto& annotation = _memberAccess.annotation();
// Retrieve the types of the arguments if this is used to call a function.
auto const& arguments = _memberAccess.annotation().arguments;
auto const& arguments = annotation.arguments;
MemberList::MemberMap possibleMembers = exprType->members(currentDefinitionScope()).membersByName(memberName);
size_t const initialMemberCount = possibleMembers.size();
if (initialMemberCount > 1 && arguments)
@ -2561,8 +2563,6 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
++it;
}
auto& annotation = _memberAccess.annotation();
annotation.isConstant = false;
if (possibleMembers.empty())
@ -2661,6 +2661,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
annotation.referencedDeclaration = possibleMembers.front().declaration;
annotation.type = possibleMembers.front().type;
VirtualLookup requiredLookup = VirtualLookup::Static;
if (auto funType = dynamic_cast<FunctionType const*>(annotation.type))
{
solAssert(
@ -2679,8 +2681,15 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
_memberAccess.location(),
"Using \"." + memberName + "(...)\" is deprecated. Use \"{" + memberName + ": ...}\" instead."
);
if (!funType->bound())
if (auto contractType = dynamic_cast<ContractType const*>(exprType))
requiredLookup = contractType->isSuper() ? VirtualLookup::Super : VirtualLookup::Virtual;
}
annotation.requiredLookup = requiredLookup;
if (auto const* structType = dynamic_cast<StructType const*>(exprType))
annotation.isLValue = !structType->dataStoredIn(DataLocation::CallData);
else if (exprType->category() == Type::Category::Array)
@ -3076,6 +3085,10 @@ bool TypeChecker::visit(Identifier const& _identifier)
annotation.isConstant = isConstant;
annotation.requiredLookup =
dynamic_cast<CallableDeclaration const*>(annotation.referencedDeclaration) ?
VirtualLookup::Virtual : VirtualLookup::Static;
// Check for deprecated function names.
// The check is done here for the case without an actual function call.
if (FunctionType const* fType = dynamic_cast<FunctionType const*>(_identifier.annotation().type))

View File

@ -560,7 +560,10 @@ public:
ASTPointer<UserDefinedTypeName> _baseName,
std::unique_ptr<std::vector<ASTPointer<Expression>>> _arguments
):
ASTNode(_id, _location), m_baseName(std::move(_baseName)), m_arguments(std::move(_arguments)) {}
ASTNode(_id, _location), m_baseName(std::move(_baseName)), m_arguments(std::move(_arguments))
{
solAssert(m_baseName != nullptr, "Name cannot be null.");
}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -590,7 +593,10 @@ public:
ASTPointer<UserDefinedTypeName> _libraryName,
ASTPointer<TypeName> _typeName
):
ASTNode(_id, _location), m_libraryName(std::move(_libraryName)), m_typeName(std::move(_typeName)) {}
ASTNode(_id, _location), m_libraryName(std::move(_libraryName)), m_typeName(std::move(_typeName))
{
solAssert(m_libraryName != nullptr, "Name cannot be null.");
}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1053,7 +1059,10 @@ public:
ASTPointer<Identifier> _name,
std::unique_ptr<std::vector<ASTPointer<Expression>>> _arguments
):
ASTNode(_id, _location), m_modifierName(std::move(_name)), m_arguments(std::move(_arguments)) {}
ASTNode(_id, _location), m_modifierName(std::move(_name)), m_arguments(std::move(_arguments))
{
solAssert(m_modifierName != nullptr, "Name cannot be null.");
}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1196,6 +1205,7 @@ class UserDefinedTypeName: public TypeName
public:
UserDefinedTypeName(int64_t _id, SourceLocation const& _location, std::vector<ASTString> _namePath):
TypeName(_id, _location), m_namePath(std::move(_namePath)) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;

View File

@ -264,6 +264,8 @@ struct IdentifierAnnotation: ExpressionAnnotation
{
/// Referenced declaration, set at latest during overload resolution stage.
Declaration const* referencedDeclaration = nullptr;
/// What kind of lookup needs to be done (static, virtual, super) find the declaration.
SetOnce<VirtualLookup> requiredLookup;
/// List of possible declarations it could refer to (can contain duplicates).
std::vector<Declaration const*> candidateDeclarations;
/// List of possible declarations it could refer to.
@ -274,6 +276,8 @@ struct MemberAccessAnnotation: ExpressionAnnotation
{
/// Referenced declaration, set at latest during overload resolution stage.
Declaration const* referencedDeclaration = nullptr;
/// What kind of lookup needs to be done (static, virtual, super) find the declaration.
SetOnce<VirtualLookup> requiredLookup;
};
struct BinaryOperationAnnotation: ExpressionAnnotation

View File

@ -30,6 +30,9 @@
namespace solidity::frontend
{
/// Possible lookups for function resolving
enum class VirtualLookup { Static, Virtual, Super };
// How a function can mutate the EVM state.
enum class StateMutability { Pure, View, NonPayable, Payable };

View File

@ -1380,7 +1380,9 @@ private:
bool const m_arbitraryParameters = false;
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
bool const m_bound = false; ///< true iff the function is called as arg1.fun(arg2, ..., argn)
/// true iff the function is called as arg1.fun(arg2, ..., argn).
/// This is achieved through the "using for" directive.
bool const m_bound = false;
Declaration const* m_declaration = nullptr;
bool m_saltSet = false; ///< true iff the salt value to be used is on the stack
};

View File

@ -1299,6 +1299,8 @@ void ContractCompiler::appendModifierOrFunctionCode()
appendModifierOrFunctionCode();
else
{
solAssert(*modifierInvocation->name()->annotation().requiredLookup == VirtualLookup::Virtual, "");
ModifierDefinition const& modifier = dynamic_cast<ModifierDefinition const&>(
*modifierInvocation->name()->annotation().referencedDeclaration
).resolveVirtual(m_context.mostDerivedContract());

View File

@ -595,6 +595,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
// Do not directly visit the identifier, because this way, we can avoid
// the runtime entry label to be created at the creation time context.
CompilerContext::LocationSetter locationSetter2(m_context, *identifier);
solAssert(*identifier->annotation().requiredLookup == VirtualLookup::Virtual, "");
utils().pushCombinedFunctionEntryLabel(
functionDef->resolveVirtual(m_context.mostDerivedContract()),
false
@ -1311,6 +1312,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
if (funType->kind() == FunctionType::Kind::Internal)
{
FunctionDefinition const& funDef = dynamic_cast<decltype(funDef)>(funType->declaration());
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, "");
utils().pushCombinedFunctionEntryLabel(funDef);
utils().moveIntoStack(funType->selfType()->sizeOnStack(), 1);
}
@ -1346,7 +1348,10 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
// internal library function call, this would push the library address forcing
// us to link against it although we actually do not need it.
if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration))
{
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, "");
utils().pushCombinedFunctionEntryLabel(*function);
}
else
solAssert(false, "Function not found in member access");
break;
@ -1460,6 +1465,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
if (type.isSuper())
{
solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved.");
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Super, "");
utils().pushCombinedFunctionEntryLabel(m_context.superFunction(
dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration),
type.contractDefinition()
@ -1742,6 +1748,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
auto const* funDef = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration);
solAssert(funDef && funDef->isFree(), "");
solAssert(funType->kind() == FunctionType::Kind::Internal, "");
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, "");
utils().pushCombinedFunctionEntryLabel(*funDef);
}
break;
@ -1933,11 +1940,14 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
}
}
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
{
// If the identifier is called right away, this code is executed in visit(FunctionCall...), because
// we want to avoid having a reference to the runtime function entry point in the
// constructor context, since this would force the compiler to include unreferenced
// internal functions in the runtime context.
solAssert(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual, "");
utils().pushCombinedFunctionEntryLabel(functionDef->resolveVirtual(m_context.mostDerivedContract()));
}
else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
appendVariable(*variable, static_cast<Expression const&>(_identifier));
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))

View File

@ -839,7 +839,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
solAssert(functionType->declaration() == *functionDef, "");
if (identifier)
{
solAssert(*identifier->annotation().requiredLookup == VirtualLookup::Virtual, "");
functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract());
}
else
{
ContractType const* type = dynamic_cast<ContractType const*>(memberAccess->expression().annotation().type);
@ -847,6 +850,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
{
ContractDefinition const* super = type->contractDefinition().superContract(m_context.mostDerivedContract());
solAssert(super, "Super contract not available.");
solAssert(*memberAccess->annotation().requiredLookup == VirtualLookup::Super, "");
functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract(), super);
}
}
@ -2082,6 +2086,7 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
}
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
{
solAssert(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual, "");
FunctionDefinition const& resolvedFunctionDef = functionDef->resolveVirtual(m_context.mostDerivedContract());
define(_identifier) << to_string(resolvedFunctionDef.id()) << "\n";