diff options
Diffstat (limited to 'compiler/sem.nim')
-rw-r--r-- | compiler/sem.nim | 869 |
1 files changed, 869 insertions, 0 deletions
diff --git a/compiler/sem.nim b/compiler/sem.nim new file mode 100644 index 000000000..2cf93d365 --- /dev/null +++ b/compiler/sem.nim @@ -0,0 +1,869 @@ +# +# +# 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, packedsets] + +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 ch + 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 + +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 find(result.name.s, '`') >= 0: + result.flags.incl sfWasGenSym + #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, fromTopLevel = false): 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" + +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) + case aTypSkip.kind + of 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 + of 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 + of 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 + of tyRange: + if c.graph.config.isDefined("nimPreviewRangeDefault"): + result = firstRange(c.config, aTypSkip) + 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.instantiateOnlyProcType = instantiateOnlyProcType + 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) |