semLambda removed, semProcAux reworked (#17379)

* simplified proc-like name ident to symbol code

* wip - reworking generic param sem

* wip - closer to removing nkEmpty generic params

* it's hacky but tests pass

* slowly tweaking semProcAux to take on semLambda

* fix pragma superset check proto vs current

* Set the symbol owner earlier

* partial progress reworking proto

found bug where default values between forward and impl lead to overload
resolution issues.

* simplified pragma handling and callConv checks

Co-authored-by: Clyybber <Clyybber@users.noreply.github.com>

* partially working

* cgexprs issue

* It works!

* comment clean-up

* clean-up asserts, comments, and other bits

* add isGenericParams, inline isGeneric queries

* seeing if this is sufficiently consistent
* can use this approach or continue it in a further PR

* commentary about nullary generics and clean-ups

* fixed a mistake in PNode isGenericRoutine

* Some small cleanups

* Small cleanup

* for func lambdas ensure we use lambda pragmas

* add some basic compileTime func tests

* [ci skip] remove comments

Co-authored-by: Clyybber <Clyybber@users.noreply.github.com>
Co-authored-by: Clyybber <darkmine956@gmail.com>
This commit is contained in:
Saem Ghani 2021-03-17 11:51:50 -07:00 • committed by GitHub
commit 72b89eff82
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 286 additions and 212 deletions

View file

@ -1040,6 +1040,7 @@ const
declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef} declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef}
routineDefs* = declarativeDefs + {nkMacroDef, nkTemplateDef} routineDefs* = declarativeDefs + {nkMacroDef, nkTemplateDef}
procDefs* = nkLambdaKinds + declarativeDefs procDefs* = nkLambdaKinds + declarativeDefs
callableDefs* = nkLambdaKinds + routineDefs
nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice} nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice}
nkStrKinds* = {nkStrLit..nkTripleStrLit} nkStrKinds* = {nkStrLit..nkTripleStrLit}
@ -1760,12 +1761,32 @@ proc getStrOrChar*(a: PNode): string =
#internalError(a.info, "getStrOrChar") #internalError(a.info, "getStrOrChar")
#result = "" #result = ""
proc isGenericRoutine*(s: PSym): bool = proc isGenericParams*(n: PNode): bool {.inline.} =
case s.kind ## used to judge whether a node is generic params.
of skProcKinds: n != nil and n.kind == nkGenericParams
result = sfFromGeneric in s.flags or
(s.ast != nil and s.ast[genericParamsPos].kind != nkEmpty) proc isGenericRoutine*(n: PNode): bool {.inline.} =
else: discard n != nil and n.kind in callableDefs and n[genericParamsPos].isGenericParams
proc isGenericRoutineStrict*(s: PSym): bool {.inline.} =
## determines if this symbol represents a generic routine
## the unusual name is so it doesn't collide and eventually replaces
## `isGenericRoutine`
s.kind in skProcKinds and s.ast.isGenericRoutine
proc isGenericRoutine*(s: PSym): bool {.inline.} =
## determines if this symbol represents a generic routine or an instance of
## one. This should be renamed accordingly and `isGenericRoutineStrict`
## should take this name instead.
##
## Warning/XXX: Unfortunately, it considers a proc kind symbol flagged with
## sfFromGeneric as a generic routine. Instead this should likely not be the
## case and the concepts should be teased apart:
## - generic definition
## - generic instance
## - either generic definition or instance
s.kind in skProcKinds and (sfFromGeneric in s.flags or
s.ast.isGenericRoutine)
proc skipGenericOwner*(s: PSym): PSym = proc skipGenericOwner*(s: PSym): PSym =
## Generic instantiations are owned by their originating generic ## Generic instantiations are owned by their originating generic

View file

@ -268,7 +268,7 @@ proc genOp(c: var Con; op: PSym; dest: PNode): PNode =
proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode = proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
var op = getAttachedOp(c.graph, t, kind) var op = getAttachedOp(c.graph, t, kind)
if op == nil or op.ast[genericParamsPos].kind != nkEmpty: if op == nil or op.ast.isGenericRoutine:
# give up and find the canonical type instead: # give up and find the canonical type instead:
let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct}) let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct})
let canon = c.graph.canonTypes.getOrDefault(h) let canon = c.graph.canonTypes.getOrDefault(h)
@ -278,7 +278,7 @@ proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode
#echo dest.typ.id #echo dest.typ.id
globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] & globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
"' operator not found for type " & typeToString(t)) "' operator not found for type " & typeToString(t))
elif op.ast[genericParamsPos].kind != nkEmpty: elif op.ast.isGenericRoutine:
globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] & globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
"' operator is generic") "' operator is generic")
dbg: dbg:

View file

