mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-31 02:12:11 +00:00
covariance for arrays and sequences
This commit is contained in:
@@ -889,7 +889,7 @@ proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
|
||||
return false
|
||||
|
||||
proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
flags: TTypeRelFlags = {}): TTypeRelation =
|
||||
flags: TTypeRelFlags = {}): TTypeRelation =
|
||||
# typeRel can be used to establish various relationships between types:
|
||||
#
|
||||
# 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
|
||||
else:
|
||||
fRange = prev
|
||||
result = typeRel(c, f.sons[1].skipTypes({tyTypeDesc}),
|
||||
a.sons[1].skipTypes({tyTypeDesc}))
|
||||
if result < isGeneric: return isNone
|
||||
let ff = f.sons[1].skipTypes({tyTypeDesc})
|
||||
let aa = a.sons[1].skipTypes({tyTypeDesc})
|
||||
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:
|
||||
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 f.sons[0].kind == tyExpr: 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
|
||||
of tyOpenArray, tyVarargs:
|
||||
result = typeRel(c, base(f), base(a))
|
||||
if result < isGeneric: result = isNone
|
||||
of tyArray:
|
||||
if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty):
|
||||
result = isSubtype
|
||||
elif typeRel(c, base(f), a.sons[1]) >= isGeneric:
|
||||
result = isConvertible
|
||||
return isSubtype
|
||||
matchArrayOrSeq(a.sons[1])
|
||||
of tySequence:
|
||||
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
|
||||
result = isConvertible
|
||||
elif typeRel(c, base(f), a.sons[0]) >= isGeneric:
|
||||
result = isConvertible
|
||||
return isConvertible
|
||||
matchArrayOrSeq(a.sons[0])
|
||||
of tyString:
|
||||
if f.kind == tyOpenArray:
|
||||
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):
|
||||
result = isSubtype
|
||||
else:
|
||||
result = typeRel(c, f.sons[0], a.sons[0])
|
||||
if result < isGeneric: result = isNone
|
||||
let ff = f.sons[0]
|
||||
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:
|
||||
result = isNilConversion
|
||||
of tyNil: result = f.allowsNil
|
||||
|
||||
@@ -1344,6 +1344,7 @@ const
|
||||
hasThreadSupport = compileOption("threads") and not defined(nimscript)
|
||||
hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own
|
||||
taintMode = compileOption("taintmode")
|
||||
nimEnableCovariance* = defined(nimEnableCovariance) # or true
|
||||
|
||||
when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"):
|
||||
# tcc doesn't support TLS
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
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) =
|
||||
@@ -8,9 +32,20 @@ template accept(x) =
|
||||
template reject(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
|
||||
Animal = object of TObject
|
||||
x: int
|
||||
x: string
|
||||
|
||||
Dog = object of Animal
|
||||
y: int
|
||||
@@ -21,24 +56,94 @@ type
|
||||
AnimalRef = ref Animal
|
||||
AnimalPtr = ptr Animal
|
||||
|
||||
var dog = new(Dog)
|
||||
var cat = new(Cat)
|
||||
RefAlias[T] = ref T
|
||||
|
||||
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:
|
||||
wantsRefArray([AnimalRef(dog), AnimalRef(dog)])
|
||||
wantsRefArray([AnimalRef(cat), AnimalRef(dog)])
|
||||
wantsRefArray([AnimalRef(cat), dog])
|
||||
wantsCovariantArray([AnimalRef(dog), AnimalRef(dog)])
|
||||
wantsCovariantArray([AnimalRef(cat), AnimalRef(dog)])
|
||||
wantsCovariantArray([AnimalRef(cat), dog])
|
||||
|
||||
# there is a special rule that detects the base
|
||||
# type of polymorphic arrays
|
||||
wantsRefArray([cat, dog])
|
||||
wantsCovariantArray([cat, dog])
|
||||
|
||||
reject:
|
||||
# But the current lack of covariance kicks in
|
||||
# when we try to pass a derived type array
|
||||
wantsRefArray([cat, cat])
|
||||
acceptWithCovariance:
|
||||
wantsCovariantArray([cat, cat])
|
||||
else:
|
||||
echo "cat"
|
||||
echo "cat"
|
||||
|
||||
var animalRefArray: array[2, ref Animal]
|
||||
|
||||
@@ -46,15 +151,21 @@ accept:
|
||||
animalRefArray = [AnimalRef(dog), AnimalRef(dog)]
|
||||
animalRefArray = [AnimalRef(cat), dog]
|
||||
|
||||
reject:
|
||||
acceptWithCovariance:
|
||||
animalRefArray = [dog, dog]
|
||||
wantsCovariantArray animalRefArray
|
||||
else:
|
||||
echo "dog"
|
||||
echo "dog"
|
||||
|
||||
accept:
|
||||
var animal: AnimalRef = dog
|
||||
animal = cat
|
||||
|
||||
var vdog: Dog
|
||||
vdog.x = "dog value"
|
||||
var vcat: Cat
|
||||
vcat.x = "cat value"
|
||||
|
||||
reject:
|
||||
vcat = vdog
|
||||
@@ -79,22 +190,93 @@ accept:
|
||||
proc wantsRefSeq(x: seq[AnimalRef]) = discard
|
||||
|
||||
accept:
|
||||
wantsRefSeq(@[AnimalRef(dog), AnimalRef(dog)])
|
||||
wantsRefSeq(@[AnimalRef(cat), AnimalRef(dog)])
|
||||
wantsRefSeq(@[AnimalRef(cat), dog])
|
||||
wantsRefSeq(@[cat, dog])
|
||||
wantsCovariantSeq1(@[AnimalRef(dog), AnimalRef(dog)])
|
||||
wantsCovariantSeq1(@[AnimalRef(cat), AnimalRef(dog)])
|
||||
wantsCovariantSeq1(@[AnimalRef(cat), dog])
|
||||
wantsCovariantSeq1(@[cat, dog])
|
||||
|
||||
reject:
|
||||
wantsRefSeq(@[cat, cat])
|
||||
wantsCovariantSeq2(@[AnimalRef(dog), AnimalRef(dog)])
|
||||
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]
|
||||
|
||||
accept:
|
||||
animalRefArray = [AnimalRef(dog), AnimalRef(dog)]
|
||||
animalRefArray = [AnimalRef(cat), dog]
|
||||
animalRefSeq = @[AnimalRef(dog), AnimalRef(dog)]
|
||||
animalRefSeq = @[AnimalRef(cat), dog]
|
||||
|
||||
reject:
|
||||
animalRefArray = [dog, dog]
|
||||
acceptWithCovariance:
|
||||
animalRefSeq = @[makeDerivedRef("dog 1"), makeDerivedRef("dog 2")]
|
||||
wantsCovariantSeq1(animalRefSeq)
|
||||
else:
|
||||
echo "dog 1"
|
||||
echo "dog 2"
|
||||
|
||||
var pdog: ptr Dog
|
||||
var pcat: ptr Cat
|
||||
@@ -116,17 +298,6 @@ proc wantsVarPointer2(x: var AnimalPtr) =
|
||||
reject wantsVarPointer1(pdog)
|
||||
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
|
||||
|
||||
{.emit: """
|
||||
@@ -178,9 +349,6 @@ var
|
||||
|
||||
reject: ptrPtrDog = ptrPtrAnimal
|
||||
|
||||
type
|
||||
RefAlias[T] = ref T
|
||||
|
||||
# Try to break the rules by introducing some tricky
|
||||
# double indirection types:
|
||||
var
|
||||
|
||||
Reference in New Issue
Block a user