diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index 2282c9bc47..a790573036 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -3881,7 +3881,7 @@ func TestRuntimeImportExportDictionaryValue(t *testing.T) { KeyType: interpreter.PrimitiveStaticTypeString, ValueType: interpreter.DictionaryStaticType{ KeyType: interpreter.PrimitiveStaticTypeSignedInteger, - ValueType: interpreter.PrimitiveStaticTypeAnyStruct, + ValueType: interpreter.PrimitiveStaticTypeHashableStruct, }, }, @@ -3916,52 +3916,47 @@ func TestRuntimeImportExportDictionaryValue(t *testing.T) { t.Run("import dictionary with heterogeneous keys", func(t *testing.T) { t.Parallel() - script := - `pub fun main(arg: Foo) { - } + dictionaryWithHeterogenousKeys := cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("foo"), + Value: cadence.String("value1"), + }, + { + Key: cadence.NewInt(5), + Value: cadence.String("value2"), + }, + }) - pub struct Foo { - pub var a: AnyStruct + inter := newTestInterpreter(t) - init() { - self.a = nil - } - }` + actual, err := ImportValue( + inter, + interpreter.EmptyLocationRange, + nil, + dictionaryWithHeterogenousKeys, + sema.AnyStructType, + ) + require.NoError(t, err) - // Struct with nested malformed dictionary value - malformedStruct := cadence.Struct{ - StructType: &cadence.StructType{ - Location: common.ScriptLocation{}, - QualifiedIdentifier: "Foo", - Fields: []cadence.Field{ - { - Identifier: "a", - Type: cadence.AnyStructType{}, - }, + AssertValuesEqual( + t, + inter, + interpreter.NewDictionaryValue( + inter, + interpreter.EmptyLocationRange, + interpreter.DictionaryStaticType{ + KeyType: interpreter.PrimitiveStaticTypeHashableStruct, + ValueType: interpreter.PrimitiveStaticTypeString, }, - }, - Fields: []cadence.Value{ - cadence.NewDictionary([]cadence.KeyValuePair{ - { - Key: cadence.String("foo"), - Value: cadence.String("value1"), - }, - { - Key: cadence.NewInt(5), - Value: cadence.String("value2"), - }, - }), - }, - } - - _, err := executeTestScript(t, script, malformedStruct) - RequireError(t, err) - assertUserError(t, err) - var argErr *InvalidEntryPointArgumentError - require.ErrorAs(t, err, &argErr) + interpreter.NewUnmeteredStringValue("foo"), + interpreter.NewUnmeteredStringValue("value1"), - assert.Contains(t, argErr.Error(), "cannot import dictionary: keys does not belong to the same type") + interpreter.NewIntValueFromInt64(nil, 5), + interpreter.NewUnmeteredStringValue("value2"), + ), + actual, + ) }) t.Run("nested dictionary with mismatching element", func(t *testing.T) { diff --git a/runtime/sema/type_test.go b/runtime/sema/type_test.go index 3233d9c323..f4ec6bc233 100644 --- a/runtime/sema/type_test.go +++ b/runtime/sema/type_test.go @@ -778,10 +778,11 @@ func TestCommonSuperType(t *testing.T) { testLeastCommonSuperType := func(t *testing.T, tests []testCase) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + tt := LeastCommonSuperType(test.types...) assert.Equal( t, test.expectedSuperType, - LeastCommonSuperType(test.types...), + tt, ) }) } @@ -901,7 +902,7 @@ func TestCommonSuperType(t *testing.T) { StringType, Int8Type, }, - expectedSuperType: AnyStructType, + expectedSuperType: HashableStructType, }, { name: "all nil", @@ -1115,7 +1116,7 @@ func TestCommonSuperType(t *testing.T) { stringArray, &VariableSizedType{Type: BoolType}, }, - expectedSuperType: &VariableSizedType{Type: AnyStructType}, + expectedSuperType: &VariableSizedType{Type: HashableStructType}, }, { name: "simple-typed array & resource array", @@ -1239,7 +1240,7 @@ func TestCommonSuperType(t *testing.T) { }, expectedSuperType: &DictionaryType{ KeyType: StringType, - ValueType: AnyStructType, + ValueType: HashableStructType, }, }, { @@ -1404,7 +1405,7 @@ func TestCommonSuperType(t *testing.T) { StoragePathType, StringType, }, - expectedSuperType: AnyStructType, + expectedSuperType: HashableStructType, }, } @@ -1658,7 +1659,9 @@ func TestCommonSuperType(t *testing.T) { Int8Type, StringType, }, - expectedSuperType: AnyStructType, + expectedSuperType: &OptionalType{ + Type: HashableStructType, + }, }, { name: "nil with simple type", @@ -1677,7 +1680,9 @@ func TestCommonSuperType(t *testing.T) { Int8Type, StringType, }, - expectedSuperType: AnyStructType, + expectedSuperType: &OptionalType{ + Type: HashableStructType, + }, }, { name: "multi-level simple optional types", diff --git a/runtime/tests/checker/arrays_dictionaries_test.go b/runtime/tests/checker/arrays_dictionaries_test.go index 09cab5a4ca..700f49df18 100644 --- a/runtime/tests/checker/arrays_dictionaries_test.go +++ b/runtime/tests/checker/arrays_dictionaries_test.go @@ -133,7 +133,11 @@ func TestCheckInvalidDictionaryKeys(t *testing.T) { t.Parallel() _, err := ParseAndCheck(t, ` - let z = {"a": 1, true: 2} + let f = fun (_ x: Int): Int { + return x + 10 + } + + let z = {f: 1, true: 2} `) errs := RequireCheckerErrors(t, err, 1) diff --git a/runtime/tests/checker/type_inference_test.go b/runtime/tests/checker/type_inference_test.go index 9cd0d3365e..baa3a85f9e 100644 --- a/runtime/tests/checker/type_inference_test.go +++ b/runtime/tests/checker/type_inference_test.go @@ -787,7 +787,7 @@ func TestCheckArraySupertypeInference(t *testing.T) { { name: "mixed simple values", code: `let x = [0, true]`, - expectedElementType: sema.AnyStructType, + expectedElementType: sema.HashableStructType, }, { name: "signed integer values", @@ -893,7 +893,7 @@ func TestCheckArraySupertypeInference(t *testing.T) { code: `let x = [[[1, 2]], [["foo", "bar"]], [[5.3, 6.4]]]`, expectedElementType: &sema.VariableSizedType{ Type: &sema.VariableSizedType{ - Type: sema.AnyStructType, + Type: sema.HashableStructType, }, }, }, @@ -902,7 +902,7 @@ func TestCheckArraySupertypeInference(t *testing.T) { code: `let x = [[[1, 2] as [Int; 2]], [["foo", "bar"] as [String; 2]], [[5.3, 6.4] as [Fix64; 2]]]`, expectedElementType: &sema.VariableSizedType{ Type: &sema.ConstantSizedType{ - Type: sema.AnyStructType, + Type: sema.HashableStructType, Size: 2, }, }, @@ -977,7 +977,7 @@ func TestCheckDictionarySupertypeInference(t *testing.T) { name: "mixed simple values", code: `let x = {0: 0, 1: true}`, expectedKeyType: sema.IntType, - expectedValueType: sema.AnyStructType, + expectedValueType: sema.HashableStructType, }, { name: "signed integer values", @@ -1011,6 +1011,12 @@ func TestCheckDictionarySupertypeInference(t *testing.T) { Type: sema.StringType, }, }, + { + name: "int and string keys", + code: `let x = {0: 1, "hello": 2}`, + expectedKeyType: sema.HashableStructType, + expectedValueType: sema.IntType, + }, { name: "common interfaced values", code: ` @@ -1109,8 +1115,15 @@ func TestCheckDictionarySupertypeInference(t *testing.T) { pub resource Foo {} `, - expectedKeyType: sema.IntType, - expectedValueType: sema.AnyResourceType, + expectedKeyType: sema.IntType, + expectedValueType: &sema.DictionaryType{ + KeyType: sema.HashableStructType, + ValueType: &sema.InterfaceType{ + Location: common.StringLocation("test"), + Identifier: "Foo", + CompositeKind: common.CompositeKindStructure, + }, + }, }, } @@ -1146,33 +1159,6 @@ func TestCheckDictionarySupertypeInference(t *testing.T) { assert.IsType(t, &sema.TypeAnnotationRequiredError{}, errs[0]) }) - t.Run("no supertype for keys", func(t *testing.T) { - t.Parallel() - - code := ` - let x = {1: 1, "two": 2} - ` - _, err := ParseAndCheck(t, code) - errs := RequireCheckerErrors(t, err, 1) - - assert.IsType(t, &sema.InvalidDictionaryKeyTypeError{}, errs[0]) - }) - - t.Run("unsupported supertype for keys", func(t *testing.T) { - t.Parallel() - - code := ` - let x = {0: 1, "hello": 2} - ` - _, err := ParseAndCheck(t, code) - errs := RequireCheckerErrors(t, err, 1) - - require.IsType(t, &sema.InvalidDictionaryKeyTypeError{}, errs[0]) - invalidKeyError := errs[0].(*sema.InvalidDictionaryKeyTypeError) - - assert.Equal(t, sema.AnyStructType, invalidKeyError.Type) - }) - t.Run("empty dictionary", func(t *testing.T) { t.Parallel()