@ -354,7 +354,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
# markUsed(c.g.config, c.info, op, c.g.usageSym) # markUsed(c.g.config, c.info, op, c.g.usageSym)
onUse(c.info, op) onUse(c.info, op)
# We also now do generic instantiations in the destructor lifting pass: # We also now do generic instantiations in the destructor lifting pass:
if op.ast[genericParamsPos].kind != nkEmpty: if op.ast.isGenericRoutine:
op = instantiateGeneric(c, op, t, t.typeInst) op = instantiateGeneric(c, op, t, t.typeInst)
field = op field = op
#echo "trying to use ", op.ast #echo "trying to use ", op.ast
@ -370,7 +370,7 @@ proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
var op = t.destructor var op = t.destructor
if op != nil and sfOverriden in op.flags: if op != nil and sfOverriden in op.flags:
if op.ast[genericParamsPos].kind != nkEmpty: if op.ast.isGenericRoutine:
# patch generic destructor: # patch generic destructor:
op = instantiateGeneric(c, op, t, t.typeInst) op = instantiateGeneric(c, op, t, t.typeInst)
setAttachedOp(c.g, c.idgen.module, t, attachedDestructor, op) setAttachedOp(c.g, c.idgen.module, t, attachedDestructor, op)
@ -394,7 +394,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
var op = t.destructor var op = t.destructor
if op != nil and sfOverriden in op.flags: if op != nil and sfOverriden in op.flags:
if op.ast[genericParamsPos].kind != nkEmpty: if op.ast.isGenericRoutine:
# patch generic destructor: # patch generic destructor:
op = instantiateGeneric(c, op, t, t.typeInst) op = instantiateGeneric(c, op, t, t.typeInst)
setAttachedOp(c.g, c.idgen.module, t, attachedDestructor, op) setAttachedOp(c.g, c.idgen.module, t, attachedDestructor, op)
@ -1021,7 +1021,7 @@ proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo; idgen: Id
let op = getAttachedOp(g, t, attachedDestructor) let op = getAttachedOp(g, t, attachedDestructor)
if op != nil: if op != nil:
if op.ast[genericParamsPos].kind != nkEmpty: if op.ast.isGenericRoutine:
internalError(g.config, info, "resolved destructor is generic") internalError(g.config, info, "resolved destructor is generic")
if op.magic == mDestroy: if op.magic == mDestroy:
internalError(g.config, info, "patching mDestroy with mDestroy?") internalError(g.config, info, "patching mDestroy with mDestroy?")
@ -1031,7 +1031,7 @@ proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo; idgen: Id
proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: IdGenerator; proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: IdGenerator;
info: TLineInfo) = info: TLineInfo) =
let op = getAttachedOp(g, t, kind) let op = getAttachedOp(g, t, kind)
if op != nil and op.ast != nil and op.ast[genericParamsPos].kind != nkEmpty: if op != nil and op.ast != nil and op.ast.isGenericRoutine:
if t.typeInst != nil: if t.typeInst != nil:
var a: TLiftCtx var a: TLiftCtx
a.info = info a.info = info

View file

@ -287,6 +287,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string; proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string;
conflictsWith: TLineInfo) = conflictsWith: TLineInfo) =
## Emit a redefinition error if in non-interactive mode
if c.config.cmd != cmdInteractive: if c.config.cmd != cmdInteractive:
localError(c.config, info, localError(c.config, info,
"redefinition of '$1'; previous declaration here: $2" % "redefinition of '$1'; previous declaration here: $2" %

View file

@ -60,7 +60,7 @@ const
wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader, wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader,
wThread, wAsmNoStackFrame, wThread, wAsmNoStackFrame,
wRaises, wLocks, wTags, wRequires, wEnsures, wRaises, wLocks, wTags, wRequires, wEnsures,
wGcSafe, wCodegenDecl, wNoInit} wGcSafe, wCodegenDecl, wNoInit, wCompileTime}
typePragmas* = declPragmas + {wMagic, wAcyclic, typePragmas* = declPragmas + {wMagic, wAcyclic,
wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wShallow, wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wShallow,
wIncompleteStruct, wCompleteStruct, wByCopy, wByRef, wIncompleteStruct, wCompleteStruct, wByCopy, wByRef,
@ -1245,23 +1245,23 @@ proc mergePragmas(n, pragmas: PNode) =
else: else:
for p in pragmas: n[pragmasPos].add p for p in pragmas: n[pragmasPos].add p
proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
validPragmas: TSpecialWords) = validPragmas: TSpecialWords) =
if sym != nil and sym.kind != skModule: if sym != nil and sym.kind != skModule:
for it in c.optionStack: for it in c.optionStack:
let o = it.otherPragmas let o = it.otherPragmas
if not o.isNil and sfFromGeneric notin sym.flags: # see issue #12985 if not o.isNil and sfFromGeneric notin sym.flags: # see issue #12985
pushInfoContext(c.config, n.info) pushInfoContext(c.config, info)
var i = 0 var i = 0
while i < o.len: while i < o.len:
if singlePragma(c, sym, o, i, validPragmas, true, false): if singlePragma(c, sym, o, i, validPragmas, true, false):
internalError(c.config, n.info, "implicitPragmas") internalError(c.config, info, "implicitPragmas")
inc i inc i
popInfoContext(c.config) popInfoContext(c.config)
if sym.kind in routineKinds and sym.ast != nil: mergePragmas(sym.ast, o) if sym.kind in routineKinds and sym.ast != nil: mergePragmas(sym.ast, o)
if lfExportLib in sym.loc.flags and sfExportc notin sym.flags: if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
localError(c.config, n.info, ".dynlib requires .exportc") localError(c.config, info, ".dynlib requires .exportc")
var lib = c.optionStack[^1].dynlib var lib = c.optionStack[^1].dynlib
if {lfDynamicLib, lfHeader} * sym.loc.flags == {} and if {lfDynamicLib, lfHeader} * sym.loc.flags == {} and
sfImportc in sym.flags and lib != nil: sfImportc in sym.flags and lib != nil:
@ -1291,4 +1291,11 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
isStatement: bool) = isStatement: bool) =
if n == nil: return if n == nil: return
pragmaRec(c, sym, n, validPragmas, isStatement) pragmaRec(c, sym, n, validPragmas, isStatement)
implicitPragmas(c, sym, n, validPragmas) # XXX: in the case of a callable def, this should use its info
implicitPragmas(c, sym, n.info, validPragmas)
proc pragmaCallable*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords,
isStatement: bool = false) =
if n == nil: return
if n[pragmasPos].kind != nkEmpty:
pragmaRec(c, sym, n[pragmasPos], validPragmas, isStatement)

