Browse Source

implement overload resolution

cl-refactor
Lu Guanqun 10 years ago
parent
commit
17f79a5c6e
  1. 159
      libsolidity/AST.cpp
  2. 15
      libsolidity/AST.h
  3. 4
      libsolidity/CompilerContext.cpp
  4. 6
      libsolidity/ExpressionCompiler.cpp
  5. 39
      libsolidity/NameAndTypeResolver.cpp
  6. 4
      libsolidity/NameAndTypeResolver.h
  7. 7
      libsolidity/Parser.cpp
  8. 15
      libsolidity/Types.h

159
libsolidity/AST.cpp

@ -76,6 +76,15 @@ void ContractDefinition::checkTypeRequirements()
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
function->checkTypeRequirements();
// check for duplicate declaration
set<string> functions;
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
{
string signature = function->getCanonicalSignature();
if (functions.count(signature))
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Duplicate functions are not allowed."));
functions.insert(signature);
}
for (ASTPointer<VariableDeclaration> const& variable: m_stateVariables)
variable->checkTypeRequirements();
@ -129,6 +138,7 @@ void ContractDefinition::checkIllegalOverrides() const
// TODO unify this at a later point. for this we need to put the constness and the access specifier
// into the types
map<string, FunctionDefinition const*> functions;
set<string> functionNames;
map<string, ModifierDefinition const*> modifiers;
// We search from derived to base, so the stored item causes the error.
@ -141,7 +151,8 @@ void ContractDefinition::checkIllegalOverrides() const
string const& name = function->getName();
if (modifiers.count(name))
BOOST_THROW_EXCEPTION(modifiers[name]->createTypeError("Override changes function to modifier."));
FunctionDefinition const*& override = functions[name];
FunctionDefinition const*& override = functions[function->getCanonicalSignature()];
functionNames.insert(name);
if (!override)
override = function.get();
else if (override->getVisibility() != function->getVisibility() ||
@ -152,13 +163,13 @@ void ContractDefinition::checkIllegalOverrides() const
for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers())
{
string const& name = modifier->getName();
if (functions.count(name))
BOOST_THROW_EXCEPTION(functions[name]->createTypeError("Override changes modifier to function."));
ModifierDefinition const*& override = modifiers[name];
if (!override)
override = modifier.get();
else if (ModifierType(*override) != ModifierType(*modifier))
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier signature."));
if (functionNames.count(name))
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier to function."));
}
}
}
@ -185,16 +196,21 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getIn
if (!m_interfaceFunctionList)
{
set<string> functionsSeen;
set<string> signaturesSeen;
m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>());
for (ContractDefinition const* contract: getLinearizedBaseContracts())
{
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
if (f->isPublic() && !f->isConstructor() && !f->getName().empty() && functionsSeen.count(f->getName()) == 0)
{
string functionSignature = f->getCanonicalSignature();
if (f->isPublic() && !f->isConstructor() && !f->getName().empty() && signaturesSeen.count(functionSignature) == 0)
{
functionsSeen.insert(f->getName());
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
signaturesSeen.insert(functionSignature);
FixedHash<4> hash(dev::sha3(functionSignature));
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*f, false)));
}
}
for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables())
if (v->isPublic() && functionsSeen.count(v->getName()) == 0)
@ -467,7 +483,43 @@ void Return::checkTypeRequirements()
void VariableDeclarationStatement::checkTypeRequirements()
{
<<<<<<< HEAD
m_variable->checkTypeRequirements();
=======
// Variables can be declared without type (with "var"), in which case the first assignment
// sets the type.
// Note that assignments before the first declaration are legal because of the special scoping
// rules inherited from JavaScript.
if (m_variable->getValue())
{
if (m_variable->getType())
{
std::cout << "getType() ok" << std::endl;
m_variable->getValue()->expectType(*m_variable->getType());
}
else
{
// no type declared and no previous assignment, infer the type
std::cout << "here's where called...." << std::endl;
Identifier* identifier = dynamic_cast<Identifier*>(m_variable->getValue().get());
if (identifier)
identifier->checkTypeRequirementsFromVariableDeclaration();
else
m_variable->getValue()->checkTypeRequirements();
TypePointer type = m_variable->getValue()->getType();
if (type->getCategory() == Type::Category::IntegerConstant)
{
auto intType = dynamic_pointer_cast<IntegerConstantType const>(type)->getIntegerType();
if (!intType)
BOOST_THROW_EXCEPTION(m_variable->getValue()->createTypeError("Invalid integer constant " + type->toString()));
type = intType;
}
else if (type->getCategory() == Type::Category::Void)
BOOST_THROW_EXCEPTION(m_variable->createTypeError("var cannot be void type"));
m_variable->setType(type);
}
}
>>>>>>> implement overload resolution
}
void Assignment::checkTypeRequirements()
@ -544,10 +596,16 @@ void BinaryOperation::checkTypeRequirements()
void FunctionCall::checkTypeRequirements()
{
m_expression->checkTypeRequirements();
// we need to check arguments' type first as their info will be used by m_express(Identifier).
for (ASTPointer<Expression> const& argument: m_arguments)
argument->checkTypeRequirements();
auto identifier = dynamic_cast<Identifier*>(m_expression.get());
if (identifier)
identifier->checkTypeRequirementsWithFunctionCall(*this);
else
m_expression->checkTypeRequirements();
Type const* expressionType = m_expression->getType().get();
if (isTypeConversion())
{
@ -617,6 +675,19 @@ void FunctionCall::checkTypeRequirements()
else
m_type = functionType->getReturnParameterTypes().front();
}
else if (OverloadedFunctionType const* overloadedTypes = dynamic_cast<OverloadedFunctionType const*>(expressionType))
{
// this only applies to "x(3)" where x is assigned by "var x = f;" where f is an overloaded functions.
overloadedTypes->m_identifier->overloadResolution(*this);
FunctionType const* functionType = dynamic_cast<FunctionType const*>(overloadedTypes->m_identifier->getType().get());
// @todo actually the return type should be an anonymous struct,
// but we change it to the type of the first return value until we have structs
if (functionType->getReturnParameterTypes().empty())
m_type = make_shared<VoidType>();
else
m_type = functionType->getReturnParameterTypes().front();
}
else
BOOST_THROW_EXCEPTION(createTypeError("Type is not callable."));
}
@ -709,16 +780,92 @@ void IndexAccess::checkTypeRequirements()
}
}
void Identifier::checkTypeRequirementsWithFunctionCall(FunctionCall const& _functionCall)
{
solAssert(m_referencedDeclaration || !m_overloadedDeclarations.empty(), "Identifier not resolved.");
if (!m_referencedDeclaration)
overloadResolution(_functionCall);
checkTypeRequirements();
}
void Identifier::checkTypeRequirementsFromVariableDeclaration()
{
solAssert(m_referencedDeclaration || !m_overloadedDeclarations.empty(), "Identifier not resolved.");
if (!m_referencedDeclaration)
m_type = make_shared<OverloadedFunctionType>(m_overloadedDeclarations, this);
else
checkTypeRequirements();
m_isLValue = true;
}
void Identifier::checkTypeRequirements()
{
// var x = f; TODO!
solAssert(m_referencedDeclaration, "Identifier not resolved.");
m_isLValue = m_referencedDeclaration->isLValue();
if (m_isLValue)
std::cout << "Identifier: " << string(getName()) << " -> true" << std::endl;
else
std::cout << "Identifier: " << string(getName()) << " -> true" << std::endl;
m_type = m_referencedDeclaration->getType(m_currentContract);
if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined."));
}
void Identifier::overloadResolution(FunctionCall const& _functionCall)
{
solAssert(m_overloadedDeclarations.size() > 1, "FunctionIdentifier not resolved.");
solAssert(!m_referencedDeclaration, "Referenced declaration should be null before overload resolution.");
bool resolved = false;
std::vector<ASTPointer<Expression const>> arguments = _functionCall.getArguments();
std::vector<ASTPointer<ASTString>> const& argumentNames = _functionCall.getNames();
if (argumentNames.empty())
{
// positional arguments
std::vector<Declaration const*> possibles;
for (Declaration const* declaration: m_overloadedDeclarations)
{
TypePointer const& function = declaration->getType();
auto const& functionType = dynamic_cast<FunctionType const&>(*function);
TypePointers const& parameterTypes = functionType.getParameterTypes();
if (functionType.takesArbitraryParameters() ||
(arguments.size() == parameterTypes.size() &&
std::equal(arguments.cbegin(), arguments.cend(), parameterTypes.cbegin(),
[](ASTPointer<Expression const> const& argument, TypePointer const& parameterType)
{
return argument->getType()->isImplicitlyConvertibleTo(*parameterType);
})))
possibles.push_back(declaration);
}
std::cout << "possibles: " << possibles.size() << std::endl;
if (possibles.empty())
BOOST_THROW_EXCEPTION(createTypeError("Can't resolve identifier"));
else if (std::none_of(possibles.cbegin() + 1, possibles.cend(),
[&possibles](Declaration const* declaration)
{
return declaration->getScope() == possibles.front()->getScope();
}))
setReferencedDeclaration(*possibles.front());
else
BOOST_THROW_EXCEPTION(createTypeError("Can't resolve identifier"));
}
else
{
// named arguments
// TODO: don't support right now
// BOOST_THROW_EXCEPTION(createTypeError("Named arguments with overloaded functions are not supported yet."));
}
}
void ElementaryTypeNameExpression::checkTypeRequirements()
{
m_type = make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken));

