Skip to content

Commit

Permalink
make sigmatch use prepareNode for tyFromExpr (#24095)
Browse files Browse the repository at this point in the history
fixes regression remaining after #24092

In #24092 `prepareNode` was updated so it wouldn't try to instantiate
generic type symbols (like `Generic` when `type Generic[T] = object`,
and `prepareNode` is what `tyFromExpr` uses in most of the compiler. An
exception is in sigmatch, which is now changed to use `prepareNode` to
make generic type symbols work in the same way as usual. However this
requires another change to work:

Dot fields and matches to `typedesc` on generic types generate
`tyFromExpr` in generic contexts since #24005, including generic type
symbols. But this means when we try to instantiate the `tyFromExpr` in
sigmatch, which increases `c.inGenericContext` for potentially remaining
unresolved expressions, dotcalls stay as `tyFromExpr` and so never
match. To fix this, we change the "generic type" check in dot fields and
`typedesc` matching to an "unresolved type" check which excludes generic
body types; and for generic body types, we only generate `tyFromExpr` if
the dot field is a generic parameter of the generic type (so that it
gets resolved only at instantiation).

Notes for the future:

* Sigmatch shouldn't have to `inc c.inGenericContext`, if a `tyFromExpr`
can't instantiate it's fine if we just fail the match (i.e. redirect the
instantiation errors from `semtypinst` to a match failure). Then again
maybe this is the best way to check for inability to instantiate.
* The `elif c.inGenericContext > 0 and t.containsUnresolvedType` check
in dotfields could maybe be simplified to just checking for `tyFromExpr`
and `tyGenericParam`, but I don't know if this is an exhaustive list.
  • Loading branch information
metagn authored Sep 11, 2024
1 parent 7713692 commit 9dda7ff
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 5 deletions.
13 changes: 11 additions & 2 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
n[1] = makeTypeSymNode(c, lhsType, n[1].info)
lhsType = n[1].typ
else:
if c.inGenericContext > 0 and lhsType.base.containsGenericType:
if c.inGenericContext > 0 and lhsType.base.containsUnresolvedType:
# BUGFIX: don't evaluate this too early: ``T is void``
return

Expand Down Expand Up @@ -1504,7 +1504,16 @@ proc tryReadingGenericParam(c: PContext, n: PNode, i: PIdent, t: PType): PNode =
result.typ = makeTypeFromExpr(c, copyTree(result))
else:
result = nil
elif c.inGenericContext > 0 and t.containsGenericType:
of tyGenericBody, tyCompositeTypeClass:
if c.inGenericContext > 0:
result = readTypeParameter(c, t, i, n.info)
if result != nil:
# generic parameter exists, stop here but delay until instantiation
result = semGenericStmt(c, n)
result.typ = makeTypeFromExpr(c, copyTree(result))
else:
result = nil
elif c.inGenericContext > 0 and t.containsUnresolvedType:
result = semGenericStmt(c, n)
result.typ = makeTypeFromExpr(c, copyTree(result))
else:
Expand Down
8 changes: 8 additions & 0 deletions compiler/semtypinst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,14 @@ proc replaceTypesInBody*(p: PContext, pt: TypeMapping, n: PNode;
result = replaceTypeVarsN(cl, n, expectedType = expectedType)
popInfoContext(p.config)

proc prepareTypesInBody*(p: PContext, pt: TypeMapping, n: PNode;
owner: PSym = nil): PNode =
var typeMap = initLayeredTypeMap(pt)
var cl = initTypeVars(p, typeMap, n.info, owner)
pushInfoContext(p.config, n.info)
result = prepareNode(cl, n)
popInfoContext(p.config)

when false:
# deadcode
proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode;
Expand Down
8 changes: 5 additions & 3 deletions compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ proc matchGenericParam(m: var TCandidate, formal: PType, n: PNode) =
# don't match yet-unresolved generic instantiations
while arg != nil and arg.kind == tyGenericParam:
arg = idTableGet(m.bindings, arg)
if arg == nil or arg.containsGenericType:
if arg == nil or arg.containsUnresolvedType:
m.state = csNoMatch
return
# fix up the type to get ready to match formal:
Expand Down Expand Up @@ -2048,7 +2048,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
# proc foo(T: typedesc, x: T)
# when `f` is an unresolved typedesc, `a` could be any
# type, so we should not perform this check earlier
if c.c.inGenericContext > 0 and a.containsGenericType:
if c.c.inGenericContext > 0 and a.containsUnresolvedType:
# generic type bodies can sometimes compile call expressions
# prevent unresolved generic parameters from being passed to procs as
# typedesc parameters
Expand Down Expand Up @@ -2087,7 +2087,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
# also prevent infinite recursion below
return isNone
inc c.c.inGenericContext # to generate tyFromExpr again if unresolved
let reevaluated = tryResolvingStaticExpr(c, f.n, allowCalls = true).typ
# use prepareNode for consistency with other tyFromExpr in semtypinst:
let instantiated = prepareTypesInBody(c.c, c.bindings, f.n)
let reevaluated = c.c.semExpr(c.c, instantiated).typ
dec c.c.inGenericContext
case reevaluated.kind
of tyFromExpr:
Expand Down
17 changes: 17 additions & 0 deletions compiler/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1494,6 +1494,23 @@ proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
proc containsGenericType*(t: PType): bool =
result = iterOverType(t, containsGenericTypeIter, nil)

proc containsUnresolvedTypeIter(t: PType, closure: RootRef): bool =
if tfUnresolved in t.flags: return true
case t.kind
of tyStatic:
return t.n == nil
of tyTypeDesc:
if t.base.kind == tyNone: return true
if containsUnresolvedTypeIter(t.base, closure): return true
return false
of tyGenericInvocation, tyGenericParam, tyFromExpr, tyAnything:
return true
else:
return false

proc containsUnresolvedType*(t: PType): bool =
result = iterOverType(t, containsUnresolvedTypeIter, nil)

proc baseOfDistinct*(t: PType; g: ModuleGraph; idgen: IdGenerator): PType =
if t.kind == tyDistinct:
result = t.elementType
Expand Down
6 changes: 6 additions & 0 deletions tests/generics/tuninstantiatedgenericcalls.nim
Original file line number Diff line number Diff line change
Expand Up @@ -435,3 +435,9 @@ block: # issue #24090
proc foo[T: M](x: T = default(T)) = discard x
foo[M[int]]()
doAssert not compiles(foo())

block: # above but encountered by sigmatch using replaceTypeVarsN
type Opt[T] = object
proc none[T](x: type Opt, y: typedesc[T]): Opt[T] = discard
proc foo[T](x: T, a = Opt.none(int)) = discard
foo(1, a = Opt.none(int))

0 comments on commit 9dda7ff

Please sign in to comment.