mirror of
https://github.com/dolthub/dolt.git
synced 2026-05-12 19:39:32 -05:00
Fix subtype issue with unions as elem types (#2506)
For compound types (List, Set, Map, Ref) the concrete type may be a union. If that is the case all the types in the union must be a subtype of the concrete type's element type. `C<T | V>` is a subtype of `C<S>` iff T is a subtype of S and V is a subtype of S.
This commit is contained in:
@@ -65,6 +65,20 @@ func TestNewCommit(t *testing.T) {
|
||||
types.StringType,
|
||||
})
|
||||
assertTypeEquals(et4, at4)
|
||||
|
||||
// Merge-commit with different parent types
|
||||
commit5 := NewCommit(types.String("Hi"), types.NewSet(types.NewRef(commit2), types.NewRef(commit3)), types.EmptyStruct)
|
||||
at5 := commit5.Type()
|
||||
et5 := types.MakeStructType("Commit", commitFieldNames, []*types.Type{
|
||||
types.EmptyStructType,
|
||||
types.MakeSetType(types.MakeRefType(types.MakeStructType("Commit", commitFieldNames, []*types.Type{
|
||||
types.EmptyStructType,
|
||||
types.MakeSetType(types.MakeRefType(types.MakeCycleType(0))),
|
||||
types.MakeUnionType(types.NumberType, types.StringType),
|
||||
}))),
|
||||
types.StringType,
|
||||
})
|
||||
assertTypeEquals(et5, at5)
|
||||
}
|
||||
|
||||
func TestCommitWithoutMetaField(t *testing.T) {
|
||||
|
||||
@@ -87,9 +87,16 @@ func isSubtype(requiredType, concreteType *Type, parentStructTypes []*Type) bool
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// compoundSubtype is called when comparing the element types of two compound types. This is the only case
|
||||
// where a concrete type may have be a union type.
|
||||
func compoundSubtype(requiredType, concreteType *Type, parentStructTypes []*Type) bool {
|
||||
// In a compound type it is OK to have an empty union.
|
||||
if concreteType.Kind() == UnionKind && len(concreteType.Desc.(CompoundDesc).ElemTypes) == 0 {
|
||||
// If the concrete type is a union then all the types in the union must be subtypes of the required typ. This also means that a compound type with an empty union is going to be a subtype of all compounds, List<> is a subtype of List<T> for all T.
|
||||
if concreteType.Kind() == UnionKind {
|
||||
for _, ct := range concreteType.Desc.(CompoundDesc).ElemTypes {
|
||||
if !isSubtype(requiredType, ct, parentStructTypes) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return isSubtype(requiredType, concreteType, parentStructTypes)
|
||||
|
||||
@@ -340,3 +340,18 @@ func TestIsSubtypeEmptySruct(tt *testing.T) {
|
||||
assert.False(tt, IsSubtype(t1, t2))
|
||||
assert.True(tt, IsSubtype(t2, t1))
|
||||
}
|
||||
|
||||
func TestIsSubtypeCompundUnion(tt *testing.T) {
|
||||
rt := MakeListType(EmptyStructType)
|
||||
|
||||
st1 := MakeStructType("One", []string{"a"}, []*Type{NumberType})
|
||||
st2 := MakeStructType("Two", []string{"b"}, []*Type{StringType})
|
||||
ct := MakeListType(MakeUnionType(st1, st2))
|
||||
|
||||
assert.True(tt, IsSubtype(rt, ct))
|
||||
assert.False(tt, IsSubtype(ct, rt))
|
||||
|
||||
ct2 := MakeListType(MakeUnionType(st1, st2, NumberType))
|
||||
assert.False(tt, IsSubtype(rt, ct2))
|
||||
assert.False(tt, IsSubtype(ct2, rt))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user