View file

@ -36,7 +36,6 @@ proc semProcBody(c: PContext, n: PNode): PNode
proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode
proc changeType(c: PContext; n: PNode, newType: PType, check: bool) proc changeType(c: PContext; n: PNode, newType: PType, check: bool)
proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType proc semTypeNode(c: PContext, n: PNode, prev: PType): PType
proc semStmt(c: PContext, n: PNode; flags: TExprFlags): PNode proc semStmt(c: PContext, n: PNode; flags: TExprFlags): PNode
proc semOpAux(c: PContext, n: PNode) proc semOpAux(c: PContext, n: PNode)

View file

@ -445,7 +445,7 @@ proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
let a = if a.kind == nkHiddenDeref: a[0] else: a let a = if a.kind == nkHiddenDeref: a[0] else: a
if a.kind == nkHiddenCallConv and a[0].kind == nkSym: if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
let s = a[0].sym let s = a[0].sym
if s.ast != nil and s.ast[genericParamsPos].kind != nkEmpty: if s.isGenericRoutineStrict:
let finalCallee = generateInstance(c, s, x.bindings, a.info) let finalCallee = generateInstance(c, s, x.bindings, a.info)
a[0].sym = finalCallee a[0].sym = finalCallee
a[0].typ = finalCallee.typ a[0].typ = finalCallee.typ
@ -524,7 +524,7 @@ proc semResolvedCall(c: PContext, x: TCandidate,
if result.typ.kind == tyError: incl result.typ.flags, tfCheckedForDestructor if result.typ.kind == tyError: incl result.typ.flags, tfCheckedForDestructor
return return
let gp = finalCallee.ast[genericParamsPos] let gp = finalCallee.ast[genericParamsPos]
if gp.kind != nkEmpty: if gp.isGenericParams:
if x.calleeSym.kind notin {skMacro, skTemplate}: if x.calleeSym.kind notin {skMacro, skTemplate}:
if x.calleeSym.magic in {mArrGet, mArrPut}: if x.calleeSym.magic in {mArrGet, mArrPut}:
finalCallee = x.calleeSym finalCallee = x.calleeSym

View file

@ -2856,7 +2856,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkCurly: result = semSetConstr(c, n) of nkCurly: result = semSetConstr(c, n)
of nkBracket: result = semArrayConstr(c, n, flags) of nkBracket: result = semArrayConstr(c, n, flags)
of nkObjConstr: result = semObjConstr(c, n, flags) of nkObjConstr: result = semObjConstr(c, n, flags)
of nkLambdaKinds: result = semLambda(c, n, flags) of nkLambdaKinds: result = semProcAux(c, n, skProc, lambdaPragmas, flags)
of nkDerefExpr: result = semDeref(c, n) of nkDerefExpr: result = semDeref(c, n)
of nkAddr: of nkAddr:
result = n result = n

View file

@ -358,7 +358,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
openScope(c) openScope(c)
let gp = n[genericParamsPos] let gp = n[genericParamsPos]
internalAssert c.config, gp.kind != nkEmpty internalAssert c.config, gp.kind == nkGenericParams
n[namePos] = newSymNode(result) n[namePos] = newSymNode(result)
pushInfoContext(c.config, info, fn.detailedInfo) pushInfoContext(c.config, info, fn.detailedInfo)
var entry = TInstantiation.new var entry = TInstantiation.new

View file

@ -1523,75 +1523,9 @@ proc semProcAnnotation(c: PContext, prc: PNode;
return return
proc setGenericParamsMisc(c: PContext; n: PNode): PNode =
let orig = n[genericParamsPos]
# we keep the original params around for better error messages, see
# issue https://github.com/nim-lang/Nim/issues/1713
result = semGenericParamList(c, orig)
if n[miscPos].kind == nkEmpty:
n[miscPos] = newTree(nkBracket, c.graph.emptyNode, orig)
else:
n[miscPos][1] = orig
n[genericParamsPos] = result
proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
# XXX semProcAux should be good enough for this now, we will eventually
# remove semLambda
result = semProcAnnotation(c, n, lambdaPragmas)
if result != nil: return result
result = n
checkSonsLen(n, bodyPos + 1, c.config)
var s: PSym
if n[namePos].kind != nkSym:
s = newSym(skProc, c.cache.idAnon, nextSymId c.idgen, getCurrOwner(c), n.info)
s.ast = n
n[namePos] = newSymNode(s)
else:
s = n[namePos].sym
pushOwner(c, s)
openScope(c)
var gp: PNode
if n[genericParamsPos].kind != nkEmpty:
gp = setGenericParamsMisc(c, n)
else:
gp = newNodeI(nkGenericParams, n.info)
if n[paramsPos].kind != nkEmpty:
semParamList(c, n[paramsPos], gp, s)
# paramsTypeCheck(c, s.typ)
if gp.len > 0 and n[genericParamsPos].kind == nkEmpty:
# we have a list of implicit type parameters:
n[genericParamsPos] = gp
else:
s.typ = newProcType(c, n.info)
if n[pragmasPos].kind != nkEmpty:
pragma(c, s, n[pragmasPos], lambdaPragmas)
s.options = c.config.options
if n[bodyPos].kind != nkEmpty:
if sfImportc in s.flags:
localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s)
#if efDetermineType notin flags:
# XXX not good enough; see tnamedparamanonproc.nim
if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags):
pushProcCon(c, s)
addResult(c, n, s.typ[0], skProc)
s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos]))
trackProc(c, s, s.ast[bodyPos])
popProcCon(c)
elif efOperand notin flags:
localError(c.config, n.info, errGenericLambdaNotAllowed)
sideEffectsCheck(c, s)
else:
localError(c.config, n.info, errImplOfXexpected % s.name.s)
closeScope(c) # close scope for parameters
popOwner(c)
result.typ = s.typ
if optOwnedRefs in c.config.globalOptions:
result.typ = makeVarType(c, result.typ, tyOwned)
proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} = proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} =
## used for resolving 'auto' in lambdas based on their callsite
var n = n var n = n
let original = n[namePos].sym let original = n[namePos].sym
let s = original #copySym(original, false) let s = original #copySym(original, false)
#incl(s.flags, sfFromGeneric) #incl(s.flags, sfFromGeneric)
@ -1798,11 +1732,6 @@ proc cursorInProc(conf: ConfigRef; n: PNode): bool =
if n.info.fileIndex == conf.m.trackPos.fileIndex: if n.info.fileIndex == conf.m.trackPos.fileIndex:
result = cursorInProcAux(conf, n) result = cursorInProcAux(conf, n)
type
TProcCompilationSteps = enum
stepRegisterSymbol,
stepDetermineType,
proc hasObjParam(s: PSym): bool = proc hasObjParam(s: PSym): bool =
var t = s.typ var t = s.typ
for col in 1..<t.len: for col in 1..<t.len:
@ -1814,7 +1743,7 @@ proc finishMethod(c: PContext, s: PSym) =
methodDef(c.graph, c.idgen, s) methodDef(c.graph, c.idgen, s)
proc semMethodPrototype(c: PContext; s: PSym; n: PNode) = proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
if isGenericRoutine(s): if s.isGenericRoutine:
let tt = s.typ let tt = s.typ
var foundObj = false var foundObj = false
# we start at 1 for now so that tparsecombnum continues to compile. # we start at 1 for now so that tparsecombnum continues to compile.
@ -1840,28 +1769,45 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
else: else:
localError(c.config, n.info, "'method' needs a parameter that has an object type") localError(c.config, n.info, "'method' needs a parameter that has an object type")
proc setGenericParamsMisc(c: PContext; n: PNode) =
let orig = n[genericParamsPos]
doAssert orig.kind in {nkEmpty, nkGenericParams}
if n[genericParamsPos].kind == nkEmpty:
n[genericParamsPos] = newNodeI(nkGenericParams, n.info)
else:
# we keep the original params around for better error messages, see
# issue https://github.com/nim-lang/Nim/issues/1713
n[genericParamsPos] = semGenericParamList(c, orig)
if n[miscPos].kind == nkEmpty:
n[miscPos] = newTree(nkBracket, c.graph.emptyNode, orig)
else:
n[miscPos][1] = orig
proc semProcAux(c: PContext, n: PNode, kind: TSymKind, proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
validPragmas: TSpecialWords, validPragmas: TSpecialWords, flags: TExprFlags = {}): PNode =
phase = stepRegisterSymbol): PNode =
result = semProcAnnotation(c, n, validPragmas) result = semProcAnnotation(c, n, validPragmas)
if result != nil: return result if result != nil: return result
result = n result = n
checkMinSonsLen(n, bodyPos + 1, c.config) checkMinSonsLen(n, bodyPos + 1, c.config)
var s: PSym
var typeIsDetermined = false
var isAnon = false
if n[namePos].kind != nkSym:
assert phase == stepRegisterSymbol
if n[namePos].kind == nkEmpty: let isAnon = n[namePos].kind == nkEmpty
s = newSym(kind, c.cache.idAnon, nextSymId c.idgen, getCurrOwner(c), n.info)
incl(s.flags, sfUsed) var s: PSym
isAnon = true
else: case n[namePos].kind
s = semIdentDef(c, n[0], kind) of nkEmpty:
s = newSym(kind, c.cache.idAnon, nextSymId c.idgen, c.getCurrOwner, n.info)
s.flags.incl sfUsed
n[namePos] = newSymNode(s)
of nkSym:
s = n[namePos].sym
s.owner = c.getCurrOwner
else:
s = semIdentDef(c, n[namePos], kind)
n[namePos] = newSymNode(s) n[namePos] = newSymNode(s)
s.ast = n
#s.scope = c.currentScope
when false: when false:
# disable for now # disable for now
if sfNoForward in c.module.flags and if sfNoForward in c.module.flags and
@ -1869,37 +1815,50 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
addInterfaceOverloadableSymAt(c, c.currentScope, s) addInterfaceOverloadableSymAt(c, c.currentScope, s)
s.flags.incl sfForward s.flags.incl sfForward
return return
else:
s = n[namePos].sym assert s.kind in skProcKinds
s.owner = getCurrOwner(c)
typeIsDetermined = s.typ == nil
s.ast = n s.ast = n
s.options = c.config.options
#s.scope = c.currentScope #s.scope = c.currentScope
s.options = c.config.options # before compiling the proc params & body, set as current the scope
# before compiling the proc body, set as current the scope
# where the proc was declared # where the proc was declared
let oldScope = c.currentScope let delcarationScope = c.currentScope
#c.currentScope = s.scope
pushOwner(c, s) pushOwner(c, s)
openScope(c) openScope(c)
var gp: PNode
if n[genericParamsPos].kind != nkEmpty:
gp = setGenericParamsMisc(c, n)
else:
gp = newNodeI(nkGenericParams, n.info)
# process parameters: # process parameters:
# generic parameters, parameters, and also the implicit generic parameters
# within are analysed. This is often the entirety of their semantic analysis
# but later we will have to do a check for forward declarations, which can by
# way of pragmas, default params, and so on invalidate this parsing.
# Nonetheless, we need to carry out this analysis to perform the search for a
# potential forward declaration.
setGenericParamsMisc(c, n)
if n[paramsPos].kind != nkEmpty: if n[paramsPos].kind != nkEmpty:
semParamList(c, n[paramsPos], gp, s) semParamList(c, n[paramsPos], n[genericParamsPos], s)
if gp.len > 0: # we maybe have implicit type parameters:
if n[genericParamsPos].kind == nkEmpty:
# we have a list of implicit type parameters:
n[genericParamsPos] = gp
# check for semantics again:
# semParamList(c, n[ParamsPos], nil, s)
else: else:
s.typ = newProcType(c, n.info) s.typ = newProcType(c, n.info)
if n[genericParamsPos].safeLen == 0:
# if there exist no explicit or implicit generic parameters, then this is
# at most a nullary generic (generic with no type params). Regardless of
# whether it's a nullary generic or non-generic, we restore the original.
# In the case of `nkEmpty` it's non-generic and an empty `nkGeneircParams`
# is a nullary generic.
#
# Remarks about nullary generics vs non-generics:
# The difference between a non-generic and nullary generic is minor in
# most cases but there are subtle and significant differences as well.
# Due to instantiation that generic procs go through, a static echo in the
# body of a nullary generic will not be executed immediately, as it's
# instantiated and not immediately evaluated.
n[genericParamsPos] = n[miscPos][1]
n[miscPos] = c.graph.emptyNode
if tfTriggersCompileTime in s.typ.flags: incl(s.flags, sfCompileTime) if tfTriggersCompileTime in s.typ.flags: incl(s.flags, sfCompileTime)
if n[patternPos].kind != nkEmpty: if n[patternPos].kind != nkEmpty:
n[patternPos] = semPattern(c, n[patternPos]) n[patternPos] = semPattern(c, n[patternPos])
@ -1908,47 +1867,66 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
elif s.kind == skFunc: elif s.kind == skFunc:
incl(s.flags, sfNoSideEffect) incl(s.flags, sfNoSideEffect)
incl(s.typ.flags, tfNoSideEffect) incl(s.typ.flags, tfNoSideEffect)
var (proto, comesFromShadowScope) = if isAnon: (nil, false)
else: searchForProc(c, oldScope, s) var (proto, comesFromShadowScope) =
if isAnon: (nil, false)
else: searchForProc(c, delcarationScope, s)
if proto == nil and sfForward in s.flags: if proto == nil and sfForward in s.flags:
#This is a definition that shares its sym with its forward declaration (generated by a macro), ## In cases such as a macro generating a proc with a gensymmed name we
#if the symbol is also gensymmed we won't find it with searchForProc, so we check here ## know `searchForProc` will not find it and sfForward will be set. In
## such scenarios the sym is shared between forward declaration and we
## can treat the `s` as the proto.
proto = s proto = s
if proto == nil: let hasProto = proto != nil
if s.kind == skIterator:
# set the default calling conventions
case s.kind
of skIterator:
if s.typ.callConv != ccClosure: if s.typ.callConv != ccClosure:
s.typ.callConv = if isAnon: ccClosure else: ccInline s.typ.callConv = if isAnon: ccClosure else: ccInline
of skMacro, skTemplate:
# we don't bother setting calling conventions for macros and templates
discard
else: else:
# NB: procs with a forward decl have theirs determined by the forward decl
if not hasProto:
# in this case we're either a forward declaration or we're an impl without
# a forward decl. We set the calling convention or will be set during
# pragma analysis further down.
s.typ.callConv = lastOptionEntry(c).defaultCC s.typ.callConv = lastOptionEntry(c).defaultCC
# add it here, so that recursive procs are possible:
if sfGenSym in s.flags: if not hasProto and sfGenSym notin s.flags: #and not isAnon:
if s.owner == nil: s.owner = getCurrOwner(c) if s.kind in OverloadableSyms:
elif kind in OverloadableSyms: addInterfaceOverloadableSymAt(c, delcarationScope, s)
if not typeIsDetermined:
addInterfaceOverloadableSymAt(c, oldScope, s)
else: else:
if not typeIsDetermined: addInterfaceDeclAt(c, delcarationScope, s)
addInterfaceDeclAt(c, oldScope, s)
if n[pragmasPos].kind != nkEmpty: pragmaCallable(c, s, n, validPragmas)
pragma(c, s, n[pragmasPos], validPragmas) if not hasProto:
else: implicitPragmas(c, s, n.info, validPragmas)
implicitPragmas(c, s, n, validPragmas)
styleCheckDef(c.config, s)
onDef(n[namePos].info, s)
else:
if n[pragmasPos].kind != nkEmpty:
pragma(c, s, n[pragmasPos], validPragmas)
# To ease macro generation that produce forwarded .async procs we now # To ease macro generation that produce forwarded .async procs we now
# allow a bit redundancy in the pragma declarations. The rule is # allow a bit redundancy in the pragma declarations. The rule is
# a prototype's pragma list must be a superset of the current pragma # a prototype's pragma list must be a superset of the current pragma
# list. # list.
# XXX This needs more checks eventually, for example that external # XXX This needs more checks eventually, for example that external
# linking names do agree: # linking names do agree:
if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags: if hasProto and (
# calling convention mismatch
tfExplicitCallConv in s.typ.flags and proto.typ.callConv != s.typ.callConv or
# implementation has additional pragmas
proto.typ.flags < s.typ.flags):
localError(c.config, n[pragmasPos].info, errPragmaOnlyInHeaderOfProcX % localError(c.config, n[pragmasPos].info, errPragmaOnlyInHeaderOfProcX %
("'" & proto.name.s & "' from " & c.config$proto.info)) ("'" & proto.name.s & "' from " & c.config$proto.info &
" '" & s.name.s & "' from " & c.config$s.info))
styleCheckDef(c.config, s) styleCheckDef(c.config, s)
if hasProto:
onDefResolveForward(n[namePos].info, proto) onDefResolveForward(n[namePos].info, proto)
else:
onDef(n[namePos].info, s)
if hasProto:
if sfForward notin proto.flags and proto.magic == mNone: if sfForward notin proto.flags and proto.magic == mNone:
wrongRedefinition(c, n.info, proto.name.s, proto.info) wrongRedefinition(c, n.info, proto.name.s, proto.info)
if not comesFromShadowScope: if not comesFromShadowScope:
@ -1957,7 +1935,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
suggestSym(c.graph, s.info, proto, c.graph.usageSym) suggestSym(c.graph, s.info, proto, c.graph.usageSym)
closeScope(c) # close scope with wrong parameter symbols closeScope(c) # close scope with wrong parameter symbols
openScope(c) # open scope for old (correct) parameter symbols openScope(c) # open scope for old (correct) parameter symbols
if proto.ast[genericParamsPos].kind != nkEmpty: if proto.ast[genericParamsPos].isGenericParams:
addGenericParamListToScope(c, proto.ast[genericParamsPos]) addGenericParamListToScope(c, proto.ast[genericParamsPos])
addParams(c, proto.typ.n, proto.kind) addParams(c, proto.typ.n, proto.kind)
proto.info = s.info # more accurate line information proto.info = s.info # more accurate line information
@ -1974,8 +1952,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
popOwner(c) popOwner(c)
pushOwner(c, s) pushOwner(c, s)
if not isAnon:
if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
if s.name.s[0] in {'.', '('}: elif s.name.s[0] in {'.', '('}:
if s.name.s in [".", ".()", ".="] and {Feature.destructor, dotOperators} * c.features == {}: if s.name.s in [".", ".()", ".="] and {Feature.destructor, dotOperators} * c.features == {}:
localError(c.config, n.info, "the overloaded " & s.name.s & localError(c.config, n.info, "the overloaded " & s.name.s &
" operator has to be enabled with {.experimental: \"dotOperators\".}") " operator has to be enabled with {.experimental: \"dotOperators\".}")
@ -1987,18 +1966,28 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
# for DLL generation we allow sfImportc to have a body, for use in VM # for DLL generation we allow sfImportc to have a body, for use in VM
if sfBorrow in s.flags: if sfBorrow in s.flags:
localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s) localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s)
let usePseudoGenerics = kind in {skMacro, skTemplate} if c.config.ideCmd in {ideSug, ideCon} and s.kind notin {skMacro, skTemplate} and not
# Macros and Templates can have generic parameters, but they are
# only used for overload resolution (there is no instantiation of
# the symbol, so we must process the body now)
if not usePseudoGenerics and c.config.ideCmd in {ideSug, ideCon} and not
cursorInProc(c.config, n[bodyPos]): cursorInProc(c.config, n[bodyPos]):
discard "speed up nimsuggest" # speed up nimsuggest
if s.kind == skMethod: semMethodPrototype(c, s, n) if s.kind == skMethod: semMethodPrototype(c, s, n)
elif isAnon:
let gp = n[genericParamsPos]
if gp.kind == nkEmpty or (gp.len == 1 and tfRetType in gp[0].typ.flags):
# absolutely no generics (empty) or a single generic return type are
# allowed, everything else, including a nullary generic is an error.
pushProcCon(c, s)
addResult(c, n, s.typ[0], skProc)
s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos]))
trackProc(c, s, s.ast[bodyPos])
popProcCon(c)
elif efOperand notin flags:
localError(c.config, n.info, errGenericLambdaNotAllowed)
else: else:
pushProcCon(c, s) pushProcCon(c, s)
if n[genericParamsPos].kind == nkEmpty or usePseudoGenerics: if n[genericParamsPos].kind == nkEmpty or s.kind in {skMacro, skTemplate}:
if not usePseudoGenerics and s.magic == mNone: paramsTypeCheck(c, s.typ) # Macros and Templates can have generic parameters, but they are only
# used for overload resolution (there is no instantiation of the symbol)
if s.kind notin {skMacro, skTemplate} and s.magic == mNone: paramsTypeCheck(c, s.typ)
maybeAddResult(c, s, n) maybeAddResult(c, s, n)
# semantic checking also needed with importc in case used in VM # semantic checking also needed with importc in case used in VM
@ -2006,9 +1995,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
# unfortunately we cannot skip this step when in 'system.compiles' # unfortunately we cannot skip this step when in 'system.compiles'
# context as it may even be evaluated in 'system.compiles': # context as it may even be evaluated in 'system.compiles':
trackProc(c, s, s.ast[bodyPos]) trackProc(c, s, s.ast[bodyPos])
if s.kind == skMethod: semMethodPrototype(c, s, n)
else: else:
if (s.typ[0] != nil and kind != skIterator) or kind == skMacro: if (s.typ[0] != nil and s.kind != skIterator):
addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nextSymId c.idgen, nil, n.info)) addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nextSymId c.idgen, nil, n.info))
openScope(c) openScope(c)
@ -2017,19 +2005,16 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if s.magic == mNone: if s.magic == mNone:
fixupInstantiatedSymbols(c, s) fixupInstantiatedSymbols(c, s)
if s.kind == skMethod: semMethodPrototype(c, s, n) if s.kind == skMethod: semMethodPrototype(c, s, n)
if sfImportc in s.flags:
# don't ignore the body in case used in VM
# n[bodyPos] = c.graph.emptyNode
discard
popProcCon(c) popProcCon(c)
else: else:
if s.kind == skMethod: semMethodPrototype(c, s, n)
if hasProto: localError(c.config, n.info, errImplOfXexpected % proto.name.s)
if {sfImportc, sfBorrow, sfError} * s.flags == {} and s.magic == mNone:
# this is a forward declaration and we're building the prototype
if s.kind in {skProc, skFunc} and s.typ[0] != nil and s.typ[0].kind == tyUntyped: if s.kind in {skProc, skFunc} and s.typ[0] != nil and s.typ[0].kind == tyUntyped:
# `auto` is represented as `tyUntyped` at this point in compilation. # `auto` is represented as `tyUntyped` at this point in compilation.
localError(c.config, n[paramsPos][0].info, "return type 'auto' cannot be used in forward declarations") localError(c.config, n[paramsPos][0].info, "return type 'auto' cannot be used in forward declarations")
if s.kind == skMethod: semMethodPrototype(c, s, n)
if proto != nil: localError(c.config, n.info, errImplOfXexpected % proto.name.s)
if {sfImportc, sfBorrow, sfError} * s.flags == {} and s.magic == mNone:
incl(s.flags, sfForward) incl(s.flags, sfForward)
incl(s.flags, sfWasForwarded) incl(s.flags, sfWasForwarded)
elif sfBorrow in s.flags: semBorrow(c, n, s) elif sfBorrow in s.flags: semBorrow(c, n, s)
@ -2044,15 +2029,14 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
result.typ = s.typ result.typ = s.typ
if optOwnedRefs in c.config.globalOptions: if optOwnedRefs in c.config.globalOptions:
result.typ = makeVarType(c, result.typ, tyOwned) result.typ = makeVarType(c, result.typ, tyOwned)
if isTopLevel(c) and s.kind != skIterator and elif isTopLevel(c) and s.kind != skIterator and s.typ.callConv == ccClosure:
s.typ.callConv == ccClosure:
localError(c.config, s.info, "'.closure' calling convention for top level routines is invalid") localError(c.config, s.info, "'.closure' calling convention for top level routines is invalid")
proc determineType(c: PContext, s: PSym) = proc determineType(c: PContext, s: PSym) =
if s.typ != nil: return if s.typ != nil: return
#if s.magic != mNone: return #if s.magic != mNone: return
#if s.ast.isNil: return #if s.ast.isNil: return
discard semProcAux(c, s.ast, s.kind, {}, stepDetermineType) discard semProcAux(c, s.ast, s.kind, {})
proc semIterator(c: PContext, n: PNode): PNode = proc semIterator(c: PContext, n: PNode): PNode =
# gensym'ed iterator? # gensym'ed iterator?
@ -2086,7 +2070,9 @@ proc semProc(c: PContext, n: PNode): PNode =
result = semProcAux(c, n, skProc, procPragmas) result = semProcAux(c, n, skProc, procPragmas)
proc semFunc(c: PContext, n: PNode): PNode = proc semFunc(c: PContext, n: PNode): PNode =
result = semProcAux(c, n, skFunc, procPragmas) let validPragmas = if n[namePos].kind != nkEmpty: procPragmas
else: lambdaPragmas
result = semProcAux(c, n, skFunc, validPragmas)
proc semMethod(c: PContext, n: PNode): PNode = proc semMethod(c: PContext, n: PNode): PNode =
if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "method") if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "method")

