Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow conversions between var types of range types and base types #24037

Merged
merged 4 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions compiler/ccgexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2234,12 +2234,16 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc) =
raiseInstr(p, p.s(cpsStmts))
linefmt p, cpsStmts, "}$n", []

putIntoDest(p, d, n, "(($1) ($2))" %
if sameBackendTypeIgnoreRange(dest, n[0].typ):
# don't cast so an address can be taken for `var` conversions
putIntoDest(p, d, n, "($1)" % [rdCharLoc(a)], a.storage)
else:
putIntoDest(p, d, n, "(($1) ($2))" %
[getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)

proc genConv(p: BProc, e: PNode, d: var TLoc) =
let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
if sameBackendType(destType, e[1].typ):
if sameBackendTypeIgnoreRange(destType, e[1].typ):
expr(p, e[1], d)
else:
genSomeCast(p, e, d)
Expand Down
2 changes: 1 addition & 1 deletion compiler/parampatterns.nim
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in
{tyOpenArray, tyTuple, tyObject}:
result = isAssignable(owner, n[1])
elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct):
elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct, {IgnoreRangeShallow}):
# types that are equal modulo distinction preserve l-value:
result = isAssignable(owner, n[1])
of nkHiddenDeref:
Expand Down
66 changes: 47 additions & 19 deletions compiler/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,7 @@ type
PickyCAliases # be picky about the distinction between 'cint' and 'int32'
IgnoreFlags # used for borrowed functions and methods; ignores the tfVarIsPtr flag
PickyBackendAliases # be picky about different aliases
IgnoreRangeShallow

TTypeCmpFlags* = set[TTypeCmpFlag]

Expand Down Expand Up @@ -1213,25 +1214,39 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
inc c.recCheck
else:
if containsOrIncl(c, a, b): return true
template maybeSkipRange(x: set[TTypeKind]): set[TTypeKind] =
if IgnoreRangeShallow in c.flags:
x + {tyRange}
else:
x

template withoutShallowFlags(body) =
let oldFlags = c.flags
c.flags.excl IgnoreRangeShallow
body
c.flags = oldFlags

if x == y: return true
var a = skipTypes(x, {tyAlias})
let aliasSkipSet = maybeSkipRange({tyAlias})
var a = skipTypes(x, aliasSkipSet)
while a.kind == tyUserTypeClass and tfResolved in a.flags:
a = skipTypes(a.last, {tyAlias})
var b = skipTypes(y, {tyAlias})
a = skipTypes(a.last, aliasSkipSet)
var b = skipTypes(y, aliasSkipSet)
while b.kind == tyUserTypeClass and tfResolved in b.flags:
b = skipTypes(b.last, {tyAlias})
b = skipTypes(b.last, aliasSkipSet)
assert(a != nil)
assert(b != nil)
if a.kind != b.kind:
case c.cmp
of dcEq: return false
of dcEqIgnoreDistinct:
a = a.skipTypes({tyDistinct, tyGenericInst})
b = b.skipTypes({tyDistinct, tyGenericInst})
let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
a = a.skipTypes(distinctSkipSet)
b = b.skipTypes(distinctSkipSet)
if a.kind != b.kind: return false
of dcEqOrDistinctOf:
a = a.skipTypes({tyDistinct, tyGenericInst})
let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
a = a.skipTypes(distinctSkipSet)
if a.kind != b.kind: return false

#[
Expand All @@ -1246,8 +1261,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
rhs = y.skipGenericAlias
if rhs.kind != tyGenericInst or lhs.base != rhs.base or rhs.kidsLen != lhs.kidsLen:
return false
for ff, aa in underspecifiedPairs(rhs, lhs, 1, -1):
if not sameTypeAux(ff, aa, c): return false
withoutShallowFlags:
for ff, aa in underspecifiedPairs(rhs, lhs, 1, -1):
if not sameTypeAux(ff, aa, c): return false
return true

