123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921 |
- #
- #
- # The Nim Compiler
- # (c) Copyright 2013 Andreas Rumpf
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- # This module implements the semantic checking pass.
- import
- ast, options, astalgo, trees,
- wordrecg, ropes, msgs, idents, renderer, types, platform,
- magicsys, nversion, nimsets, semfold, modulepaths, importer,
- procfind, lookups, pragmas, semdata, semtypinst, sigmatch,
- transf, vmdef, vm, aliases, cgmeth, lambdalifting,
- evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
- lowerings, plugins/active, lineinfos, int128,
- isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs,
- extccomp
- import vtables
- import std/[strtabs, math, tables, intsets, strutils]
- when not defined(leanCompiler):
- import spawn
- when defined(nimPreviewSlimSystem):
- import std/[
- formatfloat,
- assertions,
- ]
- # implementation
- proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode
- proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode
- proc semExprNoType(c: PContext, n: PNode): PNode
- proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
- proc semProcBody(c: PContext, n: PNode; expectedType: PType = nil): PNode
- proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode
- proc changeType(c: PContext; n: PNode, newType: PType, check: bool)
- proc semTypeNode(c: PContext, n: PNode, prev: PType): PType
- proc semStmt(c: PContext, n: PNode; flags: TExprFlags): PNode
- proc semOpAux(c: PContext, n: PNode)
- proc semParamList(c: PContext, n, genericParams: PNode, s: PSym)
- proc addParams(c: PContext, n: PNode, kind: TSymKind)
- proc maybeAddResult(c: PContext, s: PSym, n: PNode)
- proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
- proc activate(c: PContext, n: PNode)
- proc semQuoteAst(c: PContext, n: PNode): PNode
- proc finishMethod(c: PContext, s: PSym)
- proc evalAtCompileTime(c: PContext, n: PNode): PNode
- proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode
- proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode
- proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType
- proc semTypeOf(c: PContext; n: PNode): PNode
- proc computeRequiresInit(c: PContext, t: PType): bool
- proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo)
- proc hasUnresolvedArgs(c: PContext, n: PNode): bool
- proc isArrayConstr(n: PNode): bool {.inline.} =
- result = n.kind == nkBracket and
- n.typ.skipTypes(abstractInst).kind == tyArray
- template semIdeForTemplateOrGenericCheck(conf, n, requiresCheck) =
- # we check quickly if the node is where the cursor is
- when defined(nimsuggest):
- if n.info.fileIndex == conf.m.trackPos.fileIndex and n.info.line == conf.m.trackPos.line:
- requiresCheck = true
- template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
- requiresCheck: bool) =
- # use only for idetools support; this is pretty slow so generics and
- # templates perform some quick check whether the cursor is actually in
- # the generic or template.
- when defined(nimsuggest):
- if c.config.cmd == cmdIdeTools and requiresCheck:
- #if optIdeDebug in gGlobalOptions:
- # echo "passing to safeSemExpr: ", renderTree(n)
- discard safeSemExpr(c, n)
- proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
- let x = arg.skipConv
- if (x.kind == nkCurly and formal.kind == tySet and formal.base.kind != tyGenericParam) or
- (x.kind in {nkPar, nkTupleConstr}) and formal.kind notin {tyUntyped, tyBuiltInTypeClass, tyAnything}:
- changeType(c, x, formal, check=true)
- result = arg
- result = skipHiddenSubConv(result, c.graph, c.idgen)
- proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
- if arg.typ.isNil:
- localError(c.config, arg.info, "expression has no type: " &
- renderTree(arg, {renderNoComments}))
- # error correction:
- result = copyTree(arg)
- result.typ = formal
- elif arg.kind in nkSymChoices and formal.skipTypes(abstractInst).kind == tyEnum:
- # Pick the right 'sym' from the sym choice by looking at 'formal' type:
- result = nil
- for ch in arg:
- if sameType(ch.typ, formal):
- return getConstExpr(c.module, ch, c.idgen, c.graph)
- typeMismatch(c.config, info, formal, arg.typ, arg)
- else:
- result = indexTypesMatch(c, formal, arg.typ, arg)
- if result == nil:
- typeMismatch(c.config, info, formal, arg.typ, arg)
- # error correction:
- result = copyTree(arg)
- result.typ = formal
- else:
- result = fitNodePostMatch(c, formal, result)
- proc fitNodeConsiderViewType(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
- let a = fitNode(c, formal, arg, info)
- if formal.kind in {tyVar, tyLent}:
- #classifyViewType(formal) != noView:
- result = newNodeIT(nkHiddenAddr, a.info, formal)
- result.add a
- formal.flags.incl tfVarIsPtr
- else:
- result = a
- proc inferWithMetatype(c: PContext, formal: PType,
- arg: PNode, coerceDistincts = false): PNode
- template commonTypeBegin*(): PType = PType(kind: tyUntyped)
- proc commonType*(c: PContext; x, y: PType): PType =
- # new type relation that is used for array constructors,
- # if expressions, etc.:
- if x == nil: return x
- if y == nil: return y
- var a = skipTypes(x, {tyGenericInst, tyAlias, tySink})
- var b = skipTypes(y, {tyGenericInst, tyAlias, tySink})
- result = x
- if a.kind in {tyUntyped, tyNil}: result = y
- elif b.kind in {tyUntyped, tyNil}: result = x
- elif a.kind == tyTyped: result = a
- elif b.kind == tyTyped: result = b
- elif a.kind == tyTypeDesc:
- # turn any concrete typedesc into the abstract typedesc type
- if not a.hasElementType: result = a
- else:
- result = newType(tyTypeDesc, c.idgen, a.owner)
- rawAddSon(result, newType(tyNone, c.idgen, a.owner))
- elif b.kind in {tyArray, tySet, tySequence} and
- a.kind == b.kind:
- # check for seq[empty] vs. seq[int]
- let idx = ord(b.kind == tyArray)
- if a[idx].kind == tyEmpty: return y
- elif a.kind == tyTuple and b.kind == tyTuple and sameTupleLengths(a, b):
- var nt: PType = nil
- for i, aa, bb in tupleTypePairs(a, b):
- let aEmpty = isEmptyContainer(aa)
- let bEmpty = isEmptyContainer(bb)
- if aEmpty != bEmpty:
- if nt.isNil:
- nt = copyType(a, c.idgen, a.owner)
- copyTypeProps(c.graph, c.idgen.module, nt, a)
- nt[i] = if aEmpty: bb else: aa
- if not nt.isNil: result = nt
- #elif b[idx].kind == tyEmpty: return x
- elif a.kind == tyRange and b.kind == tyRange:
- # consider: (range[0..3], range[0..4]) here. We should make that
- # range[0..4]. But then why is (range[0..4], 6) not range[0..6]?
- # But then why is (2,4) not range[2..4]? But I think this would break
- # too much code. So ... it's the same range or the base type. This means
- # typeof(if b: 0 else 1) == int and not range[0..1]. For now. In the long
- # run people expect ranges to work properly within a tuple.
- if not sameType(a, b):
- result = skipTypes(a, {tyRange}).skipIntLit(c.idgen)
- when false:
- if a.kind != tyRange and b.kind == tyRange:
- # XXX This really needs a better solution, but a proper fix now breaks
- # code.
- result = a #.skipIntLit
- elif a.kind == tyRange and b.kind != tyRange:
- result = b #.skipIntLit
- elif a.kind in IntegralTypes and a.n != nil:
- result = a #.skipIntLit
- elif a.kind == tyProc and b.kind == tyProc:
- if a.callConv == ccClosure and b.callConv != ccClosure:
- result = x
- elif compatibleEffects(a, b) != efCompat or
- (b.flags * {tfNoSideEffect, tfGcSafe}) < (a.flags * {tfNoSideEffect, tfGcSafe}):
- result = y
- else:
- var k = tyNone
- if a.kind in {tyRef, tyPtr}:
- k = a.kind
- if b.kind != a.kind: return x
- # bug #7601, array construction of ptr generic
- a = a.elementType.skipTypes({tyGenericInst})
- b = b.elementType.skipTypes({tyGenericInst})
- if a.kind == tyObject and b.kind == tyObject:
- result = commonSuperclass(a, b)
- # this will trigger an error later:
- if result.isNil or result == a: return x
- if result == b: return y
- # bug #7906, tyRef/tyPtr + tyGenericInst of ref/ptr object ->
- # ill-formed AST, no need for additional tyRef/tyPtr
- if k != tyNone and x.kind != tyGenericInst:
- let r = result
- result = newType(k, c.idgen, r.owner)
- result.addSonSkipIntLit(r, c.idgen)
- const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64, tyBool}
- proc shouldCheckCaseCovered(caseTyp: PType): bool =
- result = false
- case caseTyp.kind
- of shouldChckCovered:
- result = true
- of tyRange:
- if skipTypes(caseTyp[0], abstractInst).kind in shouldChckCovered:
- result = true
- else:
- discard
- proc endsInNoReturn(n: PNode): bool =
- ## check if expr ends the block like raising or call of noreturn procs do
- result = false # assume it does return
- template checkBranch(branch) =
- if not endsInNoReturn(branch):
- # proved a branch returns
- return false
- var it = n
- # skip these beforehand, no special handling needed
- while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
- it = it.lastSon
- case it.kind
- of nkIfStmt:
- var hasElse = false
- for branch in it:
- checkBranch:
- if branch.len == 2:
- branch[1]
- elif branch.len == 1:
- hasElse = true
- branch[0]
- else:
- raiseAssert "Malformed `if` statement during endsInNoReturn"
- # none of the branches returned
- result = hasElse # Only truly a no-return when it's exhaustive
- of nkCaseStmt:
- let caseTyp = skipTypes(it[0].typ, abstractVar-{tyTypeDesc})
- # semCase should already have checked for exhaustiveness in this case
- # effectively the same as having an else
- var hasElse = caseTyp.shouldCheckCaseCovered()
- # actual noreturn checks
- for i in 1 ..< it.len:
- let branch = it[i]
- checkBranch:
- case branch.kind
- of nkOfBranch:
- branch[^1]
- of nkElifBranch:
- branch[1]
- of nkElse:
- hasElse = true
- branch[0]
- else:
- raiseAssert "Malformed `case` statement in endsInNoReturn"
- # Can only guarantee a noreturn if there is an else or it's exhaustive
- result = hasElse
- of nkTryStmt:
- checkBranch(it[0])
- for i in 1 ..< it.len:
- let branch = it[i]
- checkBranch(branch[^1])
- # none of the branches returned
- result = true
- else:
- result = it.kind in nkLastBlockStmts or
- it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
- proc commonType*(c: PContext; x: PType, y: PNode): PType =
- # ignore exception raising branches in case/if expressions
- if endsInNoReturn(y): return x
- commonType(c, x, y.typ)
- proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
- result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info)
- when defined(nimsuggest):
- suggestDecl(c, n, result)
- proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
- # like newSymS, but considers gensym'ed symbols
- if n.kind == nkSym:
- # and sfGenSym in n.sym.flags:
- result = n.sym
- if result.kind notin {kind, skTemp}:
- localError(c.config, n.info, "cannot use symbol of kind '$1' as a '$2'" %
- [result.kind.toHumanStr, kind.toHumanStr])
- when false:
- if sfGenSym in result.flags and result.kind notin {skTemplate, skMacro, skParam}:
- # declarative context, so produce a fresh gensym:
- result = copySym(result)
- result.ast = n.sym.ast
- put(c.p, n.sym, result)
- # when there is a nested proc inside a template, semtmpl
- # will assign a wrong owner during the first pass over the
- # template; we must fix it here: see #909
- result.owner = getCurrOwner(c)
- else:
- result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info)
- #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
- # incl(result.flags, sfGlobal)
- when defined(nimsuggest):
- suggestDecl(c, n, result)
- proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
- allowed: TSymFlags): PSym
- # identifier with visibility
- proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
- allowed: TSymFlags): PSym
- proc typeAllowedCheck(c: PContext; info: TLineInfo; typ: PType; kind: TSymKind;
- flags: TTypeAllowedFlags = {}) =
- let t = typeAllowed(typ, kind, c, flags)
- if t != nil:
- var err: string
- if t == typ:
- err = "invalid type: '$1' for $2" % [typeToString(typ), toHumanStr(kind)]
- if kind in {skVar, skLet, skConst} and taIsTemplateOrMacro in flags:
- err &= ". Did you mean to call the $1 with '()'?" % [toHumanStr(typ.owner.kind)]
- else:
- err = "invalid type: '$1' in this context: '$2' for $3" % [typeToString(t),
- typeToString(typ), toHumanStr(kind)]
- localError(c.config, info, err)
- proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
- typeAllowedCheck(c, typ.n.info, typ, skProc)
- proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
- proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode
- proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode
- proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
- flags: TExprFlags = {}; expectedType: PType = nil): PNode
- proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
- flags: TExprFlags = {}; expectedType: PType = nil): PNode
- when false:
- proc createEvalContext(c: PContext, mode: TEvalMode): PEvalContext =
- result = newEvalContext(c.module, mode)
- result.getType = proc (n: PNode): PNode =
- result = tryExpr(c, n)
- if result == nil:
- result = newSymNode(errorSym(c, n))
- elif result.typ == nil:
- result = newSymNode(getSysSym"void")
- else:
- result.typ = makeTypeDesc(c, result.typ)
- result.handleIsOperator = proc (n: PNode): PNode =
- result = isOpImpl(c, n)
- proc hasCycle(n: PNode): bool =
- result = false
- incl n.flags, nfNone
- for i in 0..<n.safeLen:
- if nfNone in n[i].flags or hasCycle(n[i]):
- result = true
- break
- excl n.flags, nfNone
- proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
- # recompute the types as 'eval' isn't guaranteed to construct types nor
- # that the types are sound:
- when true:
- if eOrig.typ.kind in {tyUntyped, tyTyped, tyTypeDesc}:
- result = semExprWithType(c, evaluated)
- else:
- result = evaluated
- let expectedType = eOrig.typ.skipTypes({tyStatic})
- if hasCycle(result):
- result = localErrorNode(c, eOrig, "the resulting AST is cyclic and cannot be processed further")
- else:
- semmacrosanity.annotateType(result, expectedType, c.config)
- else:
- result = semExprWithType(c, evaluated)
- #result = fitNode(c, e.typ, result) inlined with special case:
- let arg = result
- result = indexTypesMatch(c, eOrig.typ, arg.typ, arg)
- if result == nil:
- result = arg
- # for 'tcnstseq' we support [] to become 'seq'
- if eOrig.typ.skipTypes(abstractInst).kind == tySequence and
- isArrayConstr(arg):
- arg.typ = eOrig.typ
- proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
- var e = semExprWithType(c, n, expectedType = expectedType)
- if e == nil: return
- result = getConstExpr(c.module, e, c.idgen, c.graph)
- if result != nil: return
- let oldErrorCount = c.config.errorCounter
- let oldErrorMax = c.config.errorMax
- let oldErrorOutputs = c.config.m.errorOutputs
- c.config.m.errorOutputs = {}
- c.config.errorMax = high(int) # `setErrorMaxHighMaybe` not appropriate here
- when defined(nimsuggest):
- # Remove the error hook so nimsuggest doesn't report errors there
- let tempHook = c.graph.config.structuredErrorHook
- c.graph.config.structuredErrorHook = nil
- try:
- result = evalConstExpr(c.module, c.idgen, c.graph, e)
- if result == nil or result.kind == nkEmpty:
- result = nil
- else:
- result = fixupTypeAfterEval(c, result, e)
- except ERecoverableError:
- result = nil
- when defined(nimsuggest):
- # Restore the error hook
- c.graph.config.structuredErrorHook = tempHook
- c.config.errorCounter = oldErrorCount
- c.config.errorMax = oldErrorMax
- c.config.m.errorOutputs = oldErrorOutputs
- const
- errConstExprExpected = "constant expression expected"
- proc semConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
- var e = semExprWithType(c, n, expectedType = expectedType)
- if e == nil:
- localError(c.config, n.info, errConstExprExpected)
- return n
- if e.kind in nkSymChoices and e[0].typ.skipTypes(abstractInst).kind == tyEnum:
- return e
- result = getConstExpr(c.module, e, c.idgen, c.graph)
- if result == nil:
- #if e.kind == nkEmpty: globalError(n.info, errConstExprExpected)
- result = evalConstExpr(c.module, c.idgen, c.graph, e)
- if result == nil or result.kind == nkEmpty:
- if e.info != n.info:
- pushInfoContext(c.config, n.info)
- localError(c.config, e.info, errConstExprExpected)
- popInfoContext(c.config)
- else:
- localError(c.config, e.info, errConstExprExpected)
- # error correction:
- result = e
- else:
- result = fixupTypeAfterEval(c, result, e)
- proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
- if efNeedStatic in flags:
- if efPreferNilResult in flags:
- return tryConstExpr(c, n, expectedType)
- else:
- return semConstExpr(c, n, expectedType)
- else:
- result = semExprWithType(c, n, flags, expectedType)
- if efPreferStatic in flags:
- var evaluated = getConstExpr(c.module, result, c.idgen, c.graph)
- if evaluated != nil: return evaluated
- evaluated = evalAtCompileTime(c, result)
- if evaluated != nil: return evaluated
- proc semGenericStmt(c: PContext, n: PNode): PNode
- include hlo, seminst, semcall
- proc resetSemFlag(n: PNode) =
- if n != nil:
- excl n.flags, nfSem
- for i in 0..<n.safeLen:
- resetSemFlag(n[i])
- proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
- s: PSym, flags: TExprFlags; expectedType: PType = nil): PNode =
- ## Semantically check the output of a macro.
- ## This involves processes such as re-checking the macro output for type
- ## coherence, making sure that variables declared with 'let' aren't
- ## reassigned, and binding the unbound identifiers that the macro output
- ## contains.
- inc(c.config.evalTemplateCounter)
- if c.config.evalTemplateCounter > evalTemplateLimit:
- globalError(c.config, s.info, "template instantiation too nested")
- c.friendModules.add(s.owner.getModule)
- result = macroResult
- resetSemFlag result
- if s.typ.returnType == nil:
- result = semStmt(c, result, flags)
- else:
- var retType = s.typ.returnType
- if retType.kind == tyTypeDesc and tfUnresolved in retType.flags and
- retType.hasElementType:
- # bug #11941: template fails(T: type X, v: auto): T
- # does not mean we expect a tyTypeDesc.
- retType = retType.skipModifier
- case retType.kind
- of tyUntyped, tyAnything:
- # Not expecting a type here allows templates like in ``tmodulealias.in``.
- result = semExpr(c, result, flags, expectedType)
- of tyTyped:
- # More restrictive version.
- result = semExprWithType(c, result, flags, expectedType)
- of tyTypeDesc:
- if result.kind == nkStmtList: result.transitionSonsKind(nkStmtListType)
- var typ = semTypeNode(c, result, nil)
- if typ == nil:
- localError(c.config, result.info, "expression has no type: " &
- renderTree(result, {renderNoComments}))
- result = newSymNode(errorSym(c, result))
- else:
- result.typ = makeTypeDesc(c, typ)
- #result = symNodeFromType(c, typ, n.info)
- else:
- 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 = initTypeMapping()
- for param, value in genericParamsInMacroCall(s, call):
- var givenType = value.typ
- # the sym nodes used for the supplied generic arguments for
- # templates and macros leave type nil so regular sem can handle it
- # in this case, get the type directly from the sym
- if givenType == nil and value.kind == nkSym and value.sym.typ != nil:
- givenType = value.sym.typ
- idTablePut(paramTypes, param.typ, givenType)
- retType = generateTypeInstance(c, paramTypes,
- macroResult.info, retType)
- if retType.kind == tyVoid:
- result = semStmt(c, result, flags)
- else:
- result = semExpr(c, result, flags, expectedType)
- result = fitNode(c, retType, result, result.info)
- #globalError(s.info, errInvalidParamKindX, typeToString(s.typ.returnType))
- dec(c.config.evalTemplateCounter)
- discard c.friendModules.pop()
- const
- errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
- errFloatToString = "cannot convert '$1' to '$2'"
- proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
- flags: TExprFlags = {}; expectedType: PType = nil): PNode =
- rememberExpansion(c, nOrig.info, sym)
- pushInfoContext(c.config, nOrig.info, sym.detailedInfo)
- let info = getCallLineInfo(n)
- markUsed(c, info, sym)
- onUse(info, sym)
- if sym == c.p.owner:
- globalError(c.config, info, "recursive dependency: '$1'" % sym.name.s)
- let genericParams = sym.ast[genericParamsPos].len
- let suppliedParams = max(n.safeLen - 1, 0)
- if suppliedParams < genericParams:
- globalError(c.config, info, errMissingGenericParamsForTemplate % n.renderTree)
- #if c.evalContext == nil:
- # c.evalContext = c.createEvalContext(emStatic)
- result = evalMacroCall(c.module, c.idgen, c.graph, c.templInstCounter, n, nOrig, sym)
- if efNoSemCheck notin flags:
- result = semAfterMacroCall(c, n, result, sym, flags, expectedType)
- if c.config.macrosToExpand.hasKey(sym.name.s):
- message(c.config, nOrig.info, hintExpandMacro, renderTree(result))
- result = wrapInComesFrom(nOrig.info, sym, result)
- popInfoContext(c.config)
- proc forceBool(c: PContext, n: PNode): PNode =
- result = fitNode(c, getSysType(c.graph, n.info, tyBool), n, n.info)
- if result == nil: result = n
- proc semConstBoolExpr(c: PContext, n: PNode): PNode =
- result = forceBool(c, semConstExpr(c, n, getSysType(c.graph, n.info, tyBool)))
- if result.kind != nkIntLit:
- localError(c.config, n.info, errConstExprExpected)
- proc semConceptBody(c: PContext, n: PNode): PNode
- include semtypes
- proc setGenericParamsMisc(c: PContext; n: PNode) =
- ## used by call defs (procs, templates, macros, ...) to analyse their generic
- ## params, and store the originals in miscPos for better error reporting.
- 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 caseBranchMatchesExpr(branch, matched: PNode): bool =
- result = false
- for i in 0 ..< branch.len-1:
- if branch[i].kind == nkRange:
- if overlap(branch[i], matched): return true
- elif exprStructuralEquivalent(branch[i], matched):
- return true
- proc pickCaseBranchIndex(caseExpr, matched: PNode): int =
- result = 0
- let endsWithElse = caseExpr[^1].kind == nkElse
- for i in 1..<caseExpr.len - endsWithElse.int:
- if caseExpr[i].caseBranchMatchesExpr(matched):
- return i
- if endsWithElse:
- return caseExpr.len - 1
- proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: bool): seq[PNode]
- proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode
- proc defaultNodeField(c: PContext, a: PNode, checkDefault: bool): PNode
- const defaultFieldsSkipTypes = {tyGenericInst, tyAlias, tySink}
- proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool, checkDefault: bool): seq[PNode] =
- result = @[]
- case recNode.kind
- of nkRecList:
- for field in recNode:
- result.add defaultFieldsForTuple(c, field, hasDefault, checkDefault)
- of nkSym:
- let field = recNode.sym
- let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
- if field.ast != nil: #Try to use default value
- hasDefault = true
- result.add newTree(nkExprColonExpr, recNode, field.ast)
- else:
- if recType.kind in {tyObject, tyArray, tyTuple}:
- let asgnExpr = defaultNodeField(c, recNode, recNode.typ, checkDefault)
- if asgnExpr != nil:
- hasDefault = true
- asgnExpr.flags.incl nfSkipFieldChecking
- result.add newTree(nkExprColonExpr, recNode, asgnExpr)
- return
- let asgnType = newType(tyTypeDesc, c.idgen, recNode.typ.owner)
- rawAddSon(asgnType, recNode.typ)
- let asgnExpr = newTree(nkCall,
- newSymNode(getSysMagic(c.graph, recNode.info, "zeroDefault", mZeroDefault)),
- newNodeIT(nkType, recNode.info, asgnType)
- )
- asgnExpr.flags.incl nfSkipFieldChecking
- asgnExpr.typ = recNode.typ
- result.add newTree(nkExprColonExpr, recNode, asgnExpr)
- else:
- raiseAssert "unreachable"
- proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: bool): seq[PNode] =
- result = @[]
- case recNode.kind
- of nkRecList:
- for field in recNode:
- result.add defaultFieldsForTheUninitialized(c, field, checkDefault)
- of nkRecCase:
- let discriminator = recNode[0]
- var selectedBranch: int
- var defaultValue = discriminator.sym.ast
- if defaultValue == nil:
- # None of the branches were explicitly selected by the user and no value
- # was given to the discrimator. We can assume that it will be initialized
- # to zero and this will select a particular branch as a result:
- if checkDefault: # don't add defaults when checking whether a case branch has default fields
- return
- defaultValue = newIntNode(nkIntLit#[c.graph]#, 0)
- defaultValue.typ = discriminator.typ
- selectedBranch = recNode.pickCaseBranchIndex defaultValue
- defaultValue.flags.incl nfSkipFieldChecking
- result.add newTree(nkExprColonExpr, discriminator, defaultValue)
- result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1], checkDefault)
- of nkSym:
- let field = recNode.sym
- let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
- if field.ast != nil: #Try to use default value
- result.add newTree(nkExprColonExpr, recNode, field.ast)
- elif recType.kind in {tyObject, tyArray, tyTuple}:
- let asgnExpr = defaultNodeField(c, recNode, recNode.typ, checkDefault)
- if asgnExpr != nil:
- asgnExpr.typ = recNode.typ
- asgnExpr.flags.incl nfSkipFieldChecking
- result.add newTree(nkExprColonExpr, recNode, asgnExpr)
- else:
- raiseAssert "unreachable"
- proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode =
- let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes)
- if aTypSkip.kind == tyObject:
- let child = defaultFieldsForTheUninitialized(c, aTypSkip.n, checkDefault)
- if child.len > 0:
- var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp))
- asgnExpr.typ = aTyp
- asgnExpr.sons.add child
- result = semExpr(c, asgnExpr)
- else:
- result = nil
- elif aTypSkip.kind == tyArray:
- let child = defaultNodeField(c, a, aTypSkip[1], checkDefault)
- if child != nil:
- let node = newNode(nkIntLit)
- node.intVal = toInt64(lengthOrd(c.graph.config, aTypSkip))
- result = semExpr(c, newTree(nkCall, newSymNode(getSysSym(c.graph, a.info, "arrayWith"), a.info),
- semExprWithType(c, child),
- node
- ))
- result.typ = aTyp
- else:
- result = nil
- elif aTypSkip.kind == tyTuple:
- var hasDefault = false
- if aTypSkip.n != nil:
- let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault, checkDefault)
- if hasDefault and children.len > 0:
- result = newNodeI(nkTupleConstr, a.info)
- result.typ = aTyp
- result.sons.add children
- result = semExpr(c, result)
- else:
- result = nil
- else:
- result = nil
- else:
- result = nil
- proc defaultNodeField(c: PContext, a: PNode, checkDefault: bool): PNode =
- result = defaultNodeField(c, a, a.typ, checkDefault)
- include semtempl, semgnrc, semstmts, semexprs
- proc addCodeForGenerics(c: PContext, n: PNode) =
- for i in c.lastGenericIdx..<c.generics.len:
- var prc = c.generics[i].inst.sym
- if prc.kind in {skProc, skFunc, skMethod, skConverter} and prc.magic == mNone:
- if prc.ast == nil or prc.ast[bodyPos] == nil:
- internalError(c.config, prc.info, "no code for " & prc.name.s)
- else:
- n.add prc.ast
- c.lastGenericIdx = c.generics.len
- proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PContext =
- result = newContext(graph, module)
- result.idgen = idgen
- result.enforceVoidContext = newType(tyTyped, idgen, nil)
- result.voidType = newType(tyVoid, idgen, nil)
- if result.p != nil: internalError(graph.config, module.info, "sem.preparePContext")
- result.semConstExpr = semConstExpr
- result.semExpr = semExpr
- result.semExprWithType = semExprWithType
- result.semTryExpr = tryExpr
- result.semTryConstExpr = tryConstExpr
- result.computeRequiresInit = computeRequiresInit
- result.semOperand = semOperand
- result.semConstBoolExpr = semConstBoolExpr
- result.semOverloadedCall = semOverloadedCall
- result.semInferredLambda = semInferredLambda
- result.semGenerateInstance = generateInstance
- result.semTypeNode = semTypeNode
- result.instTypeBoundOp = sigmatch.instTypeBoundOp
- result.hasUnresolvedArgs = hasUnresolvedArgs
- result.templInstCounter = new int
- pushProcCon(result, module)
- pushOwner(result, result.module)
- result.moduleScope = openScope(result)
- result.moduleScope.addSym(module) # a module knows itself
- if sfSystemModule in module.flags:
- graph.systemModule = module
- result.topLevelScope = openScope(result)
- proc isImportSystemStmt(g: ModuleGraph; n: PNode): bool =
- if g.systemModule == nil: return false
- var n = n
- if n.kind == nkStmtList:
- for i in 0..<n.len-1:
- if n[i].kind notin {nkCommentStmt, nkEmpty}:
- n = n[i]
- break
- case n.kind
- of nkImportStmt:
- result = false
- for x in n:
- if x.kind == nkIdent:
- let f = checkModuleName(g.config, x, false)
- if f == g.systemModule.info.fileIndex:
- return true
- of nkImportExceptStmt, nkFromStmt:
- result = false
- if n[0].kind == nkIdent:
- let f = checkModuleName(g.config, n[0], false)
- if f == g.systemModule.info.fileIndex:
- return true
- else: result = false
- proc isEmptyTree(n: PNode): bool =
- case n.kind
- of nkStmtList:
- for it in n:
- if not isEmptyTree(it): return false
- result = true
- of nkEmpty, nkCommentStmt: result = true
- else: result = false
- proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
- if c.topStmts == 0 and not isImportSystemStmt(c.graph, n):
- if sfSystemModule notin c.module.flags and not isEmptyTree(n):
- assert c.graph.systemModule != nil
- c.moduleScope.addSym c.graph.systemModule # import the "System" identifier
- importAllSymbols(c, c.graph.systemModule)
- inc c.topStmts
- else:
- inc c.topStmts
- if sfNoForward in c.module.flags:
- result = semAllTypeSections(c, n)
- else:
- result = n
- result = semStmt(c, result, {})
- when false:
- # Code generators are lazy now and can deal with undeclared procs, so these
- # steps are not required anymore and actually harmful for the upcoming
- # destructor support.
- # BUGFIX: process newly generated generics here, not at the end!
- if c.lastGenericIdx < c.generics.len:
- var a = newNodeI(nkStmtList, n.info)
- addCodeForGenerics(c, a)
- if a.len > 0:
- # a generic has been added to `a`:
- if result.kind != nkEmpty: a.add result
- result = a
- result = hloStmt(c, result)
- if c.config.cmd == cmdInteractive and not isEmptyType(result.typ):
- result = buildEchoStmt(c, result)
- if c.config.cmd == cmdIdeTools:
- appendToModule(c.module, result)
- trackStmt(c, c.module, result, isTopLevel = true)
- if optMultiMethods notin c.config.globalOptions and
- c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
- Feature.vtables in c.config.features:
- sortVTableDispatchers(c.graph)
- if sfMainModule in c.module.flags:
- collectVTableDispatchers(c.graph)
- proc recoverContext(c: PContext) =
- # clean up in case of a semantic error: We clean up the stacks, etc. This is
- # faster than wrapping every stack operation in a 'try finally' block and
- # requires far less code.
- c.currentScope = c.topLevelScope
- while getCurrOwner(c).kind != skModule: popOwner(c)
- while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next
- proc semWithPContext*(c: PContext, n: PNode): PNode =
- # no need for an expensive 'try' if we stop after the first error anyway:
- if c.config.errorMax <= 1:
- result = semStmtAndGenerateGenerics(c, n)
- else:
- let oldContextLen = msgs.getInfoContextLen(c.config)
- let oldInGenericInst = c.inGenericInst
- try:
- result = semStmtAndGenerateGenerics(c, n)
- except ERecoverableError, ESuggestDone:
- recoverContext(c)
- c.inGenericInst = oldInGenericInst
- msgs.setInfoContextLen(c.config, oldContextLen)
- if getCurrentException() of ESuggestDone:
- c.suggestionsMade = true
- result = nil
- else:
- result = newNodeI(nkEmpty, n.info)
- #if c.config.cmd == cmdIdeTools: findSuggest(c, n)
- storeRodNode(c, result)
- proc reportUnusedModules(c: PContext) =
- if c.config.cmd == cmdM: return
- for i in 0..high(c.unusedImports):
- if sfUsed notin c.unusedImports[i][0].flags:
- message(c.config, c.unusedImports[i][1], warnUnusedImportX, c.unusedImports[i][0].name.s)
- proc closePContext*(graph: ModuleGraph; c: PContext, n: PNode): PNode =
- if c.config.cmd == cmdIdeTools and not c.suggestionsMade:
- suggestSentinel(c)
- closeScope(c) # close module's scope
- rawCloseScope(c) # imported symbols; don't check for unused ones!
- reportUnusedModules(c)
- result = newNode(nkStmtList)
- if n != nil:
- internalError(c.config, n.info, "n is not nil") #result := n;
- addCodeForGenerics(c, result)
- if c.module.ast != nil:
- result.add(c.module.ast)
- popOwner(c)
- popProcCon(c)
- sealRodFile(c)
|