diff --git a/Changelog.md b/Changelog.md index c4c5450eb..c316e891f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,7 +1,7 @@ ### 0.6.3 (unreleased) Language Features: - + * Allow contract types and enums as keys for mappings. Compiler Features: diff --git a/docs/grammar.txt b/docs/grammar.txt index 06b306499..490e53bda 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -59,7 +59,7 @@ TypeName = ElementaryTypeName UserDefinedTypeName = Identifier ( '.' Identifier )* -Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')' +Mapping = 'mapping' '(' ( ElementaryTypeName | UserDefinedTypeName ) '=>' TypeName ')' ArrayTypeName = TypeName '[' Expression? ']' FunctionTypeName = 'function' FunctionTypeParameterList ( 'internal' | 'external' | StateMutability )* ( 'returns' FunctionTypeParameterList )? diff --git a/docs/types/mapping-types.rst b/docs/types/mapping-types.rst index 5b41d4bd0..acb48fb8a 100644 --- a/docs/types/mapping-types.rst +++ b/docs/types/mapping-types.rst @@ -7,9 +7,8 @@ Mapping Types Mapping types use the syntax ``mapping(_KeyType => _ValueType)`` and variables of mapping type are declared using the syntax ``mapping(_KeyType => _ValueType) _VariableName``. The ``_KeyType`` can be any -built-in value type plus ``bytes`` and ``string``. User-defined -or complex types such as contract types, enums, mappings, structs or array types -apart from ``bytes`` and ``string`` are not allowed. +built-in value type, ``bytes``, ``string``, or any contract or enum type. Other user-defined +or complex types, such as mappings, structs or array types are not allowed. ``_ValueType`` can be any type, including mappings, arrays and structs. You can think of mappings as `hash tables `_, which are virtually initialised diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 82a362989..1673424a2 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2876,6 +2876,25 @@ void TypeChecker::endVisit(Literal const& _literal) _literal.annotation().isPure = true; } +bool TypeChecker::visit(Mapping const& _mapping) +{ + if (auto const* keyType = dynamic_cast(&_mapping.keyType())) + { + if ( + keyType->annotation().type->category() != Type::Category::Contract && + keyType->annotation().type->category() != Type::Category::Enum + ) + m_errorReporter.typeError( + keyType->location(), + "Only elementary types, contract types or enums are allowed as mapping keys." + ); + } + else + solAssert(dynamic_cast(&_mapping.keyType()), ""); + return true; +} + + bool TypeChecker::contractDependenciesAreCyclic( ContractDefinition const& _contract, std::set const& _seenContracts diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index d428a6ac9..5dae91e2d 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -143,6 +143,7 @@ private: bool visit(Identifier const& _identifier) override; void endVisit(ElementaryTypeNameExpression const& _expr) override; void endVisit(Literal const& _literal) override; + bool visit(Mapping const& _mapping) override; bool contractDependenciesAreCyclic( ContractDefinition const& _contract, diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 5a5e561c9..4bb9a0570 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1152,18 +1152,18 @@ public: Mapping( int64_t _id, SourceLocation const& _location, - ASTPointer const& _keyType, + ASTPointer const& _keyType, ASTPointer const& _valueType ): TypeName(_id, _location), m_keyType(_keyType), m_valueType(_valueType) {} void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; - ElementaryTypeName const& keyType() const { return *m_keyType; } + TypeName const& keyType() const { return *m_keyType; } TypeName const& valueType() const { return *m_valueType; } private: - ASTPointer m_keyType; + ASTPointer m_keyType; ASTPointer m_valueType; }; diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index 8bdecdbb8..aa28e3f17 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -511,7 +511,7 @@ ASTPointer ASTJsonImporter::createMapping(Json::Value const& _node) { return createASTNode( _node, - createElementaryTypeName(member(_node, "keyType")), + convertJsonToASTNode(member(_node, "keyType")), convertJsonToASTNode(member(_node, "valueType")) ); } diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index bd2099c4a..0798ebc37 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -159,21 +159,70 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) solAssert(!_varDecl.isConstant(), ""); solAssert(_varDecl.isStateVariable(), ""); - solUnimplementedAssert(type->isValueType(), ""); - - return m_context.functionCollector()->createFunction(functionName, [&]() { - pair slot_offset = m_context.storageLocationOfVariable(_varDecl); - - return Whiskers(R"( - function () -> rval { - rval := () + if (auto const* mappingType = dynamic_cast(type)) + return m_context.functionCollector()->createFunction(functionName, [&]() { + pair slot_offset = m_context.storageLocationOfVariable(_varDecl); + solAssert(slot_offset.second == 0, ""); + FunctionType funType(_varDecl); + solUnimplementedAssert(funType.returnParameterTypes().size() == 1, ""); + TypePointer returnType = funType.returnParameterTypes().front(); + unsigned num_keys = 0; + stringstream indexAccesses; + string slot = m_context.newYulVariable(); + do + { + solUnimplementedAssert( + mappingType->keyType()->sizeOnStack() == 1, + "Multi-slot mapping key unimplemented - might not be a problem" + ); + indexAccesses << + slot << + " := " << + m_utils.mappingIndexAccessFunction(*mappingType, *mappingType->keyType()) << + "(" << + slot; + if (mappingType->keyType()->sizeOnStack() > 0) + indexAccesses << + ", " << + suffixedVariableNameList("key", num_keys, num_keys + mappingType->keyType()->sizeOnStack()); + indexAccesses << ")\n"; + num_keys += mappingType->keyType()->sizeOnStack(); } - )") - ("functionName", functionName) - ("readStorage", m_utils.readFromStorage(*type, slot_offset.second, false)) - ("slot", slot_offset.first.str()) - .render(); - }); + while ((mappingType = dynamic_cast(mappingType->valueType()))); + + return Whiskers(R"( + function () -> rval { + let := + + rval := () + } + )") + ("functionName", functionName) + ("keys", suffixedVariableNameList("key", 0, num_keys)) + ("readStorage", m_utils.readFromStorage(*returnType, 0, false)) + ("indexAccesses", indexAccesses.str()) + ("slot", slot) + ("base", slot_offset.first.str()) + .render(); + }); + else + { + solUnimplementedAssert(type->isValueType(), ""); + + return m_context.functionCollector()->createFunction(functionName, [&]() { + pair slot_offset = m_context.storageLocationOfVariable(_varDecl); + + return Whiskers(R"( + function () -> rval { + rval := () + } + )") + ("functionName", functionName) + ("readStorage", m_utils.readFromStorage(*type, slot_offset.second, false)) + ("slot", slot_offset.first.str()) + .render(); + }); + } } string IRGenerator::constructorCode(ContractDefinition const& _contract) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 2b0e9131f..6072ef6bb 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1019,16 +1019,22 @@ ASTPointer Parser::parseMapping() ASTNodeFactory nodeFactory(*this); expectToken(Token::Mapping); expectToken(Token::LParen); - ASTPointer keyType; + ASTPointer keyType; Token token = m_scanner->currentToken(); - if (!TokenTraits::isElementaryTypeName(token)) - fatalParserError(string("Expected elementary type name for mapping key type")); unsigned firstSize; unsigned secondSize; tie(firstSize, secondSize) = m_scanner->currentTokenInfo(); - ElementaryTypeNameToken elemTypeName(token, firstSize, secondSize); - keyType = ASTNodeFactory(*this).createNode(elemTypeName); - m_scanner->next(); + if (token == Token::Identifier) + keyType = parseUserDefinedTypeName(); + else if (TokenTraits::isElementaryTypeName(token)) + { + keyType = ASTNodeFactory(*this).createNode( + ElementaryTypeNameToken{token, firstSize, secondSize} + ); + m_scanner->next(); + } + else + fatalParserError(string("Expected elementary type name or identifier for mapping key type")); expectToken(Token::Arrow); bool const allowVar = false; ASTPointer valueType = parseTypeName(allowVar); diff --git a/test/libsolidity/ASTJSON/mappings.json b/test/libsolidity/ASTJSON/mappings.json new file mode 100644 index 000000000..0bd1ecd98 --- /dev/null +++ b/test/libsolidity/ASTJSON/mappings.json @@ -0,0 +1,227 @@ +{ + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 17 + ] + }, + "id": 18, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 17, + "linearizedBaseContracts": + [ + 17 + ], + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "canonicalName": "C.E", + "id": 4, + "members": + [ + { + "id": 1, + "name": "A", + "nodeType": "EnumValue", + "src": "26:1:1" + }, + { + "id": 2, + "name": "B", + "nodeType": "EnumValue", + "src": "29:1:1" + }, + { + "id": 3, + "name": "C", + "nodeType": "EnumValue", + "src": "32:1:1" + } + ], + "name": "E", + "nodeType": "EnumDefinition", + "src": "17:18:1" + }, + { + "constant": false, + "id": 8, + "name": "a", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 17, + "src": "40:20:1", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_contract$_C_$17_$_t_bool_$", + "typeString": "mapping(contract C => bool)" + }, + "typeName": + { + "id": 7, + "keyType": + { + "contractScope": null, + "id": 5, + "name": "C", + "nodeType": "UserDefinedTypeName", + "referencedDeclaration": 17, + "src": "48:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_contract$_C_$17", + "typeString": "contract C" + } + }, + "nodeType": "Mapping", + "src": "40:18:1", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_contract$_C_$17_$_t_bool_$", + "typeString": "mapping(contract C => bool)" + }, + "valueType": + { + "id": 6, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "53:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + } + }, + "value": null, + "visibility": "internal" + }, + { + "constant": false, + "id": 12, + "name": "b", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 17, + "src": "66:26:1", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_address_$_t_bool_$", + "typeString": "mapping(address => bool)" + }, + "typeName": + { + "id": 11, + "keyType": + { + "id": 9, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "74:7:1", + "typeDescriptions": + { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "nodeType": "Mapping", + "src": "66:24:1", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_address_$_t_bool_$", + "typeString": "mapping(address => bool)" + }, + "valueType": + { + "id": 10, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "85:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + } + }, + "value": null, + "visibility": "internal" + }, + { + "constant": false, + "id": 16, + "name": "c", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 17, + "src": "98:20:1", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_enum$_E_$4_$_t_bool_$", + "typeString": "mapping(enum C.E => bool)" + }, + "typeName": + { + "id": 15, + "keyType": + { + "contractScope": null, + "id": 13, + "name": "E", + "nodeType": "UserDefinedTypeName", + "referencedDeclaration": 4, + "src": "106:1:1", + "typeDescriptions": + { + "typeIdentifier": "t_enum$_E_$4", + "typeString": "enum C.E" + } + }, + "nodeType": "Mapping", + "src": "98:18:1", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_enum$_E_$4_$_t_bool_$", + "typeString": "mapping(enum C.E => bool)" + }, + "valueType": + { + "id": 14, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "111:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + } + }, + "value": null, + "visibility": "internal" + } + ], + "scope": 18, + "src": "0:121:1" + } + ], + "src": "0:122:1" +} diff --git a/test/libsolidity/ASTJSON/mappings.sol b/test/libsolidity/ASTJSON/mappings.sol new file mode 100644 index 000000000..794396a7d --- /dev/null +++ b/test/libsolidity/ASTJSON/mappings.sol @@ -0,0 +1,8 @@ +contract C { + enum E { A, B, C } + mapping(C => bool) a; + mapping(address => bool) b; + mapping(E => bool) c; +} + +// ---- diff --git a/test/libsolidity/ASTJSON/mappings_legacy.json b/test/libsolidity/ASTJSON/mappings_legacy.json new file mode 100644 index 000000000..79814effe --- /dev/null +++ b/test/libsolidity/ASTJSON/mappings_legacy.json @@ -0,0 +1,248 @@ +{ + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 17 + ] + } + }, + "children": + [ + { + "attributes": + { + "abstract": false, + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 17 + ], + "name": "C", + "scope": 18 + }, + "children": + [ + { + "attributes": + { + "canonicalName": "C.E", + "name": "E" + }, + "children": + [ + { + "attributes": + { + "name": "A" + }, + "id": 1, + "name": "EnumValue", + "src": "26:1:1" + }, + { + "attributes": + { + "name": "B" + }, + "id": 2, + "name": "EnumValue", + "src": "29:1:1" + }, + { + "attributes": + { + "name": "C" + }, + "id": 3, + "name": "EnumValue", + "src": "32:1:1" + } + ], + "id": 4, + "name": "EnumDefinition", + "src": "17:18:1" + }, + { + "attributes": + { + "constant": false, + "name": "a", + "overrides": null, + "scope": 17, + "stateVariable": true, + "storageLocation": "default", + "type": "mapping(contract C => bool)", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "type": "mapping(contract C => bool)" + }, + "children": + [ + { + "attributes": + { + "contractScope": null, + "name": "C", + "referencedDeclaration": 17, + "type": "contract C" + }, + "id": 5, + "name": "UserDefinedTypeName", + "src": "48:1:1" + }, + { + "attributes": + { + "name": "bool", + "type": "bool" + }, + "id": 6, + "name": "ElementaryTypeName", + "src": "53:4:1" + } + ], + "id": 7, + "name": "Mapping", + "src": "40:18:1" + } + ], + "id": 8, + "name": "VariableDeclaration", + "src": "40:20:1" + }, + { + "attributes": + { + "constant": false, + "name": "b", + "overrides": null, + "scope": 17, + "stateVariable": true, + "storageLocation": "default", + "type": "mapping(address => bool)", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "type": "mapping(address => bool)" + }, + "children": + [ + { + "attributes": + { + "name": "address", + "type": "address" + }, + "id": 9, + "name": "ElementaryTypeName", + "src": "74:7:1" + }, + { + "attributes": + { + "name": "bool", + "type": "bool" + }, + "id": 10, + "name": "ElementaryTypeName", + "src": "85:4:1" + } + ], + "id": 11, + "name": "Mapping", + "src": "66:24:1" + } + ], + "id": 12, + "name": "VariableDeclaration", + "src": "66:26:1" + }, + { + "attributes": + { + "constant": false, + "name": "c", + "overrides": null, + "scope": 17, + "stateVariable": true, + "storageLocation": "default", + "type": "mapping(enum C.E => bool)", + "value": null, + "visibility": "internal" + }, + "children": + [ + { + "attributes": + { + "type": "mapping(enum C.E => bool)" + }, + "children": + [ + { + "attributes": + { + "contractScope": null, + "name": "E", + "referencedDeclaration": 4, + "type": "enum C.E" + }, + "id": 13, + "name": "UserDefinedTypeName", + "src": "106:1:1" + }, + { + "attributes": + { + "name": "bool", + "type": "bool" + }, + "id": 14, + "name": "ElementaryTypeName", + "src": "111:4:1" + } + ], + "id": 15, + "name": "Mapping", + "src": "98:18:1" + } + ], + "id": 16, + "name": "VariableDeclaration", + "src": "98:20:1" + } + ], + "id": 17, + "name": "ContractDefinition", + "src": "0:121:1" + } + ], + "id": 18, + "name": "SourceUnit", + "src": "0:122:1" +} diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index eb01764bc..027fcf1a0 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -61,10 +61,26 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer } m_settings.erase("compileViaYul"); } + if (m_settings.count("ABIEncoderV1Only")) + { + if (m_settings["ABIEncoderV1Only"] == "true") + { + m_validatedSettings["ABIEncoderV1Only"] = "true"; + m_runWithABIEncoderV1Only = true; + } + m_settings.erase("ABIEncoderV1Only"); + } parseExpectations(file); soltestAssert(!m_tests.empty(), "No tests specified in " + _filename); } +bool SemanticTest::validateSettings(langutil::EVMVersion _evmVersion) +{ + if (m_runWithABIEncoderV1Only && solidity::test::CommonOptions::get().useABIEncoderV2) + return false; + return EVMVersionRestrictedTestCase::validateSettings(_evmVersion); +} + TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) { for(bool compileViaYul: set{!m_runWithoutYul, m_runWithYul}) diff --git a/test/libsolidity/SemanticTest.h b/test/libsolidity/SemanticTest.h index 680e03316..5cd8e5cc7 100644 --- a/test/libsolidity/SemanticTest.h +++ b/test/libsolidity/SemanticTest.h @@ -44,6 +44,8 @@ public: explicit SemanticTest(std::string const& _filename, langutil::EVMVersion _evmVersion); + bool validateSettings(langutil::EVMVersion _evmVersion) override; + TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; void printSource(std::ostream &_stream, std::string const& _linePrefix = "", bool _formatted = false) const override; void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix = "") const override; @@ -64,6 +66,7 @@ private: std::vector m_tests; bool m_runWithYul = false; bool m_runWithoutYul = true; + bool m_runWithABIEncoderV1Only = false; }; } diff --git a/test/libsolidity/semanticTests/types/mapping_contract_key.sol b/test/libsolidity/semanticTests/types/mapping_contract_key.sol new file mode 100644 index 000000000..877ead6de --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_contract_key.sol @@ -0,0 +1,28 @@ +interface A {} +contract test { + mapping(A => uint8) table; + function get(A k) public returns (uint8 v) { + return table[k]; + } + function set(A k, uint8 v) public { + table[k] = v; + } +} +// ==== +// compileViaYul: also +// ---- +// get(address): 0 -> 0 +// get(address): 0x01 -> 0 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x01, 0xa1 -> +// get(address): 0 -> 0 +// get(address): 0x01 -> 0xa1 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x00, 0xef -> +// get(address): 0 -> 0xef +// get(address): 0x01 -> 0xa1 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x01, 0x05 -> +// get(address): 0 -> 0xef +// get(address): 0x01 -> 0x05 +// get(address): 0xa7 -> 0 diff --git a/test/libsolidity/semanticTests/types/mapping_contract_key_getter.sol b/test/libsolidity/semanticTests/types/mapping_contract_key_getter.sol new file mode 100644 index 000000000..ee916a63d --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_contract_key_getter.sol @@ -0,0 +1,38 @@ +interface A {} +contract test { + mapping(A => uint8) public table; + function set(A k, uint8 v) public { + table[k] = v; + } + function get(A k) public returns (uint8) { + return this.table(k); + } +} +// ---- +// table(address): 0 -> 0 +// table(address): 0x01 -> 0 +// table(address): 0xa7 -> 0 +// get(address): 0 -> 0 +// get(address): 0x01 -> 0 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x01, 0xa1 -> +// table(address): 0 -> 0 +// table(address): 0x01 -> 0xa1 +// table(address): 0xa7 -> 0 +// get(address): 0 -> 0 +// get(address): 0x01 -> 0xa1 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x00, 0xef -> +// table(address): 0 -> 0xef +// table(address): 0x01 -> 0xa1 +// table(address): 0xa7 -> 0 +// get(address): 0 -> 0xef +// get(address): 0x01 -> 0xa1 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x01, 0x05 -> +// table(address): 0 -> 0xef +// table(address): 0x01 -> 0x05 +// table(address): 0xa7 -> 0 +// get(address): 0 -> 0xef +// get(address): 0x01 -> 0x05 +// get(address): 0xa7 -> 0 diff --git a/test/libsolidity/semanticTests/types/mapping_contract_key_library.sol b/test/libsolidity/semanticTests/types/mapping_contract_key_library.sol new file mode 100644 index 000000000..bedb605c1 --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_contract_key_library.sol @@ -0,0 +1,35 @@ +interface A {} +library L { + function get(mapping(A => uint8) storage table, A k) external returns (uint8) { + return table[k]; + } + function set(mapping(A => uint8) storage table, A k, uint8 v) external { + table[k] = v; + } +} +contract test { + mapping(A => uint8) table; + function get(A k) public returns (uint8 v) { + return L.get(table, k); + } + function set(A k, uint8 v) public { + L.set(table, k, v); + } +} +// ---- +// library: L +// get(address): 0 -> 0 +// get(address): 0x01 -> 0 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x01, 0xa1 -> +// get(address): 0 -> 0 +// get(address): 0x01 -> 0xa1 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x00, 0xef -> +// get(address): 0 -> 0xef +// get(address): 0x01 -> 0xa1 +// get(address): 0xa7 -> 0 +// set(address,uint8): 0x01, 0x05 -> +// get(address): 0 -> 0xef +// get(address): 0x01 -> 0x05 +// get(address): 0xa7 -> 0 diff --git a/test/libsolidity/semanticTests/types/mapping_enum_key.sol b/test/libsolidity/semanticTests/types/mapping_enum_key.sol new file mode 100644 index 000000000..befc46a87 --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_enum_key.sol @@ -0,0 +1,30 @@ +enum E { A, B, C } +contract test { + mapping(E => uint8) table; + function get(E k) public returns (uint8 v) { + return table[k]; + } + function set(E k, uint8 v) public { + table[k] = v; + } +} +// ==== +// compileViaYul: also +// ---- +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0 +// get(uint8): 0x02 -> 0 +// get(uint8): 0x03 -> FAILURE +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0xa1 -> +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x00, 0xef -> +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0x05 -> +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0x05 +// get(uint8): 0xa7 -> FAILURE diff --git a/test/libsolidity/semanticTests/types/mapping_enum_key_getter.sol b/test/libsolidity/semanticTests/types/mapping_enum_key_getter.sol new file mode 100644 index 000000000..9b1b23b83 --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_enum_key_getter.sol @@ -0,0 +1,40 @@ +contract test { + enum E { A, B, C } + mapping(E => uint8) public table; + function set(E k, uint8 v) public { + table[k] = v; + } + function get(E k) public returns (uint8) { + return this.table(k); + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// table(uint8): 0 -> 0 +// table(uint8): 0x01 -> 0 +// table(uint8): 0xa7 -> 0 +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0xa1 -> +// table(uint8): 0 -> 0 +// table(uint8): 0x01 -> 0xa1 +// table(uint8): 0xa7 -> 0 +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x00, 0xef -> +// table(uint8): 0 -> 0xef +// table(uint8): 0x01 -> 0xa1 +// table(uint8): 0xa7 -> 0 +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0x05 -> +// table(uint8): 0 -> 0xef +// table(uint8): 0x01 -> 0x05 +// table(uint8): 0xa7 -> 0 +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0x05 +// get(uint8): 0xa7 -> FAILURE diff --git a/test/libsolidity/semanticTests/types/mapping_enum_key_getter_v2.sol b/test/libsolidity/semanticTests/types/mapping_enum_key_getter_v2.sol new file mode 100644 index 000000000..f1fd184ed --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_enum_key_getter_v2.sol @@ -0,0 +1,39 @@ +pragma experimental ABIEncoderV2; +contract test { + enum E { A, B, C } + mapping(E => uint8) public table; + function set(E k, uint8 v) public { + table[k] = v; + } + function get(E k) public returns (uint8) { + return this.table(k); + } +} +// ---- +// table(uint8): 0 -> 0 +// table(uint8): 0x01 -> 0 +// table(uint8): 0xa7 -> FAILURE +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0xa1 -> +// table(uint8): 0 -> 0 +// table(uint8): 0x01 -> 0xa1 +// table(uint8): 0xa7 -> FAILURE +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x00, 0xef -> +// table(uint8): 0 -> 0xef +// table(uint8): 0x01 -> 0xa1 +// table(uint8): 0xa7 -> FAILURE +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0x05 -> +// table(uint8): 0 -> 0xef +// table(uint8): 0x01 -> 0x05 +// table(uint8): 0xa7 -> FAILURE +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0x05 +// get(uint8): 0xa7 -> FAILURE diff --git a/test/libsolidity/semanticTests/types/mapping_enum_key_library.sol b/test/libsolidity/semanticTests/types/mapping_enum_key_library.sol new file mode 100644 index 000000000..d1484238d --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_enum_key_library.sol @@ -0,0 +1,35 @@ +enum E { A, B, C } +library L { + function get(mapping(E => uint8) storage table, E k) external returns (uint8) { + return table[k]; + } + function set(mapping(E => uint8) storage table, E k, uint8 v) external { + table[k] = v; + } +} +contract test { + mapping(E => uint8) table; + function get(E k) public returns (uint8 v) { + return L.get(table, k); + } + function set(E k, uint8 v) public { + L.set(table, k, v); + } +} +// ---- +// library: L +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0xa1 -> +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x00, 0xef -> +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0x05 -> +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0x05 +// get(uint8): 0xa7 -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/mapping_enum_key_getter.sol b/test/libsolidity/semanticTests/viaYul/mapping_enum_key_getter.sol new file mode 100644 index 000000000..78d9a39b9 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/mapping_enum_key_getter.sol @@ -0,0 +1,26 @@ +pragma experimental ABIEncoderV2; +contract test { + enum E { A, B, C } + mapping(E => uint8) public table; + function set(E k, uint8 v) public { + table[k] = v; + } +} +// ==== +// compileViaYul: also +// ---- +// table(uint8): 0 -> 0 +// table(uint8): 0x01 -> 0 +// table(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0xa1 -> +// table(uint8): 0 -> 0 +// table(uint8): 0x01 -> 0xa1 +// table(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x00, 0xef -> +// table(uint8): 0 -> 0xef +// table(uint8): 0x01 -> 0xa1 +// table(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0x05 -> +// table(uint8): 0 -> 0xef +// table(uint8): 0x01 -> 0x05 +// table(uint8): 0xa7 -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/mapping_getters.sol b/test/libsolidity/semanticTests/viaYul/mapping_getters.sol new file mode 100644 index 000000000..1db4eb8c5 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/mapping_getters.sol @@ -0,0 +1,40 @@ +contract test { + mapping(uint256 => uint256) public m1; + mapping(uint256 => mapping(uint256 => uint256)) public m2; + function set(uint256 k, uint256 v) public { + m1[k] = v; + } + function set(uint256 k1, uint256 k2, uint256 v) public { + m2[k1][k2] = v; + } +} +// ==== +// compileViaYul: also +// ---- +// m1(uint256): 0 -> 0 +// m1(uint256): 0x01 -> 0 +// m1(uint256): 0xa7 -> 0 +// set(uint256,uint256): 0x01, 0xa1 -> +// m1(uint256): 0 -> 0 +// m1(uint256): 0x01 -> 0xa1 +// m1(uint256): 0xa7 -> 0 +// set(uint256,uint256): 0x00, 0xef -> +// m1(uint256): 0 -> 0xef +// m1(uint256): 0x01 -> 0xa1 +// m1(uint256): 0xa7 -> 0 +// set(uint256,uint256): 0x01, 0x05 -> +// m1(uint256): 0 -> 0xef +// m1(uint256): 0x01 -> 0x05 +// m1(uint256): 0xa7 -> 0 +// m2(uint256,uint256): 0, 0 -> 0 +// m2(uint256,uint256): 0, 0x01 -> 0 +// m2(uint256,uint256): 0xa7, 0 -> 0 +// m2(uint256,uint256): 0xa7, 0x01 -> 0 +// set(uint256,uint256,uint256): 0xa7, 0x01, 0x23 +// m2(uint256,uint256): 0, 0x01 -> 0 +// m2(uint256,uint256): 0xa7, 0 -> 0 +// m2(uint256,uint256): 0xa7, 0x01 -> 0x23 +// set(uint256,uint256,uint256): 0, 0x01, 0xef +// m2(uint256,uint256): 0, 0x01 -> 0xef +// m2(uint256,uint256): 0xa7, 0 -> 0 +// m2(uint256,uint256): 0xa7, 0x01 -> 0x23 diff --git a/test/libsolidity/syntaxTests/mappings/contract_mapping.sol b/test/libsolidity/syntaxTests/mappings/contract_mapping.sol new file mode 100644 index 000000000..f1a4f54a9 --- /dev/null +++ b/test/libsolidity/syntaxTests/mappings/contract_mapping.sol @@ -0,0 +1,9 @@ +interface I {} +contract J {} +contract C { + mapping(I => bool) i; + mapping(J => bool) j; + function f(I x, J y) public view returns (bool, bool) { + return (i[x], j[y]); + } +} diff --git a/test/libsolidity/syntaxTests/mappings/contract_mapping_invalid.sol b/test/libsolidity/syntaxTests/mappings/contract_mapping_invalid.sol new file mode 100644 index 000000000..ffe100fac --- /dev/null +++ b/test/libsolidity/syntaxTests/mappings/contract_mapping_invalid.sol @@ -0,0 +1,13 @@ +interface I {} +contract J {} +contract C { + mapping(I => bool) i; + mapping(J => bool) j; + function f(I x, J y, address z) public view returns (bool, bool, bool) { + return (i[y], j[x], i[z]); + } +} +// ---- +// TypeError: (189-190): Type contract J is not implicitly convertible to expected type contract I. +// TypeError: (195-196): Type contract I is not implicitly convertible to expected type contract J. +// TypeError: (201-202): Type address is not implicitly convertible to expected type contract I. diff --git a/test/libsolidity/syntaxTests/mappings/enum_mapping.sol b/test/libsolidity/syntaxTests/mappings/enum_mapping.sol new file mode 100644 index 000000000..88163302a --- /dev/null +++ b/test/libsolidity/syntaxTests/mappings/enum_mapping.sol @@ -0,0 +1,7 @@ +enum E { A, B, C } +contract C { + mapping(E => bool) e; + function f(E v) public view returns (bool, bool) { + return (e[v], e[E.A]); + } +} diff --git a/test/libsolidity/syntaxTests/mappings/enum_mapping_invalid.sol b/test/libsolidity/syntaxTests/mappings/enum_mapping_invalid.sol new file mode 100644 index 000000000..02eb516e6 --- /dev/null +++ b/test/libsolidity/syntaxTests/mappings/enum_mapping_invalid.sol @@ -0,0 +1,10 @@ +enum E { A, B, C } +contract C { + mapping(E => bool) e; + function f(uint256 a, uint8 b) public view returns (bool, bool) { + return (e[a], e[b]); + } +} +// ---- +// TypeError: (146-147): Type uint256 is not implicitly convertible to expected type enum E. +// TypeError: (152-153): Type uint8 is not implicitly convertible to expected type enum E. diff --git a/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_2.sol b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_2.sol index 713cddeb3..e15e1aea9 100644 --- a/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_2.sol +++ b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_2.sol @@ -5,4 +5,4 @@ contract c { mapping(S => uint) data; } // ---- -// ParserError: (47-48): Expected elementary type name for mapping key type +// TypeError: (47-48): Only elementary types, contract types or enums are allowed as mapping keys. diff --git a/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_3.sol b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_3.sol index 655af9de7..b17cf4139 100644 --- a/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_3.sol +++ b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_3.sol @@ -5,4 +5,4 @@ contract c { mapping(S => uint) data; } // ---- -// ParserError: (49-50): Expected elementary type name for mapping key type +// TypeError: (49-50): Only elementary types, contract types or enums are allowed as mapping keys. diff --git a/test/libsolidity/syntaxTests/parsing/variable_definition_in_mapping.sol b/test/libsolidity/syntaxTests/parsing/variable_definition_in_mapping.sol index 61f5be532..f9ccfb580 100644 --- a/test/libsolidity/syntaxTests/parsing/variable_definition_in_mapping.sol +++ b/test/libsolidity/syntaxTests/parsing/variable_definition_in_mapping.sol @@ -4,4 +4,4 @@ contract test { } } // ---- -// ParserError: (44-47): Expected elementary type name for mapping key type +// ParserError: (44-47): Expected elementary type name or identifier for mapping key type