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

sem: simplify generic parameter symbol handling #644

Closed
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
6 changes: 6 additions & 0 deletions compiler/ast/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1577,3 +1577,9 @@ proc isCyclePossible*(typ: PType, g: ModuleGraph): bool =
var marker = initIntSet() # keeps track of which object types we've already
# analysed
result = check(marker, g, typ, typ, isInd=false)

proc isMetaReturnTypeForMacro*(t: PType): bool =
## Returns whether `t` is considered a meta-type when used as the return
## type of macro/template
# note: make sure this matches the logic in ``semAfterMacroCall``
t != nil and t.kind notin {tyUntyped, tyTyped, tyTypeDesc} and t.isMetaType
24 changes: 15 additions & 9 deletions compiler/sem/sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -744,14 +744,20 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
if s.ast[genericParamsPos] != nil and retType.isMetaType:
# The return type may depend on the Macro arguments
# e.g. template foo(T: typedesc): seq[T]
# We will instantiate the return type here, because
# we now know the supplied arguments
var paramTypes = newIdTable()
for param, value in genericParamsInMacroCall(s, call):
idTablePut(paramTypes, param.typ, value.typ)

retType = generateTypeInstance(c, paramTypes,
macroResult.info, retType)
# After overload resolution, the instantiated type is assigned to the
# call expression, and we retrieve it here
if call.typ == nil:
# XXX: this is really more of an internal error. Not all callsites
# of ``sem(Macro|Template)Expr`` use overload resolution yet,
# but they should. These are (non-exhaustive):
# - explicit instantiation syntax (semSubscript)
# - immediate macros and templates in generics
# - pattern matching
return c.config.newError(call,
PAstDiag(kind: adSemCannotInstantiate,
callLineInfo: getCallLineInfo(call)))

retType = call.typ

result = semExpr(c, result, flags)
result = fitNode(c, retType, result, result.info)
Expand Down Expand Up @@ -807,7 +813,7 @@ proc semConstBoolExpr(c: PContext, n: PNode): PNode =
if result.kind != nkIntLit:
localReport(c.config, n, reportSem rsemConstExprExpected)

proc semGenericStmt(c: PContext, n: PNode): PNode
proc semGenericStmt(c: PContext, n: PNode; bindParams = false): PNode
proc semConceptBody(c: PContext, n: PNode): PNode

include semtypes, semtempl, semgnrc, semstmts, semexprs
Expand Down
12 changes: 11 additions & 1 deletion compiler/sem/semcall.nim
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,17 @@ proc semResolvedCall(c: PContext, x: TCandidate,
result = x.call
instGenericConvertersSons(c, result, x)
result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
result.typ = finalCallee.typ[0]

let retType = finalCallee.typ[0]
# macros/templates are not instantiated themselves, so instantiating a
# generic return type has to happen here, while we still have access to
# the type bindings
if finalCallee.kind in {skMacro, skTemplate} and gp.isGenericParams and
isMetaReturnTypeForMacro(retType):
result.typ = generateTypeInstance(c, x.bindings, n.info, retType)
else:
result.typ = retType

result = updateDefaultParams(c.config, result)

proc canDeref(n: PNode): bool {.inline.} =
Expand Down
15 changes: 7 additions & 8 deletions compiler/sem/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,8 @@ proc semConv(c: PContext, n: PNode): PNode =
result.add op

if targetType.kind == tyGenericParam:
# this case is reached for a ``expr.T`` expression located in a template
# or generic routine where ``T`` is a ``tyGenericParam``
result.typ = makeTypeFromExpr(c, copyTree(result))
return handleError(c, result, hasError)

Expand Down Expand Up @@ -1645,6 +1647,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
markUsed(c, n.info, s)
if s.typ != nil and s.typ.kind == tyStatic and s.typ.n != nil:
# XXX see the hack in sigmatch.nim ...
# TODO: see if all usages of ``static`` params can be redirected to its
# lifted ``skGenericParam``
return s.typ.n
elif sfGenSym in s.flags:
# the owner should have been set by now by addParamOrResult
Expand All @@ -1663,17 +1667,12 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
if hasWarn(c.config, rsemResultUsed):
localReport(c.config, n, reportSem rsemResultUsed)
of skGenericParam:
if s.typ.kind == tyStatic:
result = newSymNode(s, n.info)
result.typ = s.typ
elif s.ast != nil:
result = semExpr(c, s.ast)
else:
n.typ = s.typ
return n
markUsed(c, n.info, s)
result = newSymNode(s, n.info)
of skType:
markUsed(c, n.info, s)
if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil:
doAssert false
return s.typ.n
result = newSymNode(s, n.info)
result.typ = makeTypeDesc(c, s.typ)
Expand Down
43 changes: 21 additions & 22 deletions compiler/sem/semgnrc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ type
GenericCtx = object
toMixin, toBind: IntSet
cursorInBody: bool # only for nimsuggest
bracketExpr: PNode

bindParams: bool ## whether symbols of parameters (both normal and generic)
## should be bound or left as unresolved identifiers

TSemGenericFlag = enum
withinBind,
Expand Down Expand Up @@ -71,6 +73,12 @@ template isMixedIn(sym): bool =
s.magic == mNone and
s.kind in OverloadableSyms)

