Support for abstract contracts.

This commit is contained in:
Alexander Arlt 2019-09-04 17:01:13 -05:00
parent 106d8ec3b6
commit 62950a9234
7 changed files with 39 additions and 22 deletions

View File

@ -391,6 +391,19 @@ void ContractLevelChecker::checkAbstractFunctions(ContractDefinition const& _con
_contract.annotation().unimplementedFunctions.push_back(function);
break;
}
if (_contract.abstract() && _contract.contractKind() != ContractDefinition::ContractKind::Contract)
m_errorReporter.typeError(_contract.location(), "Only contracts can be abstract.");
if (
_contract.contractKind() == ContractDefinition::ContractKind::Contract &&
!_contract.annotation().unimplementedFunctions.empty() &&
!_contract.abstract()
)
m_errorReporter.typeError(
_contract.location(),
"Contract \"" + _contract.annotation().canonicalName + "\" should be marked as abstract."
);
}

View File

@ -88,7 +88,6 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
for (auto const& n: _contract.subNodes())
n->accept(*this);
return false;
}
@ -2152,21 +2151,10 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract.");
if (contract->isInterface())
m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface.");
if (!contract->annotation().unimplementedFunctions.empty())
{
SecondarySourceLocation ssl;
for (auto function: contract->annotation().unimplementedFunctions)
ssl.append("Missing implementation:", function->location());
string msg = "Trying to create an instance of an abstract contract.";
ssl.limitSize(msg);
m_errorReporter.typeError(
_newExpression.location(),
ssl,
msg
);
}
if (!contract->constructorIsPublic())
m_errorReporter.typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly.");
if (contract->abstract() || !contract->annotation().unimplementedFunctions.empty())
m_errorReporter.typeError(_newExpression.location(), "Cannot instantiate an abstract contract.");
solAssert(!!m_scope, "");
m_scope->annotation().contractDependencies.insert(contract);

View File

@ -151,7 +151,7 @@ bool ContractDefinition::constructorIsPublic() const
bool ContractDefinition::canBeDeployed() const
{
return constructorIsPublic() && annotation().unimplementedFunctions.empty();
return constructorIsPublic() && annotation().unimplementedFunctions.empty() && !abstract();
}
FunctionDefinition const* ContractDefinition::fallbackFunction() const

View File

@ -388,13 +388,15 @@ public:
ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<ASTNode>> const& _subNodes,
ContractKind _contractKind = ContractKind::Contract
ContractKind _contractKind = ContractKind::Contract,
bool _abstract = false
):
Declaration(_location, _name),
Documented(_documentation),
m_baseContracts(_baseContracts),
m_subNodes(_subNodes),
m_contractKind(_contractKind)
m_contractKind(_contractKind),
m_abstract(_abstract)
{}
void accept(ASTVisitor& _visitor) override;
@ -441,10 +443,13 @@ public:
ContractKind contractKind() const { return m_contractKind; }
bool abstract() const { return m_abstract; }
private:
std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts;
std::vector<ASTPointer<ASTNode>> m_subNodes;
ContractKind m_contractKind;
bool m_abstract{false};
mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList;
mutable std::unique_ptr<std::vector<EventDefinition const*>> m_interfaceEvents;

View File

@ -260,6 +260,7 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node)
make_pair("name", _node.name()),
make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue),
make_pair("contractKind", contractKind(_node.contractKind())),
make_pair("abstract", _node.abstract()),
make_pair("fullyImplemented", _node.annotation().unimplementedFunctions.empty()),
make_pair("linearizedBaseContracts", getContainerIds(_node.annotation().linearizedBaseContracts)),
make_pair("baseContracts", toJson(_node.baseContracts())),

View File

@ -91,6 +91,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
case Token::Import:
nodes.push_back(parseImportDirective());
break;
case Token::Abstract:
case Token::Interface:
case Token::Contract:
case Token::Library:
@ -244,9 +245,15 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
return nodeFactory.createNode<ImportDirective>(path, unitAlias, move(symbolAliases));
}
ContractDefinition::ContractKind Parser::parseContractKind()
std::pair<ContractDefinition::ContractKind, bool> Parser::parseContractKind()
{
ContractDefinition::ContractKind kind;
bool abstract = false;
if (m_scanner->currentToken() == Token::Abstract)
{
abstract = true;
m_scanner->next();
}
switch(m_scanner->currentToken())
{
case Token::Interface:
@ -262,7 +269,7 @@ ContractDefinition::ContractKind Parser::parseContractKind()
solAssert(false, "Invalid contract kind.");
}
m_scanner->next();
return kind;
return std::make_pair(kind, abstract);
}
ASTPointer<ContractDefinition> Parser::parseContractDefinition()
@ -273,7 +280,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
ASTPointer<ASTString> docString;
vector<ASTPointer<InheritanceSpecifier>> baseContracts;
vector<ASTPointer<ASTNode>> subNodes;
ContractDefinition::ContractKind contractKind = ContractDefinition::ContractKind::Contract;
std::pair<ContractDefinition::ContractKind, bool> contractKind{};
try
{
if (m_scanner->currentCommentLiteral() != "")
@ -343,7 +350,8 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
docString,
baseContracts,
subNodes,
contractKind
contractKind.first,
contractKind.second
);
}

View File

@ -85,7 +85,9 @@ private:
void parsePragmaVersion(langutil::SourceLocation const& _location, std::vector<Token> const& _tokens, std::vector<std::string> const& _literals);
ASTPointer<PragmaDirective> parsePragmaDirective();
ASTPointer<ImportDirective> parseImportDirective();
ContractDefinition::ContractKind parseContractKind();
/// @returns an std::pair<ContractDefinition::ContractKind, bool>, where
/// result.second is set to true, if an abstract contract was parsed, false otherwise.
std::pair<ContractDefinition::ContractKind, bool> parseContractKind();
ASTPointer<ContractDefinition> parseContractDefinition();
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
Declaration::Visibility parseVisibilitySpecifier();