diff options
author | chriseth <c@ethdev.com> | 2015-10-10 01:35:41 +0800 |
---|---|---|
committer | chriseth <c@ethdev.com> | 2015-10-10 01:35:41 +0800 |
commit | a5d12b876180088904a026c472da23201a35d59c (patch) | |
tree | 277c5e2da4bc1ea04f10b3369f96c5f087fb01db /libsolidity | |
parent | c54a033bf036d16651fb992689adfcffb2f3a951 (diff) | |
download | dexon-solidity-a5d12b876180088904a026c472da23201a35d59c.tar dexon-solidity-a5d12b876180088904a026c472da23201a35d59c.tar.gz dexon-solidity-a5d12b876180088904a026c472da23201a35d59c.tar.bz2 dexon-solidity-a5d12b876180088904a026c472da23201a35d59c.tar.lz dexon-solidity-a5d12b876180088904a026c472da23201a35d59c.tar.xz dexon-solidity-a5d12b876180088904a026c472da23201a35d59c.tar.zst dexon-solidity-a5d12b876180088904a026c472da23201a35d59c.zip |
Introduced tuple type and added multi variable declarations to type
checker.
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/AST.h | 4 | ||||
-rw-r--r-- | libsolidity/Parser.cpp | 24 | ||||
-rw-r--r-- | libsolidity/TypeChecker.cpp | 144 | ||||
-rw-r--r-- | libsolidity/Types.cpp | 55 | ||||
-rw-r--r-- | libsolidity/Types.h | 44 |
5 files changed, 184 insertions, 87 deletions
diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 354beb7c..5c4c889d 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -558,7 +558,9 @@ protected: private: ASTPointer<TypeName> m_typeName; ///< can be empty ("var") - ASTPointer<Expression> m_value; ///< the assigned value, can be missing + /// Initially assigned value, can be missing. For local variables, this is stored inside + /// VariableDeclarationStatement and not here. + ASTPointer<Expression> m_value; bool m_isStateVariable; ///< Whether or not this is a contract state variable bool m_isIndexed; ///< Whether this is an indexed variable (used by events). bool m_isConstant; ///< Whether the variable is a compile-time constant. diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index c6b74e1c..e02a70d5 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -783,14 +783,34 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme ) { // Parse `var (a, b, ,, c) = ...` into a single VariableDeclarationStatement with multiple variables. - solAssert(false, "To be implemented."); + m_scanner->next(); + m_scanner->next(); + do + { + ASTPointer<VariableDeclaration> var; + if (m_scanner->currentToken() == Token::Comma) + m_scanner->next(); + else + { + ASTNodeFactory varDeclNodeFactory(*this); + ASTPointer<ASTString> name = expectIdentifierToken(); + var = varDeclNodeFactory.createNode<VariableDeclaration>( + ASTPointer<TypeName>(), + name, + ASTPointer<Expression>(), + VariableDeclaration::Visibility::Default + ); + } + variables.push_back(var); + } while (m_scanner->currentToken() != Token::RParen); + nodeFactory.markEndPosition(); + m_scanner->next(); } else { VarDeclParserOptions options; options.allowVar = true; options.allowLocationSpecifier = true; - options.allowInitialValue = false; variables.push_back(parseVariableDeclaration(options, _lookAheadArrayType)); } if (m_scanner->currentToken() == Token::Assign) diff --git a/libsolidity/TypeChecker.cpp b/libsolidity/TypeChecker.cpp index 8207e1ff..b1007276 100644 --- a/libsolidity/TypeChecker.cpp +++ b/libsolidity/TypeChecker.cpp @@ -449,18 +449,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) } if (_variable.value()) expectType(*_variable.value(), *varType); - else - { - if (auto ref = dynamic_cast<ReferenceType const *>(varType.get())) - if (ref->dataStoredIn(DataLocation::Storage) && _variable.isLocalVariable() && !_variable.isCallableParameter()) - { - auto err = make_shared<Warning>(); - *err << - errinfo_sourceLocation(_variable.location()) << - errinfo_comment("Uninitialized storage pointer. Did you mean '<type> memory " + _variable.name() + "'?"); - m_errors.push_back(err); - } - } if (!_variable.isStateVariable()) { if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData)) @@ -601,36 +589,101 @@ void TypeChecker::endVisit(Return const& _return) bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { - solAssert(_statement.declarations().size() == 1, "To be implemented."); - solAssert(!!_statement.declarations().front(), ""); - VariableDeclaration const& var = *_statement.declarations().front(); - solAssert(!var.value(), "Value has to be tied to statement."); - if (!var.annotation().type) + solAssert(!_statement.declarations().empty(), ""); + if (!_statement.initialValue()) { - solAssert(!var.typeName(), ""); - // Infer type from value. - if (!_statement.initialValue()) + // No initial value is only permitted for single variables with specified type. + if (_statement.declarations().size() != 1 || !_statement.declarations().front()) + fatalTypeError(_statement, "Assignment necessary for type detection."); + VariableDeclaration const& varDecl = *_statement.declarations().front(); + if (!varDecl.annotation().type) fatalTypeError(_statement, "Assignment necessary for type detection."); - _statement.initialValue()->accept(*this); - - TypePointer const& valueType = type(*_statement.initialValue()); - solAssert(!!valueType, ""); - if ( - valueType->category() == Type::Category::IntegerConstant && - !dynamic_pointer_cast<IntegerConstantType const>(valueType)->integerType() - ) - fatalTypeError(*_statement.initialValue(), "Invalid integer constant " + valueType->toString() + "."); - else if (valueType->category() == Type::Category::Void) - fatalTypeError(_statement, "Variable cannot have void type."); - var.annotation().type = valueType->mobileType(); - var.accept(*this); + if (auto ref = dynamic_cast<ReferenceType const*>(varDecl.annotation().type.get())) + { + if (ref->dataStoredIn(DataLocation::Storage)) + { + auto err = make_shared<Warning>(); + *err << + errinfo_sourceLocation(varDecl.location()) << + errinfo_comment("Uninitialized storage pointer. Did you mean '<type> memory " + varDecl.name() + "'?"); + m_errors.push_back(err); + } + } + varDecl.accept(*this); return false; } - else + + // Here we have an initial value and might have to derive some types before we can visit + // the variable declaration(s). + + _statement.initialValue()->accept(*this); + shared_ptr<TupleType const> valueType = dynamic_pointer_cast<TupleType const>(_statement.initialValue()->annotation().type); + if (!valueType) + valueType = make_shared<TupleType const>(TypePointers{_statement.initialValue()->annotation().type}); + + vector<ASTPointer<VariableDeclaration>> variables = _statement.declarations(); + // If numbers do not match, fill up if variables begin or end empty (not both). + if (valueType->components().size() != variables.size()) + { + if (!variables.front() && !variables.back()) + fatalTypeError( + _statement, + "Wildcard both at beginning and end of variable declaration list is only allowed " + "if the number of components is equal." + ); + while (valueType->components().size() > variables.size()) + if (!variables.front()) + variables.insert(variables.begin(), shared_ptr<VariableDeclaration>()); + else + variables.push_back(shared_ptr<VariableDeclaration>()); + while (valueType->components().size() < variables.size()) + if (!variables.empty() && !variables.front()) + variables.erase(variables.begin()); + else if (!variables.empty() && !variables.back()) + variables.pop_back(); + else + break; + if (valueType->components().size() != variables.size()) + fatalTypeError( + _statement, + "Unable to match the number of variables to the number of values." + ); + } + solAssert(variables.size() == valueType->components().size(), ""); + + for (size_t i = 0; i < variables.size(); ++i) { - var.accept(*this); - if (_statement.initialValue()) - expectType(*_statement.initialValue(), *var.annotation().type); + if (!variables[i]) + continue; + VariableDeclaration const& var = *variables[i]; + solAssert(!var.value(), "Value has to be tied to statement."); + TypePointer const& valueComponentType = valueType->components()[i]; + solAssert(!!valueComponentType, ""); + if (!var.annotation().type) + { + // Infer type from value. + solAssert(!var.typeName(), ""); + if ( + valueComponentType->category() == Type::Category::IntegerConstant && + !dynamic_pointer_cast<IntegerConstantType const>(valueComponentType)->integerType() + ) + fatalTypeError(*_statement.initialValue(), "Invalid integer constant " + valueComponentType->toString() + "."); + var.annotation().type = valueComponentType->mobileType(); + var.accept(*this); + } + else + { + var.accept(*this); + if (!valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type)) + typeError( + _statement, + "Type " + + valueComponentType->toString() + + " is not implicitly convertible to expected type " + + var.annotation().type->toString() + + "." + ); + } } return false; } @@ -799,23 +852,14 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) if (!functionType) { typeError(_functionCall, "Type is not callable"); - _functionCall.annotation().type = make_shared<VoidType>(); + _functionCall.annotation().type = make_shared<TupleType>(); return false; } + else if (functionType->returnParameterTypes().size() == 1) + _functionCall.annotation().type = functionType->returnParameterTypes().front(); else - { - // @todo actually the return type should be an anonymous struct, - // but we change it to the type of the first return value until we have anonymous - // structs and tuples - if (functionType->returnParameterTypes().empty()) - _functionCall.annotation().type = make_shared<VoidType>(); - else - _functionCall.annotation().type = functionType->returnParameterTypes().front(); - } + _functionCall.annotation().type = make_shared<TupleType>(functionType->returnParameterTypes()); - //@todo would be nice to create a struct type from the arguments - // and then ask if that is implicitly convertible to the struct represented by the - // function parameters TypePointers const& parameterTypes = functionType->parameterTypes(); if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size()) { diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index f0c67bba..3d80a163 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -223,7 +223,7 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const { // "delete" is ok for all integer types if (_operator == Token::Delete) - return make_shared<VoidType>(); + return make_shared<TupleType>(); // no further unary operators for addresses else if (isAddress()) return TypePointer(); @@ -562,7 +562,7 @@ TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const { // "delete" and "~" is okay for FixedBytesType if (_operator == Token::Delete) - return make_shared<VoidType>(); + return make_shared<TupleType>(); else if (_operator == Token::BitNot) return shared_from_this(); @@ -617,7 +617,7 @@ u256 BoolType::literalValue(Literal const* _literal) const TypePointer BoolType::unaryOperatorResult(Token::Value _operator) const { if (_operator == Token::Delete) - return make_shared<VoidType>(); + return make_shared<TupleType>(); return (_operator == Token::Not) ? shared_from_this() : TypePointer(); } @@ -658,7 +658,7 @@ bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const { - return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer(); + return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer(); } TypePointer ReferenceType::unaryOperatorResult(Token::Value _operator) const @@ -672,9 +672,9 @@ TypePointer ReferenceType::unaryOperatorResult(Token::Value _operator) const case DataLocation::CallData: return TypePointer(); case DataLocation::Memory: - return make_shared<VoidType>(); + return make_shared<TupleType>(); case DataLocation::Storage: - return m_isPointer ? TypePointer() : make_shared<VoidType>(); + return m_isPointer ? TypePointer() : make_shared<TupleType>(); default: solAssert(false, ""); } @@ -1175,7 +1175,7 @@ set<string> StructType::membersMissingInMemory() const TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const { - return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer(); + return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer(); } bool EnumType::operator==(Type const& _other) const @@ -1222,6 +1222,40 @@ unsigned int EnumType::memberValue(ASTString const& _member) const BOOST_THROW_EXCEPTION(m_enum.createTypeError("Requested unknown enum value ." + _member)); } +bool TupleType::operator==(Type const& _other) const +{ + if (auto tupleType = dynamic_cast<TupleType const*>(&_other)) + return components() == tupleType->components(); + else + return false; +} + +string TupleType::toString(bool _short) const +{ + if (m_components.empty()) + return "tuple()"; + string str = "tuple("; + for (auto const& t: m_components) + str += t->toString(_short) + ", "; + str.resize(str.size() - 2); + return str + ")"; +} + +u256 TupleType::storageSize() const +{ + BOOST_THROW_EXCEPTION( + InternalCompilerError() + << errinfo_comment("Storage size of non-storable tuple type requested.")); +} + +unsigned TupleType::sizeOnStack() const +{ + unsigned size = 0; + for (auto const& t: m_components) + size += t->sizeOnStack(); + return size; +} + FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal): m_location(_isInternal ? Location::Internal : Location::External), m_isConstant(_function.isDeclaredConst()), @@ -1647,13 +1681,6 @@ string MappingType::canonicalName(bool) const return "mapping(" + keyType()->canonicalName(false) + " => " + valueType()->canonicalName(false) + ")"; } -u256 VoidType::storageSize() const -{ - BOOST_THROW_EXCEPTION( - InternalCompilerError() - << errinfo_comment("Storage size of non-storable void type requested.")); -} - bool TypeType::operator==(Type const& _other) const { if (_other.category() != category()) diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 7a65ca92..e73cd3cd 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -132,8 +132,8 @@ public: enum class Category { Integer, IntegerConstant, StringLiteral, Bool, Real, Array, - FixedBytes, Contract, Struct, Function, Enum, - Mapping, Void, TypeType, Modifier, Magic + FixedBytes, Contract, Struct, Function, Enum, Tuple, + Mapping, TypeType, Modifier, Magic }; /// @{ @@ -683,6 +683,28 @@ private: }; /** + * Type that can hold a finite sequence of values of different types. + */ +class TupleType: public Type +{ +public: + virtual Category category() const override { return Category::Tuple; } + explicit TupleType(std::vector<TypePointer> const& _types = std::vector<TypePointer>()): m_components(_types) {} + virtual bool operator==(Type const& _other) const override; + virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual std::string toString(bool) const override; + virtual bool canBeStored() const override { return false; } + virtual u256 storageSize() const override; + virtual bool canLiveOutsideStorage() const override { return false; } + virtual unsigned sizeOnStack() const override; + + std::vector<TypePointer> const& components() const { return m_components; } + +private: + std::vector<TypePointer> const m_components; +}; + +/** * The type of a function, identified by its (return) parameter types. * @todo the return parameters should also have names, i.e. return parameters should be a struct * type. @@ -875,24 +897,6 @@ private: }; /** - * The void type, can only be implicitly used as the type that is returned by functions without - * return parameters. - */ -class VoidType: public Type -{ -public: - virtual Category category() const override { return Category::Void; } - VoidType() {} - - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } - virtual std::string toString(bool) const override { return "void"; } - virtual bool canBeStored() const override { return false; } - virtual u256 storageSize() const override; - virtual bool canLiveOutsideStorage() const override { return false; } - virtual unsigned sizeOnStack() const override { return 0; } -}; - -/** * The type of a type reference. The type of "uint32" when used in "a = uint32(2)" is an example * of a TypeType. * For super contracts or libraries, this has members directly. |