View file

@ -636,8 +636,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
param.flags.incl sfTemplateParam param.flags.incl sfTemplateParam
param.flags.excl sfGenSym param.flags.excl sfGenSym
if param.typ.kind != tyUntyped: allUntyped = false if param.typ.kind != tyUntyped: allUntyped = false
if gp.len > 0: if gp.len > 0 and n[genericParamsPos].kind == nkEmpty:
if n[genericParamsPos].kind == nkEmpty:
# we have a list of implicit type parameters: # we have a list of implicit type parameters:
n[genericParamsPos] = gp n[genericParamsPos] = gp
else: else:

View file

@ -1233,7 +1233,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if hasDefault: if hasDefault:
def = a[^1] def = a[^1]
block determineType: block determineType:
if genericParams != nil and genericParams.len > 0: if genericParams.isGenericParams:
def = semGenericStmt(c, def) def = semGenericStmt(c, def)
if hasUnresolvedArgs(c, def): if hasUnresolvedArgs(c, def):
def.typ = makeTypeFromExpr(c, def.copyTree) def.typ = makeTypeFromExpr(c, def.copyTree)
@ -1361,7 +1361,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
result.flags.excl tfHasMeta result.flags.excl tfHasMeta
result.n.typ = r result.n.typ = r
if genericParams != nil and genericParams.len > 0: if genericParams.isGenericParams:
for n in genericParams: for n in genericParams:
if {sfUsed, sfAnon} * n.sym.flags == {}: if {sfUsed, sfAnon} * n.sym.flags == {}:
result.flags.incl tfUnresolved result.flags.incl tfUnresolved
@ -1666,7 +1666,7 @@ proc semProcTypeWithScope(c: PContext, n: PNode,
# we construct a fake 'nkProcDef' for the 'mergePragmas' inside 'implicitPragmas'... # we construct a fake 'nkProcDef' for the 'mergePragmas' inside 'implicitPragmas'...
s.ast = newTree(nkProcDef, newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info), s.ast = newTree(nkProcDef, newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info),
newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info)) newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info))
implicitPragmas(c, s, n, {wTags, wRaises}) implicitPragmas(c, s, n.info, {wTags, wRaises})
when useEffectSystem: setEffectsForProcType(c.graph, result, s.ast[pragmasPos]) when useEffectSystem: setEffectsForProcType(c.graph, result, s.ast[pragmasPos])
closeScope(c) closeScope(c)

