mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #9208 from ethereum/develop
Merge develop into breaking.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
+63
-45
@@ -311,10 +311,15 @@ TypePointer Type::commonType(Type const* _a, Type const* _b)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MemberList const& Type::members(ContractDefinition const* _currentScope) const
|
||||
MemberList const& Type::members(ASTNode const* _currentScope) const
|
||||
{
|
||||
if (!m_members[_currentScope])
|
||||
{
|
||||
solAssert(
|
||||
_currentScope == nullptr ||
|
||||
dynamic_cast<SourceUnit const*>(_currentScope) ||
|
||||
dynamic_cast<ContractDefinition const*>(_currentScope),
|
||||
"");
|
||||
MemberList::MemberMap members = nativeMembers(_currentScope);
|
||||
if (_currentScope)
|
||||
members += boundFunctions(*this, *_currentScope);
|
||||
@@ -344,8 +349,20 @@ TypePointer Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool) c
|
||||
return encodingType;
|
||||
}
|
||||
|
||||
MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition const& _scope)
|
||||
MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _scope)
|
||||
{
|
||||
vector<UsingForDirective const*> usingForDirectives;
|
||||
if (auto const* sourceUnit = dynamic_cast<SourceUnit const*>(&_scope))
|
||||
usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(sourceUnit->nodes());
|
||||
else if (auto const* contract = dynamic_cast<ContractDefinition const*>(&_scope))
|
||||
{
|
||||
for (ContractDefinition const* contract: contract->annotation().linearizedBaseContracts)
|
||||
usingForDirectives += contract->usingForDirectives();
|
||||
usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(contract->sourceUnit().nodes());
|
||||
}
|
||||
else
|
||||
solAssert(false, "");
|
||||
|
||||
// Normalise data location of type.
|
||||
DataLocation typeLocation = DataLocation::Storage;
|
||||
if (auto refType = dynamic_cast<ReferenceType const*>(&_type))
|
||||
@@ -353,38 +370,39 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition
|
||||
|
||||
set<Declaration const*> seenFunctions;
|
||||
MemberList::MemberMap members;
|
||||
for (ContractDefinition const* contract: _scope.annotation().linearizedBaseContracts)
|
||||
for (UsingForDirective const* ufd: contract->usingForDirectives())
|
||||
{
|
||||
// Convert both types to pointers for comparison to see if the `using for`
|
||||
// directive applies.
|
||||
// Further down, we check more detailed for each function if `_type` is
|
||||
// convertible to the function parameter type.
|
||||
if (ufd->typeName() &&
|
||||
*TypeProvider::withLocationIfReference(typeLocation, &_type, true) !=
|
||||
*TypeProvider::withLocationIfReference(
|
||||
typeLocation,
|
||||
ufd->typeName()->annotation().type,
|
||||
true
|
||||
)
|
||||
|
||||
for (UsingForDirective const* ufd: usingForDirectives)
|
||||
{
|
||||
// Convert both types to pointers for comparison to see if the `using for`
|
||||
// directive applies.
|
||||
// Further down, we check more detailed for each function if `_type` is
|
||||
// convertible to the function parameter type.
|
||||
if (ufd->typeName() &&
|
||||
*TypeProvider::withLocationIfReference(typeLocation, &_type, true) !=
|
||||
*TypeProvider::withLocationIfReference(
|
||||
typeLocation,
|
||||
ufd->typeName()->annotation().type,
|
||||
true
|
||||
)
|
||||
)
|
||||
continue;
|
||||
auto const& library = dynamic_cast<ContractDefinition const&>(
|
||||
*ufd->libraryName().annotation().referencedDeclaration
|
||||
);
|
||||
for (FunctionDefinition const* function: library.definedFunctions())
|
||||
{
|
||||
if (!function->isVisibleAsLibraryMember() || seenFunctions.count(function))
|
||||
continue;
|
||||
auto const& library = dynamic_cast<ContractDefinition const&>(
|
||||
*ufd->libraryName().annotation().referencedDeclaration
|
||||
);
|
||||
for (FunctionDefinition const* function: library.definedFunctions())
|
||||
{
|
||||
if (!function->isVisibleAsLibraryMember() || seenFunctions.count(function))
|
||||
continue;
|
||||
seenFunctions.insert(function);
|
||||
if (function->parameters().empty())
|
||||
continue;
|
||||
FunctionTypePointer fun =
|
||||
dynamic_cast<FunctionType const&>(*function->typeViaContractName()).asBoundFunction();
|
||||
if (_type.isImplicitlyConvertibleTo(*fun->selfType()))
|
||||
members.emplace_back(function->name(), fun, function);
|
||||
}
|
||||
seenFunctions.insert(function);
|
||||
if (function->parameters().empty())
|
||||
continue;
|
||||
FunctionTypePointer fun =
|
||||
dynamic_cast<FunctionType const&>(*function->typeViaContractName()).asBoundFunction();
|
||||
if (_type.isImplicitlyConvertibleTo(*fun->selfType()))
|
||||
members.emplace_back(function->name(), fun, function);
|
||||
}
|
||||
}
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
@@ -464,7 +482,7 @@ bool AddressType::operator==(Type const& _other) const
|
||||
return other.m_stateMutability == m_stateMutability;
|
||||
}
|
||||
|
||||
MemberList::MemberMap AddressType::nativeMembers(ContractDefinition const*) const
|
||||
MemberList::MemberMap AddressType::nativeMembers(ASTNode const*) const
|
||||
{
|
||||
MemberList::MemberMap members = {
|
||||
{"balance", TypeProvider::uint256()},
|
||||
@@ -1428,7 +1446,7 @@ TypeResult FixedBytesType::binaryOperatorResult(Token _operator, Type const* _ot
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MemberList::MemberMap FixedBytesType::nativeMembers(ContractDefinition const*) const
|
||||
MemberList::MemberMap FixedBytesType::nativeMembers(ASTNode const*) const
|
||||
{
|
||||
return MemberList::MemberMap{MemberList::Member{"length", TypeProvider::uint(8)}};
|
||||
}
|
||||
@@ -1884,7 +1902,7 @@ string ArrayType::signatureInExternalFunction(bool _structsByName) const
|
||||
}
|
||||
}
|
||||
|
||||
MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const
|
||||
MemberList::MemberMap ArrayType::nativeMembers(ASTNode const*) const
|
||||
{
|
||||
MemberList::MemberMap members;
|
||||
if (!isString())
|
||||
@@ -2057,10 +2075,9 @@ string ContractType::canonicalName() const
|
||||
return m_contract.annotation().canonicalName;
|
||||
}
|
||||
|
||||
MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _contract) const
|
||||
MemberList::MemberMap ContractType::nativeMembers(ASTNode const*) const
|
||||
{
|
||||
MemberList::MemberMap members;
|
||||
solAssert(_contract, "");
|
||||
if (m_super)
|
||||
{
|
||||
// add the most derived of all functions which are visible in derived contracts
|
||||
@@ -2091,14 +2108,13 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con
|
||||
}
|
||||
}
|
||||
else if (!m_contract.isLibrary())
|
||||
{
|
||||
for (auto const& it: m_contract.interfaceFunctions())
|
||||
members.emplace_back(
|
||||
it.second->declaration().name(),
|
||||
it.second->asExternallyCallableFunction(m_contract.isLibrary()),
|
||||
&it.second->declaration()
|
||||
);
|
||||
}
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
@@ -2269,7 +2285,7 @@ string StructType::toString(bool _short) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const
|
||||
MemberList::MemberMap StructType::nativeMembers(ASTNode const*) const
|
||||
{
|
||||
MemberList::MemberMap members;
|
||||
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
|
||||
@@ -3174,7 +3190,7 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const
|
||||
);
|
||||
}
|
||||
|
||||
MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const* _scope) const
|
||||
MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const
|
||||
{
|
||||
switch (m_kind)
|
||||
{
|
||||
@@ -3193,7 +3209,8 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const* _sco
|
||||
functionDefinition->isPartOfExternalInterface()
|
||||
)
|
||||
{
|
||||
solAssert(_scope->derivesFrom(*functionDefinition->annotation().contract), "");
|
||||
auto const* contractScope = dynamic_cast<ContractDefinition const*>(_scope);
|
||||
solAssert(contractScope && contractScope->derivesFrom(*functionDefinition->annotation().contract), "");
|
||||
return {{"selector", TypeProvider::fixedBytes(4)}};
|
||||
}
|
||||
else
|
||||
@@ -3668,13 +3685,14 @@ vector<tuple<string, TypePointer>> TypeType::makeStackItems() const
|
||||
return {};
|
||||
}
|
||||
|
||||
MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _currentScope) const
|
||||
MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) const
|
||||
{
|
||||
MemberList::MemberMap members;
|
||||
if (m_actualType->category() == Category::Contract)
|
||||
{
|
||||
auto const* contractScope = dynamic_cast<ContractDefinition const*>(_currentScope);
|
||||
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_actualType).contractDefinition();
|
||||
bool inDerivingScope = _currentScope && _currentScope->derivesFrom(contract);
|
||||
bool inDerivingScope = contractScope && contractScope->derivesFrom(contract);
|
||||
|
||||
for (auto const* declaration: contract.declarations())
|
||||
{
|
||||
@@ -3776,7 +3794,7 @@ bool ModuleType::operator==(Type const& _other) const
|
||||
return &m_sourceUnit == &dynamic_cast<ModuleType const&>(_other).m_sourceUnit;
|
||||
}
|
||||
|
||||
MemberList::MemberMap ModuleType::nativeMembers(ContractDefinition const*) const
|
||||
MemberList::MemberMap ModuleType::nativeMembers(ASTNode const*) const
|
||||
{
|
||||
MemberList::MemberMap symbols;
|
||||
for (auto const& symbolName: m_sourceUnit.annotation().exportedSymbols)
|
||||
@@ -3817,7 +3835,7 @@ bool MagicType::operator==(Type const& _other) const
|
||||
return other.m_kind == m_kind;
|
||||
}
|
||||
|
||||
MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
|
||||
MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
||||
{
|
||||
switch (m_kind)
|
||||
{
|
||||
|
||||
+14
-14
@@ -315,9 +315,9 @@ public:
|
||||
|
||||
/// Returns the list of all members of this type. Default implementation: no members apart from bound.
|
||||
/// @param _currentScope scope in which the members are accessed.
|
||||
MemberList const& members(ContractDefinition const* _currentScope) const;
|
||||
MemberList const& members(ASTNode const* _currentScope) const;
|
||||
/// Convenience method, returns the type of the given named member or an empty pointer if no such member exists.
|
||||
TypePointer memberType(std::string const& _name, ContractDefinition const* _currentScope = nullptr) const
|
||||
TypePointer memberType(std::string const& _name, ASTNode const* _currentScope = nullptr) const
|
||||
{
|
||||
return members(_currentScope).memberType(_name);
|
||||
}
|
||||
@@ -361,12 +361,12 @@ public:
|
||||
|
||||
private:
|
||||
/// @returns a member list containing all members added to this type by `using for` directives.
|
||||
static MemberList::MemberMap boundFunctions(Type const& _type, ContractDefinition const& _scope);
|
||||
static MemberList::MemberMap boundFunctions(Type const& _type, ASTNode const& _scope);
|
||||
|
||||
protected:
|
||||
/// @returns the members native to this type depending on the given context. This function
|
||||
/// is used (in conjunction with boundFunctions to fill m_members below.
|
||||
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* /*_currentScope*/) const
|
||||
virtual MemberList::MemberMap nativeMembers(ASTNode const* /*_currentScope*/) const
|
||||
{
|
||||
return MemberList::MemberMap();
|
||||
}
|
||||
@@ -379,7 +379,7 @@ protected:
|
||||
|
||||
|
||||
/// List of member types (parameterised by scape), will be lazy-initialized.
|
||||
mutable std::map<ContractDefinition const*, std::unique_ptr<MemberList>> m_members;
|
||||
mutable std::map<ASTNode const*, std::unique_ptr<MemberList>> m_members;
|
||||
mutable std::optional<std::vector<std::tuple<std::string, TypePointer>>> m_stackItems;
|
||||
mutable std::optional<size_t> m_stackSize;
|
||||
};
|
||||
@@ -408,7 +408,7 @@ public:
|
||||
bool isValueType() const override { return true; }
|
||||
bool nameable() const override { return true; }
|
||||
|
||||
MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
|
||||
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
|
||||
|
||||
std::string toString(bool _short) const override;
|
||||
std::string canonicalName() const override;
|
||||
@@ -649,7 +649,7 @@ public:
|
||||
bool nameable() const override { return true; }
|
||||
|
||||
std::string toString(bool) const override { return "bytes" + util::toString(m_bytes); }
|
||||
MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
|
||||
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
|
||||
TypePointer encodingType() const override { return this; }
|
||||
TypeResult interfaceType(bool) const override { return this; }
|
||||
|
||||
@@ -786,7 +786,7 @@ public:
|
||||
std::string toString(bool _short) const override;
|
||||
std::string canonicalName() const override;
|
||||
std::string signatureInExternalFunction(bool _structsByName) const override;
|
||||
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
||||
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
|
||||
TypePointer encodingType() const override;
|
||||
TypePointer decodingType() const override;
|
||||
TypeResult interfaceType(bool _inLibrary) const override;
|
||||
@@ -889,7 +889,7 @@ public:
|
||||
std::string toString(bool _short) const override;
|
||||
std::string canonicalName() const override;
|
||||
|
||||
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
||||
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
|
||||
|
||||
Type const* encodingType() const override;
|
||||
|
||||
@@ -949,7 +949,7 @@ public:
|
||||
bool nameable() const override { return true; }
|
||||
std::string toString(bool _short) const override;
|
||||
|
||||
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
||||
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
|
||||
|
||||
Type const* encodingType() const override;
|
||||
TypeResult interfaceType(bool _inLibrary) const override;
|
||||
@@ -1220,7 +1220,7 @@ public:
|
||||
bool nameable() const override;
|
||||
bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; }
|
||||
bool hasSimpleZeroValueInMemory() const override { return false; }
|
||||
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
||||
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
|
||||
TypePointer encodingType() const override;
|
||||
TypeResult interfaceType(bool _inLibrary) const override;
|
||||
|
||||
@@ -1389,7 +1389,7 @@ public:
|
||||
bool canLiveOutsideStorage() const override { return false; }
|
||||
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
||||
std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
|
||||
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
||||
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
|
||||
|
||||
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
protected:
|
||||
@@ -1441,7 +1441,7 @@ public:
|
||||
bool canBeStored() const override { return false; }
|
||||
bool canLiveOutsideStorage() const override { return true; }
|
||||
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
||||
MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
|
||||
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
|
||||
|
||||
std::string toString(bool _short) const override;
|
||||
|
||||
@@ -1481,7 +1481,7 @@ public:
|
||||
bool canBeStored() const override { return false; }
|
||||
bool canLiveOutsideStorage() const override { return true; }
|
||||
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
||||
MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
|
||||
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
|
||||
|
||||
std::string toString(bool _short) const override;
|
||||
|
||||
|
||||
@@ -384,12 +384,11 @@ void CompilerContext::appendInlineAssembly(
|
||||
yul::Identifier const& _identifier,
|
||||
yul::IdentifierContext,
|
||||
bool _insideFunction
|
||||
) -> size_t
|
||||
) -> bool
|
||||
{
|
||||
if (_insideFunction)
|
||||
return numeric_limits<size_t>::max();
|
||||
auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name.str());
|
||||
return it == _localVariables.end() ? numeric_limits<size_t>::max() : 1;
|
||||
return false;
|
||||
return contains(_localVariables, _identifier.name.str());
|
||||
};
|
||||
identifierAccess.generateCode = [&](
|
||||
yul::Identifier const& _identifier,
|
||||
|
||||
@@ -326,28 +326,28 @@ bool CompilerStack::analyze()
|
||||
if (source->ast && !resolver.performImports(*source->ast, sourceUnitsByName))
|
||||
return false;
|
||||
|
||||
// This is the main name and type resolution loop. Needs to be run for every contract, because
|
||||
// the special variables "this" and "super" must be set appropriately.
|
||||
for (Source const* source: m_sourceOrder)
|
||||
if (source->ast && !resolver.resolveNamesAndTypes(*source->ast))
|
||||
return false;
|
||||
|
||||
// Store contract definitions.
|
||||
for (Source const* source: m_sourceOrder)
|
||||
if (source->ast)
|
||||
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
||||
for (
|
||||
ContractDefinition const* contract:
|
||||
ASTNode::filteredNodes<ContractDefinition>(source->ast->nodes())
|
||||
)
|
||||
{
|
||||
if (!resolver.resolveNamesAndTypes(*node))
|
||||
return false;
|
||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||
{
|
||||
// Note that we now reference contracts by their fully qualified names, and
|
||||
// thus contracts can only conflict if declared in the same source file. This
|
||||
// should already cause a double-declaration error elsewhere.
|
||||
if (m_contracts.find(contract->fullyQualifiedName()) == m_contracts.end())
|
||||
m_contracts[contract->fullyQualifiedName()].contract = contract;
|
||||
else
|
||||
solAssert(
|
||||
m_errorReporter.hasErrors(),
|
||||
"Contract already present (name clash?), but no error was reported."
|
||||
);
|
||||
}
|
||||
|
||||
// Note that we now reference contracts by their fully qualified names, and
|
||||
// thus contracts can only conflict if declared in the same source file. This
|
||||
// should already cause a double-declaration error elsewhere.
|
||||
if (!m_contracts.count(contract->fullyQualifiedName()))
|
||||
m_contracts[contract->fullyQualifiedName()].contract = contract;
|
||||
else
|
||||
solAssert(
|
||||
m_errorReporter.hasErrors(),
|
||||
"Contract already present (name clash?), but no error was reported."
|
||||
);
|
||||
}
|
||||
|
||||
DeclarationTypeChecker declarationTypeChecker(m_errorReporter, m_evmVersion);
|
||||
@@ -376,11 +376,8 @@ bool CompilerStack::analyze()
|
||||
// which is only done one step later.
|
||||
TypeChecker typeChecker(m_evmVersion, m_errorReporter);
|
||||
for (Source const* source: m_sourceOrder)
|
||||
if (source->ast)
|
||||
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||
if (!typeChecker.checkTypeRequirements(*contract))
|
||||
noErrors = false;
|
||||
if (source->ast && !typeChecker.checkTypeRequirements(*source->ast))
|
||||
noErrors = false;
|
||||
|
||||
if (noErrors)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user