Skip to content

Commit

Permalink
Merge pull request #1049 from onflow/supun/infer-static-type
Browse files Browse the repository at this point in the history
Infer static type from the value itself for imported arrays/dictionaries
  • Loading branch information
SupunS authored Nov 18, 2021
2 parents a523a95 + a21446f commit 54ee085
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 46 deletions.
83 changes: 42 additions & 41 deletions runtime/convertValues.go
Original file line number Diff line number Diff line change
Expand Up @@ -659,8 +659,23 @@ func importArrayValue(
if arrayType != nil {
staticArrayType = interpreter.ConvertSemaArrayTypeToStaticArrayType(arrayType)
} else {
types := make([]sema.Type, len(v.Values))

for i, value := range values {
typ, err := inter.ConvertStaticToSemaType(value.StaticType())
if err != nil {
return nil, err
}
types[i] = typ
}

elementSuperType := sema.LeastCommonSuperType(types...)
if elementSuperType == sema.InvalidType {
return nil, fmt.Errorf("cannot import array: elements do not belong to the same type")
}

staticArrayType = interpreter.VariableSizedStaticType{
Type: interpreter.PrimitiveStaticTypeAnyStruct,
Type: interpreter.ConvertSemaToStaticType(elementSuperType),
}
}

Expand Down Expand Up @@ -709,54 +724,40 @@ func importDictionaryValue(
if dictionaryType != nil {
dictionaryStaticType = interpreter.ConvertSemaDictionaryTypeToStaticDictionaryType(dictionaryType)
} else {
var keyStaticType, valueStaticType interpreter.StaticType

if len(keysAndValues) == 0 {
keyStaticType = interpreter.PrimitiveStaticTypeNever
valueStaticType = interpreter.PrimitiveStaticTypeNever
} else {
// Infer the keys type from the first entry

key := keysAndValues[0]
keyStaticType = key.StaticType()
keySemaType := inter.MustConvertStaticToSemaType(keyStaticType)

// Dictionary Keys could be a mix of numeric sub-types or path sub-types, and can be still valid.
// So always go for the safest option, by inferring the most generic type for numbers/paths.
// e.g: sending a value similar to: `var map: {Number:String} = {1: "int", 2.3: "fixed-point"}`

if sema.IsSubType(keySemaType, sema.NumberType) {
keyStaticType = interpreter.PrimitiveStaticTypeNumber
} else if sema.IsSubType(keySemaType, sema.PathType) {
keyStaticType = interpreter.PrimitiveStaticTypePath
} else if sema.IsValidDictionaryKeyType(keySemaType) {
// NO-OP. Use 'keyStaticType' as static type for keys
} else {
return nil, fmt.Errorf(
"cannot import dictionary: unsupported key: %v",
key,
)
size := len(v.Pairs)
keyTypes := make([]sema.Type, size)
valueTypes := make([]sema.Type, size)

for i := 0; i < size; i++ {
keyType, err := inter.ConvertStaticToSemaType(keysAndValues[i*2].StaticType())
if err != nil {
return nil, err
}
keyTypes[i] = keyType

// Make sure all keys are subtype of the inferred key-type
valueType, err := inter.ConvertStaticToSemaType(keysAndValues[i*2+1].StaticType())
if err != nil {
return nil, err
}
valueTypes[i] = valueType
}

inferredKeySemaType := inter.MustConvertStaticToSemaType(keyStaticType)
keySuperType := sema.LeastCommonSuperType(keyTypes...)
valueSuperType := sema.LeastCommonSuperType(valueTypes...)

for i := 1; i < len(v.Pairs); i++ {
keySemaType := inter.MustConvertStaticToSemaType(keysAndValues[i*2].StaticType())
if !sema.IsSubType(keySemaType, inferredKeySemaType) {
return nil, fmt.Errorf(
"cannot import dictionary: keys does not belong to the same type",
)
}
}
if !sema.IsValidDictionaryKeyType(keySuperType) {
return nil, fmt.Errorf(
"cannot import dictionary: keys does not belong to the same type",
)
}

valueStaticType = interpreter.PrimitiveStaticTypeAnyStruct
if valueSuperType == sema.InvalidType {
return nil, fmt.Errorf("cannot import dictionary: values does not belong to the same type")
}

dictionaryStaticType = interpreter.DictionaryStaticType{
KeyType: keyStaticType,
ValueType: valueStaticType,
KeyType: interpreter.ConvertSemaToStaticType(keySuperType),
ValueType: interpreter.ConvertSemaToStaticType(valueSuperType),
}
}

Expand Down
12 changes: 7 additions & 5 deletions runtime/convertValues_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3032,13 +3032,15 @@ func TestRuntimeImportExportArrayValue(t *testing.T) {
interpreter.NewArrayValue(
inter,
interpreter.VariableSizedStaticType{
Type: interpreter.PrimitiveStaticTypeAnyStruct,
Type: interpreter.VariableSizedStaticType{
Type: interpreter.PrimitiveStaticTypeInt8,
},
},
common.Address{},
interpreter.NewArrayValue(
inter,
interpreter.VariableSizedStaticType{
Type: interpreter.PrimitiveStaticTypeAnyStruct,
Type: interpreter.PrimitiveStaticTypeInt8,
},
common.Address{},
interpreter.Int8Value(4),
Expand All @@ -3047,7 +3049,7 @@ func TestRuntimeImportExportArrayValue(t *testing.T) {
interpreter.NewArrayValue(
inter,
interpreter.VariableSizedStaticType{
Type: interpreter.PrimitiveStaticTypeAnyStruct,
Type: interpreter.PrimitiveStaticTypeInt8,
},
common.Address{},
interpreter.Int8Value(42),
Expand Down Expand Up @@ -3247,7 +3249,7 @@ func TestRuntimeImportExportDictionaryValue(t *testing.T) {
interpreter.NewDictionaryValue(
inter,
interpreter.DictionaryStaticType{
KeyType: interpreter.PrimitiveStaticTypeNumber,
KeyType: interpreter.PrimitiveStaticTypeInt8,
ValueType: interpreter.PrimitiveStaticTypeAnyStruct,
},
interpreter.Int8Value(1), interpreter.NewIntValueFromInt64(100),
Expand All @@ -3258,7 +3260,7 @@ func TestRuntimeImportExportDictionaryValue(t *testing.T) {
interpreter.NewDictionaryValue(
inter,
interpreter.DictionaryStaticType{
KeyType: interpreter.PrimitiveStaticTypeNumber,
KeyType: interpreter.PrimitiveStaticTypeSignedInteger,
ValueType: interpreter.PrimitiveStaticTypeAnyStruct,
},
interpreter.Int8Value(1), interpreter.NewStringValue("foo"),
Expand Down

0 comments on commit 54ee085

Please sign in to comment.