15
libsolidity/AST.h

@ -1134,8 +1134,8 @@ public:
class Identifier: public PrimaryExpression
{
public:
Identifier(SourceLocation const& _location, ASTPointer<ASTString> const& _name, bool _isCallable):
PrimaryExpression(_location), m_name(_name), m_isCallable(_isCallable) {}
Identifier(SourceLocation const& _location, ASTPointer<ASTString> const& _name):
PrimaryExpression(_location), m_name(_name) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
@ -1151,9 +1151,15 @@ public:
Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; }
ContractDefinition const* getCurrentContract() const { return m_currentContract; }
bool isCallable() const { return m_isCallable; }
void setOverloadedDeclarations(std::set<Declaration const*> const& _declarations) { m_overloadedDeclarations = _declarations; }
std::set<Declaration const*> getOverloadedDeclarations() const { return m_overloadedDeclarations; }
void checkTypeRequirementsWithFunctionCall(FunctionCall const& _functionCall);
void checkTypeRequirementsFromVariableDeclaration();
void overloadResolution(FunctionCall const& _functionCall);
private:
ASTPointer<ASTString> m_name;
/// Declaration the name refers to.
@ -1161,7 +1167,8 @@ private:
/// Stores a reference to the current contract. This is needed because types of base contracts
/// change depending on the context.
ContractDefinition const* m_currentContract = nullptr;
bool m_isCallable = false;
/// A set of overloaded declarations, right now only FunctionDefinition has overloaded declarations.
std::set<Declaration const*> m_overloadedDeclarations;
};
/**

4
libsolidity/CompilerContext.cpp

@ -108,8 +108,8 @@ eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefiniti
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
{
if (!function->isConstructor() &&
dynamic_cast<FunctionType const&>(*function->getType()).getCanonicalSignature() ==
dynamic_cast<FunctionType const&>(*_function.getType()).getCanonicalSignature())
dynamic_cast<FunctionType const&>(*function->getType(contract)).getCanonicalSignature() ==
dynamic_cast<FunctionType const&>(*_function.getType(contract)).getCanonicalSignature())
return getFunctionEntryLabel(*function);
}
solAssert(false, "Virtual function " + _function.getName() + " not found.");

6
libsolidity/ExpressionCompiler.cpp

@ -822,7 +822,11 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
void ExpressionCompiler::endVisit(Identifier const& _identifier)
{
Declaration const* declaration = _identifier.getReferencedDeclaration();
if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
if (declaration == nullptr)
{
// no-op
}
else if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
{
if (magicVar->getType()->getCategory() == Type::Category::Contract)
// "this" or "super"

39
libsolidity/NameAndTypeResolver.cpp

@ -90,15 +90,15 @@ void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
solAssert(_declaration.getScope() == nullptr, "Updated declaration outside global scope.");
}
Declaration const* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const
std::set<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const
{
auto iterator = m_scopes.find(_scope);
if (iterator == end(m_scopes))
return nullptr;
return std::set<Declaration const*>({});
return iterator->second.resolveName(_name, false);
}
Declaration const* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive)
std::set<Declaration const*> NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive)
{
return m_currentScope->resolveName(_name, _recursive);
}
@ -108,13 +108,11 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
auto iterator = m_scopes.find(&_base);
solAssert(iterator != end(m_scopes), "");
for (auto const& nameAndDeclaration: iterator->second.getDeclarations())
{
Declaration const* declaration = nameAndDeclaration.second;
// Import if it was declared in the base, is not the constructor and is visible in derived classes
if (declaration->getScope() == &_base && declaration->getName() != _base.getName() &&
declaration->isVisibleInDerivedContracts())
m_currentScope->registerDeclaration(*declaration);
}
for (auto const& declaration: nameAndDeclaration.second)
// Import if it was declared in the base, is not the constructor and is visible in derived classes
if (declaration->getScope() == &_base && declaration->getName() != _base.getName() &&
declaration->isVisibleInDerivedContracts())
m_currentScope->registerDeclaration(*declaration);
}
void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) const
@ -361,24 +359,31 @@ bool ReferencesResolver::visit(Mapping&)
bool ReferencesResolver::visit(UserDefinedTypeName& _typeName)
{
Declaration const* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName());
if (!declaration)
auto declarations = m_resolver.getNameFromCurrentScope(_typeName.getName());
if (declarations.empty())
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation())
<< errinfo_comment("Undeclared identifier."));
_typeName.setReferencedDeclaration(*declaration);
else if (declarations.size() > 1)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation())
<< errinfo_comment("Duplicate identifier."));
else
_typeName.setReferencedDeclaration(**declarations.begin());
return false;
}
bool ReferencesResolver::visit(Identifier& _identifier)
{
Declaration const* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName());
if (!declaration)
auto declarations = m_resolver.getNameFromCurrentScope(_identifier.getName());
if (declarations.empty())
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation())
<< errinfo_comment("Undeclared identifier."));
_identifier.setReferencedDeclaration(*declaration, m_currentContract);
else if (declarations.size() == 1)
_identifier.setReferencedDeclaration(**declarations.begin(), m_currentContract);
else
// Duplicate declaration will be checked in checkTypeRequirements()
_identifier.setOverloadedDeclarations(declarations);
return false;
}
}
}

4
libsolidity/NameAndTypeResolver.h

@ -56,11 +56,11 @@ public:
/// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,
/// the global scope is used (i.e. the one containing only the contract).
/// @returns a pointer to the declaration on success or nullptr on failure.
Declaration const* resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const;
std::set<Declaration const*> resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const;
/// Resolves a name in the "current" scope. Should only be called during the initial
/// resolving phase.
Declaration const* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true);
std::set<Declaration const*> getNameFromCurrentScope(ASTString const& _name, bool _recursive = true);
private:
void reset();

7
libsolidity/Parser.cpp

@ -837,14 +837,9 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
break;
case Token::Identifier:
{
nodeFactory.markEndPosition();
// if the next token is '(', this identifier looks like function call,
// it could be a contract, event etc.
bool isCallable = m_scanner->peekNextToken() == Token::LParen;
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance(), isCallable);
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
break;
}
case Token::LParen:
{
m_scanner->next();

15
libsolidity/Types.h

@ -77,7 +77,7 @@ public:
enum class Category
{
Integer, IntegerConstant, Bool, Real, Array,
String, Contract, Struct, Function, Enum,
String, Contract, Struct, Function, OverloadedFunctions, Enum,
Mapping, Void, TypeType, Modifier, Magic
};
@ -524,6 +524,19 @@ private:
Declaration const* m_declaration = nullptr;
};
class OverloadedFunctionType: public Type
{
public:
explicit OverloadedFunctionType(std::set<Declaration const*> const& _overloadedDeclarations, Identifier* _identifier):
m_overloadedDeclarations(_overloadedDeclarations), m_identifier(_identifier) {}
virtual Category getCategory() const override { return Category::OverloadedFunctions; }
virtual std::string toString() const override { return "OverloadedFunctions"; }
// private:
std::set<Declaration const*> m_overloadedDeclarations;
Identifier * m_identifier;
};
/**
* The type of a mapping, there is one distinct type per key/value type pair.
*/

Loading…
Cancel
Save