Skip to content

Commit

Permalink
fix type matching failing for generic range types (#853)
Browse files Browse the repository at this point in the history
## Summary

Fix two bugs with `typeRel` that caused no relationship to be detected
when a formal range type's underlying type is dependent on an unresolved
expression.

## Details

#### Problem One

In case they hadn't been resolved yet (because they depend on late-bound
type variables), the bounding values of the formal range type were
*modified* when computing the relationship between two range types,
which in effect meant that all following queries of the routine
signature saw an instantiated range type, instead of the original
generic one.

The resolved expressions are now stored locally and then directly passed
on to `typeRangeRel`.

#### Problem Two

Neither range-against-range nor non-range-against-range considered the
case where the range's underlying type was dependent on an unresolved
expression -- both returning `isNone` when the formal range type's base
is a `tyFromExpr`.

If the formal range type's underlying type is a `tyFromExpr`, the
dependent type is now resolved first. Were `f` was used previously
directly, the resolved underlying type is used instead.

#### Tests

An incorrect test case from the now-working `tdependent_range_type` test
is removed.
  • Loading branch information
zerbina authored Aug 23, 2023
1 parent 05da5d2 commit 2d79c7b
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 14 deletions.
26 changes: 17 additions & 9 deletions compiler/sem/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
result = f.allowsNil
else: discard

proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
proc typeRangeRel(f: PType, lo, hi: PNode, a: PType): TTypeRelation {.noinline.} =
template checkRange[T](a0, a1, f0, f1: T): TTypeRelation =
if a0 == f0 and a1 == f1:
isEqual
Expand All @@ -672,9 +672,9 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
isNone

if f.isOrdinalType:
checkRange(firstOrd(nil, a), lastOrd(nil, a), firstOrd(nil, f), lastOrd(nil, f))
checkRange(firstOrd(nil, a), lastOrd(nil, a), getOrdValue(lo), getOrdValue(hi))
else:
checkRange(firstFloat(a), lastFloat(a), firstFloat(f), lastFloat(f))
checkRange(firstFloat(a), lastFloat(a), getFloatValue(lo), getFloatValue(hi))


proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
Expand Down Expand Up @@ -1174,22 +1174,30 @@ typeRel can be used to establish various relationships between types:
elif skipTypes(a, {tyRange}).kind == f.kind:
result = isSubtype
of tyRange:
var base = f.base
if base.kind == tyFromExpr:
# `f`'s underlying type depends on some type variables. The type needs
# to be computed first
base = tryResolvingStaticExpr(c, base.n).typ.skipTypes({tyStatic})

if a.kind == f.kind:
if f.base.kind == tyNone:
return isGeneric

result = typeRel(c, base(f), base(a), flags)
result = typeRel(c, base, base(a), flags)
# bugfix: accept integer conversions here
#if result < isGeneric: result = isNone
if result notin {isNone, isGeneric}:
# resolve any late-bound static expressions
# that may appear in the range:
for i in 0..1:
if f.n[i].kind == nkStaticExpr:
f.n[i] = tryResolvingStaticExpr(c, f.n[i])
result = typeRangeRel(f, a)
var r = [f.n[0], f.n[1]]
for it in r.mitems:
if it.kind == nkStaticExpr:
it = tryResolvingStaticExpr(c, it)

result = typeRangeRel(base, r[0], r[1], a)
else:
let f = skipTypes(f, {tyRange})
let f = skipTypes(base, {tyRange})
if f.kind == a.kind and (f.kind != tyEnum or sameEnumTypes(f, a)):
result = isIntConv
elif isConvertibleToRange(f, a):
Expand Down
5 changes: 0 additions & 5 deletions tests/lang_callable/generics/tdependent_range_type.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ discard """
description: '''
Range types depending on type variables must work in routine signatures
'''
knownIssue: '''
``typeRel`` doesn't properly handle the case where a formal range type is
depends on a type variables
'''
"""

import std/typetraits
Expand Down Expand Up @@ -35,5 +31,4 @@ static:

# test with a different name:
doAssert f(Typ, 0) == 6
doAssert not compiles(f(Typ, 5) == 6)
doAssert not compiles(f(Typ, a) == 6)

0 comments on commit 2d79c7b

Please sign in to comment.