covariance for arrays and sequences
This commit is contained in:
parent
a802d72452
commit
f4e73344d4
3 changed files with 247 additions and 50 deletions
|
|
@ -889,7 +889,7 @@ proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
|
||||||
return false
|
return false
|
||||||
|
|
||||||
proc typeRel(c: var TCandidate, f, aOrig: PType,
|
proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||||
flags: TTypeRelFlags = {}): TTypeRelation =
|
flags: TTypeRelFlags = {}): TTypeRelation =
|
||||||
# typeRel can be used to establish various relationships between types:
|
# typeRel can be used to establish various relationships between types:
|
||||||
#
|
#
|
||||||
# 1) When used with concrete types, it will check for type equivalence
|
# 1) When used with concrete types, it will check for type equivalence
|
||||||
|
|
@ -1092,9 +1092,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||||
fRange = a
|
fRange = a
|
||||||
else:
|
else:
|
||||||
fRange = prev
|
fRange = prev
|
||||||
result = typeRel(c, f.sons[1].skipTypes({tyTypeDesc}),
|
let ff = f.sons[1].skipTypes({tyTypeDesc})
|
||||||
a.sons[1].skipTypes({tyTypeDesc}))
|
let aa = a.sons[1].skipTypes({tyTypeDesc})
|
||||||
if result < isGeneric: return isNone
|
result = typeRel(c, ff, aa)
|
||||||
|
if result < isGeneric:
|
||||||
|
if nimEnableCovariance and
|
||||||
|
trNoCovariance notin flags and
|
||||||
|
ff.kind == aa.kind and
|
||||||
|
isCovariantPtr(c, ff, aa):
|
||||||
|
result = isSubtype
|
||||||
|
else:
|
||||||
|
return isNone
|
||||||
|
|
||||||
if fRange.rangeHasUnresolvedStatic:
|
if fRange.rangeHasUnresolvedStatic:
|
||||||
return inferStaticsInRange(c, fRange, a)
|
return inferStaticsInRange(c, fRange, a)
|
||||||
|
|
@ -1113,20 +1121,31 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||||
if tfOldSchoolExprStmt in f.sons[0].flags:
|
if tfOldSchoolExprStmt in f.sons[0].flags:
|
||||||
if f.sons[0].kind == tyExpr: return
|
if f.sons[0].kind == tyExpr: return
|
||||||
elif f.sons[0].kind == tyStmt: return
|
elif f.sons[0].kind == tyStmt: return
|
||||||
|
|
||||||
|
template matchArrayOrSeq(aBase: PType) =
|
||||||
|
let ff = f.base
|
||||||
|
let aa = aBase
|
||||||
|
let baseRel = typeRel(c, ff, aa)
|
||||||
|
if baseRel >= isGeneric:
|
||||||
|
result = isConvertible
|
||||||
|
elif nimEnableCovariance and
|
||||||
|
trNoCovariance notin flags and
|
||||||
|
ff.kind == aa.kind and
|
||||||
|
isCovariantPtr(c, ff, aa):
|
||||||
|
result = isConvertible
|
||||||
|
|
||||||
case a.kind
|
case a.kind
|
||||||
of tyOpenArray, tyVarargs:
|
of tyOpenArray, tyVarargs:
|
||||||
result = typeRel(c, base(f), base(a))
|
result = typeRel(c, base(f), base(a))
|
||||||
if result < isGeneric: result = isNone
|
if result < isGeneric: result = isNone
|
||||||
of tyArray:
|
of tyArray:
|
||||||
if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty):
|
if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty):
|
||||||
result = isSubtype
|
return isSubtype
|
||||||
elif typeRel(c, base(f), a.sons[1]) >= isGeneric:
|
matchArrayOrSeq(a.sons[1])
|
||||||
result = isConvertible
|
|
||||||
of tySequence:
|
of tySequence:
|
||||||
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
|
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
|
||||||
result = isConvertible
|
return isConvertible
|
||||||
elif typeRel(c, base(f), a.sons[0]) >= isGeneric:
|
matchArrayOrSeq(a.sons[0])
|
||||||
result = isConvertible
|
|
||||||
of tyString:
|
of tyString:
|
||||||
if f.kind == tyOpenArray:
|
if f.kind == tyOpenArray:
|
||||||
if f.sons[0].kind == tyChar:
|
if f.sons[0].kind == tyChar:
|
||||||
|
|
@ -1141,8 +1160,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||||
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
|
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
|
||||||
result = isSubtype
|
result = isSubtype
|
||||||
else:
|
else:
|
||||||
result = typeRel(c, f.sons[0], a.sons[0])
|
let ff = f.sons[0]
|
||||||
if result < isGeneric: result = isNone
|
let aa = a.sons[0]
|
||||||
|
result = typeRel(c, ff, aa)
|
||||||
|
if result < isGeneric:
|
||||||
|
if nimEnableCovariance and
|
||||||
|
trNoCovariance notin flags and
|
||||||
|
ff.kind == aa.kind and
|
||||||
|
isCovariantPtr(c, ff, aa):
|
||||||
|
result = isSubtype
|
||||||
|
else:
|
||||||
|
result = isNone
|
||||||
elif tfNotNil in f.flags and tfNotNil notin a.flags:
|
elif tfNotNil in f.flags and tfNotNil notin a.flags:
|
||||||
result = isNilConversion
|
result = isNilConversion
|
||||||
of tyNil: result = f.allowsNil
|
of tyNil: result = f.allowsNil
|
||||||
|
|
|
||||||
|
|
@ -1344,6 +1344,7 @@ const
|
||||||
hasThreadSupport = compileOption("threads") and not defined(nimscript)
|
hasThreadSupport = compileOption("threads") and not defined(nimscript)
|
||||||
hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own
|
hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own
|
||||||
taintMode = compileOption("taintmode")
|
taintMode = compileOption("taintmode")
|
||||||
|
nimEnableCovariance* = defined(nimEnableCovariance) # or true
|
||||||
|
|
||||||
when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"):
|
when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"):
|
||||||
# tcc doesn't support TLS
|
# tcc doesn't support TLS
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,29 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim check $file"
|
cmd: "nim cpp $file"
|
||||||
|
output: '''
|
||||||
|
cat
|
||||||
|
cat
|
||||||
|
dog
|
||||||
|
dog
|
||||||
|
cat
|
||||||
|
cat
|
||||||
|
dog
|
||||||
|
dog X
|
||||||
|
cat
|
||||||
|
cat
|
||||||
|
dog
|
||||||
|
dog
|
||||||
|
dog value
|
||||||
|
cat value
|
||||||
|
dog value
|
||||||
|
cat value
|
||||||
|
dog
|
||||||
|
dog
|
||||||
|
dog value
|
||||||
|
cat value
|
||||||
|
dog 1
|
||||||
|
dog 2
|
||||||
|
'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
template accept(x) =
|
template accept(x) =
|
||||||
|
|
@ -8,9 +32,20 @@ template accept(x) =
|
||||||
template reject(x) =
|
template reject(x) =
|
||||||
static: assert(not compiles(x))
|
static: assert(not compiles(x))
|
||||||
|
|
||||||
|
import macros
|
||||||
|
|
||||||
|
macro skipElse(n: untyped): typed = n[0]
|
||||||
|
|
||||||
|
template acceptWithCovariance(x, otherwise): typed =
|
||||||
|
when nimEnableCovariance:
|
||||||
|
x
|
||||||
|
else:
|
||||||
|
reject(x)
|
||||||
|
skipElse(otherwise)
|
||||||
|
|
||||||
type
|
type
|
||||||
Animal = object of TObject
|
Animal = object of TObject
|
||||||
x: int
|
x: string
|
||||||
|
|
||||||
Dog = object of Animal
|
Dog = object of Animal
|
||||||
y: int
|
y: int
|
||||||
|
|
@ -21,24 +56,94 @@ type
|
||||||
AnimalRef = ref Animal
|
AnimalRef = ref Animal
|
||||||
AnimalPtr = ptr Animal
|
AnimalPtr = ptr Animal
|
||||||
|
|
||||||
var dog = new(Dog)
|
RefAlias[T] = ref T
|
||||||
var cat = new(Cat)
|
|
||||||
|
|
||||||
proc wantsRefArray(x: array[2, ref Animal]) = discard
|
var dog = new(Dog)
|
||||||
|
dog.x = "dog"
|
||||||
|
|
||||||
|
var cat = new(Cat)
|
||||||
|
cat.x = "cat"
|
||||||
|
|
||||||
|
proc makeDerivedRef(x: string): ref Dog =
|
||||||
|
new(result)
|
||||||
|
result.x = x
|
||||||
|
|
||||||
|
proc makeDerived(x: string): Dog =
|
||||||
|
result.x = x
|
||||||
|
|
||||||
|
var covariantSeq = @[makeDerivedRef("dog 1"), makeDerivedRef("dog 2")]
|
||||||
|
var nonCovariantSeq = @[makeDerived("dog 1"), makeDerived("dog 2")]
|
||||||
|
var covariantArr = [makeDerivedRef("dog 1"), makeDerivedRef("dog 2")]
|
||||||
|
var nonCovariantArr = [makeDerived("dog 1"), makeDerived("dog 2")]
|
||||||
|
|
||||||
|
proc wantsCovariantSeq1(s: seq[ref Animal]) =
|
||||||
|
for a in s: echo a.x
|
||||||
|
|
||||||
|
proc wantsCovariantSeq2(s: seq[AnimalRef]) =
|
||||||
|
for a in s: echo a.x
|
||||||
|
|
||||||
|
proc wantsCovariantSeq3(s: seq[RefAlias[Animal]]) =
|
||||||
|
for a in s: echo a.x
|
||||||
|
|
||||||
|
proc wantsCovariantOperArray(s: openarray[ref Animal]) =
|
||||||
|
for a in s: echo a.x
|
||||||
|
|
||||||
|
proc modifiesCovariantOperArray(s: var openarray[ref Animal]) =
|
||||||
|
for a in s: echo a.x
|
||||||
|
|
||||||
|
proc modifiesDerivedOperArray(s: var openarray[ref Dog]) =
|
||||||
|
for a in s: echo a.x
|
||||||
|
|
||||||
|
proc wantsNonCovariantOperArray(s: openarray[Animal]) =
|
||||||
|
for a in s: echo a.x
|
||||||
|
|
||||||
|
proc wantsCovariantArray(s: array[2, ref Animal]) =
|
||||||
|
for a in s: echo a.x
|
||||||
|
|
||||||
|
proc wantsNonCovariantSeq(s: seq[Animal]) =
|
||||||
|
for a in s: echo a.x
|
||||||
|
|
||||||
|
proc wantsNonCovariantArray(s: array[2, Animal]) =
|
||||||
|
for a in s: echo a.x
|
||||||
|
|
||||||
|
proc modifiesCovariantSeq(s: var seq[ref Animal]) =
|
||||||
|
for a in s: echo a.x
|
||||||
|
|
||||||
|
proc modifiesCovariantArray(s: var array[2, ref Animal]) =
|
||||||
|
for a in s: echo a.x
|
||||||
|
|
||||||
|
proc modifiesCovariantSeq(s: ptr seq[ref Animal]) =
|
||||||
|
for a in s[]: echo a.x
|
||||||
|
|
||||||
|
proc modifiesCovariantArray(s: ptr array[2, ref Animal]) =
|
||||||
|
for a in s[]: echo a.x
|
||||||
|
|
||||||
|
proc modifiesDerivedSeq(s: var seq[ref Dog]) =
|
||||||
|
for a in s: echo a.x
|
||||||
|
|
||||||
|
proc modifiesDerivedArray(s: var array[2, ref Dog]) =
|
||||||
|
for a in s: echo a.x
|
||||||
|
|
||||||
|
proc modifiesDerivedSeq(s: ptr seq[ref Dog]) =
|
||||||
|
for a in s[]: echo a.x
|
||||||
|
|
||||||
|
proc modifiesDerivedArray(s: ptr array[2, ref Dog]) =
|
||||||
|
for a in s[]: echo a.x
|
||||||
|
|
||||||
accept:
|
accept:
|
||||||
wantsRefArray([AnimalRef(dog), AnimalRef(dog)])
|
wantsCovariantArray([AnimalRef(dog), AnimalRef(dog)])
|
||||||
wantsRefArray([AnimalRef(cat), AnimalRef(dog)])
|
wantsCovariantArray([AnimalRef(cat), AnimalRef(dog)])
|
||||||
wantsRefArray([AnimalRef(cat), dog])
|
wantsCovariantArray([AnimalRef(cat), dog])
|
||||||
|
|
||||||
# there is a special rule that detects the base
|
# there is a special rule that detects the base
|
||||||
# type of polymorphic arrays
|
# type of polymorphic arrays
|
||||||
wantsRefArray([cat, dog])
|
wantsCovariantArray([cat, dog])
|
||||||
|
|
||||||
reject:
|
acceptWithCovariance:
|
||||||
# But the current lack of covariance kicks in
|
wantsCovariantArray([cat, cat])
|
||||||
# when we try to pass a derived type array
|
else:
|
||||||
wantsRefArray([cat, cat])
|
echo "cat"
|
||||||
|
echo "cat"
|
||||||
|
|
||||||
var animalRefArray: array[2, ref Animal]
|
var animalRefArray: array[2, ref Animal]
|
||||||
|
|
||||||
|
|
@ -46,15 +151,21 @@ accept:
|
||||||
animalRefArray = [AnimalRef(dog), AnimalRef(dog)]
|
animalRefArray = [AnimalRef(dog), AnimalRef(dog)]
|
||||||
animalRefArray = [AnimalRef(cat), dog]
|
animalRefArray = [AnimalRef(cat), dog]
|
||||||
|
|
||||||
reject:
|
acceptWithCovariance:
|
||||||
animalRefArray = [dog, dog]
|
animalRefArray = [dog, dog]
|
||||||
|
wantsCovariantArray animalRefArray
|
||||||
|
else:
|
||||||
|
echo "dog"
|
||||||
|
echo "dog"
|
||||||
|
|
||||||
accept:
|
accept:
|
||||||
var animal: AnimalRef = dog
|
var animal: AnimalRef = dog
|
||||||
animal = cat
|
animal = cat
|
||||||
|
|
||||||
var vdog: Dog
|
var vdog: Dog
|
||||||
|
vdog.x = "dog value"
|
||||||
var vcat: Cat
|
var vcat: Cat
|
||||||
|
vcat.x = "cat value"
|
||||||
|
|
||||||
reject:
|
reject:
|
||||||
vcat = vdog
|
vcat = vdog
|
||||||
|
|
@ -79,22 +190,93 @@ accept:
|
||||||
proc wantsRefSeq(x: seq[AnimalRef]) = discard
|
proc wantsRefSeq(x: seq[AnimalRef]) = discard
|
||||||
|
|
||||||
accept:
|
accept:
|
||||||
wantsRefSeq(@[AnimalRef(dog), AnimalRef(dog)])
|
wantsCovariantSeq1(@[AnimalRef(dog), AnimalRef(dog)])
|
||||||
wantsRefSeq(@[AnimalRef(cat), AnimalRef(dog)])
|
wantsCovariantSeq1(@[AnimalRef(cat), AnimalRef(dog)])
|
||||||
wantsRefSeq(@[AnimalRef(cat), dog])
|
wantsCovariantSeq1(@[AnimalRef(cat), dog])
|
||||||
wantsRefSeq(@[cat, dog])
|
wantsCovariantSeq1(@[cat, dog])
|
||||||
|
|
||||||
reject:
|
wantsCovariantSeq2(@[AnimalRef(dog), AnimalRef(dog)])
|
||||||
wantsRefSeq(@[cat, cat])
|
wantsCovariantSeq2(@[AnimalRef(cat), AnimalRef(dog)])
|
||||||
|
wantsCovariantSeq2(@[AnimalRef(cat), dog])
|
||||||
|
wantsCovariantSeq2(@[cat, dog])
|
||||||
|
|
||||||
|
wantsCovariantSeq3(@[AnimalRef(dog), AnimalRef(dog)])
|
||||||
|
wantsCovariantSeq3(@[AnimalRef(cat), AnimalRef(dog)])
|
||||||
|
wantsCovariantSeq3(@[AnimalRef(cat), dog])
|
||||||
|
wantsCovariantSeq3(@[cat, dog])
|
||||||
|
|
||||||
|
wantsCovariantOperArray([cat, dog])
|
||||||
|
|
||||||
|
acceptWithCovariance:
|
||||||
|
wantsCovariantSeq1(@[cat, cat])
|
||||||
|
wantsCovariantSeq2(@[dog, makeDerivedRef("dog X")])
|
||||||
|
# XXX: wantsCovariantSeq3(@[cat, cat])
|
||||||
|
|
||||||
|
wantsCovariantOperArray(@[cat, cat])
|
||||||
|
wantsCovariantOperArray([dog, dog])
|
||||||
|
else:
|
||||||
|
echo "cat"
|
||||||
|
echo "cat"
|
||||||
|
echo "dog"
|
||||||
|
echo "dog X"
|
||||||
|
echo "cat"
|
||||||
|
echo "cat"
|
||||||
|
echo "dog"
|
||||||
|
echo "dog"
|
||||||
|
|
||||||
|
var dogRefs = @[dog, dog]
|
||||||
|
var dogRefsArray = [dog, dog]
|
||||||
|
var animalRefs = @[dog, cat]
|
||||||
|
|
||||||
|
accept:
|
||||||
|
modifiesDerivedArray(dogRefsArray)
|
||||||
|
modifiesDerivedSeq(dogRefs)
|
||||||
|
|
||||||
|
reject modifiesCovariantSeq(dogRefs)
|
||||||
|
reject modifiesCovariantSeq(addr(dogRefs))
|
||||||
|
reject modifiesCovariantSeq(dogRefs.addr)
|
||||||
|
|
||||||
|
reject modifiesCovariantArray([dog, dog])
|
||||||
|
reject modifiesCovariantArray(dogRefsArray)
|
||||||
|
reject modifiesCovariantArray(addr(dogRefsArray))
|
||||||
|
reject modifiesCovariantArray(dogRefsArray.addr)
|
||||||
|
|
||||||
|
var dogValues = @[vdog, vdog]
|
||||||
|
var dogValuesArray = [vdog, vdog]
|
||||||
|
var animalValues = @[Animal(vdog), Animal(vcat)]
|
||||||
|
var animalValuesArray = [Animal(vdog), Animal(vcat)]
|
||||||
|
|
||||||
|
wantsNonCovariantSeq animalValues
|
||||||
|
wantsNonCovariantArray animalValuesArray
|
||||||
|
|
||||||
|
reject wantsNonCovariantSeq(dogRefs)
|
||||||
|
reject modifiesCovariantOperArray(dogRefs)
|
||||||
|
reject wantsNonCovariantArray(dogRefsArray)
|
||||||
|
reject wantsNonCovariantSeq(dogValues)
|
||||||
|
reject wantsNonCovariantArray(dogValuesArray)
|
||||||
|
reject modifiesValueArray()
|
||||||
|
|
||||||
|
modifiesDerivedOperArray dogRefs
|
||||||
|
reject modifiesDerivedOperArray(dogValues)
|
||||||
|
reject modifiesDerivedOperArray(animalRefs)
|
||||||
|
|
||||||
|
wantsNonCovariantOperArray animalValues
|
||||||
|
reject wantsNonCovariantOperArray(animalRefs)
|
||||||
|
reject wantsNonCovariantOperArray(dogRefs)
|
||||||
|
reject wantsNonCovariantOperArray(dogValues)
|
||||||
|
|
||||||
var animalRefSeq: seq[ref Animal]
|
var animalRefSeq: seq[ref Animal]
|
||||||
|
|
||||||
accept:
|
accept:
|
||||||
animalRefArray = [AnimalRef(dog), AnimalRef(dog)]
|
animalRefSeq = @[AnimalRef(dog), AnimalRef(dog)]
|
||||||
animalRefArray = [AnimalRef(cat), dog]
|
animalRefSeq = @[AnimalRef(cat), dog]
|
||||||
|
|
||||||
reject:
|
acceptWithCovariance:
|
||||||
animalRefArray = [dog, dog]
|
animalRefSeq = @[makeDerivedRef("dog 1"), makeDerivedRef("dog 2")]
|
||||||
|
wantsCovariantSeq1(animalRefSeq)
|
||||||
|
else:
|
||||||
|
echo "dog 1"
|
||||||
|
echo "dog 2"
|
||||||
|
|
||||||
var pdog: ptr Dog
|
var pdog: ptr Dog
|
||||||
var pcat: ptr Cat
|
var pcat: ptr Cat
|
||||||
|
|
@ -116,17 +298,6 @@ proc wantsVarPointer2(x: var AnimalPtr) =
|
||||||
reject wantsVarPointer1(pdog)
|
reject wantsVarPointer1(pdog)
|
||||||
reject wantsVarPointer2(pcat)
|
reject wantsVarPointer2(pcat)
|
||||||
|
|
||||||
proc usesVarRefSeq(x: var seq[AnimalRef]) = discard
|
|
||||||
proc usesVarRefArray(x: var array[2, AnimalRef]) = discard
|
|
||||||
|
|
||||||
reject:
|
|
||||||
var catsSeq = @[cat, cat]
|
|
||||||
usesVarRefSeq catsSeq
|
|
||||||
|
|
||||||
reject:
|
|
||||||
var catsArray = [cat, cat]
|
|
||||||
usesVarRefArray catsArray
|
|
||||||
|
|
||||||
# covariance may be allowed for certain extern types
|
# covariance may be allowed for certain extern types
|
||||||
|
|
||||||
{.emit: """
|
{.emit: """
|
||||||
|
|
@ -178,9 +349,6 @@ var
|
||||||
|
|
||||||
reject: ptrPtrDog = ptrPtrAnimal
|
reject: ptrPtrDog = ptrPtrAnimal
|
||||||
|
|
||||||
type
|
|
||||||
RefAlias[T] = ref T
|
|
||||||
|
|
||||||
# Try to break the rules by introducing some tricky
|
# Try to break the rules by introducing some tricky
|
||||||
# double indirection types:
|
# double indirection types:
|
||||||
var
|
var
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue