diff options
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.cpp | 6 | ||||
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.cpp | 9 | ||||
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.h | 1 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 87 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.h | 4 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 11 | ||||
-rw-r--r-- | libsolidity/ast/ASTAnnotations.h | 3 | ||||
-rw-r--r-- | libsolidity/ast/AST_accept.h | 4 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 6 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.cpp | 24 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.h | 5 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 47 | ||||
-rw-r--r-- | libsolidity/formal/Why3Translator.h | 2 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.cpp | 4 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 46 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 54 |
16 files changed, 258 insertions, 55 deletions
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 806f1322..fa894456 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -49,7 +49,7 @@ bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit) { DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errors); } - catch (FatalError const& _e) + catch (FatalError const&) { if (m_errors.empty()) throw; // Something is weird here, rather throw again. @@ -146,7 +146,7 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) if (!success) return false; } - catch (FatalError const& _e) + catch (FatalError const&) { if (m_errors.empty()) throw; // Something is weird here, rather throw again. @@ -162,7 +162,7 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) m_scopes[nullptr].registerDeclaration(_declaration, false, true); solAssert(_declaration.scope() == nullptr, "Updated declaration outside global scope."); } - catch (FatalError const& _error) + catch (FatalError const&) { if (m_errors.empty()) throw; // Something is weird here, rather throw again. diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 95643578..f0afc4f9 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -37,6 +37,11 @@ bool ReferencesResolver::visit(Return const& _return) return true; } +void ReferencesResolver::endVisit(NewExpression const& _new) +{ + typeFor(_new.typeName()); +} + bool ReferencesResolver::visit(UserDefinedTypeName const& _typeName) { Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath()); @@ -44,6 +49,8 @@ bool ReferencesResolver::visit(UserDefinedTypeName const& _typeName) fatalDeclarationError(_typeName.location(), "Identifier not found or not unique."); _typeName.annotation().referencedDeclaration = declaration; + + _typeName.annotation().contractScope = m_currentContract; return true; } @@ -53,7 +60,7 @@ bool ReferencesResolver::resolve(ASTNode& _root) { _root.accept(*this); } - catch (FatalError const& e) + catch (FatalError const&) { solAssert(m_errorOccurred, ""); } diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index 21cb1d35..62104611 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -64,6 +64,7 @@ private: virtual bool visit(Identifier const& _identifier) override; virtual bool visit(UserDefinedTypeName const& _typeName) override; virtual bool visit(Return const& _return) override; + virtual void endVisit(NewExpression const& _new) override; virtual void endVisit(VariableDeclaration const& _variable) override; TypePointer typeFor(TypeName const& _typeName); diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 0990a9e4..42fdad91 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1045,34 +1045,63 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) void TypeChecker::endVisit(NewExpression const& _newExpression) { - auto contract = dynamic_cast<ContractDefinition const*>(&dereference(_newExpression.contractName())); + TypePointer type = _newExpression.typeName().annotation().type; + solAssert(!!type, "Type name not resolved."); - if (!contract) - fatalTypeError(_newExpression.location(), "Identifier is not a contract."); - if (!contract->annotation().isFullyImplemented) - typeError(_newExpression.location(), "Trying to create an instance of an abstract contract."); - - auto scopeContract = _newExpression.contractName().annotation().contractScope; - scopeContract->annotation().contractDependencies.insert(contract); - solAssert( - !contract->annotation().linearizedBaseContracts.empty(), - "Linearized base contracts not yet available." - ); - if (contractDependenciesAreCyclic(*scopeContract)) - typeError( - _newExpression.location(), - "Circular reference for contract creation (cannot create instance of derived or same contract)." + if (auto contractName = dynamic_cast<UserDefinedTypeName const*>(&_newExpression.typeName())) + { + auto contract = dynamic_cast<ContractDefinition const*>(&dereference(*contractName)); + + if (!contract) + fatalTypeError(_newExpression.location(), "Identifier is not a contract."); + if (!contract->annotation().isFullyImplemented) + typeError(_newExpression.location(), "Trying to create an instance of an abstract contract."); + + auto scopeContract = contractName->annotation().contractScope; + scopeContract->annotation().contractDependencies.insert(contract); + solAssert( + !contract->annotation().linearizedBaseContracts.empty(), + "Linearized base contracts not yet available." ); + if (contractDependenciesAreCyclic(*scopeContract)) + typeError( + _newExpression.location(), + "Circular reference for contract creation (cannot create instance of derived or same contract)." + ); - auto contractType = make_shared<ContractType>(*contract); - TypePointers const& parameterTypes = contractType->constructorType()->parameterTypes(); - _newExpression.annotation().type = make_shared<FunctionType>( - parameterTypes, - TypePointers{contractType}, - strings(), - strings(), - FunctionType::Location::Creation - ); + auto contractType = make_shared<ContractType>(*contract); + TypePointers const& parameterTypes = contractType->constructorType()->parameterTypes(); + _newExpression.annotation().type = make_shared<FunctionType>( + parameterTypes, + TypePointers{contractType}, + strings(), + strings(), + FunctionType::Location::Creation + ); + } + else if (type->category() == Type::Category::Array) + { + if (!type->canLiveOutsideStorage()) + fatalTypeError( + _newExpression.typeName().location(), + "Type cannot live outside storage." + ); + if (!type->isDynamicallySized()) + typeError( + _newExpression.typeName().location(), + "Length has to be placed in parentheses after the array type for new expression." + ); + type = ReferenceType::copyForLocationIfReference(DataLocation::Memory, type); + _newExpression.annotation().type = make_shared<FunctionType>( + TypePointers{make_shared<IntegerType>(256)}, + TypePointers{type}, + strings(), + strings(), + FunctionType::Location::ObjectCreation + ); + } + else + fatalTypeError(_newExpression.location(), "Contract or array type expected."); } bool TypeChecker::visit(MemberAccess const& _memberAccess) @@ -1282,12 +1311,18 @@ bool TypeChecker::contractDependenciesAreCyclic( return false; } -Declaration const& TypeChecker::dereference(Identifier const& _identifier) +Declaration const& TypeChecker::dereference(Identifier const& _identifier) const { solAssert(!!_identifier.annotation().referencedDeclaration, "Declaration not stored."); return *_identifier.annotation().referencedDeclaration; } +Declaration const& TypeChecker::dereference(UserDefinedTypeName const& _typeName) const +{ + solAssert(!!_typeName.annotation().referencedDeclaration, "Declaration not stored."); + return *_typeName.annotation().referencedDeclaration; +} + void TypeChecker::expectType(Expression const& _expression, Type const& _expectedType) { _expression.accept(*this); diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index f163f47c..2295bc22 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -107,7 +107,9 @@ private: ) const; /// @returns the referenced declaration and throws on error. - Declaration const& dereference(Identifier const& _identifier); + Declaration const& dereference(Identifier const& _identifier) const; + /// @returns the referenced declaration and throws on error. + Declaration const& dereference(UserDefinedTypeName const& _typeName) const; /// Runs type checks on @a _expression to infer its type and then checks that it is implicitly /// convertible to @a _expectedType. diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 69186cb7..e2ed1853 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1216,23 +1216,24 @@ private: }; /** - * Expression that creates a new contract, e.g. the "new SomeContract" part in "new SomeContract(1, 2)". + * Expression that creates a new contract or memory-array, + * e.g. the "new SomeContract" part in "new SomeContract(1, 2)". */ class NewExpression: public Expression { public: NewExpression( SourceLocation const& _location, - ASTPointer<Identifier> const& _contractName + ASTPointer<TypeName> const& _typeName ): - Expression(_location), m_contractName(_contractName) {} + Expression(_location), m_typeName(_typeName) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - Identifier const& contractName() const { return *m_contractName; } + TypeName const& typeName() const { return *m_typeName; } private: - ASTPointer<Identifier> m_contractName; + ASTPointer<TypeName> m_typeName; }; /** diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index bb59ceae..b9667302 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -111,6 +111,9 @@ struct UserDefinedTypeNameAnnotation: TypeNameAnnotation { /// Referenced declaration, set during reference resolution stage. Declaration const* referencedDeclaration = nullptr; + /// Stores a reference to the current contract. + /// This is needed because types of base contracts change depending on the context. + ContractDefinition const* contractScope = nullptr; }; struct VariableDeclarationStatementAnnotation: StatementAnnotation diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 12a26ea7..99d1bf6a 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -634,14 +634,14 @@ void FunctionCall::accept(ASTConstVisitor& _visitor) const void NewExpression::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) - m_contractName->accept(_visitor); + m_typeName->accept(_visitor); _visitor.endVisit(*this); } void NewExpression::accept(ASTConstVisitor& _visitor) const { if (_visitor.visit(*this)) - m_contractName->accept(_visitor); + m_typeName->accept(_visitor); _visitor.endVisit(*this); } diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index f5aefa25..63207a51 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -141,9 +141,6 @@ public: /// Factory functions that convert an AST @ref TypeName to a Type. static TypePointer fromElementaryTypeName(Token::Value _typeToken); static TypePointer fromElementaryTypeName(std::string const& _name); - static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); - static TypePointer fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType); - static TypePointer fromArrayTypeName(TypeName& _baseTypeName, Expression* _length); /// @} /// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does @@ -751,7 +748,8 @@ public: AddMod, ///< ADDMOD MulMod, ///< MULMOD ArrayPush, ///< .push() to a dynamically sized array in storage - ByteArrayPush ///< .push() to a dynamically sized byte array in storage + ByteArrayPush, ///< .push() to a dynamically sized byte array in storage + ObjectCreation ///< array creation using new }; virtual Category category() const override { return Category::Function; } diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 2ebf5b64..357013e6 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -266,6 +266,19 @@ void CompilerUtils::encodeToMemory( popStackSlots(argSize + dynPointers + 1); } +void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) +{ + auto repeat = m_context.newTag(); + m_context << repeat; + pushZeroValue(*_type.baseType()); + storeInMemoryDynamic(*_type.baseType()); + m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::SWAP1; + m_context << eth::Instruction::SUB << eth::Instruction::SWAP1; + m_context << eth::Instruction::DUP2; + m_context.appendConditionalJumpTo(repeat); + m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; +} + void CompilerUtils::memoryCopy() { // Stack here: size target source @@ -646,15 +659,8 @@ void CompilerUtils::pushZeroValue(Type const& _type) { m_context << arrayType->length() << eth::Instruction::SWAP1; // stack: items_to_do memory_pos - auto repeat = m_context.newTag(); - m_context << repeat; - pushZeroValue(*arrayType->baseType()); - storeInMemoryDynamic(*arrayType->baseType()); - m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::SWAP1; - m_context << eth::Instruction::SUB << eth::Instruction::SWAP1; - m_context << eth::Instruction::DUP2; - m_context.appendConditionalJumpTo(repeat); - m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; + zeroInitialiseMemoryArray(*arrayType); + // stack: updated_memory_pos } } else diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 6292e5c7..134afd78 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -103,6 +103,11 @@ public: bool _encodeAsLibraryTypes = false ); + /// Zero-initialises (the data part of) an already allocated memory array. + /// Stack pre: <length> <memptr> + /// Stack post: <updated_memptr> + void zeroInitialiseMemoryArray(ArrayType const& _type); + /// Uses a CALL to the identity contract to perform a memory-to-memory copy. /// Stack pre: <size> <target> <source> /// Stack post: diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 1a089d63..c94c988b 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -703,6 +703,53 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) StorageByteArrayElement(m_context).storeValue(*type, _functionCall.location(), true); break; } + case Location::ObjectCreation: + { + // Will allocate at the end of memory (MSIZE) and not write at all unless the base + // type is dynamically sized. + ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_functionCall.annotation().type); + _functionCall.expression().accept(*this); + solAssert(arguments.size() == 1, ""); + + // Fetch requested length. + arguments[0]->accept(*this); + utils().convertType(*arguments[0]->annotation().type, IntegerType(256)); + + // Stack: requested_length + // Allocate at max(MSIZE, freeMemoryPointer) + utils().fetchFreeMemoryPointer(); + m_context << eth::Instruction::DUP1 << eth::Instruction::MSIZE; + m_context << eth::Instruction::LT; + auto initialise = m_context.appendConditionalJump(); + // Free memory pointer does not point to empty memory, use MSIZE. + m_context << eth::Instruction::POP; + m_context << eth::Instruction::MSIZE; + m_context << initialise; + + // Stack: requested_length memptr + m_context << eth::Instruction::SWAP1; + // Stack: memptr requested_length + // store length + m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::MSTORE; + // Stack: memptr requested_length + // update free memory pointer + m_context << eth::Instruction::DUP1 << arrayType.baseType()->memoryHeadSize(); + m_context << eth::Instruction::MUL << u256(32) << eth::Instruction::ADD; + m_context << eth::Instruction::DUP3 << eth::Instruction::ADD; + utils().storeFreeMemoryPointer(); + // Stack: memptr requested_length + + // We only have to initialise if the base type is a not a value type. + if (dynamic_cast<ReferenceType const*>(arrayType.baseType().get())) + { + m_context << eth::Instruction::DUP2 << u256(32) << eth::Instruction::ADD; + utils().zeroInitialiseMemoryArray(arrayType); + m_context << eth::Instruction::POP; + } + else + m_context << eth::Instruction::POP; + break; + } default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type.")); } diff --git a/libsolidity/formal/Why3Translator.h b/libsolidity/formal/Why3Translator.h index 34c6c34f..e5c298b4 100644 --- a/libsolidity/formal/Why3Translator.h +++ b/libsolidity/formal/Why3Translator.h @@ -43,7 +43,7 @@ class SourceUnit; class Why3Translator: private ASTConstVisitor { public: - Why3Translator(ErrorList& _errors): m_lines{{std::string(), 0}}, m_errors(_errors) {} + Why3Translator(ErrorList& _errors): m_lines(std::vector<Line>{{std::string(), 0}}), m_errors(_errors) {} /// Appends formalisation of the given source unit to the output. /// @returns false on error. diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index d89218bb..2d4ca43e 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -88,7 +88,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner) } return nodeFactory.createNode<SourceUnit>(nodes); } - catch (FatalError const& _error) + catch (FatalError const&) { if (m_errors.empty()) throw; // Something is weird here, rather throw again. @@ -939,7 +939,7 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression( else if (m_scanner->currentToken() == Token::New) { expectToken(Token::New); - ASTPointer<Identifier> contractName(parseIdentifier()); + ASTPointer<TypeName> contractName(parseTypeName(false)); nodeFactory.setEndPositionFromNode(contractName); expression = nodeFactory.createNode<NewExpression>(contractName); } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 93b42c51..0b356145 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1955,7 +1955,7 @@ BOOST_AUTO_TEST_CASE(value_for_constructor) contract Main { Helper h; function Main() { - h = new Helper.value(10)("abc", true); + h = (new Helper).value(10)("abc", true); } function getFlag() returns (bool ret) { return h.getFlag(); } function getName() returns (bytes3 ret) { return h.getName(); } @@ -5816,6 +5816,50 @@ BOOST_AUTO_TEST_CASE(lone_struct_array_type) BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(3))); } +BOOST_AUTO_TEST_CASE(create_memory_array) +{ + char const* sourceCode = R"( + contract C { + struct S { uint[2] a; bytes b; } + function f() returns (byte, uint, uint, byte) { + var x = new bytes(200); + x[199] = 'A'; + var y = new uint[2][](300); + y[203][1] = 8; + var z = new S[](180); + z[170].a[1] = 4; + z[170].b = new bytes(102); + z[170].b[99] = 'B'; + return (x[199], y[203][1], z[170].a[1], z[170].b[99]); + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(string("A"), u256(8), u256(4), string("B"))); +} + +BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes) +{ + // Computes binomial coefficients the chinese way + char const* sourceCode = R"( + contract C { + function f(uint n, uint k) returns (uint) { + uint[][] memory rows = new uint[][](n + 1); + for (uint i = 1; i <= n; i++) { + rows[i] = new uint[](i); + rows[i][0] = rows[i][rows[i].length - 1] = 1; + for (uint j = 1; j < i - 1; j++) + rows[i][j] = rows[i - 1][j - 1] + rows[i - 1][j]; + } + return rows[n][k - 1]; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f(uint256,uint256)", encodeArgs(u256(3), u256(1))) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("f(uint256,uint256)", encodeArgs(u256(9), u256(5))) == encodeArgs(u256(70))); +} + BOOST_AUTO_TEST_CASE(memory_overwrite) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index e87e47a8..4f26fa4d 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2529,6 +2529,60 @@ BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(create_memory_arrays) +{ + char const* text = R"( + library L { + struct R { uint[10][10] y; } + struct S { uint a; uint b; uint[20][20][20] c; R d; } + } + contract C { + function f(uint size) { + L.S[][] memory x = new L.S[][](10); + var y = new uint[](20); + var z = new bytes(size); + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(mapping_in_memory_array) +{ + char const* text = R"( + contract C { + function f(uint size) { + var x = new mapping(uint => uint)[](4); + } + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(new_for_non_array) +{ + char const* text = R"( + contract C { + function f(uint size) { + var x = new uint(7); + } + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(invalid_args_creating_memory_array) +{ + char const* text = R"( + contract C { + function f(uint size) { + var x = new uint[](); + } + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + BOOST_AUTO_TEST_SUITE_END() } |