Avoid stack overflow in Azure NextGen program examples
This commit is contained in:
parent
4c8e2c222d
commit
c007f1f2f8
|
@ -41,7 +41,8 @@ type Type interface {
|
|||
String() string
|
||||
|
||||
equals(other Type, seen map[Type]struct{}) bool
|
||||
conversionFrom(src Type, unifying bool) ConversionKind
|
||||
conversionFrom(src Type, unifying bool, seen map[Type]struct{}) ConversionKind
|
||||
string(seen map[Type]struct{}) string
|
||||
unify(other Type) (Type, ConversionKind)
|
||||
isType()
|
||||
}
|
||||
|
@ -65,12 +66,12 @@ func assignableFrom(dest, src Type, assignableFrom func() bool) bool {
|
|||
return dest.Equals(src) || dest == DynamicType || assignableFrom()
|
||||
}
|
||||
|
||||
func conversionFrom(dest, src Type, unifying bool, conversionFrom func() ConversionKind) ConversionKind {
|
||||
func conversionFrom(dest, src Type, unifying bool, seen map[Type]struct{}, conversionFrom func() ConversionKind) ConversionKind {
|
||||
if dest.Equals(src) || dest == DynamicType {
|
||||
return SafeConversion
|
||||
}
|
||||
if src, isUnion := src.(*UnionType); isUnion {
|
||||
return src.conversionTo(dest, unifying)
|
||||
return src.conversionTo(dest, unifying, seen)
|
||||
}
|
||||
if src == DynamicType {
|
||||
return UnsafeConversion
|
||||
|
@ -93,7 +94,7 @@ func unify(t0, t1 Type, unify func() (Type, ConversionKind)) (Type, ConversionKi
|
|||
// The dynamic type unifies with any other type by selecting that other type.
|
||||
return t0, UnsafeConversion
|
||||
default:
|
||||
conversionFrom, conversionTo := t0.conversionFrom(t1, true), t1.conversionFrom(t0, true)
|
||||
conversionFrom, conversionTo := t0.conversionFrom(t1, true, nil), t1.conversionFrom(t0, true, nil)
|
||||
switch {
|
||||
case conversionFrom < conversionTo:
|
||||
return t1, conversionTo
|
||||
|
|
|
@ -88,20 +88,20 @@ func (t *ListType) AssignableFrom(src Type) bool {
|
|||
// to T. If any element type is unsafely convertible to T and no element type is safely convertible to T, the
|
||||
// conversion is unsafe. Otherwise, no conversion exists.
|
||||
func (t *ListType) ConversionFrom(src Type) ConversionKind {
|
||||
return t.conversionFrom(src, false)
|
||||
return t.conversionFrom(src, false, nil)
|
||||
}
|
||||
|
||||
func (t *ListType) conversionFrom(src Type, unifying bool) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, func() ConversionKind {
|
||||
func (t *ListType) conversionFrom(src Type, unifying bool, seen map[Type]struct{}) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, seen, func() ConversionKind {
|
||||
switch src := src.(type) {
|
||||
case *ListType:
|
||||
return t.ElementType.conversionFrom(src.ElementType, unifying)
|
||||
return t.ElementType.conversionFrom(src.ElementType, unifying, seen)
|
||||
case *SetType:
|
||||
return t.ElementType.conversionFrom(src.ElementType, unifying)
|
||||
return t.ElementType.conversionFrom(src.ElementType, unifying, seen)
|
||||
case *TupleType:
|
||||
conversionKind := SafeConversion
|
||||
for _, src := range src.ElementTypes {
|
||||
if ck := t.ElementType.conversionFrom(src, unifying); ck < conversionKind {
|
||||
if ck := t.ElementType.conversionFrom(src, unifying, seen); ck < conversionKind {
|
||||
conversionKind = ck
|
||||
}
|
||||
}
|
||||
|
@ -112,7 +112,11 @@ func (t *ListType) conversionFrom(src Type, unifying bool) ConversionKind {
|
|||
}
|
||||
|
||||
func (t *ListType) String() string {
|
||||
return fmt.Sprintf("list(%v)", t.ElementType)
|
||||
return t.string(nil)
|
||||
}
|
||||
|
||||
func (t *ListType) string(seen map[Type]struct{}) string {
|
||||
return fmt.Sprintf("list(%s)", t.ElementType.string(seen))
|
||||
}
|
||||
|
||||
func (t *ListType) unify(other Type) (Type, ConversionKind) {
|
||||
|
@ -139,7 +143,7 @@ func (t *ListType) unify(other Type) (Type, ConversionKind) {
|
|||
return NewListType(elementType), conversionKind
|
||||
default:
|
||||
// Prefer the list type.
|
||||
return t, t.conversionFrom(other, true)
|
||||
return t, t.conversionFrom(other, true, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -89,18 +89,18 @@ func (t *MapType) AssignableFrom(src Type) bool {
|
|||
// convertible to T. If any element type is unsafely convertible to T and no element type is safely convertible to T,
|
||||
// the conversion is unsafe. Otherwise, no conversion exists.
|
||||
func (t *MapType) ConversionFrom(src Type) ConversionKind {
|
||||
return t.conversionFrom(src, false)
|
||||
return t.conversionFrom(src, false, nil)
|
||||
}
|
||||
|
||||
func (t *MapType) conversionFrom(src Type, unifying bool) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, func() ConversionKind {
|
||||
func (t *MapType) conversionFrom(src Type, unifying bool, seen map[Type]struct{}) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, seen, func() ConversionKind {
|
||||
switch src := src.(type) {
|
||||
case *MapType:
|
||||
return t.ElementType.conversionFrom(src.ElementType, unifying)
|
||||
return t.ElementType.conversionFrom(src.ElementType, unifying, seen)
|
||||
case *ObjectType:
|
||||
conversionKind := SafeConversion
|
||||
for _, src := range src.Properties {
|
||||
if ck := t.ElementType.conversionFrom(src, unifying); ck < conversionKind {
|
||||
if ck := t.ElementType.conversionFrom(src, unifying, seen); ck < conversionKind {
|
||||
conversionKind = ck
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,11 @@ func (t *MapType) conversionFrom(src Type, unifying bool) ConversionKind {
|
|||
}
|
||||
|
||||
func (t *MapType) String() string {
|
||||
return fmt.Sprintf("map(%v)", t.ElementType)
|
||||
return t.string(nil)
|
||||
}
|
||||
|
||||
func (t *MapType) string(seen map[Type]struct{}) string {
|
||||
return fmt.Sprintf("map(%s)", t.ElementType.string(seen))
|
||||
}
|
||||
|
||||
func (t *MapType) unify(other Type) (Type, ConversionKind) {
|
||||
|
@ -134,7 +138,7 @@ func (t *MapType) unify(other Type) (Type, ConversionKind) {
|
|||
return NewMapType(elementType), conversionKind
|
||||
default:
|
||||
// Prefer the map type.
|
||||
return t, t.conversionFrom(other, true)
|
||||
return t, t.conversionFrom(other, true, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -45,11 +45,11 @@ func (noneType) AssignableFrom(src Type) bool {
|
|||
}
|
||||
|
||||
func (noneType) ConversionFrom(src Type) ConversionKind {
|
||||
return NoneType.conversionFrom(src, false)
|
||||
return NoneType.conversionFrom(src, false, nil)
|
||||
}
|
||||
|
||||
func (noneType) conversionFrom(src Type, unifying bool) ConversionKind {
|
||||
return conversionFrom(NoneType, src, unifying, func() ConversionKind {
|
||||
func (noneType) conversionFrom(src Type, unifying bool, seen map[Type]struct{}) ConversionKind {
|
||||
return conversionFrom(NoneType, src, unifying, seen, func() ConversionKind {
|
||||
return NoConversion
|
||||
})
|
||||
}
|
||||
|
@ -58,6 +58,10 @@ func (noneType) String() string {
|
|||
return "none"
|
||||
}
|
||||
|
||||
func (noneType) string(_ map[Type]struct{}) string {
|
||||
return "none"
|
||||
}
|
||||
|
||||
func (noneType) unify(other Type) (Type, ConversionKind) {
|
||||
return unify(NoneType, other, func() (Type, ConversionKind) {
|
||||
return NoneType, other.ConversionFrom(NoneType)
|
||||
|
|
|
@ -179,13 +179,22 @@ func (u *objectTypeUnifier) unify(t *ObjectType) {
|
|||
// This conversion is always unsafe, and may fail if the map does not contain an appropriate set of keys for the
|
||||
// destination type.
|
||||
func (t *ObjectType) ConversionFrom(src Type) ConversionKind {
|
||||
return t.conversionFrom(src, false)
|
||||
return t.conversionFrom(src, false, nil)
|
||||
}
|
||||
|
||||
func (t *ObjectType) conversionFrom(src Type, unifying bool) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, func() ConversionKind {
|
||||
func (t *ObjectType) conversionFrom(src Type, unifying bool, seen map[Type]struct{}) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, seen, func() ConversionKind {
|
||||
switch src := src.(type) {
|
||||
case *ObjectType:
|
||||
if seen != nil {
|
||||
if _, ok := seen[t]; ok {
|
||||
return NoConversion
|
||||
}
|
||||
} else {
|
||||
seen = map[Type]struct{}{}
|
||||
}
|
||||
seen[t] = struct{}{}
|
||||
|
||||
if unifying {
|
||||
var unifier objectTypeUnifier
|
||||
unifier.unify(t)
|
||||
|
@ -199,7 +208,7 @@ func (t *ObjectType) conversionFrom(src Type, unifying bool) ConversionKind {
|
|||
if !ok {
|
||||
src = NoneType
|
||||
}
|
||||
if ck := dst.conversionFrom(src, unifying); ck < conversionKind {
|
||||
if ck := dst.conversionFrom(src, unifying, seen); ck < conversionKind {
|
||||
conversionKind = ck
|
||||
}
|
||||
}
|
||||
|
@ -207,7 +216,7 @@ func (t *ObjectType) conversionFrom(src Type, unifying bool) ConversionKind {
|
|||
case *MapType:
|
||||
conversionKind := UnsafeConversion
|
||||
for _, dst := range t.Properties {
|
||||
if ck := dst.conversionFrom(src.ElementType, unifying); ck < conversionKind {
|
||||
if ck := dst.conversionFrom(src.ElementType, unifying, seen); ck < conversionKind {
|
||||
conversionKind = ck
|
||||
}
|
||||
}
|
||||
|
@ -218,20 +227,35 @@ func (t *ObjectType) conversionFrom(src Type, unifying bool) ConversionKind {
|
|||
}
|
||||
|
||||
func (t *ObjectType) String() string {
|
||||
if t.s == "" {
|
||||
var properties []string
|
||||
for k, v := range t.Properties {
|
||||
properties = append(properties, fmt.Sprintf("%s = %v", k, v))
|
||||
}
|
||||
sort.Strings(properties)
|
||||
return t.string(nil)
|
||||
}
|
||||
|
||||
annotations := ""
|
||||
if len(t.Annotations) != 0 {
|
||||
annotations = fmt.Sprintf(", annotated(%p)", t)
|
||||
}
|
||||
|
||||
t.s = fmt.Sprintf("object({%s}%v)", strings.Join(properties, ", "), annotations)
|
||||
func (t *ObjectType) string(seen map[Type]struct{}) string {
|
||||
if t.s != "" {
|
||||
return t.s
|
||||
}
|
||||
|
||||
if seen != nil {
|
||||
if _, ok := seen[t]; ok {
|
||||
return "..."
|
||||
}
|
||||
} else {
|
||||
seen = map[Type]struct{}{}
|
||||
}
|
||||
seen[t] = struct{}{}
|
||||
|
||||
var properties []string
|
||||
for k, v := range t.Properties {
|
||||
properties = append(properties, fmt.Sprintf("%s = %s", k, v.string(seen)))
|
||||
}
|
||||
sort.Strings(properties)
|
||||
|
||||
annotations := ""
|
||||
if len(t.Annotations) != 0 {
|
||||
annotations = fmt.Sprintf(", annotated(%p)", t)
|
||||
}
|
||||
|
||||
t.s = fmt.Sprintf("object({%s}%v)", strings.Join(properties, ", "), annotations)
|
||||
return t.s
|
||||
}
|
||||
|
||||
|
@ -258,7 +282,7 @@ func (t *ObjectType) unify(other Type) (Type, ConversionKind) {
|
|||
return NewObjectType(unifier.properties), unifier.conversionKind
|
||||
default:
|
||||
// Otherwise, prefer the object type.
|
||||
return t, t.conversionFrom(other, true)
|
||||
return t, t.conversionFrom(other, true, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -95,34 +95,34 @@ func (t *OpaqueType) AssignableFrom(src Type) bool {
|
|||
})
|
||||
}
|
||||
|
||||
func (t *OpaqueType) conversionFromImpl(src Type, unifying, checkUnsafe bool) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, func() ConversionKind {
|
||||
func (t *OpaqueType) conversionFromImpl(src Type, unifying, checkUnsafe bool, seen map[Type]struct{}) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, seen, func() ConversionKind {
|
||||
switch {
|
||||
case t == NumberType:
|
||||
// src == NumberType is handled by t == src above
|
||||
contract.Assert(src != NumberType)
|
||||
|
||||
cki := IntType.conversionFromImpl(src, unifying, false)
|
||||
cki := IntType.conversionFromImpl(src, unifying, false, seen)
|
||||
if cki == SafeConversion {
|
||||
return SafeConversion
|
||||
}
|
||||
if cki == UnsafeConversion || checkUnsafe && StringType.conversionFromImpl(src, unifying, false).Exists() {
|
||||
if cki == UnsafeConversion || checkUnsafe && StringType.conversionFromImpl(src, unifying, false, seen).Exists() {
|
||||
return UnsafeConversion
|
||||
}
|
||||
return NoConversion
|
||||
case t == IntType:
|
||||
if checkUnsafe && NumberType.conversionFromImpl(src, unifying, true).Exists() {
|
||||
if checkUnsafe && NumberType.conversionFromImpl(src, unifying, true, seen).Exists() {
|
||||
return UnsafeConversion
|
||||
}
|
||||
return NoConversion
|
||||
case t == BoolType:
|
||||
if checkUnsafe && StringType.conversionFromImpl(src, unifying, false).Exists() {
|
||||
if checkUnsafe && StringType.conversionFromImpl(src, unifying, false, seen).Exists() {
|
||||
return UnsafeConversion
|
||||
}
|
||||
return NoConversion
|
||||
case t == StringType:
|
||||
ckb := BoolType.conversionFromImpl(src, unifying, false)
|
||||
ckn := NumberType.conversionFromImpl(src, unifying, false)
|
||||
ckb := BoolType.conversionFromImpl(src, unifying, false, seen)
|
||||
ckn := NumberType.conversionFromImpl(src, unifying, false, seen)
|
||||
if ckb == SafeConversion || ckn == SafeConversion {
|
||||
return SafeConversion
|
||||
}
|
||||
|
@ -136,8 +136,8 @@ func (t *OpaqueType) conversionFromImpl(src Type, unifying, checkUnsafe bool) Co
|
|||
})
|
||||
}
|
||||
|
||||
func (t *OpaqueType) conversionFrom(src Type, unifying bool) ConversionKind {
|
||||
return t.conversionFromImpl(src, unifying, true)
|
||||
func (t *OpaqueType) conversionFrom(src Type, unifying bool, seen map[Type]struct{}) ConversionKind {
|
||||
return t.conversionFromImpl(src, unifying, true, seen)
|
||||
}
|
||||
|
||||
// ConversionFrom returns the kind of conversion (if any) that is possible from the source type to this type.
|
||||
|
@ -152,7 +152,7 @@ func (t *OpaqueType) conversionFrom(src Type, unifying bool) ConversionKind {
|
|||
// - The bool type is unsafely convertible from string
|
||||
//
|
||||
func (t *OpaqueType) ConversionFrom(src Type) ConversionKind {
|
||||
return t.conversionFrom(src, false)
|
||||
return t.conversionFrom(src, false, nil)
|
||||
}
|
||||
|
||||
func (t *OpaqueType) String() string {
|
||||
|
@ -177,6 +177,10 @@ func (t *OpaqueType) String() string {
|
|||
return t.s
|
||||
}
|
||||
|
||||
func (t *OpaqueType) string(_ map[Type]struct{}) string {
|
||||
return t.String()
|
||||
}
|
||||
|
||||
var opaquePrecedence = []*OpaqueType{StringType, NumberType, IntType, BoolType}
|
||||
|
||||
func (t *OpaqueType) unify(other Type) (Type, ConversionKind) {
|
||||
|
@ -189,10 +193,10 @@ func (t *OpaqueType) unify(other Type) (Type, ConversionKind) {
|
|||
|
||||
for _, goal := range opaquePrecedence {
|
||||
if t == goal {
|
||||
return goal, goal.conversionFrom(other, true)
|
||||
return goal, goal.conversionFrom(other, true, nil)
|
||||
}
|
||||
if other == goal {
|
||||
return goal, goal.conversionFrom(t, true)
|
||||
return goal, goal.conversionFrom(t, true, nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -77,23 +77,27 @@ func (t *OutputType) AssignableFrom(src Type) bool {
|
|||
// output(T) is convertible from a type U, output(U), or promise(U) if U is convertible to T. If the conversion from
|
||||
// U to T is unsafe, the entire conversion is unsafe. Otherwise, the conversion is safe.
|
||||
func (t *OutputType) ConversionFrom(src Type) ConversionKind {
|
||||
return t.conversionFrom(src, false)
|
||||
return t.conversionFrom(src, false, nil)
|
||||
}
|
||||
|
||||
func (t *OutputType) conversionFrom(src Type, unifying bool) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, func() ConversionKind {
|
||||
func (t *OutputType) conversionFrom(src Type, unifying bool, seen map[Type]struct{}) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, seen, func() ConversionKind {
|
||||
switch src := src.(type) {
|
||||
case *OutputType:
|
||||
return t.ElementType.conversionFrom(src.ElementType, unifying)
|
||||
return t.ElementType.conversionFrom(src.ElementType, unifying, seen)
|
||||
case *PromiseType:
|
||||
return t.ElementType.conversionFrom(src.ElementType, unifying)
|
||||
return t.ElementType.conversionFrom(src.ElementType, unifying, seen)
|
||||
}
|
||||
return t.ElementType.conversionFrom(src, unifying)
|
||||
return t.ElementType.conversionFrom(src, unifying, seen)
|
||||
})
|
||||
}
|
||||
|
||||
func (t *OutputType) String() string {
|
||||
return fmt.Sprintf("output(%v)", t.ElementType)
|
||||
return t.string(nil)
|
||||
}
|
||||
|
||||
func (t *OutputType) string(seen map[Type]struct{}) string {
|
||||
return fmt.Sprintf("output(%s)", t.ElementType.string(seen))
|
||||
}
|
||||
|
||||
func (t *OutputType) unify(other Type) (Type, ConversionKind) {
|
||||
|
@ -109,7 +113,7 @@ func (t *OutputType) unify(other Type) (Type, ConversionKind) {
|
|||
return NewOutputType(elementType), conversionKind
|
||||
default:
|
||||
// Prefer the output type.
|
||||
return t, t.conversionFrom(other, true)
|
||||
return t, t.conversionFrom(other, true, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -74,20 +74,24 @@ func (t *PromiseType) AssignableFrom(src Type) bool {
|
|||
// promise(T) is convertible from a type U or promise(U) if U is convertible to T. If the conversion from U to T is
|
||||
// unsafe, the entire conversion is unsafe. Otherwise, the conversion is safe.
|
||||
func (t *PromiseType) ConversionFrom(src Type) ConversionKind {
|
||||
return t.conversionFrom(src, false)
|
||||
return t.conversionFrom(src, false, nil)
|
||||
}
|
||||
|
||||
func (t *PromiseType) conversionFrom(src Type, unifying bool) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, func() ConversionKind {
|
||||
func (t *PromiseType) conversionFrom(src Type, unifying bool, seen map[Type]struct{}) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, seen, func() ConversionKind {
|
||||
if src, ok := src.(*PromiseType); ok {
|
||||
return t.ElementType.conversionFrom(src.ElementType, unifying)
|
||||
return t.ElementType.conversionFrom(src.ElementType, unifying, seen)
|
||||
}
|
||||
return t.ElementType.conversionFrom(src, unifying)
|
||||
return t.ElementType.conversionFrom(src, unifying, seen)
|
||||
})
|
||||
}
|
||||
|
||||
func (t *PromiseType) String() string {
|
||||
return fmt.Sprintf("promise(%v)", t.ElementType)
|
||||
return t.string(nil)
|
||||
}
|
||||
|
||||
func (t *PromiseType) string(seen map[Type]struct{}) string {
|
||||
return fmt.Sprintf("promise(%s)", t.ElementType.string(seen))
|
||||
}
|
||||
|
||||
func (t *PromiseType) unify(other Type) (Type, ConversionKind) {
|
||||
|
@ -103,7 +107,7 @@ func (t *PromiseType) unify(other Type) (Type, ConversionKind) {
|
|||
return NewOutputType(elementType), conversionKind
|
||||
default:
|
||||
// Prefer the promise type.
|
||||
return t, t.conversionFrom(other, true)
|
||||
return t, t.conversionFrom(other, true, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -72,21 +72,21 @@ func (t *SetType) AssignableFrom(src Type) bool {
|
|||
// the entire conversion is unsafe; otherwise the conversion is safe. An unsafe conversion exists from list(U) or
|
||||
// or tuple(U_0 ... U_N) to set(T) if a conversion exists from each U to T.
|
||||
func (t *SetType) ConversionFrom(src Type) ConversionKind {
|
||||
return t.conversionFrom(src, false)
|
||||
return t.conversionFrom(src, false, nil)
|
||||
}
|
||||
|
||||
func (t *SetType) conversionFrom(src Type, unifying bool) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, func() ConversionKind {
|
||||
func (t *SetType) conversionFrom(src Type, unifying bool, seen map[Type]struct{}) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, seen, func() ConversionKind {
|
||||
switch src := src.(type) {
|
||||
case *SetType:
|
||||
return t.ElementType.conversionFrom(src.ElementType, unifying)
|
||||
return t.ElementType.conversionFrom(src.ElementType, unifying, seen)
|
||||
case *ListType:
|
||||
if conversionKind := t.ElementType.conversionFrom(src.ElementType, unifying); conversionKind == NoConversion {
|
||||
if conversionKind := t.ElementType.conversionFrom(src.ElementType, unifying, seen); conversionKind == NoConversion {
|
||||
return NoConversion
|
||||
}
|
||||
return UnsafeConversion
|
||||
case *TupleType:
|
||||
if conversionKind := NewListType(t.ElementType).conversionFrom(src, unifying); conversionKind == NoConversion {
|
||||
if conversionKind := NewListType(t.ElementType).conversionFrom(src, unifying, seen); conversionKind == NoConversion {
|
||||
return NoConversion
|
||||
}
|
||||
return UnsafeConversion
|
||||
|
@ -96,7 +96,11 @@ func (t *SetType) conversionFrom(src Type, unifying bool) ConversionKind {
|
|||
}
|
||||
|
||||
func (t *SetType) String() string {
|
||||
return fmt.Sprintf("set(%v)", t.ElementType)
|
||||
return t.string(nil)
|
||||
}
|
||||
|
||||
func (t *SetType) string(seen map[Type]struct{}) string {
|
||||
return fmt.Sprintf("set(%s)", t.ElementType.string(seen))
|
||||
}
|
||||
|
||||
func (t *SetType) unify(other Type) (Type, ConversionKind) {
|
||||
|
@ -123,7 +127,7 @@ func (t *SetType) unify(other Type) (Type, ConversionKind) {
|
|||
return NewSetType(elementType), conversionKind
|
||||
default:
|
||||
// Prefer the set type.
|
||||
return t, t.conversionFrom(other, true)
|
||||
return t, t.conversionFrom(other, true, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -668,20 +668,32 @@ func TestUnifyType(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRecursiveObjectType(t *testing.T) {
|
||||
props := map[string]Type{
|
||||
"data": NewOutputType(IntType),
|
||||
makeType := func(prop string) Type {
|
||||
props := map[string]Type{
|
||||
prop: NewOutputType(IntType),
|
||||
}
|
||||
objType := NewObjectType(props)
|
||||
props["sibling"] = objType
|
||||
listType := NewListType(objType)
|
||||
linkedListType := NewOptionalType(listType)
|
||||
props["next"] = linkedListType
|
||||
return linkedListType
|
||||
}
|
||||
linkedListType := NewOptionalType(NewObjectType(props))
|
||||
props["next"] = linkedListType
|
||||
|
||||
propsOther := map[string]Type{
|
||||
"data": NewOutputType(IntType),
|
||||
}
|
||||
linkedListTypeOther := NewOptionalType(NewObjectType(propsOther))
|
||||
propsOther["next"] = linkedListTypeOther
|
||||
linkedListType := makeType("data")
|
||||
linkedListTypeEqual := makeType("data")
|
||||
linkedListTypeNonEqual := makeType("data1")
|
||||
|
||||
// Equals
|
||||
assert.True(t, linkedListType.Equals(linkedListTypeOther))
|
||||
assert.True(t, linkedListType.Equals(linkedListTypeEqual))
|
||||
assert.False(t, linkedListType.Equals(linkedListTypeNonEqual))
|
||||
|
||||
// String conversion
|
||||
// Note: 'next' property is not visible because the string value is memoized at the time of Optional creation.
|
||||
assert.Equal(t, "union(list(object({data = output(int), sibling = ...})), none)", linkedListType.String())
|
||||
|
||||
// Convert from another type
|
||||
assert.Equal(t, UnsafeConversion, linkedListType.ConversionFrom(linkedListTypeNonEqual))
|
||||
|
||||
// Contains eventuals
|
||||
hasOutputs, hasPromises := ContainsEventuals(linkedListType)
|
||||
|
@ -690,7 +702,7 @@ func TestRecursiveObjectType(t *testing.T) {
|
|||
|
||||
// Resolving eventuals
|
||||
resolvedLinkedListType := ResolveOutputs(linkedListType)
|
||||
data := resolvedLinkedListType.(*UnionType).ElementTypes[1].(*ObjectType).Properties["data"]
|
||||
data := resolvedLinkedListType.(*UnionType).ElementTypes[0].(*ListType).ElementType.(*ObjectType).Properties["data"]
|
||||
assert.True(t, data.Equals(IntType))
|
||||
hasOutputs, _ = ContainsEventuals(resolvedLinkedListType)
|
||||
assert.False(t, hasOutputs)
|
||||
|
|
|
@ -148,11 +148,11 @@ func (u *tupleElementUnifier) unify(t *TupleType) {
|
|||
}
|
||||
|
||||
func (t *TupleType) ConversionFrom(src Type) ConversionKind {
|
||||
return t.conversionFrom(src, false)
|
||||
return t.conversionFrom(src, false, nil)
|
||||
}
|
||||
|
||||
func (t *TupleType) conversionFrom(src Type, unifying bool) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, func() ConversionKind {
|
||||
func (t *TupleType) conversionFrom(src Type, unifying bool, seen map[Type]struct{}) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, seen, func() ConversionKind {
|
||||
switch src := src.(type) {
|
||||
case *TupleType:
|
||||
// When unifying, we will unify two tuples of different length to a new tuple, where elements with matching
|
||||
|
@ -170,14 +170,14 @@ func (t *TupleType) conversionFrom(src Type, unifying bool) ConversionKind {
|
|||
|
||||
conversionKind := SafeConversion
|
||||
for i, dst := range t.ElementTypes {
|
||||
if ck := dst.conversionFrom(src.ElementTypes[i], unifying); ck < conversionKind {
|
||||
if ck := dst.conversionFrom(src.ElementTypes[i], unifying, seen); ck < conversionKind {
|
||||
conversionKind = ck
|
||||
}
|
||||
}
|
||||
|
||||
// When unifying, the conversion kind of two tuple types is the lesser of the conversion in each direction.
|
||||
if unifying {
|
||||
conversionTo := src.conversionFrom(t, false)
|
||||
conversionTo := src.conversionFrom(t, false, seen)
|
||||
if conversionTo < conversionKind {
|
||||
conversionKind = conversionTo
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ func (t *TupleType) conversionFrom(src Type, unifying bool) ConversionKind {
|
|||
case *ListType:
|
||||
conversionKind := UnsafeConversion
|
||||
for _, t := range t.ElementTypes {
|
||||
if ck := t.conversionFrom(src.ElementType, unifying); ck < conversionKind {
|
||||
if ck := t.conversionFrom(src.ElementType, unifying, seen); ck < conversionKind {
|
||||
conversionKind = ck
|
||||
}
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ func (t *TupleType) conversionFrom(src Type, unifying bool) ConversionKind {
|
|||
case *SetType:
|
||||
conversionKind := UnsafeConversion
|
||||
for _, t := range t.ElementTypes {
|
||||
if ck := t.conversionFrom(src.ElementType, unifying); ck < conversionKind {
|
||||
if ck := t.conversionFrom(src.ElementType, unifying, seen); ck < conversionKind {
|
||||
conversionKind = ck
|
||||
}
|
||||
}
|
||||
|
@ -206,10 +206,14 @@ func (t *TupleType) conversionFrom(src Type, unifying bool) ConversionKind {
|
|||
}
|
||||
|
||||
func (t *TupleType) String() string {
|
||||
return t.string(nil)
|
||||
}
|
||||
|
||||
func (t *TupleType) string(seen map[Type]struct{}) string {
|
||||
if t.s == "" {
|
||||
elements := make([]string, len(t.ElementTypes))
|
||||
for i, e := range t.ElementTypes {
|
||||
elements[i] = e.String()
|
||||
elements[i] = e.string(seen)
|
||||
}
|
||||
t.s = fmt.Sprintf("tuple(%s)", strings.Join(elements, ", "))
|
||||
}
|
||||
|
@ -250,7 +254,7 @@ func (t *TupleType) unify(other Type) (Type, ConversionKind) {
|
|||
return NewSetType(elementType), conversionKind
|
||||
default:
|
||||
// Otherwise, prefer the tuple type.
|
||||
return t, t.conversionFrom(other, true)
|
||||
return t, t.conversionFrom(other, true, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -155,14 +155,14 @@ func (t *UnionType) AssignableFrom(src Type) bool {
|
|||
// type is safely convertible, the conversion is safe; if no element is safely convertible but some element is unsafely
|
||||
// convertible, the conversion is unsafe.
|
||||
func (t *UnionType) ConversionFrom(src Type) ConversionKind {
|
||||
return t.conversionFrom(src, false)
|
||||
return t.conversionFrom(src, false, nil)
|
||||
}
|
||||
|
||||
func (t *UnionType) conversionFrom(src Type, unifying bool) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, func() ConversionKind {
|
||||
func (t *UnionType) conversionFrom(src Type, unifying bool, seen map[Type]struct{}) ConversionKind {
|
||||
return conversionFrom(t, src, unifying, seen, func() ConversionKind {
|
||||
var conversionKind ConversionKind
|
||||
for _, t := range t.ElementTypes {
|
||||
if ck := t.conversionFrom(src, unifying); ck > conversionKind {
|
||||
if ck := t.conversionFrom(src, unifying, seen); ck > conversionKind {
|
||||
conversionKind = ck
|
||||
}
|
||||
}
|
||||
|
@ -173,10 +173,10 @@ func (t *UnionType) conversionFrom(src Type, unifying bool) ConversionKind {
|
|||
// If all conversions to a dest type from a union type are safe, the conversion is safe.
|
||||
// If no conversions to a dest type from a union type exist, the conversion does not exist.
|
||||
// Otherwise, the conversion is unsafe.
|
||||
func (t *UnionType) conversionTo(dest Type, unifying bool) ConversionKind {
|
||||
func (t *UnionType) conversionTo(dest Type, unifying bool, seen map[Type]struct{}) ConversionKind {
|
||||
conversionKind, exists := SafeConversion, false
|
||||
for _, t := range t.ElementTypes {
|
||||
switch dest.conversionFrom(t, unifying) {
|
||||
switch dest.conversionFrom(t, unifying, seen) {
|
||||
case SafeConversion:
|
||||
exists = true
|
||||
case UnsafeConversion:
|
||||
|
@ -192,10 +192,14 @@ func (t *UnionType) conversionTo(dest Type, unifying bool) ConversionKind {
|
|||
}
|
||||
|
||||
func (t *UnionType) String() string {
|
||||
return t.string(nil)
|
||||
}
|
||||
|
||||
func (t *UnionType) string(seen map[Type]struct{}) string {
|
||||
if t.s == "" {
|
||||
elements := make([]string, len(t.ElementTypes))
|
||||
for i, e := range t.ElementTypes {
|
||||
elements[i] = e.String()
|
||||
elements[i] = e.string(seen)
|
||||
}
|
||||
t.s = fmt.Sprintf("union(%s)", strings.Join(elements, ", "))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue