Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend type bounds #3059

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion encoding/ccf/ccf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10426,7 +10426,7 @@ func TestEncodeType(t *testing.T) {
cadence.TypeValue{
StaticType: &cadence.FunctionType{
TypeParameters: []cadence.TypeParameter{
{Name: "T", TypeBound: cadence.AnyStructType},
{Name: "T", TypeBound: cadence.NewSubtypeTypeBound(cadence.AnyStructType)},
},
Parameters: []cadence.Parameter{
{Label: "qux", Identifier: "baz", Type: cadence.StringType},
Expand Down
8 changes: 7 additions & 1 deletion encoding/ccf/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -2182,9 +2182,15 @@ func (d *Decoder) decodeTypeParameterTypeValue(visited *cadenceTypeByCCFTypeID)
return cadence.TypeParameter{}, err
}

var typeBound cadence.TypeBound
if t != nil {
typeBound = cadence.NewSubtypeTypeBound(t)
}

// Unmetered because decodeTypeParamTypeValue is metered in decodeTypeParamTypeValues and called nowhere else
// Type is metered.
return cadence.NewTypeParameter(name, t), nil
// TODO: implement this for generalized bounds
return cadence.NewTypeParameter(name, typeBound), nil
Comment on lines +2192 to +2193
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be sufficient to support decoding any function types that aren't one of the small set of builtins that this PR changes.

}

// decodeParameterTypeValues decodes composite initializer parameter types as
Expand Down
12 changes: 11 additions & 1 deletion encoding/ccf/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -1945,8 +1945,18 @@
return err
}

// TODO: perhaps generalize this?
if typeParameter.TypeBound == nil {
return e.encodeNullableTypeValue(nil, visited)
}

subTypeBound, isSubtypeBound := typeParameter.TypeBound.(cadence.SubtypeTypeBound)
if !isSubtypeBound {
panic(cadenceErrors.NewUnexpectedError("cannot store non-subtype bounded function type parameter %s", typeParameter.TypeBound))

Check warning on line 1955 in encoding/ccf/encode.go

View check run for this annotation

Codecov / codecov/patch

encoding/ccf/encode.go#L1955

Added line #L1955 was not covered by tests
}

// element 1: type as type-bound.
return e.encodeNullableTypeValue(typeParameter.TypeBound, visited)
return e.encodeNullableTypeValue(subTypeBound.Type, visited)
Comment on lines +1948 to +1959
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be sufficient to support encoding any function types that aren't one of the small set of builtins that this PR changes.

}

// encodeParameterTypeValues encodes composite initializer parameter types as
Expand Down
38 changes: 36 additions & 2 deletions encoding/json/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -928,13 +928,47 @@
)
}

func (d *Decoder) decodeTypeBound(valueJSON any, results typeDecodingResults) cadence.TypeBound {
obj := toObject(valueJSON)

kind := obj.Get("kind")

switch kind {
case "subtype":
ty := obj.Get("type")
decodedType := d.decodeType(ty, results)
return cadence.NewSubtypeTypeBound(decodedType)
case "supertype":
ty := obj.Get("type")
decodedType := d.decodeType(ty, results)
return cadence.NewSupertypeTypeBound(decodedType)
case "equal":
ty := obj.Get("type")
decodedType := d.decodeType(ty, results)
return cadence.NewEqualTypeBound(decodedType)
case "negation":
bounds := toSlice(obj.Get("bounds"))
decodedBound := d.decodeTypeBound(bounds[0], results)
return cadence.NewNegationTypeBound(decodedBound)
case "conjunction":
bounds := toSlice(obj.Get("bounds"))
var decodedBounds []cadence.TypeBound
for _, bound := range bounds {
decodedBounds = append(decodedBounds, d.decodeTypeBound(bound, results))
}
return cadence.NewConjunctionTypeBound(decodedBounds)
}

panic(errors.NewDefaultUserError("invalid kind in type bound: %s", kind))

Check warning on line 962 in encoding/json/decode.go

View check run for this annotation

Codecov / codecov/patch

encoding/json/decode.go#L962

Added line #L962 was not covered by tests
}

func (d *Decoder) decodeTypeParameter(valueJSON any, results typeDecodingResults) cadence.TypeParameter {
obj := toObject(valueJSON)
// Unmetered because decodeTypeParameter is metered in decodeTypeParameters and called nowhere else
typeBoundObj, ok := obj[typeBoundKey]
var typeBound cadence.Type
var typeBound cadence.TypeBound
if ok {
typeBound = d.decodeType(typeBoundObj, results)
typeBound = d.decodeTypeBound(typeBoundObj, results)
}

return cadence.NewTypeParameter(
Expand Down
57 changes: 53 additions & 4 deletions encoding/json/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,14 @@
}

type jsonTypeParameter struct {
Name string `json:"name"`
TypeBound jsonValue `json:"typeBound"`
Name string `json:"name"`
TypeBound jsonTypeBound `json:"typeBound"`
}

type jsonTypeBound struct {
Kind string `json:"kind"`
Type jsonValue `json:"type"`
Bounds []jsonTypeBound `json:"bounds"`
}

type jsonParameterType struct {
Expand Down Expand Up @@ -710,11 +716,54 @@
}
}

func prepareTypeBound(typeBound cadence.TypeBound, results TypePreparationResults) jsonTypeBound {
switch bound := typeBound.(type) {
case cadence.SubtypeTypeBound:
preparedType := PrepareType(bound.Type, results)
return jsonTypeBound{
Kind: "subtype",
Type: preparedType,
}
case cadence.EqualTypeBound:
preparedType := PrepareType(bound.Type, results)
return jsonTypeBound{
Kind: "equal",
Type: preparedType,
}
case cadence.SupertypeTypeBound:
preparedType := PrepareType(bound.Type, results)
return jsonTypeBound{
Kind: "supertype",
Type: preparedType,
}
case cadence.NegationTypeBound:
preparedBound := prepareTypeBound(bound.NegatedBound, results)
return jsonTypeBound{
Kind: "negation",
Bounds: []jsonTypeBound{preparedBound},
}
case cadence.ConjunctionTypeBound:
var preparedBounds []jsonTypeBound

for _, typeBound := range bound.TypeBounds {
preparedBounds = append(preparedBounds, prepareTypeBound(typeBound, results))

}

return jsonTypeBound{
Kind: "conjunction",
Bounds: preparedBounds,
}
}

panic(fmt.Errorf("failed to prepare type: unsupported type bound: %T", typeBound))

Check warning on line 759 in encoding/json/encode.go

View check run for this annotation

Codecov / codecov/patch

encoding/json/encode.go#L759

Added line #L759 was not covered by tests
}

func prepareTypeParameter(typeParameter cadence.TypeParameter, results TypePreparationResults) jsonTypeParameter {
typeBound := typeParameter.TypeBound
var preparedTypeBound jsonValue
var preparedTypeBound jsonTypeBound
if typeBound != nil {
preparedTypeBound = PrepareType(typeBound, results)
preparedTypeBound = prepareTypeBound(typeBound, results)
}
return jsonTypeParameter{
Name: typeParameter.Name,
Expand Down
Loading
Loading