aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/analysis/ControlFlowAnalyzer.cpp10
-rw-r--r--libsolidity/analysis/TypeChecker.cpp18
-rw-r--r--libsolidity/ast/Types.cpp21
-rw-r--r--libsolidity/ast/Types.h5
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp18
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp17
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp4
-rw-r--r--libsolidity/interface/CompilerStack.cpp14
-rw-r--r--libsolidity/interface/CompilerStack.h3
9 files changed, 61 insertions, 49 deletions
diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp
index 6edf7986..483d08c8 100644
--- a/libsolidity/analysis/ControlFlowAnalyzer.cpp
+++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp
@@ -144,12 +144,12 @@ void ControlFlowAnalyzer::checkUnassignedStorageReturnValues(
ssl.append("Problematic end of function:", _function.location());
}
- m_errorReporter.warning(
+ m_errorReporter.typeError(
returnVal->location(),
- "This variable is of storage pointer type and might be returned without assignment. "
- "This can cause storage corruption. Assign the variable (potentially from itself) "
- "to remove this warning.",
- ssl
+ ssl,
+ "This variable is of storage pointer type and might be returned without assignment and "
+ "could be used uninitialized. Assign the variable (potentially from itself) "
+ "to fix this error."
);
}
}
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index f4354c61..e868d111 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -1738,22 +1738,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero).");
errored = true;
}
- if (!errored)
- {
- TypePointer encodingType;
- if (
- argType->mobileType() &&
- argType->mobileType()->interfaceType(false) &&
- argType->mobileType()->interfaceType(false)->encodingType()
- )
- encodingType = argType->mobileType()->interfaceType(false)->encodingType();
- // Structs are fine as long as ABIV2 is activated and we do not do packed encoding.
- if (!encodingType || (
- dynamic_cast<StructType const*>(encodingType.get()) &&
- !(abiEncodeV2 && functionType->padArguments())
- ))
- m_errorReporter.typeError(arguments[i]->location(), "This type cannot be encoded.");
- }
+ if (!errored && !argType->fullEncodingType(false, abiEncodeV2, !functionType->padArguments()))
+ m_errorReporter.typeError(arguments[i]->location(), "This type cannot be encoded.");
}
else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
{
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index c85e0522..0954fdf3 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -390,6 +390,27 @@ MemberList const& Type::members(ContractDefinition const* _currentScope) const
return *m_members[_currentScope];
}
+TypePointer Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool _packed) const
+{
+ TypePointer encodingType = mobileType();
+ if (encodingType)
+ encodingType = encodingType->interfaceType(_inLibraryCall);
+ if (encodingType)
+ encodingType = encodingType->encodingType();
+ if (auto structType = dynamic_cast<StructType const*>(encodingType.get()))
+ {
+ // Structs are fine in the following circumstances:
+ // - ABIv2 without packed encoding or,
+ // - storage struct for a library
+ if (!(
+ (_encoderV2 && !_packed) ||
+ (structType->location() == DataLocation::Storage && _inLibraryCall)
+ ))
+ return TypePointer();
+ }
+ return encodingType;
+}
+
MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition const& _scope)
{
// Normalise data location of type.
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 474a6f33..12029a4e 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -280,6 +280,11 @@ public:
/// This for example returns address for contract types.
/// If there is no such type, returns an empty shared pointer.
virtual TypePointer encodingType() const { return TypePointer(); }
+ /// @returns the encoding type used under the given circumstances for the type of an expression
+ /// when used for e.g. abi.encode(...) or the empty pointer if the object
+ /// cannot be encoded.
+ /// This is different from encodingType since it takes implicit conversions into account.
+ TypePointer fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool _packed) const;
/// @returns a (simpler) type that is used when decoding this type in calldata.
virtual TypePointer decodingType() const { return encodingType(); }
/// @returns a type that will be used outside of Solidity for e.g. function signatures.
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp
index b3f1bc7e..1ce77d67 100644
--- a/libsolidity/codegen/ABIFunctions.cpp
+++ b/libsolidity/codegen/ABIFunctions.cpp
@@ -471,13 +471,8 @@ string ABIFunctions::abiEncodingFunction(
bool _fromStack
)
{
- solUnimplementedAssert(
- _to.mobileType() &&
- _to.mobileType()->interfaceType(_encodeAsLibraryTypes) &&
- _to.mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(),
- "Encoding type \"" + _to.toString() + "\" not yet implemented."
- );
- TypePointer toInterface = _to.mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType();
+ TypePointer toInterface = _to.fullEncodingType(_encodeAsLibraryTypes, true, false);
+ solUnimplementedAssert(toInterface, "Encoding type \"" + _to.toString() + "\" not yet implemented.");
Type const& to = *toInterface;
if (_from.category() == Type::Category::StringLiteral)
@@ -886,13 +881,8 @@ string ABIFunctions::abiEncodingFunctionStruct(
solAssert(member.type, "");
if (!member.type->canLiveOutsideStorage())
continue;
- solUnimplementedAssert(
- member.type->mobileType() &&
- member.type->mobileType()->interfaceType(_encodeAsLibraryTypes) &&
- member.type->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(),
- "Encoding type \"" + member.type->toString() + "\" not yet implemented."
- );
- auto memberTypeTo = member.type->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType();
+ TypePointer memberTypeTo = member.type->fullEncodingType(_encodeAsLibraryTypes, true, false);
+ solUnimplementedAssert(memberTypeTo, "Encoding type \"" + member.type->toString() + "\" not yet implemented.");
auto memberTypeFrom = _from.memberType(member.name);
solAssert(memberTypeFrom, "");
bool dynamicMember = memberTypeTo->isDynamicallyEncoded();
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 2d81a106..bd3ec8f5 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -333,26 +333,19 @@ void CompilerUtils::encodeToMemory(
)
{
// stack: <v1> <v2> ... <vn> <mem>
+ bool const encoderV2 = m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2);
TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes;
solAssert(targetTypes.size() == _givenTypes.size(), "");
for (TypePointer& t: targetTypes)
{
- solUnimplementedAssert(
- t->mobileType() &&
- t->mobileType()->interfaceType(_encodeAsLibraryTypes) &&
- t->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(),
- "Encoding type \"" + t->toString() + "\" not yet implemented."
- );
- t = t->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType();
+ TypePointer tEncoding = t->fullEncodingType(_encodeAsLibraryTypes, encoderV2, !_padToWordBoundaries);
+ solUnimplementedAssert(tEncoding, "Encoding type \"" + t->toString() + "\" not yet implemented.");
+ t = std::move(tEncoding);
}
if (_givenTypes.empty())
return;
- else if (
- _padToWordBoundaries &&
- !_copyDynamicDataInPlace &&
- m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)
- )
+ else if (_padToWordBoundaries && !_copyDynamicDataInPlace && encoderV2)
{
// Use the new Yul-based encoding function
auto stackHeightBefore = m_context.stackHeight();
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index bbb3db3d..2f15a33d 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -71,7 +71,11 @@ void ContractCompiler::compileContract(
appendDelegatecallCheck();
initializeContext(_contract, _contracts);
+ // This generates the dispatch function for externally visible functions
+ // and adds the function to the compilation queue. Additionally internal functions,
+ // which are referenced directly or indirectly will be added.
appendFunctionSelector(_contract);
+ // This processes the above populated queue until it is empty.
appendMissingFunctions();
}
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index aa33bad8..32cb488f 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -191,6 +191,8 @@ bool CompilerStack::analyze()
if (!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)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
@@ -204,11 +206,15 @@ bool CompilerStack::analyze()
// thus contracts can only conflict if declared in the same source file. This
// already causes a double-declaration error elsewhere, so we do not report
// an error here and instead silently drop any additional contracts we find.
-
if (m_contracts.find(contract->fullyQualifiedName()) == m_contracts.end())
m_contracts[contract->fullyQualifiedName()].contract = contract;
}
+ // This cannot be done in the above loop, because cross-contract types couldn't be resolved.
+ // A good example is `LibraryName.TypeName x;`.
+ //
+ // Note: this does not resolve overloaded functions. In order to do that, types of arguments are needed,
+ // which is only done one step later.
TypeChecker typeChecker(m_evmVersion, m_errorReporter);
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
@@ -218,6 +224,7 @@ bool CompilerStack::analyze()
if (noErrors)
{
+ // Checks that can only be done when all types of all AST nodes are known.
PostTypeChecker postTypeChecker(m_errorReporter);
for (Source const* source: m_sourceOrder)
if (!postTypeChecker.check(*source->ast))
@@ -226,6 +233,8 @@ bool CompilerStack::analyze()
if (noErrors)
{
+ // Control flow graph generator and analyzer. It can check for issues such as
+ // variable is used before it is assigned to.
CFG cfg(m_errorReporter);
for (Source const* source: m_sourceOrder)
if (!cfg.constructFlow(*source->ast))
@@ -242,6 +251,7 @@ bool CompilerStack::analyze()
if (noErrors)
{
+ // Checks for common mistakes. Only generates warnings.
StaticAnalyzer staticAnalyzer(m_errorReporter);
for (Source const* source: m_sourceOrder)
if (!staticAnalyzer.analyze(*source->ast))
@@ -250,6 +260,7 @@ bool CompilerStack::analyze()
if (noErrors)
{
+ // Check for state mutability in every function.
vector<ASTPointer<ASTNode>> ast;
for (Source const* source: m_sourceOrder)
ast.push_back(source->ast);
@@ -300,6 +311,7 @@ bool CompilerStack::compile()
if (!parseAndAnalyze())
return false;
+ // Only compile contracts individually which have been requested.
map<ContractDefinition const*, eth::Assembly const*> compiledContracts;
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index 0578ac86..7b144660 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -352,8 +352,9 @@ private:
std::vector<Remapping> m_remappings;
std::map<std::string const, Source> m_sources;
std::shared_ptr<GlobalContext> m_globalContext;
- std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes;
std::vector<Source const*> m_sourceOrder;
+ /// This is updated during compilation.
+ std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes;
std::map<std::string const, Contract> m_contracts;
ErrorList m_errorList;
ErrorReporter m_errorReporter;