Remove to unresolved type (#3366)

This commit is contained in:
Rafael Weinstein
2017-04-08 11:12:54 -07:00
committed by GitHub
parent d8b5d03520
commit a2bb7d7a75
3 changed files with 38 additions and 71 deletions
+21 -14
View File
@@ -20,26 +20,26 @@ import "github.com/attic-labs/noms/go/d"
// - else return true
func ContainCommonSupertype(a, b *Type) bool {
// Avoid cycles internally.
return containCommonSupertypeImpl(ToUnresolvedType(a), ToUnresolvedType(b))
return containCommonSupertypeImpl(a, b, nil, nil)
}
func containCommonSupertypeImpl(a, b *Type) bool {
func containCommonSupertypeImpl(a, b *Type, aVisited, bVisited []*Type) bool {
if a.TargetKind() == ValueKind || b.TargetKind() == ValueKind {
return true
}
if a.TargetKind() == UnionKind || b.TargetKind() == UnionKind {
return unionsIntersect(a, b)
return unionsIntersect(a, b, aVisited, bVisited)
}
if a.TargetKind() != b.TargetKind() {
return false
}
switch k := a.TargetKind(); k {
case StructKind:
return structsIntersect(a, b)
return structsIntersect(a, b, aVisited, bVisited)
case ListKind, SetKind, RefKind:
return containersIntersect(k, a, b)
return containersIntersect(k, a, b, aVisited, bVisited)
case MapKind:
return mapsIntersect(a, b)
return mapsIntersect(a, b, aVisited, bVisited)
default:
return true
}
@@ -48,11 +48,11 @@ func containCommonSupertypeImpl(a, b *Type) bool {
// Checks for intersection between types that may be unions. If either or
// both is a union, union, tests all types for intersection.
func unionsIntersect(a, b *Type) bool {
func unionsIntersect(a, b *Type, aVisited, bVisited []*Type) bool {
aTypes, bTypes := typeList(a), typeList(b)
for _, t := range aTypes {
for _, u := range bTypes {
if containCommonSupertypeImpl(t, u) {
if containCommonSupertypeImpl(t, u, aVisited, bVisited) {
return true
}
}
@@ -68,12 +68,12 @@ func typeList(t *Type) typeSlice {
return typeSlice{t}
}
func containersIntersect(kind NomsKind, a, b *Type) bool {
func containersIntersect(kind NomsKind, a, b *Type, aVisited, bVisited []*Type) bool {
d.Chk.True(kind == a.Desc.Kind() && kind == b.Desc.Kind())
return containCommonSupertypeImpl(a.Desc.(CompoundDesc).ElemTypes[0], b.Desc.(CompoundDesc).ElemTypes[0])
return containCommonSupertypeImpl(a.Desc.(CompoundDesc).ElemTypes[0], b.Desc.(CompoundDesc).ElemTypes[0], aVisited, bVisited)
}
func mapsIntersect(a, b *Type) bool {
func mapsIntersect(a, b *Type, aVisited, bVisited []*Type) bool {
// true if a and b are the same or (if either is a union) there is
// common type between them.
hasCommonType := func(a, b *Type) bool {
@@ -94,10 +94,17 @@ func mapsIntersect(a, b *Type) bool {
if !hasCommonType(aDesc.ElemTypes[0], bDesc.ElemTypes[0]) {
return false
}
return containCommonSupertypeImpl(aDesc.ElemTypes[1], bDesc.ElemTypes[1])
return containCommonSupertypeImpl(aDesc.ElemTypes[1], bDesc.ElemTypes[1], aVisited, bVisited)
}
func structsIntersect(a, b *Type) bool {
func structsIntersect(a, b *Type, aVisited, bVisited []*Type) bool {
_, aFound := indexOfType(a, aVisited)
_, bFound := indexOfType(b, bVisited)
if aFound && bFound {
return true
}
d.Chk.True(StructKind == a.TargetKind() && StructKind == b.TargetKind())
aDesc := a.Desc.(StructDesc)
bDesc := b.Desc.(StructDesc)
@@ -111,7 +118,7 @@ func structsIntersect(a, b *Type) bool {
i++
} else if bName < aName {
j++
} else if !containCommonSupertypeImpl(aDesc.fields[i].Type, bDesc.fields[j].Type) {
} else if !containCommonSupertypeImpl(aDesc.fields[i].Type, bDesc.fields[j].Type, append(aVisited, a), append(bVisited, b)) {
i++
j++
} else {
+17
View File
@@ -148,6 +148,23 @@ func TestContainCommonSupertype(t *testing.T) {
MakeStructType("", StructField{"b", BoolType, true}),
false,
},
// struct A {b: struct {a: Cycle<A>}} & struct {b: Struct A {b: struct {b: Cycle<A>}}} -> false
{
MakeStructType("A",
StructField{"a", MakeStructType("",
StructField{"a", MakeCycleType("A"), false},
), false},
),
MakeStructType("",
StructField{"a", MakeStructType("A",
StructField{"a", MakeStructType("",
StructField{"a", MakeCycleType("A"), false},
), false},
), false},
),
true,
},
}
for i, c := range cases {
-57
View File
@@ -44,63 +44,6 @@ func indexOfType(t *Type, tl []*Type) (uint32, bool) {
return 0, false
}
// Returns a new type where cyclic pointer references are replaced with Cycle<Name> types.
func toUnresolvedType(t *Type, seenStructs map[string]*Type) (*Type, bool) {
switch desc := t.Desc.(type) {
case CompoundDesc:
ts := make(typeSlice, len(desc.ElemTypes))
didChange := false
for i, et := range desc.ElemTypes {
st, changed := toUnresolvedType(et, seenStructs)
ts[i] = st
didChange = didChange || changed
}
if !didChange {
return t, false
}
return newType(CompoundDesc{t.TargetKind(), ts}), true
case StructDesc:
name := desc.Name
if name != "" {
if _, ok := seenStructs[name]; ok {
return newType(CycleDesc(name)), true
}
}
nt := newType(StructDesc{Name: name})
if name != "" {
seenStructs[name] = nt
}
fs := make(structTypeFields, len(desc.fields))
didChange := false
for i, f := range desc.fields {
st, changed := toUnresolvedType(f.Type, seenStructs)
fs[i] = StructField{f.Name, st, f.Optional}
didChange = didChange || changed
}
desc.fields = fs
nt.Desc = desc
return nt, true
case CycleDesc:
cycleName := string(desc)
d.PanicIfTrue(cycleName == "")
_, ok := seenStructs[cycleName]
return t, ok // Only cycles which can be resolved in the current struct.
}
return t, false
}
// ToUnresolvedType replaces cycles (by pointer comparison) in types to Cycle types.
func ToUnresolvedType(t *Type) *Type {
t2, _ := toUnresolvedType(t, map[string]*Type{})
return t2
}
func validateType(t *Type) {
validateTypeImpl(t, map[string]struct{}{})
}