diff --git a/migrations/statictypes/statictype_migration.go b/migrations/statictypes/statictype_migration.go index 79ea8570d2..73f884506d 100644 --- a/migrations/statictypes/statictype_migration.go +++ b/migrations/statictypes/statictype_migration.go @@ -355,6 +355,18 @@ func (m *StaticTypeMigration) maybeConvertStaticType(staticType, parentType inte convertedType = compositeTypeConverter(staticType) } + // Convert built-in types in composite type form to primitive type + if convertedType == nil && staticType.Location == nil { + primitiveStaticType := interpreter.PrimitiveStaticTypeFromTypeID(staticType.TypeID) + if primitiveStaticType != interpreter.PrimitiveStaticTypeUnknown { + convertedPrimitiveStaticType := m.maybeConvertStaticType(primitiveStaticType, parentType) + if convertedPrimitiveStaticType != nil { + return convertedPrimitiveStaticType + } + return primitiveStaticType + } + } + // Interface types need to be placed in intersection types. // If the composite type was converted to an interface type, // and if the parent type is not an intersection type, diff --git a/migrations/statictypes/statictype_migration_test.go b/migrations/statictypes/statictype_migration_test.go index 332b7f2d89..61c5c21bf9 100644 --- a/migrations/statictypes/statictype_migration_test.go +++ b/migrations/statictypes/statictype_migration_test.go @@ -150,6 +150,84 @@ func TestStaticTypeMigration(t *testing.T) { ) }) + t.Run("TypeValue with reference to AuthAccount (as primitive)", func(t *testing.T) { + t.Parallel() + + staticTypeMigration := NewStaticTypeMigration() + + actual := migrate(t, + staticTypeMigration, + interpreter.NewUnmeteredTypeValue( + interpreter.NewDictionaryStaticType(nil, + interpreter.PrimitiveStaticTypeAddress, + interpreter.NewCapabilityStaticType(nil, + interpreter.NewReferenceStaticType( + nil, + interpreter.UnauthorizedAccess, + interpreter.PrimitiveStaticTypeAuthAccount, //nolint:staticcheck + ), + ), + ), + ), + true, + ) + assert.Equal(t, + interpreter.NewUnmeteredTypeValue( + interpreter.NewDictionaryStaticType(nil, + interpreter.PrimitiveStaticTypeAddress, + interpreter.NewCapabilityStaticType(nil, + // NOTE: NOT reference to reference type + authAccountReferenceType, + ), + ), + ), + actual, + ) + }) + + t.Run("TypeValue with reference to AuthAccount (as composite)", func(t *testing.T) { + t.Parallel() + + staticTypeMigration := NewStaticTypeMigration() + + authAccountTypeID := interpreter.PrimitiveStaticTypeAuthAccount.ID() //nolint:staticcheck + + actual := migrate(t, + staticTypeMigration, + interpreter.NewUnmeteredTypeValue( + interpreter.NewDictionaryStaticType(nil, + interpreter.PrimitiveStaticTypeAddress, + interpreter.NewCapabilityStaticType(nil, + interpreter.NewReferenceStaticType( + nil, + interpreter.UnauthorizedAccess, + // NOTE: AuthAccount as composite type + interpreter.NewCompositeStaticType( + nil, + nil, + string(authAccountTypeID), + authAccountTypeID, + ), + ), + ), + ), + ), + true, + ) + assert.Equal(t, + interpreter.NewUnmeteredTypeValue( + interpreter.NewDictionaryStaticType(nil, + interpreter.PrimitiveStaticTypeAddress, + interpreter.NewCapabilityStaticType(nil, + // NOTE: NOT reference to reference type + authAccountReferenceType, + ), + ), + ), + actual, + ) + }) + t.Run("PathCapabilityValue with nil borrow type", func(t *testing.T) { t.Parallel() @@ -718,6 +796,51 @@ func TestStaticTypeMigration(t *testing.T) { assert.Equal(t, expected, actual) }) + + t.Run( + "composite types of (non-deprecated) built-in types are converted to primitive static types", + func(t *testing.T) { + t.Parallel() + + test := func(t *testing.T, ty interpreter.PrimitiveStaticType) { + + typeID := ty.ID() + + t.Run(string(typeID), func(t *testing.T) { + t.Parallel() + + staticTypeMigration := NewStaticTypeMigration() + + actual := migrate(t, + staticTypeMigration, + interpreter.NewUnmeteredTypeValue( + // NOTE: AuthAccount as composite type + interpreter.NewCompositeStaticType( + nil, + nil, + string(typeID), + typeID, + ), + ), + true, + ) + assert.Equal(t, + interpreter.NewUnmeteredTypeValue(ty), + actual, + ) + }) + } + + for ty := interpreter.PrimitiveStaticTypeUnknown + 1; ty < interpreter.PrimitiveStaticType_Count; ty++ { + if !ty.IsDefined() || ty.IsDeprecated() { //nolint:staticcheck + continue + } + + test(t, ty) + } + }, + ) + } func TestMigratingNestedContainers(t *testing.T) { diff --git a/runtime/interpreter/primitivestatictype.go b/runtime/interpreter/primitivestatictype.go index b34ae18139..b30c160eaf 100644 --- a/runtime/interpreter/primitivestatictype.go +++ b/runtime/interpreter/primitivestatictype.go @@ -394,6 +394,9 @@ func (t PrimitiveStaticType) elementSize() uint { PrimitiveStaticTypeAccountKey: // These types are deprecated, and only exist for migration purposes return UnknownElementSize + + case PrimitiveStaticTypeUnknown: + case PrimitiveStaticType_Count: } panic(errors.NewUnexpectedError("missing case for %s", t)) @@ -422,6 +425,35 @@ func (t PrimitiveStaticType) MeteredString(memoryGauge common.MemoryGauge) strin } func (t PrimitiveStaticType) ID() TypeID { + + // Handle deprecated types specially, because they do not have a sema type equivalent anymore + switch t { + case PrimitiveStaticTypeAuthAccount: //nolint:staticcheck + return "AuthAccount" + case PrimitiveStaticTypePublicAccount: //nolint:staticcheck + return "PublicAccount" + case PrimitiveStaticTypeAuthAccountContracts: //nolint:staticcheck + return "AuthAccount.Contracts" + case PrimitiveStaticTypePublicAccountContracts: //nolint:staticcheck + return "PublicAccount.Contracts" + case PrimitiveStaticTypeAuthAccountKeys: //nolint:staticcheck + return "AuthAccount.Keys" + case PrimitiveStaticTypePublicAccountKeys: //nolint:staticcheck + return "PublicAccount.Keys" + case PrimitiveStaticTypeAuthAccountInbox: //nolint:staticcheck + return "AuthAccount.Inbox" + case PrimitiveStaticTypeAuthAccountStorageCapabilities: //nolint:staticcheck + return "AuthAccount.StorageCapabilities" + case PrimitiveStaticTypeAuthAccountAccountCapabilities: //nolint:staticcheck + return "AuthAccount.AccountCapabilities" + case PrimitiveStaticTypeAuthAccountCapabilities: //nolint:staticcheck + return "AuthAccount.Capabilities" + case PrimitiveStaticTypePublicAccountCapabilities: //nolint:staticcheck + return "PublicAccount.Capabilities" + case PrimitiveStaticTypeAccountKey: //nolint:staticcheck + return "AccountKey" + } + return t.SemaType().ID() } @@ -654,6 +686,21 @@ func (t PrimitiveStaticType) SemaType() sema.Type { case PrimitiveStaticTypePublicAccount: //nolint:staticcheck // deprecated, but needed for migration purposes return sema.AccountReferenceType + + case PrimitiveStaticTypeAuthAccountContracts: + case PrimitiveStaticTypePublicAccountContracts: + case PrimitiveStaticTypeAuthAccountKeys: + case PrimitiveStaticTypePublicAccountKeys: + case PrimitiveStaticTypeAccountKey: + case PrimitiveStaticTypeAuthAccountInbox: + case PrimitiveStaticTypeAuthAccountStorageCapabilities: + case PrimitiveStaticTypeAuthAccountAccountCapabilities: + case PrimitiveStaticTypeAuthAccountCapabilities: + case PrimitiveStaticTypePublicAccountCapabilities: + panic(errors.NewUnexpectedError("cannot convert deprecated type %s", t)) + + case PrimitiveStaticTypeUnknown: + case PrimitiveStaticType_Count: } if t.IsDeprecated() { @@ -919,3 +966,27 @@ func ConvertSemaToPrimitiveStaticType( return NewPrimitiveStaticType(memoryGauge, typ) } + +var primitiveStaticTypesByTypeID = map[TypeID]PrimitiveStaticType{} + +func init() { + // Check all defined primitive static types, + // and construct a type ID to primitive static type mapping + for ty := PrimitiveStaticTypeUnknown + 1; ty < PrimitiveStaticType_Count; ty++ { + if !ty.IsDefined() { + continue + } + + _ = ty.elementSize() + + primitiveStaticTypesByTypeID[ty.ID()] = ty + } +} + +func PrimitiveStaticTypeFromTypeID(typeID TypeID) PrimitiveStaticType { + ty, ok := primitiveStaticTypesByTypeID[typeID] + if !ok { + return PrimitiveStaticTypeUnknown + } + return ty +}