proc newParamSymNode(ctx: GenericCtx, orig: PNode, s: PSym): PNode =
if ctx.bindParams:
newSymNode(s, orig.info)
else:
orig

proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
ctx: var GenericCtx; flags: TSemGenericFlags,
fromDotExpr=false): PNode =
Expand All @@ -94,22 +102,15 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
result = semGenericStmt(c, result, {}, ctx)
else:
result = symChoice(c, n, s, scOpen)
of skGenericParam:
if s.typ != nil and s.typ.kind == tyStatic:
if s.typ.n != nil:
result = s.typ.n
else:
result = n
else:
result = newSymNodeTypeDesc(s, c.idgen, n.info)
of skParam:
result = n
of skGenericParam, skParam:
result = newParamSymNode(ctx, n, s)
of skType:
if (s.typ != nil) and
(s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
if s.typ.isNil:
result = n # an invalid type symbol probably constructed by a macro
elif s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}:
result = newSymNodeTypeDesc(s, c.idgen, n.info)
else:
result = n
result = newParamSymNode(ctx, n, s)
of skEnumField:
if overloadableEnums in c.features:
result = symChoice(c, n, s, scOpen)
Expand Down Expand Up @@ -307,13 +308,10 @@ proc semGenericStmt(c: PContext, n: PNode,
# skip it
if s.magic == mRunnableExamples:
first = result.safeLen # see trunnableexamples.fun3
of skGenericParam:
result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info)
first = 1
of skType:
# bad hack for generics:
if (s.typ != nil) and (s.typ.kind != tyGenericParam):
result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info)
of skGenericParam, skType:
let x = semGenericStmtSymbol(c, n[0], s, ctx, flags)
if x != n[0]:
result[0] = x
first = 1
of skError:
if s.isError: # has the error ast
Expand Down Expand Up @@ -577,10 +575,11 @@ proc semGenericStmt(c: PContext, n: PNode,
when defined(nimsuggest):
if withinTypeDesc in flags: dec c.inTypeContext

proc semGenericStmt(c: PContext, n: PNode): PNode =
proc semGenericStmt(c: PContext, n: PNode; bindParams = false): PNode =
var ctx: GenericCtx
ctx.toMixin = initIntSet()
ctx.toBind = initIntSet()
ctx.bindParams = bindParams
result = semGenericStmt(c, n, {}, ctx)
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)

Expand Down
54 changes: 28 additions & 26 deletions compiler/sem/seminst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
header.kind == tyGenericInvocation,
"Expected generic invocation for header kind, but found " & $header.kind)

# TODO: clean up and consider using ``generateTypeInstance`` instead

var
cl: TReplTypeVars

Expand All @@ -159,34 +161,34 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
cl.c = c
cl.allowMetaTypes = allowMetaTypes

# We must add all generic params in scope, because the generic body
# may include tyFromExpr nodes depending on these generic params.
# XXX: This looks quite similar to the code in matchUserTypeClass,
# perhaps the code can be extracted in a shared function.
openScope(c)
let genericTyp = header.base
for i in 0..<genericTyp.len - 1:
let genParam = genericTyp[i]
var param: PSym

template paramSym(kind): untyped =
newSym(kind, genParam.sym.name, nextSymId c.idgen, genericTyp.sym, genParam.sym.info)

if genParam.kind == tyStatic:
param = paramSym skConst
param.ast = header[i+1].n
param.typ = header[i+1]
else:
param = paramSym skType
param.typ = makeTypeDesc(c, header[i+1])

# this scope was not created by the user,
# unused params shouldn't be reported.
param.flags.incl sfUsed
addDecl(c, param)
# # We must add all generic params in scope, because the generic body
# # may include tyFromExpr nodes depending on these generic params.
# # XXX: This looks quite similar to the code in matchUserTypeClass,
# # perhaps the code can be extracted in a shared function.
# openScope(c)
# let genericTyp = header.base
# for i in 0..<genericTyp.len - 1:
# let genParam = genericTyp[i]
# var param: PSym

# template paramSym(kind): untyped =
# newSym(kind, genParam.sym.name, nextSymId c.idgen, genericTyp.sym, genParam.sym.info)

# if genParam.kind == tyStatic:
# param = paramSym skConst
# param.ast = header[i+1].n
# param.typ = header[i+1]
# else:
# param = paramSym skType
# param.typ = makeTypeDesc(c, header[i+1])

# # this scope was not created by the user,
# # unused params shouldn't be reported.
# param.flags.incl sfUsed
# addDecl(c, param)

result = replaceTypeVarsT(cl, header)
closeScope(c)
# closeScope(c)

proc referencesAnotherParam(n: PNode, p: PSym): bool =
case n.kind
Expand Down
Loading