case a.kind
Expand All @@ -1273,9 +1289,10 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
cycleCheck()
result = sameTypeAux(a.skipModifier, b.skipModifier, c)
of tyObject:
ifFastObjectTypeCheckFailed(a, b):
cycleCheck()
result = sameObjectStructures(a, b, c) and sameFlags(a, b)
withoutShallowFlags:
ifFastObjectTypeCheckFailed(a, b):
cycleCheck()
result = sameObjectStructures(a, b, c) and sameFlags(a, b)
of tyDistinct:
cycleCheck()
if c.cmp == dcEq:
Expand All @@ -1290,8 +1307,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
of tyError:
result = b.kind == tyError
of tyTuple:
cycleCheck()
result = sameTuple(a, b, c) and sameFlags(a, b)
withoutShallowFlags:
cycleCheck()
result = sameTuple(a, b, c) and sameFlags(a, b)
of tyTypeDesc:
if c.cmp == dcEqIgnoreDistinct: result = false
elif ExactTypeDescValues in c.flags:
Expand All @@ -1315,7 +1333,8 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
tyAnd, tyOr, tyNot, tyAnything, tyOwned:
cycleCheck()
if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
result = sameChildrenAux(a, b, c)
withoutShallowFlags:
result = sameChildrenAux(a, b, c)
if result and IgnoreFlags notin c.flags:
if IgnoreTupleFields in c.flags:
result = a.flags * {tfVarIsPtr, tfIsOutParam} == b.flags * {tfVarIsPtr, tfIsOutParam}
Expand All @@ -1328,8 +1347,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
((ExactConstraints notin c.flags) or sameConstraints(a.n, b.n))
of tyRange:
cycleCheck()
result = sameTypeOrNilAux(a.elementType, b.elementType, c) and
sameValue(a.n[0], b.n[0]) and
result = sameTypeOrNilAux(a.elementType, b.elementType, c)
if result and IgnoreRangeShallow notin c.flags:
result = sameValue(a.n[0], b.n[0]) and
sameValue(a.n[1], b.n[1])
of tyAlias, tyInferred, tyIterable:
cycleCheck()
Expand All @@ -1339,8 +1359,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
# The type system must distinguish between `T[int] = object #[empty]#`
# and `T[float] = object #[empty]#`!
cycleCheck()
for ff, aa in underspecifiedPairs(a, b, 1, -1):
if not sameTypeAux(ff, aa, c): return false
withoutShallowFlags:
for ff, aa in underspecifiedPairs(a, b, 1, -1):
if not sameTypeAux(ff, aa, c): return false
result = sameTypeAux(a.skipModifier, b.skipModifier, c)
of tyNone: result = false
of tyConcept:
Expand All @@ -1352,6 +1373,13 @@ proc sameBackendType*(x, y: PType): bool =
c.cmp = dcEqIgnoreDistinct
result = sameTypeAux(x, y, c)

proc sameBackendTypeIgnoreRange*(x, y: PType): bool =
var c = initSameTypeClosure()
c.flags.incl IgnoreTupleFields
c.flags.incl IgnoreRangeShallow
c.cmp = dcEqIgnoreDistinct
result = sameTypeAux(x, y, c)

proc sameBackendTypePickyAliases*(x, y: PType): bool =
var c = initSameTypeClosure()
c.flags.incl {IgnoreTupleFields, PickyCAliases, PickyBackendAliases}
Expand Down
13 changes: 13 additions & 0 deletions tests/range/texplicitvarconv.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# related to issue #24032

proc `++`(n: var int) =
n += 1

type
r = range[ 0..15 ]

var a: r = 14

++int(a) # this should be mutable

doAssert a == 15
14 changes: 14 additions & 0 deletions tests/range/toutofrangevarconv.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
discard """
outputsub: "value out of range: 5 notin 0 .. 3 [RangeDefect]"
exitcode: "1"
"""

# make sure out of bounds range conversion is detected for `var` conversions

type R = range[0..3]

proc foo(x: var R) =
doAssert x in 0..3

var x = 5
foo(R(x))