mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Move constructor argument override check to TypeChecker and reuse annotations in ContractCompiler.
This commit is contained in:
parent
b8fdb666e2
commit
b918a105a4
@ -90,48 +90,6 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&)
|
|||||||
m_localVarUseCount.clear();
|
m_localVarUseCount.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StaticAnalyzer::visit(ModifierInvocation const& _modifier)
|
|
||||||
{
|
|
||||||
if (!m_constructor)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
bool const v050 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
|
|
||||||
|
|
||||||
if (auto contract = dynamic_cast<ContractDefinition const*>(_modifier.name()->annotation().referencedDeclaration))
|
|
||||||
for (auto const& base: m_currentContract->annotation().linearizedBaseContracts)
|
|
||||||
for (auto const& specifier: base->baseContracts())
|
|
||||||
{
|
|
||||||
Declaration const* parent = specifier->name().annotation().referencedDeclaration;
|
|
||||||
if (contract == parent && !specifier->arguments().empty())
|
|
||||||
{
|
|
||||||
if (v050)
|
|
||||||
{
|
|
||||||
SecondarySourceLocation ssl;
|
|
||||||
ssl.append("Second constructor call is here:", specifier->location());
|
|
||||||
|
|
||||||
m_errorReporter.declarationError(
|
|
||||||
_modifier.location(),
|
|
||||||
ssl,
|
|
||||||
"Duplicated super constructor call."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SecondarySourceLocation ssl;
|
|
||||||
ssl.append("Overridden constructor call is here:", specifier->location());
|
|
||||||
|
|
||||||
m_errorReporter.warning(
|
|
||||||
_modifier.location(),
|
|
||||||
"Duplicated super constructor calls are deprecated.",
|
|
||||||
ssl
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool StaticAnalyzer::visit(Identifier const& _identifier)
|
bool StaticAnalyzer::visit(Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
if (m_currentFunction)
|
if (m_currentFunction)
|
||||||
|
@ -57,7 +57,6 @@ private:
|
|||||||
|
|
||||||
virtual bool visit(FunctionDefinition const& _function) override;
|
virtual bool visit(FunctionDefinition const& _function) override;
|
||||||
virtual void endVisit(FunctionDefinition const& _function) override;
|
virtual void endVisit(FunctionDefinition const& _function) override;
|
||||||
virtual bool visit(ModifierInvocation const& _modifier) override;
|
|
||||||
|
|
||||||
virtual bool visit(ExpressionStatement const& _statement) override;
|
virtual bool visit(ExpressionStatement const& _statement) override;
|
||||||
virtual bool visit(VariableDeclaration const& _variable) override;
|
virtual bool visit(VariableDeclaration const& _variable) override;
|
||||||
|
@ -101,7 +101,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
|
|||||||
checkContractDuplicateEvents(_contract);
|
checkContractDuplicateEvents(_contract);
|
||||||
checkContractIllegalOverrides(_contract);
|
checkContractIllegalOverrides(_contract);
|
||||||
checkContractAbstractFunctions(_contract);
|
checkContractAbstractFunctions(_contract);
|
||||||
checkContractAbstractConstructors(_contract);
|
checkContractBaseConstructorArguments(_contract);
|
||||||
|
|
||||||
FunctionDefinition const* function = _contract.constructor();
|
FunctionDefinition const* function = _contract.constructor();
|
||||||
if (function)
|
if (function)
|
||||||
@ -291,42 +291,75 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _contract)
|
void TypeChecker::checkContractBaseConstructorArguments(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
set<ContractDefinition const*> argumentsNeeded;
|
|
||||||
// check that we get arguments for all base constructors that need it.
|
|
||||||
// If not mark the contract as abstract (not fully implemented)
|
|
||||||
|
|
||||||
vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
|
vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
|
||||||
for (ContractDefinition const* contract: bases)
|
|
||||||
if (FunctionDefinition const* constructor = contract->constructor())
|
|
||||||
if (contract != &_contract && !constructor->parameters().empty())
|
|
||||||
argumentsNeeded.insert(contract);
|
|
||||||
|
|
||||||
|
// Determine the arguments that are used for the base constructors.
|
||||||
for (ContractDefinition const* contract: bases)
|
for (ContractDefinition const* contract: bases)
|
||||||
{
|
{
|
||||||
if (FunctionDefinition const* constructor = contract->constructor())
|
if (FunctionDefinition const* constructor = contract->constructor())
|
||||||
for (auto const& modifier: constructor->modifiers())
|
for (auto const& modifier: constructor->modifiers())
|
||||||
{
|
{
|
||||||
auto baseContract = dynamic_cast<ContractDefinition const*>(
|
auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(*modifier->name()));
|
||||||
&dereference(*modifier->name())
|
if (baseContract && baseContract->constructor() && !modifier->arguments().empty())
|
||||||
);
|
annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get());
|
||||||
if (baseContract)
|
|
||||||
argumentsNeeded.erase(baseContract);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
|
for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
|
||||||
{
|
{
|
||||||
auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(base->name()));
|
auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(base->name()));
|
||||||
solAssert(baseContract, "");
|
solAssert(baseContract, "");
|
||||||
if (base->arguments() && !base->arguments()->empty())
|
|
||||||
argumentsNeeded.erase(baseContract);
|
if (baseContract->constructor() && base->arguments() && !base->arguments()->empty())
|
||||||
|
annotateBaseConstructorArguments(_contract, baseContract->constructor(), base.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!argumentsNeeded.empty())
|
|
||||||
for (ContractDefinition const* contract: argumentsNeeded)
|
// check that we get arguments for all base constructors that need it.
|
||||||
_contract.annotation().unimplementedFunctions.push_back(contract->constructor());
|
// If not mark the contract as abstract (not fully implemented)
|
||||||
|
for (ContractDefinition const* contract: bases)
|
||||||
|
if (FunctionDefinition const* constructor = contract->constructor())
|
||||||
|
if (contract != &_contract && !constructor->parameters().empty())
|
||||||
|
if (!_contract.annotation().baseConstructorArguments.count(constructor))
|
||||||
|
_contract.annotation().unimplementedFunctions.push_back(constructor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TypeChecker::annotateBaseConstructorArguments(
|
||||||
|
ContractDefinition const& _currentContract,
|
||||||
|
FunctionDefinition const* _baseConstructor,
|
||||||
|
ASTNode const* _argumentNode
|
||||||
|
)
|
||||||
|
{
|
||||||
|
bool const v050 = _currentContract.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
|
||||||
|
|
||||||
|
solAssert(_baseConstructor, "");
|
||||||
|
solAssert(_argumentNode, "");
|
||||||
|
|
||||||
|
auto insertionResult = _currentContract.annotation().baseConstructorArguments.insert(
|
||||||
|
std::make_pair(_baseConstructor, _argumentNode)
|
||||||
|
);
|
||||||
|
if (!insertionResult.second)
|
||||||
|
{
|
||||||
|
ASTNode const* previousNode = insertionResult.first->second;
|
||||||
|
|
||||||
|
SecondarySourceLocation ssl;
|
||||||
|
ssl.append("Second constructor call is here:", _argumentNode->location());
|
||||||
|
|
||||||
|
if (v050)
|
||||||
|
m_errorReporter.declarationError(
|
||||||
|
previousNode->location(),
|
||||||
|
ssl,
|
||||||
|
"Base constructor arguments given twice."
|
||||||
|
);
|
||||||
|
else
|
||||||
|
m_errorReporter.warning(
|
||||||
|
previousNode->location(),
|
||||||
|
"Base constructor arguments given twice.",
|
||||||
|
ssl
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract)
|
void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract)
|
||||||
|
@ -73,7 +73,12 @@ private:
|
|||||||
void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super);
|
void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super);
|
||||||
void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message);
|
void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message);
|
||||||
void checkContractAbstractFunctions(ContractDefinition const& _contract);
|
void checkContractAbstractFunctions(ContractDefinition const& _contract);
|
||||||
void checkContractAbstractConstructors(ContractDefinition const& _contract);
|
void checkContractBaseConstructorArguments(ContractDefinition const& _contract);
|
||||||
|
void annotateBaseConstructorArguments(
|
||||||
|
ContractDefinition const& _currentContract,
|
||||||
|
FunctionDefinition const* _baseConstructor,
|
||||||
|
ASTNode const* _argumentNode
|
||||||
|
);
|
||||||
/// Checks that different functions with external visibility end up having different
|
/// Checks that different functions with external visibility end up having different
|
||||||
/// external argument types (i.e. different signature).
|
/// external argument types (i.e. different signature).
|
||||||
void checkContractExternalTypeClashes(ContractDefinition const& _contract);
|
void checkContractExternalTypeClashes(ContractDefinition const& _contract);
|
||||||
|
@ -90,6 +90,9 @@ struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnota
|
|||||||
/// List of contracts this contract creates, i.e. which need to be compiled first.
|
/// List of contracts this contract creates, i.e. which need to be compiled first.
|
||||||
/// Also includes all contracts from @a linearizedBaseContracts.
|
/// Also includes all contracts from @a linearizedBaseContracts.
|
||||||
std::set<ContractDefinition const*> contractDependencies;
|
std::set<ContractDefinition const*> contractDependencies;
|
||||||
|
/// Mapping containing the nodes that define the arguments for base constructors.
|
||||||
|
/// These can either be inheritance specifiers or modifier invocations.
|
||||||
|
std::map<FunctionDefinition const*, ASTNode const*> baseConstructorArguments;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FunctionDefinitionAnnotation: ASTAnnotation, DocumentedAnnotation
|
struct FunctionDefinitionAnnotation: ASTAnnotation, DocumentedAnnotation
|
||||||
|
@ -135,34 +135,13 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c
|
|||||||
{
|
{
|
||||||
solAssert(!_contract.isLibrary(), "Tried to initialize library.");
|
solAssert(!_contract.isLibrary(), "Tried to initialize library.");
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, _contract);
|
CompilerContext::LocationSetter locationSetter(m_context, _contract);
|
||||||
// Determine the arguments that are used for the base constructors.
|
|
||||||
std::vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
|
|
||||||
for (ContractDefinition const* contract: bases)
|
|
||||||
{
|
|
||||||
if (FunctionDefinition const* constructor = contract->constructor())
|
|
||||||
for (auto const& modifier: constructor->modifiers())
|
|
||||||
{
|
|
||||||
auto baseContract = dynamic_cast<ContractDefinition const*>(
|
|
||||||
modifier->name()->annotation().referencedDeclaration
|
|
||||||
);
|
|
||||||
if (baseContract && !modifier->arguments().empty())
|
|
||||||
if (m_baseArguments.count(baseContract->constructor()) == 0)
|
|
||||||
m_baseArguments[baseContract->constructor()] = &modifier->arguments();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
|
m_baseArguments = &_contract.annotation().baseConstructorArguments;
|
||||||
{
|
|
||||||
ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
|
|
||||||
base->name().annotation().referencedDeclaration
|
|
||||||
);
|
|
||||||
solAssert(baseContract, "");
|
|
||||||
|
|
||||||
if (!m_baseArguments.count(baseContract->constructor()) && base->arguments() && !base->arguments()->empty())
|
|
||||||
m_baseArguments[baseContract->constructor()] = base->arguments();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Initialization of state variables in base-to-derived order.
|
// Initialization of state variables in base-to-derived order.
|
||||||
for (ContractDefinition const* contract: boost::adaptors::reverse(bases))
|
for (ContractDefinition const* contract: boost::adaptors::reverse(
|
||||||
|
_contract.annotation().linearizedBaseContracts
|
||||||
|
))
|
||||||
initializeStateVariables(*contract);
|
initializeStateVariables(*contract);
|
||||||
|
|
||||||
if (FunctionDefinition const* constructor = _contract.constructor())
|
if (FunctionDefinition const* constructor = _contract.constructor())
|
||||||
@ -236,8 +215,14 @@ void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _construc
|
|||||||
FunctionType constructorType(_constructor);
|
FunctionType constructorType(_constructor);
|
||||||
if (!constructorType.parameterTypes().empty())
|
if (!constructorType.parameterTypes().empty())
|
||||||
{
|
{
|
||||||
solAssert(m_baseArguments.count(&_constructor), "");
|
solAssert(m_baseArguments, "");
|
||||||
std::vector<ASTPointer<Expression>> const* arguments = m_baseArguments[&_constructor];
|
solAssert(m_baseArguments->count(&_constructor), "");
|
||||||
|
std::vector<ASTPointer<Expression>> const* arguments = nullptr;
|
||||||
|
ASTNode const* baseArgumentNode = m_baseArguments->at(&_constructor);
|
||||||
|
if (auto inheritanceSpecifier = dynamic_cast<InheritanceSpecifier const*>(baseArgumentNode))
|
||||||
|
arguments = inheritanceSpecifier->arguments();
|
||||||
|
else if (auto modifierInvocation = dynamic_cast<ModifierInvocation const*>(baseArgumentNode))
|
||||||
|
arguments = &modifierInvocation->arguments();
|
||||||
solAssert(arguments, "");
|
solAssert(arguments, "");
|
||||||
solAssert(arguments->size() == constructorType.parameterTypes().size(), "");
|
solAssert(arguments->size() == constructorType.parameterTypes().size(), "");
|
||||||
for (unsigned i = 0; i < arguments->size(); ++i)
|
for (unsigned i = 0; i < arguments->size(); ++i)
|
||||||
|
@ -135,7 +135,7 @@ private:
|
|||||||
FunctionDefinition const* m_currentFunction = nullptr;
|
FunctionDefinition const* m_currentFunction = nullptr;
|
||||||
unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag
|
unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag
|
||||||
// arguments for base constructors, filled in derived-to-base order
|
// arguments for base constructors, filled in derived-to-base order
|
||||||
std::map<FunctionDefinition const*, std::vector<ASTPointer<Expression>> const*> m_baseArguments;
|
std::map<FunctionDefinition const*, ASTNode const*> const* m_baseArguments;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,4 +6,4 @@ contract Derived is Base, Base1 {
|
|||||||
constructor(uint i) Base(i) public {}
|
constructor(uint i) Base(i) public {}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// Warning: Duplicated super constructor calls are deprecated.
|
// Warning: Base constructor arguments given twice.
|
||||||
|
@ -2,4 +2,4 @@ contract A { constructor(uint) public { } }
|
|||||||
contract B is A(2) { constructor() public { } }
|
contract B is A(2) { constructor() public { } }
|
||||||
contract C is B { constructor() A(3) public { } }
|
contract C is B { constructor() A(3) public { } }
|
||||||
// ----
|
// ----
|
||||||
// Warning: Duplicated super constructor calls are deprecated.
|
// Warning: Base constructor arguments given twice.
|
@ -4,4 +4,4 @@ contract A { constructor(uint) public { } }
|
|||||||
contract B is A(2) { constructor() public { } }
|
contract B is A(2) { constructor() public { } }
|
||||||
contract C is B { constructor() A(3) public { } }
|
contract C is B { constructor() A(3) public { } }
|
||||||
// ----
|
// ----
|
||||||
// DeclarationError: Duplicated super constructor call.
|
// DeclarationError: Base constructor arguments given twice.
|
@ -1,4 +1,4 @@
|
|||||||
contract A { constructor(uint) public { } }
|
contract A { constructor(uint) public { } }
|
||||||
contract B is A(2) { constructor() A(3) public { } }
|
contract B is A(2) { constructor() A(3) public { } }
|
||||||
// ----
|
// ----
|
||||||
// Warning: Duplicated super constructor calls are deprecated.
|
// Warning: Base constructor arguments given twice.
|
@ -3,4 +3,4 @@ pragma experimental "v0.5.0";
|
|||||||
contract A { constructor(uint) public { } }
|
contract A { constructor(uint) public { } }
|
||||||
contract B is A(2) { constructor() A(3) public { } }
|
contract B is A(2) { constructor() A(3) public { } }
|
||||||
// ----
|
// ----
|
||||||
// DeclarationError: Duplicated super constructor call.
|
// DeclarationError: Base constructor arguments given twice.
|
@ -3,5 +3,5 @@ contract A is C(2) {}
|
|||||||
contract B is C(2) {}
|
contract B is C(2) {}
|
||||||
contract D is A, B { constructor() C(3) public {} }
|
contract D is A, B { constructor() C(3) public {} }
|
||||||
// ----
|
// ----
|
||||||
// Warning: Duplicated super constructor calls are deprecated.
|
// Warning: Base constructor arguments given twice.
|
||||||
// Warning: Duplicated super constructor calls are deprecated.
|
// Warning: Base constructor arguments given twice.
|
@ -0,0 +1,6 @@
|
|||||||
|
contract C { constructor(uint) public {} }
|
||||||
|
contract A is C(2) {}
|
||||||
|
contract B is C(2) {}
|
||||||
|
contract D is A, B {}
|
||||||
|
// ----
|
||||||
|
// Warning: Base constructor arguments given twice.
|
@ -0,0 +1,6 @@
|
|||||||
|
contract C { constructor(uint) public {} }
|
||||||
|
contract A is C { constructor() C(2) public {} }
|
||||||
|
contract B is C { constructor() C(2) public {} }
|
||||||
|
contract D is A, B { }
|
||||||
|
// ----
|
||||||
|
// Warning: Base constructor arguments given twice.
|
Loading…
Reference in New Issue
Block a user