View file

@ -2265,7 +2265,7 @@ proc genProc(c: PCtx; s: PSym): int =
genParams(c, s.typ.n) genParams(c, s.typ.n)
# allocate additional space for any generically bound parameters # allocate additional space for any generically bound parameters
if s.kind == skMacro and s.ast[genericParamsPos].kind != nkEmpty: if s.kind == skMacro and s.isGenericRoutineStrict:
genGenericParams(c, s.ast[genericParamsPos]) genGenericParams(c, s.ast[genericParamsPos])
if tfCapturesEnv in s.typ.flags: if tfCapturesEnv in s.typ.flags:

View file

@ -0,0 +1,14 @@
discard """
errormsg: "request to generate code for .compileTime proc: foo"
"""
# ensure compileTime funcs can't be called from runtime
func foo(a: int): int {.compileTime.} =
a * a
proc doAThing(): int =
for i in 0..2:
result += foo(i)
echo doAThing()

View file

@ -0,0 +1,6 @@
discard """
errormsg: "request to generate code for .compileTime proc: :anonymous"
"""
let a = func(a: varargs[int]) {.compileTime, closure.} =
discard a[0]

View file

@ -0,0 +1,11 @@
discard """
output: '''fooo fooo'''
"""
converter intToString[T](i: T): string = "fooo"
let
foo: string = 1
bar: string = intToString(2)
echo foo, " ", bar

View file

@ -0,0 +1,26 @@
discard """
nimout: '''
hah
hey
hey
hah
'''
"""
# non-generic
proc foo(s: string) =
static: echo "hah"
echo s
static: echo "hey"
foo("hoo")
# nullary generic
proc bar[](s: string) =
static: echo "hah"
echo s
static: echo "hey"
bar("hoo")

View file

@ -2,6 +2,8 @@ discard """
nimout: ''' nimout: '''
staticAlialProc instantiated with 358 staticAlialProc instantiated with 358
staticAlialProc instantiated with 368 staticAlialProc instantiated with 368
0: Foo
1: Bar
''' '''
output: ''' output: '''
16 16
@ -289,8 +291,10 @@ macro fooParam(x: static array[2, string]): untyped =
echo i, ": ", val echo i, ": ", val
macro barParam(x: static Table[int, string]): untyped = macro barParam(x: static Table[int, string]): untyped =
for i, val in x: let barParamInsides = proc(i: int, val: string): NimNode =
echo i, ": ", val echo i, ": ", val
for i, val in x:
discard barParamInsides(i, val)
fooM() fooM()
barM() barM()