Merge pull request #9208 from ethereum/develop

Merge develop into breaking.
This commit is contained in:
chriseth
2020-06-16 16:11:41 +02:00
committed by GitHub
48 changed files with 822 additions and 411 deletions
+9 -8
View File
@@ -123,11 +123,13 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
return !error;
}
bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsideCode)
bool NameAndTypeResolver::resolveNamesAndTypes(SourceUnit& _source)
{
try
{
return resolveNamesAndTypesInternal(_node, _resolveInsideCode);
for (shared_ptr<ASTNode> const& node: _source.nodes())
if (!resolveNamesAndTypesInternal(*node, true))
return false;
}
catch (langutil::FatalError const&)
{
@@ -135,6 +137,7 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsi
throw; // Something is weird here, rather throw again.
return false;
}
return true;
}
bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
@@ -227,13 +230,14 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
bool success = true;
setScope(contract->scope());
solAssert(!!m_currentScope, "");
solAssert(_resolveInsideCode, "");
m_globalContext.setCurrentContract(*contract);
updateDeclaration(*m_globalContext.currentSuper());
updateDeclaration(*m_globalContext.currentThis());
for (ASTPointer<InheritanceSpecifier> const& baseContract: contract->baseContracts())
if (!resolveNamesAndTypes(*baseContract, true))
if (!resolveNamesAndTypesInternal(*baseContract, true))
success = false;
setScope(contract);
@@ -254,23 +258,20 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
for (ASTPointer<ASTNode> const& node: contract->subNodes())
{
setScope(contract);
if (!resolveNamesAndTypes(*node, false))
if (!resolveNamesAndTypesInternal(*node, false))
success = false;
}
if (!success)
return false;
if (!_resolveInsideCode)
return success;
setScope(contract);
// now resolve references inside the code
for (ASTPointer<ASTNode> const& node: contract->subNodes())
{
setScope(contract);
if (!resolveNamesAndTypes(*node, true))
if (!resolveNamesAndTypesInternal(*node, true))
success = false;
}
return success;
+2 -5
View File
@@ -65,12 +65,9 @@ public:
bool registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope = nullptr);
/// Applies the effect of import directives.
bool performImports(SourceUnit& _sourceUnit, std::map<std::string, SourceUnit const*> const& _sourceUnits);
/// Resolves all names and types referenced from the given AST Node.
/// This is usually only called at the contract level, but with a bit of care, it can also
/// be called at deeper levels.
/// @param _resolveInsideCode if false, does not descend into nodes that contain code.
/// Resolves all names and types referenced from the given Source Node.
/// @returns false in case of error.
bool resolveNamesAndTypes(ASTNode& _node, bool _resolveInsideCode = true);
bool resolveNamesAndTypes(SourceUnit& _source);
/// Updates the given global declaration (used for "this"). Not to be used with declarations
/// that create their own scope.
/// @returns false in case of error.
+46 -38
View File
@@ -62,9 +62,11 @@ bool TypeChecker::typeSupportedByOldABIEncoder(Type const& _type, bool _isLibrar
return true;
}
bool TypeChecker::checkTypeRequirements(ASTNode const& _contract)
bool TypeChecker::checkTypeRequirements(SourceUnit const& _source)
{
_contract.accept(*this);
m_currentSourceUnit = &_source;
_source.accept(*this);
m_currentSourceUnit = nullptr;
return Error::containsOnlyWarnings(m_errorReporter.errors());
}
@@ -375,7 +377,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
}
if (
_function.isPublic() &&
!_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) &&
!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) &&
!typeSupportedByOldABIEncoder(*type(var), _function.libraryFunction())
)
m_errorReporter.typeError(
@@ -513,7 +515,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
else if (_variable.visibility() >= Visibility::Public)
{
FunctionType getter(_variable);
if (!_variable.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2))
if (!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
{
vector<string> unsupportedTypes;
for (auto const& param: getter.parameterTypes() + getter.returnParameterTypes())
@@ -624,7 +626,7 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
if (!type(*var)->interfaceType(false))
m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type.");
if (
!_eventDef.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) &&
!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) &&
!typeSupportedByOldABIEncoder(*type(*var), false /* isLibrary */)
)
m_errorReporter.typeError(
@@ -668,17 +670,18 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
{
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
if (ref == _inlineAssembly.annotation().externalReferences.end())
return numeric_limits<size_t>::max();
Declaration const* declaration = ref->second.declaration;
return false;
InlineAssemblyAnnotation::ExternalIdentifierInfo& identifierInfo = ref->second;
Declaration const* declaration = identifierInfo.declaration;
solAssert(!!declaration, "");
bool requiresStorage = ref->second.isSlot || ref->second.isOffset;
bool requiresStorage = identifierInfo.isSlot || identifierInfo.isOffset;
if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
{
solAssert(var->type(), "Expected variable type!");
if (var->immutable())
{
m_errorReporter.typeError(3773_error, _identifier.location, "Assembly access to immutable variables is not supported.");
return numeric_limits<size_t>::max();
return false;
}
if (var->isConstant())
{
@@ -687,17 +690,17 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
if (var && !var->value())
{
m_errorReporter.typeError(3224_error, _identifier.location, "Constant has no value.");
return numeric_limits<size_t>::max();
return false;
}
else if (_context == yul::IdentifierContext::LValue)
{
m_errorReporter.typeError(6252_error, _identifier.location, "Constant variables cannot be assigned to.");
return numeric_limits<size_t>::max();
return false;
}
else if (requiresStorage)
{
m_errorReporter.typeError(6617_error, _identifier.location, "The suffixes _offset and _slot can only be used on non-constant storage variables.");
return numeric_limits<size_t>::max();
return false;
}
else if (var && var->value() && !var->value()->annotation().type && !dynamic_cast<Literal const*>(var->value().get()))
{
@@ -706,7 +709,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
_identifier.location,
"Constant variables with non-literal values cannot be forward referenced from inline assembly."
);
return size_t(-1);
return false;
}
else if (!var || !type(*var)->isValueType() || (
!dynamic_cast<Literal const*>(var->value().get()) &&
@@ -714,7 +717,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
))
{
m_errorReporter.typeError(7615_error, _identifier.location, "Only direct number constants and references to such constants are supported by inline assembly.");
return size_t(-1);
return false;
}
}
@@ -725,33 +728,33 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage))
{
m_errorReporter.typeError(3622_error, _identifier.location, "The suffixes _offset and _slot can only be used on storage variables.");
return numeric_limits<size_t>::max();
return false;
}
else if (_context == yul::IdentifierContext::LValue)
{
if (var->isStateVariable())
{
m_errorReporter.typeError(4713_error, _identifier.location, "State variables cannot be assigned to - you have to use \"sstore()\".");
return numeric_limits<size_t>::max();
return false;
}
else if (ref->second.isOffset)
else if (identifierInfo.isOffset)
{
m_errorReporter.typeError(9739_error, _identifier.location, "Only _slot can be assigned to.");
return numeric_limits<size_t>::max();
return false;
}
else
solAssert(ref->second.isSlot, "");
solAssert(identifierInfo.isSlot, "");
}
}
else if (!var->isConstant() && var->isStateVariable())
{
m_errorReporter.typeError(1408_error, _identifier.location, "Only local variables are supported. To access storage variables, use the _slot and _offset suffixes.");
return numeric_limits<size_t>::max();
return false;
}
else if (var->type()->dataStoredIn(DataLocation::Storage))
{
m_errorReporter.typeError(9068_error, _identifier.location, "You have to use the _slot or _offset suffix to access storage reference variables.");
return numeric_limits<size_t>::max();
return false;
}
else if (var->type()->sizeOnStack() != 1)
{
@@ -759,21 +762,21 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
m_errorReporter.typeError(2370_error, _identifier.location, "Call data elements cannot be accessed directly. Copy to a local variable first or use \"calldataload\" or \"calldatacopy\" with manually determined offsets and sizes.");
else
m_errorReporter.typeError(9857_error, _identifier.location, "Only types that use one stack slot are supported.");
return numeric_limits<size_t>::max();
return false;
}
}
else if (requiresStorage)
{
m_errorReporter.typeError(7944_error, _identifier.location, "The suffixes _offset and _slot can only be used on storage variables.");
return numeric_limits<size_t>::max();
return false;
}
else if (_context == yul::IdentifierContext::LValue)
{
if (dynamic_cast<MagicVariableDeclaration const*>(declaration))
return numeric_limits<size_t>::max();
return false;
m_errorReporter.typeError(1990_error, _identifier.location, "Only local variables can be assigned to in inline assembly.");
return numeric_limits<size_t>::max();
return false;
}
if (_context == yul::IdentifierContext::RValue)
@@ -782,7 +785,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
if (dynamic_cast<FunctionDefinition const*>(declaration))
{
m_errorReporter.declarationError(2025_error, _identifier.location, "Access to functions is not allowed in inline assembly.");
return numeric_limits<size_t>::max();
return false;
}
else if (dynamic_cast<VariableDeclaration const*>(declaration))
{
@@ -792,14 +795,14 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
if (!contract->isLibrary())
{
m_errorReporter.typeError(4977_error, _identifier.location, "Expected a library.");
return numeric_limits<size_t>::max();
return false;
}
}
else
return numeric_limits<size_t>::max();
return false;
}
ref->second.valueSize = 1;
return size_t(1);
identifierInfo.valueSize = 1;
return true;
};
solAssert(!_inlineAssembly.annotation().analysisInfo, "");
_inlineAssembly.annotation().analysisInfo = make_shared<yul::AsmAnalysisInfo>();
@@ -1914,9 +1917,7 @@ void TypeChecker::typeCheckABIEncodeFunctions(
bool const isPacked = _functionType->kind() == FunctionType::Kind::ABIEncodePacked;
solAssert(_functionType->padArguments() != isPacked, "ABI function with unexpected padding");
bool const abiEncoderV2 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(
ExperimentalFeature::ABIEncoderV2
);
bool const abiEncoderV2 = experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2);
// Check for named arguments
if (!_functionCall.names().empty())
@@ -2313,11 +2314,10 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
{
case FunctionType::Kind::ABIDecode:
{
bool const abiEncoderV2 =
m_currentContract->sourceUnit().annotation().experimentalFeatures.count(
ExperimentalFeature::ABIEncoderV2
);
returnTypes = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2);
returnTypes = typeCheckABIDecodeAndRetrieveReturnType(
_functionCall,
experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)
);
break;
}
case FunctionType::Kind::ABIEncode:
@@ -3272,3 +3272,11 @@ void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAss
m_errorReporter.typeError(errorId, _expression.location(), description);
}
bool TypeChecker::experimentalFeatureActive(ExperimentalFeature _feature) const
{
solAssert(m_currentSourceUnit, "");
if (m_currentContract)
solAssert(m_currentSourceUnit == &m_currentContract->sourceUnit(), "");
return m_currentSourceUnit->annotation().experimentalFeatures.count(_feature);
}
+5 -2
View File
@@ -51,9 +51,9 @@ public:
m_errorReporter(_errorReporter)
{}
/// Performs type checking on the given contract and all of its sub-nodes.
/// Performs type checking on the given source and all of its sub-nodes.
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
bool checkTypeRequirements(ASTNode const& _contract);
bool checkTypeRequirements(SourceUnit const& _source);
/// @returns the type of an expression and asserts that it is present.
TypePointer const& type(Expression const& _expression) const;
@@ -165,6 +165,9 @@ private:
/// Runs type checks on @a _expression to infer its type and then checks that it is an LValue.
void requireLValue(Expression const& _expression, bool _ordinaryAssignment);
bool experimentalFeatureActive(ExperimentalFeature _feature) const;
SourceUnit const* m_currentSourceUnit = nullptr;
ContractDefinition const* m_currentContract = nullptr;
langutil::EVMVersion m_evmVersion;