diff options
Diffstat (limited to 'compiler')
91 files changed, 3473 insertions, 8624 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index e8fee6c0d..a342e1ea7 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -41,7 +41,7 @@ type TNodeKinds* = set[TNodeKind] type - TSymFlag* = enum # 51 flags! + TSymFlag* = enum # 63 flags! sfUsed, # read access of sym (for warnings) or simply used sfExported, # symbol is exported from module sfFromGeneric, # symbol is instantiation of a generic; this is needed @@ -93,7 +93,7 @@ type sfNamedParamCall, # symbol needs named parameter call syntax in target # language; for interfacing with Objective C sfDiscardable, # returned value may be discarded implicitly - sfOverridden, # proc is overridden + sfOverridden, # proc is overridden sfCallsite # A flag for template symbols to tell the # compiler it should use line information from # the calling side of the macro, not from the @@ -126,24 +126,25 @@ type sfByCopy # param is marked as pass bycopy sfMember # proc is a C++ member of a type sfCodegenDecl # type, proc, global or proc param is marked as codegenDecl + sfWasGenSym # symbol was 'gensym'ed + sfForceLift # variable has to be lifted into closure environment + + sfDirty # template is not hygienic (old styled template) module, + # compiled from a dirty-buffer + sfCustomPragma # symbol is custom pragma template + sfBase, # a base method + sfGoto # var is used for 'goto' code generation + sfAnon, # symbol name that was generated by the compiler + # the compiler will avoid printing such names + # in user messages. + sfAllUntyped # macro or template is immediately expanded in a generic context + sfTemplateRedefinition # symbol is a redefinition of an earlier template TSymFlags* = set[TSymFlag] const sfNoInit* = sfMainModule # don't generate code to init the variable - sfAllUntyped* = sfVolatile # macro or template is immediately expanded \ - # in a generic context - - sfDirty* = sfPure - # template is not hygienic (old styled template) - # module, compiled from a dirty-buffer - - sfAnon* = sfDiscardable - # symbol name that was generated by the compiler - # the compiler will avoid printing such names - # in user messages. - sfNoForward* = sfRegister # forward declarations are not required (per module) sfReorder* = sfForward @@ -152,12 +153,8 @@ const sfCompileToCpp* = sfInfixCall # compile the module as C++ code sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code sfExperimental* = sfOverridden # module uses the .experimental switch - sfGoto* = sfOverridden # var is used for 'goto' code generation sfWrittenTo* = sfBorrow # param is assigned to # currently unimplemented - sfBase* = sfDiscriminant - sfCustomPragma* = sfRegister # symbol is custom pragma template - sfTemplateRedefinition* = sfExportc # symbol is a redefinition of an earlier template sfCppMember* = { sfVirtual, sfMember, sfConstructor } # proc is a C++ member, meaning it will be attached to the type definition const @@ -217,7 +214,8 @@ type tyUncheckedArray # An array with boundaries [0,+∞] - tyProxy # used as errornous type (for idetools) + tyError # used as erroneous type (for idetools) + # as an erroneous node should match everything tyBuiltInTypeClass # Type such as the catch-all object, tuple, seq, etc @@ -279,10 +277,6 @@ static: const tyPureObject* = tyTuple GcTypeKinds* = {tyRef, tySequence, tyString} - tyError* = tyProxy # as an errornous node should match everything - tyUnknown* = tyFromExpr - - tyUnknownTypes* = {tyError, tyFromExpr} tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass, tyUserTypeClass, tyUserTypeClassInst, @@ -328,7 +322,9 @@ type nfFirstWrite # this node is a first write nfHasComment # node has a comment nfSkipFieldChecking # node skips field visable checking - nfOpenSym # node is a captured sym but can be overriden by local symbols + nfDisabledOpenSym # temporary: node should be nkOpenSym but cannot + # because openSym experimental switch is disabled + # gives warning instead TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 47) @@ -449,6 +445,8 @@ const tfGcSafe* = tfThread tfObjHasKids* = tfEnumHasHoles tfReturnsNew* = tfInheritable + tfNonConstExpr* = tfExplicitCallConv + ## tyFromExpr where the expression shouldn't be evaluated as a static value skError* = skUnknown var @@ -504,7 +502,7 @@ type mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq, mNewString, mNewStringOfCap, mParseBiggestFloat, mMove, mEnsureMove, mWasMoved, mDup, mDestroy, mTrace, - mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset, + mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, mOrdinal, mIterableType, @@ -654,7 +652,7 @@ type storage*: TStorageLoc flags*: TLocFlags # location's flags lode*: PNode # Node where the location came from; can be faked - r*: Rope # rope value of location (code generators) + snippet*: Rope # C code snippet of location (code generators) # ---------------- end of backend information ------------------------------ @@ -883,7 +881,7 @@ const nfFromTemplate, nfDefaultRefsParam, nfExecuteOnReload, nfLastRead, nfFirstWrite, nfSkipFieldChecking, - nfOpenSym} + nfDisabledOpenSym} namePos* = 0 patternPos* = 1 # empty except for term rewriting macros genericParamsPos* = 2 @@ -897,7 +895,7 @@ const nfAllFieldsSet* = nfBase2 nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice, - nkClosedSymChoice} + nkClosedSymChoice, nkOpenSym} nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit} nkLiterals* = {nkCharLit..nkTripleStrLit} @@ -925,6 +923,7 @@ proc getPIdent*(a: PNode): PIdent {.inline.} = of nkSym: a.sym.name of nkIdent: a.ident of nkOpenSymChoice, nkClosedSymChoice: a.sons[0].sym.name + of nkOpenSym: getPIdent(a.sons[0]) else: nil const @@ -1058,8 +1057,11 @@ proc getDeclPragma*(n: PNode): PNode = proc extractPragma*(s: PSym): PNode = ## gets the pragma node of routine/type/var/let/const symbol `s` - if s.kind in routineKinds: - result = s.ast[pragmasPos] + if s.kind in routineKinds: # bug #24167 + if s.ast[pragmasPos] != nil and s.ast[pragmasPos].kind != nkEmpty: + result = s.ast[pragmasPos] + else: + result = nil elif s.kind in {skType, skVar, skLet, skConst}: if s.ast != nil and s.ast.len > 0: if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1: @@ -1285,6 +1287,9 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode = result.typ = sym.typ result.info = info +proc newOpenSym*(n: PNode): PNode {.inline.} = + result = newTreeI(nkOpenSym, n.info, n) + proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode = result = newNode(kind) result.intVal = intVal @@ -1509,13 +1514,14 @@ proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType proc setSons*(dest: PType; sons: sink seq[PType]) {.inline.} = dest.sons = sons proc setSon*(dest: PType; son: sink PType) {.inline.} = dest.sons = @[son] +proc setSonsLen*(dest: PType; len: int) {.inline.} = setLen(dest.sons, len) proc mergeLoc(a: var TLoc, b: TLoc) = if a.k == low(typeof(a.k)): a.k = b.k if a.storage == low(typeof(a.storage)): a.storage = b.storage a.flags.incl b.flags if a.lode == nil: a.lode = b.lode - if a.r == "": a.r = b.r + if a.snippet == "": a.snippet = b.snippet proc newSons*(father: PNode, length: int) = setLen(father.sons, length) diff --git a/compiler/astyaml.nim b/compiler/astyaml.nim index c7e090cd3..b0fa2bfb2 100644 --- a/compiler/astyaml.nim +++ b/compiler/astyaml.nim @@ -75,7 +75,7 @@ proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; res.addf("\n$1storage: $2", [istr, makeYamlString($n.loc.storage)]) if card(n.loc.flags) > 0: res.addf("\n$1flags: $2", [istr, makeYamlString($n.loc.flags)]) - res.addf("\n$1r: $2", [istr, n.loc.r]) + res.addf("\n$1snippet: $2", [istr, n.loc.snippet]) res.addf("\n$1lode: $2", [istr]) res.treeToYamlAux(conf, n.loc.lode, marker, indent + 1, maxRecDepth - 1) diff --git a/compiler/backendpragmas.nim b/compiler/backendpragmas.nim deleted file mode 100644 index b18644810..000000000 --- a/compiler/backendpragmas.nim +++ /dev/null @@ -1,25 +0,0 @@ -import pragmas, options, ast, trees - -proc pushBackendOption(optionsStack: var seq[TOptions], options: var TOptions) = - optionsStack.add options - -proc popBackendOption(optionsStack: var seq[TOptions], options: var TOptions) = - options = optionsStack[^1] - optionsStack.setLen(optionsStack.len-1) - -proc processPushBackendOption*(optionsStack: var seq[TOptions], options: var TOptions, - n: PNode, start: int) = - pushBackendOption(optionsStack, options) - for i in start..<n.len: - let it = n[i] - if it.kind in nkPragmaCallKinds and it.len == 2 and it[1].kind == nkIntLit: - let sw = whichPragma(it[0]) - let opts = pragmaToOptions(sw) - if opts != {}: - if it[1].intVal != 0: - options.incl opts - else: - options.excl opts - -template processPopBackendOption*(optionsStack: var seq[TOptions], options: var TOptions) = - popBackendOption(optionsStack, options) diff --git a/compiler/cbuilder.nim b/compiler/cbuilder.nim new file mode 100644 index 000000000..bb1bdfe27 --- /dev/null +++ b/compiler/cbuilder.nim @@ -0,0 +1,174 @@ +type + Snippet = string + Builder = string + +template newBuilder(s: string): Builder = + s + +proc addField(obj: var Builder; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") = + obj.add('\t') + obj.add(typ) + obj.add(" ") + obj.add(name) + if isFlexArray: + obj.add("[SEQ_DECL_SIZE]") + if initializer.len != 0: + obj.add(initializer) + obj.add(";\n") + +proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") = + ## for fields based on an `skField` symbol + obj.add('\t') + if field.alignment > 0: + obj.add("NIM_ALIGN(") + obj.addInt(field.alignment) + obj.add(") ") + obj.add(typ) + if sfNoalias in field.flags: + obj.add(" NIM_NOALIAS") + obj.add(" ") + obj.add(name) + if isFlexArray: + obj.add("[SEQ_DECL_SIZE]") + if field.bitsize != 0: + obj.add(":") + obj.addInt(field.bitsize) + if initializer.len != 0: + obj.add(initializer) + obj.add(";\n") + +type + BaseClassKind = enum + bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti + StructBuilderInfo = object + baseKind: BaseClassKind + preFieldsLen: int + +proc structOrUnion(t: PType): Snippet = + let t = t.skipTypes({tyAlias, tySink}) + if tfUnion in t.flags: "union" + else: "struct" + +proc ptrType(t: Snippet): Snippet = + t & "*" + +proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet): StructBuilderInfo = + result = StructBuilderInfo(baseKind: bcNone) + obj.add("struct ") + obj.add(name) + if baseType.len != 0: + if m.compileToCpp: + result.baseKind = bcCppInherit + else: + result.baseKind = bcSupField + if result.baseKind == bcCppInherit: + obj.add(" : public ") + obj.add(baseType) + obj.add(" ") + obj.add("{\n") + result.preFieldsLen = obj.len + if result.baseKind == bcSupField: + obj.addField(name = "Sup", typ = baseType) + +proc finishSimpleStruct(obj: var Builder; m: BModule; info: StructBuilderInfo) = + if info.baseKind == bcNone and info.preFieldsLen == obj.len: + # no fields were added, add dummy field + obj.addField(name = "dummy", typ = "char") + obj.add("};\n") + +template addSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet; body: typed) = + ## for independent structs, not directly based on a Nim type + let info = startSimpleStruct(obj, m, name, baseType) + body + finishSimpleStruct(obj, m, info) + +proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType: Snippet): StructBuilderInfo = + result = StructBuilderInfo(baseKind: bcNone) + if tfPacked in t.flags: + if hasAttribute in CC[m.config.cCompiler].props: + obj.add(structOrUnion(t)) + obj.add(" __attribute__((__packed__))") + else: + obj.add("#pragma pack(push, 1)\n") + obj.add(structOrUnion(t)) + else: + obj.add(structOrUnion(t)) + obj.add(" ") + obj.add(name) + if t.kind == tyObject: + if t.baseClass == nil: + if lacksMTypeField(t): + result.baseKind = bcNone + elif optTinyRtti in m.config.globalOptions: + result.baseKind = bcNoneTinyRtti + else: + result.baseKind = bcNoneRtti + elif m.compileToCpp: + result.baseKind = bcCppInherit + else: + result.baseKind = bcSupField + elif baseType.len != 0: + if m.compileToCpp: + result.baseKind = bcCppInherit + else: + result.baseKind = bcSupField + if result.baseKind == bcCppInherit: + obj.add(" : public ") + obj.add(baseType) + obj.add(" ") + obj.add("{\n") + result.preFieldsLen = obj.len + case result.baseKind + of bcNone: + # rest of the options add a field or don't need it due to inheritance, + # we need to add the dummy field for uncheckedarray ahead of time + # so that it remains trailing + if t.itemId notin m.g.graph.memberProcsPerType and + t.n != nil and t.n.len == 1 and t.n[0].kind == nkSym and + t.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray: + # only consists of flexible array field, add *initial* dummy field + obj.addField(name = "dummy", typ = "char") + of bcCppInherit: discard + of bcNoneRtti: + obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType"))) + of bcNoneTinyRtti: + obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2"))) + of bcSupField: + obj.addField(name = "Sup", typ = baseType) + +proc finishStruct(obj: var Builder; m: BModule; t: PType; info: StructBuilderInfo) = + if info.baseKind == bcNone and info.preFieldsLen == obj.len and + t.itemId notin m.g.graph.memberProcsPerType: + # no fields were added, add dummy field + obj.addField(name = "dummy", typ = "char") + obj.add("};\n") + if tfPacked in t.flags and hasAttribute notin CC[m.config.cCompiler].props: + obj.add("#pragma pack(pop)\n") + +template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: Snippet; body: typed) = + ## for structs built directly from a Nim type + let info = startStruct(obj, m, typ, name, baseType) + body + finishStruct(obj, m, typ, info) + +template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) = + ## adds a field with a `struct { ... }` type, building it according to `body` + obj.add('\t') + if tfPacked in parentTyp.flags: + if hasAttribute in CC[m.config.cCompiler].props: + obj.add("struct __attribute__((__packed__)) {\n") + else: + obj.add("#pragma pack(push, 1)\nstruct {") + else: + obj.add("struct {\n") + body + obj.add("} ") + obj.add(fieldName) + obj.add(";\n") + if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props: + result.add("#pragma pack(pop)\n") + +template addAnonUnion(obj: var Builder; body: typed) = + obj.add "union{\n" + body + obj.add("};\n") diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 607f6d51e..ac607e3ad 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -76,6 +76,23 @@ proc isHarmlessStore(p: BProc; canRaise: bool; d: TLoc): bool = else: result = false +proc cleanupTemp(p: BProc; returnType: PType, tmp: TLoc): bool = + if returnType.kind in {tyVar, tyLent}: + # we don't need to worry about var/lent return types + result = false + elif hasDestructor(returnType) and getAttachedOp(p.module.g.graph, returnType, attachedDestructor) != nil: + let dtor = getAttachedOp(p.module.g.graph, returnType, attachedDestructor) + var op = initLocExpr(p, newSymNode(dtor)) + var callee = rdLoc(op) + let destroy = if dtor.typ.firstParamType.kind == tyVar: + callee & "(&" & rdLoc(tmp) & ")" + else: + callee & "(" & rdLoc(tmp) & ")" + raiseExitCleanup(p, destroy) + result = true + else: + result = false + proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, callee, params: Rope) = let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0]) @@ -115,7 +132,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, # with them to prevent undefined behaviour and because the codegen # is free to emit expressions multiple times! d.k = locCall - d.r = pl + d.snippet = pl excl d.flags, lfSingleUse else: if d.k == locNone and p.splitDecls == 0: @@ -123,35 +140,48 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, else: if d.k == locNone: d = getTemp(p, typ.returnType) var list = initLoc(locCall, d.lode, OnUnknown) - list.r = pl - genAssignment(p, d, list, {}) # no need for deep copying + list.snippet = pl + genAssignment(p, d, list, {needAssignCall}) # no need for deep copying if canRaise: raiseExit(p) elif isHarmlessStore(p, canRaise, d): - if d.k == locNone: d = getTemp(p, typ.returnType) + var useTemp = false + if d.k == locNone: + useTemp = true + d = getTemp(p, typ.returnType) assert(d.t != nil) # generate an assignment to d: var list = initLoc(locCall, d.lode, OnUnknown) - list.r = pl - genAssignment(p, d, list, flags) # no need for deep copying - if canRaise: raiseExit(p) + list.snippet = pl + genAssignment(p, d, list, flags+{needAssignCall}) # no need for deep copying + if canRaise: + if not (useTemp and cleanupTemp(p, typ.returnType, d)): + raiseExit(p) else: var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true) var list = initLoc(locCall, d.lode, OnUnknown) - list.r = pl - genAssignment(p, tmp, list, flags) # no need for deep copying - if canRaise: raiseExit(p) + list.snippet = pl + genAssignment(p, tmp, list, flags+{needAssignCall}) # no need for deep copying + if canRaise: + if not cleanupTemp(p, typ.returnType, tmp): + raiseExit(p) genAssignment(p, d, tmp, {}) else: pl.add(");\n") line(p, cpsStmts, pl) if canRaise: raiseExit(p) -proc genBoundsCheck(p: BProc; arr, a, b: TLoc) +proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType) proc reifiedOpenArray(n: PNode): bool {.inline.} = var x = n - while x.kind in {nkAddr, nkHiddenAddr, nkHiddenStdConv, nkHiddenDeref}: - x = x[0] + while true: + case x.kind + of {nkAddr, nkHiddenAddr, nkHiddenDeref}: + x = x[0] + of nkHiddenStdConv: + x = x[1] + else: + break if x.kind == nkSym and x.sym.kind == skParam: result = false else: @@ -161,12 +191,15 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF var a = initLocExpr(p, q[1]) var b = initLocExpr(p, q[2]) var c = initLocExpr(p, q[3]) + # bug #23321: In the function mapType, ptrs (tyPtr, tyVar, tyLent, tyRef) + # are mapped into ctPtrToArray, the dereference of which is skipped + # in the `genDeref`. We need to skip these ptrs here + let ty = skipTypes(a.t, abstractVar+{tyPtr, tyRef}) # but first produce the required index checks: if optBoundsCheck in p.options: - genBoundsCheck(p, a, b, c) + genBoundsCheck(p, a, b, c, ty) if prepareForMutation: linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) - let ty = skipTypes(a.t, abstractVar+{tyPtr}) let dest = getTypeDesc(p.module, destType) let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)] case ty.kind @@ -241,7 +274,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) = optSeqDestructors in p.config.globalOptions: linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) if ntyp.kind in {tyVar} and not compileToCpp(p.module): - var t = TLoc(r: "(*$1)" % [a.rdLoc]) + var t = TLoc(snippet: "(*$1)" % [a.rdLoc]) result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" % [a.rdLoc, lenExpr(p, t), dataField(p), dataFieldAccessor(p, "*" & a.rdLoc)] @@ -253,7 +286,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) = of tyPtr, tyRef: case elementType(a.t).kind of tyString, tySequence: - var t = TLoc(r: "(*$1)" % [a.rdLoc]) + var t = TLoc(snippet: "(*$1)" % [a.rdLoc]) result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" % [a.rdLoc, lenExpr(p, t), dataField(p), dataFieldAccessor(p, "*" & a.rdLoc)] @@ -298,18 +331,33 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result) elif p.module.compileToCpp and param.typ.kind in {tyVar} and n.kind == nkHiddenAddr: - a = initLocExprSingleUse(p, n[0]) + # bug #23748: we need to introduce a temporary here. The expression type + # will be a reference in C++ and we cannot create a temporary reference + # variable. Thus, we create a temporary pointer variable instead. + let needsIndirect = mapType(p.config, n[0].typ, mapTypeChooser(n[0]) == skParam) != ctArray + if needsIndirect: + n.typ = n.typ.exactReplica + n.typ.flags.incl tfVarIsPtr + a = initLocExprSingleUse(p, n) + a = withTmpIfNeeded(p, a, needsTmp) + if needsIndirect: a.flags.incl lfIndirect # if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still # means '*T'. See posix.nim for lots of examples that do that in the wild. let callee = call[0] if callee.kind == nkSym and {sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and - {lfHeader, lfNoDecl} * callee.sym.loc.flags != {}: + {lfHeader, lfNoDecl} * callee.sym.loc.flags != {} and + needsIndirect: addAddrLoc(p.config, a, result) else: addRdLoc(a, result) else: a = initLocExprSingleUse(p, n) + if param.typ.kind in {tyVar, tyPtr, tyRef, tySink}: + let typ = skipTypes(param.typ, abstractPtrs) + if not sameBackendTypePickyAliases(typ, n.typ.skipTypes(abstractPtrs)): + a.snippet = "(($1) ($2))" % + [getTypeDesc(p.module, param.typ), rdCharLoc(a)] addRdLoc(withTmpIfNeeded(p, a, needsTmp), result) #assert result != nil @@ -354,7 +402,7 @@ proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) = of nkCallKinds: case n.getMagic: of mIncl, mExcl, mInc, mDec, mAppendStrCh, mAppendStrStr, mAppendSeqElem, - mAddr, mNew, mNewFinalize, mWasMoved, mDestroy, mReset: + mAddr, mNew, mNewFinalize, mWasMoved, mDestroy: getPotentialWrites(n[1], true, result) for i in 2..<n.len: getPotentialWrites(n[i], mutate, result) @@ -392,9 +440,11 @@ proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) = if not needTmp[i - 1]: needTmp[i - 1] = potentialAlias(n, potentialWrites) getPotentialWrites(ri[i], false, potentialWrites) - if ri[i].kind in {nkHiddenAddr, nkAddr}: - # Optimization: don't use a temp, if we would only take the address anyway - needTmp[i - 1] = false + when false: + # this optimization is wrong, see bug #23748 + if ri[i].kind in {nkHiddenAddr, nkAddr}: + # Optimization: don't use a temp, if we would only take the address anyway + needTmp[i - 1] = false var oldLen = result.len for i in 1..<ri.len: @@ -482,9 +532,9 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = assert(d.t != nil) # generate an assignment to d: var list: TLoc = initLoc(locCall, d.lode, OnUnknown) if tfIterator in typ.flags: - list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc] + list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc] else: - list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc] + list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc] genAssignment(p, d, list, {}) # no need for deep copying if canRaise: raiseExit(p) else: @@ -492,9 +542,9 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = assert(d.t != nil) # generate an assignment to d: var list: TLoc = initLoc(locCall, d.lode, OnUnknown) if tfIterator in typ.flags: - list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc] + list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc] else: - list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc] + list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc] genAssignment(p, tmp, list, {}) if canRaise: raiseExit(p) genAssignment(p, d, tmp, {}) @@ -677,7 +727,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = var typ = skipTypes(ri[0].typ, abstractInst) assert(typ.kind == tyProc) # don't call '$' here for efficiency: - let pat = $ri[0].sym.loc.r + let pat = $ri[0].sym.loc.snippet internalAssert p.config, pat.len > 0 if pat.contains({'#', '(', '@', '\''}): var pl = newRopeAppender() @@ -690,13 +740,13 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = # with them to prevent undefined behaviour and because the codegen # is free to emit expressions multiple times! d.k = locCall - d.r = pl + d.snippet = pl excl d.flags, lfSingleUse else: if d.k == locNone: d = getTemp(p, typ.returnType) assert(d.t != nil) # generate an assignment to d: var list: TLoc = initLoc(locCall, d.lode, OnUnknown) - list.r = pl + list.snippet = pl genAssignment(p, d, list, {}) # no need for deep copying else: pl.add(";\n") @@ -706,7 +756,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = var argsCounter = 0 if 1 < ri.len: genThisArg(p, ri, 1, typ, pl) - pl.add(op.r) + pl.add(op.snippet) var params = newRopeAppender() for i in 2..<ri.len: genOtherArg(p, ri, i, typ, params, argsCounter) @@ -721,12 +771,12 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = assert(typ.kind == tyProc) # don't call '$' here for efficiency: - let pat = $ri[0].sym.loc.r + let pat = $ri[0].sym.loc.snippet internalAssert p.config, pat.len > 0 var start = 3 if ' ' in pat: start = 1 - pl.add(op.r) + pl.add(op.snippet) if ri.len > 1: pl.add(": ") genArg(p, ri[1], typ.n[1].sym, ri, pl) @@ -735,7 +785,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = if ri.len > 1: genArg(p, ri[1], typ.n[1].sym, ri, pl) pl.add(" ") - pl.add(op.r) + pl.add(op.snippet) if ri.len > 2: pl.add(": ") genArg(p, ri[2], typ.n[2].sym, ri, pl) @@ -770,7 +820,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = if d.k == locNone: d = getTemp(p, typ.returnType) assert(d.t != nil) # generate an assignment to d: var list: TLoc = initLoc(locCall, ri, OnUnknown) - list.r = pl + list.snippet = pl genAssignment(p, d, list, {}) # no need for deep copying else: pl.add("];\n") diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 042af01f1..545d43ae8 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -224,7 +224,7 @@ proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc = result = TLoc(k: locField, storage: a.storage, lode: lodeTyp t, - r: rdLoc(a) & "." & field + snippet: rdLoc(a) & "." & field ) proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = @@ -254,9 +254,9 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags, case t.kind of nkSym: let field = t.sym - if field.loc.r == "": fillObjectFields(p.module, typ) - genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r), - optAsgnLoc(src, field.typ, field.loc.r), newflags) + if field.loc.snippet == "": fillObjectFields(p.module, typ) + genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.snippet), + optAsgnLoc(src, field.typ, field.loc.snippet), newflags) of nkRecList: for child in items(t): genOptAsgnObject(p, dest, src, newflags, child, typ) else: discard @@ -343,7 +343,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = of tyString: if optSeqDestructors in p.config.globalOptions: genGenericAsgn(p, dest, src, flags) - elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest): + elif ({needToCopy, needToCopySinkParam} * flags == {} and src.storage != OnStatic) or canMove(p, src.lode, dest): genRefAssign(p, dest, src) else: if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config): @@ -379,7 +379,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = elif not isObjLackingTypeField(ty): genGenericAsgn(p, dest, src, flags) elif containsGarbageCollectedRef(ty): - if ty[0].isNil and asgnComplexity(ty.n) <= 4: + if ty[0].isNil and asgnComplexity(ty.n) <= 4 and + needAssignCall notin flags: # calls might contain side effects discard getTypeDesc(p.module, ty) internalAssert p.config, ty.n != nil genOptAsgnObject(p, dest, src, flags, ty.n, ty) @@ -482,7 +483,7 @@ proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) = if d.k != locNone: var a: TLoc = initLoc(locData, n, OnStatic) # need to generate an assignment here - a.r = r + a.snippet = r if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) else: genAssignment(p, d, a, {needToCopy}) else: @@ -490,13 +491,13 @@ proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) = # the flags field! d.k = locData d.lode = n - d.r = r + d.snippet = r proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) = if d.k != locNone: # need to generate an assignment here var a: TLoc = initLoc(locExpr, n, s) - a.r = r + a.snippet = r if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) else: genAssignment(p, d, a, {needToCopy}) else: @@ -504,7 +505,7 @@ proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) = # the flags field! d.k = locExpr d.lode = n - d.r = r + d.snippet = r proc binaryStmt(p: BProc, e: PNode, d: var TLoc, op: string) = if d.k != locNone: internalError(p.config, e.info, "binaryStmt") @@ -589,7 +590,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = # skipping 'range' is correct here as we'll generate a proper range check # later via 'chckRange' let t = e.typ.skipTypes(abstractRange) - if optOverflowCheck notin p.options: + if optOverflowCheck notin p.options or (m in {mSucc, mPred} and t.kind in {tyUInt..tyUInt64}): let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)] putIntoDest(p, d, e, res) else: @@ -808,10 +809,12 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) = # careful 'addr(myptrToArray)' needs to get the ampersand: if e[0].typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr}: var a: TLoc = initLocExpr(p, e[0]) - putIntoDest(p, d, e, "&" & a.r, a.storage) + putIntoDest(p, d, e, "&" & a.snippet, a.storage) #Message(e.info, warnUser, "HERE NEW &") elif mapType(p.config, e[0].typ, mapTypeChooser(e[0]) == skParam) == ctArray or isCppRef(p, e.typ): expr(p, e[0], d) + # bug #19497 + d.lode = e else: var a: TLoc = initLocExpr(p, e[0]) putIntoDest(p, d, e, addrLoc(p.config, a), a.storage) @@ -877,10 +880,10 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = else: var rtyp: PType = nil let field = lookupFieldAgain(p, ty, f, r, addr rtyp) - if field.loc.r == "" and rtyp != nil: fillObjectFields(p.module, rtyp) - if field.loc.r == "": internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty)) + if field.loc.snippet == "" and rtyp != nil: fillObjectFields(p.module, rtyp) + if field.loc.snippet == "": internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty)) r.add "." - r.add field.loc.r + r.add field.loc.snippet putIntoDest(p, d, e, r, a.storage) r.freeze @@ -899,10 +902,10 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = test = initLoc(locNone, it, OnStack) u = initLocExpr(p, it[1]) v = initLoc(locExpr, disc, OnUnknown) - v.r = newRopeAppender() - v.r.add obj - v.r.add(".") - v.r.add(disc.sym.loc.r) + v.snippet = newRopeAppender() + v.snippet.add obj + v.snippet.add(".") + v.snippet.add(disc.sym.loc.snippet) genInExprAux(p, it, u, v, test) var msg = "" if optDeclaredLocs in p.config.globalOptions: @@ -960,12 +963,12 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = var r = rdLoc(a) let f = e[0][1].sym let field = lookupFieldAgain(p, ty, f, r) - if field.loc.r == "": fillObjectFields(p.module, ty) - if field.loc.r == "": + if field.loc.snippet == "": fillObjectFields(p.module, ty) + if field.loc.snippet == "": internalError(p.config, e.info, "genCheckedRecordField") # generate the checks: genFieldCheck(p, e, r, field) r.add(".") - r.add field.loc.r + r.add field.loc.snippet putIntoDest(p, d, e[0], r, a.storage) r.freeze else: @@ -1019,8 +1022,8 @@ proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) = putIntoDest(p, d, n, ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) -proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = - let ty = skipTypes(arr.t, abstractVarRange) +proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType) = + let ty = arrTyp case ty.kind of tyOpenArray, tyVarargs: if reifiedOpenArray(arr.lode): @@ -1100,7 +1103,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = if d.k == locNone: d.storage = OnHeap if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: - a.r = ropecg(p.module, "(*$1)", [a.r]) + a.snippet = ropecg(p.module, "(*$1)", [a.snippet]) if lfPrepareForMutation in d.flags and ty.kind == tyString and optSeqDestructors in p.config.globalOptions: @@ -1166,9 +1169,9 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = var tmpB = initLocExprSingleUse(p, e[2]) tmpB.k = locExpr if m == mOr: - tmpB.r = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))" + tmpB.snippet = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))" else: - tmpB.r = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))" + tmpB.snippet = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))" if d.k == locNone: d = tmpB else: @@ -1271,7 +1274,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = lens.add(lenExpr(p, a)) lens.add(" + ") appends.add(ropecg(p.module, "#appendString($1, $2);$n", [strLoc(p, tmp), rdLoc(a)])) - linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", [tmp.r, lens, L]) + linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", [tmp.snippet, lens, L]) p.s(cpsStmts).add appends if d.k == locNone: d = tmp @@ -1317,7 +1320,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = [byRefLoc(p, dest), lens, L]) else: call = initLoc(locCall, e, OnHeap) - call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, L]) + call.snippet = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, L]) genAssignment(p, dest, call, {}) gcUsage(p.config, e) p.s(cpsStmts).add appends @@ -1332,12 +1335,12 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = var call = initLoc(locCall, e, OnHeap) if not p.module.compileToCpp: const seqAppendPattern = "($2) #incrSeqV3((TGenericSeq*)($1), $3)" - call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a), + call.snippet = ropecg(p.module, seqAppendPattern, [rdLoc(a), getTypeDesc(p.module, e[1].typ), genTypeInfoV1(p.module, seqType, e.info)]) else: const seqAppendPattern = "($2) #incrSeqV3($1, $3)" - call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a), + call.snippet = ropecg(p.module, seqAppendPattern, [rdLoc(a), getTypeDesc(p.module, e[1].typ), genTypeInfoV1(p.module, seqType, e.info)]) # emit the write barrier if required, but we can always move here, so @@ -1347,19 +1350,11 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) var dest = initLoc(locExpr, e[2], OnHeap) var tmpL = getIntTemp(p) - lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.r, rdLoc(a), lenField(p)]) - dest.r = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.r, dataField(p)]) + lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.snippet, rdLoc(a), lenField(p)]) + dest.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.snippet, dataField(p)]) genAssignment(p, dest, b, {needToCopy}) gcUsage(p.config, e) -proc genReset(p: BProc, n: PNode) = - var a: TLoc = initLocExpr(p, n[1]) - specializeReset(p, a) - when false: - linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", - [addrLoc(p.config, a), - genTypeInfoV1(p.module, skipTypes(a.t, {tyVar}), n.info)]) - proc genDefault(p: BProc; n: PNode; d: var TLoc) = if d.k == locNone: d = getTemp(p, n.typ, needsInit=true) else: resetLoc(p, d) @@ -1376,10 +1371,10 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = if optTinyRtti in p.config.globalOptions: if needsInit: - b.r = ropecg(p.module, "($1) #nimNewObj($2, NIM_ALIGNOF($3))", + b.snippet = ropecg(p.module, "($1) #nimNewObj($2, NIM_ALIGNOF($3))", [getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)]) else: - b.r = ropecg(p.module, "($1) #nimNewObjUninit($2, NIM_ALIGNOF($3))", + b.snippet = ropecg(p.module, "($1) #nimNewObjUninit($2, NIM_ALIGNOF($3))", [getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)]) genAssignment(p, a, b, {}) else: @@ -1404,15 +1399,15 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = if p.config.selectedGC == gcGo: # newObjRC1() would clash with unsureAsgnRef() - which is used by gcGo to # implement the write barrier - b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr]) + b.snippet = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr]) linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", [addrLoc(p.config, a), b.rdLoc]) else: # use newObjRC1 as an optimization - b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr]) + b.snippet = ropecg(p.module, "($1) #newObjRC1($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr]) linefmt(p, cpsStmts, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: - b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr]) + b.snippet = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr]) genAssignment(p, a, b, {}) # set the object type: genObjectInit(p, cpsStmts, bt, a, constructRefObj) @@ -1438,18 +1433,18 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) = if not lenIsZero: if p.config.selectedGC == gcGo: # we need the write barrier - call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype), + call.snippet = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype), genTypeInfoV1(p.module, seqtype, dest.lode.info), length]) linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", [addrLoc(p.config, dest), call.rdLoc]) else: - call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype), + call.snippet = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype), genTypeInfoV1(p.module, seqtype, dest.lode.info), length]) linefmt(p, cpsStmts, "$1 = $2;$n", [dest.rdLoc, call.rdLoc]) else: if lenIsZero: - call.r = rope"NIM_NIL" + call.snippet = rope"NIM_NIL" else: - call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype), + call.snippet = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype), genTypeInfoV1(p.module, seqtype, dest.lode.info), length]) genAssignment(p, dest, call, {}) @@ -1492,9 +1487,13 @@ proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) = if id == p.module.labels: # expression not found in the cache: inc(p.module.labels) - p.module.s[cfsData].addf("static NIM_CONST $1 $2 = ", [getTypeDesc(p.module, t), d.r]) - genBracedInit(p, n, isConst = true, t, p.module.s[cfsData]) - p.module.s[cfsData].addf(";$n", []) + var data = "static NIM_CONST $1 $2 = " % [getTypeDesc(p.module, t), d.snippet] + # bug #23627; when generating const object fields, it's likely that + # we need to generate type infos for the object, which may be an object with + # custom hooks. We need to generate potential consts in the hooks first. + genBracedInit(p, n, isConst = true, t, data) + data.addf(";$n", []) + p.module.s[cfsData].add data proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = if d.k == locNone and n.len > ord(n.kind == nkObjConstr) and n.isDeepConstExpr: @@ -1505,15 +1504,14 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = proc genFieldObjConstr(p: BProc; ty: PType; useTemp, isRef: bool; nField, val, check: PNode; d: var TLoc; r: Rope; info: TLineInfo) = - var tmp2: TLoc = default(TLoc) - tmp2.r = r - let field = lookupFieldAgain(p, ty, nField.sym, tmp2.r) - if field.loc.r == "": fillObjectFields(p.module, ty) - if field.loc.r == "": internalError(p.config, info, "genFieldObjConstr") + var tmp2 = TLoc(snippet: r) + let field = lookupFieldAgain(p, ty, nField.sym, tmp2.snippet) + if field.loc.snippet == "": fillObjectFields(p.module, ty) + if field.loc.snippet == "": internalError(p.config, info, "genFieldObjConstr") if check != nil and optFieldCheck in p.options: genFieldCheck(p, check, r, field) - tmp2.r.add(".") - tmp2.r.add(field.loc.r) + tmp2.snippet.add(".") + tmp2.snippet.add(field.loc.snippet) if useTemp: tmp2.k = locTemp tmp2.storage = if isRef: OnHeap else: OnStack @@ -1521,7 +1519,12 @@ proc genFieldObjConstr(p: BProc; ty: PType; useTemp, isRef: bool; nField, val, c tmp2.k = d.k tmp2.storage = if isRef: OnHeap else: d.storage tmp2.lode = val - expr(p, val, tmp2) + if nField.typ.skipTypes(abstractVar).kind in {tyOpenArray, tyVarargs}: + var tmp3 = getTemp(p, val.typ) + expr(p, val, tmp3) + genOpenArrayConv(p, tmp2, tmp3, {}) + else: + expr(p, val, tmp2) proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = # inheritance in C++ does not allow struct initialization so @@ -1607,7 +1610,7 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) = arr = initLoc(locExpr, n[i], OnHeap) var lit = newRopeAppender() intLiteral(i, lit) - arr.r = ropecg(p.module, "$1$3[$2]", [rdLoc(dest[]), lit, dataField(p)]) + arr.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(dest[]), lit, dataField(p)]) arr.storage = OnHeap # we know that sequences are on the heap expr(p, n[i], arr) gcUsage(p.config, n) @@ -1643,19 +1646,19 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = elem = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap) var lit = newRopeAppender() intLiteral(i, lit) - elem.r = ropecg(p.module, "$1$3[$2]", [rdLoc(d), lit, dataField(p)]) + elem.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(d), lit, dataField(p)]) elem.storage = OnHeap # we know that sequences are on the heap arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage) - arr.r = ropecg(p.module, "$1[$2]", [rdLoc(a), lit]) + arr.snippet = ropecg(p.module, "$1[$2]", [rdLoc(a), lit]) genAssignment(p, elem, arr, {needToCopy}) else: var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) - linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", [i.r, L]) + linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", [i.snippet, L]) elem = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap) - elem.r = ropecg(p.module, "$1$3[$2]", [rdLoc(d), rdLoc(i), dataField(p)]) + elem.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(d), rdLoc(i), dataField(p)]) elem.storage = OnHeap # we know that sequences are on the heap arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage) - arr.r = ropecg(p.module, "$1[$2]", [rdLoc(a), rdLoc(i)]) + arr.snippet = ropecg(p.module, "$1[$2]", [rdLoc(a), rdLoc(i)]) genAssignment(p, elem, arr, {needToCopy}) lineF(p, cpsStmts, "}$n", []) @@ -1671,7 +1674,7 @@ proc genNewFinalize(p: BProc, e: PNode) = b = initLoc(locExpr, a.lode, OnHeap) ti = genTypeInfo(p.config, p.module, refType, e.info) p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) - b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ + b.snippet = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ getTypeDesc(p.module, refType), ti, getTypeDesc(p.module, skipTypes(refType.elementType, abstractRange))]) genAssignment(p, a, b, {}) # set the object type: @@ -1836,7 +1839,7 @@ proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) = template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = var a: TLoc = initLocExpr(p, n[1]) - a.r = ropecg(p.module, frmt, [rdLoc(a)]) + a.snippet = ropecg(p.module, frmt, [rdLoc(a)]) a.flags.excl lfIndirect # this flag should not be propagated here (not just for HCR) if d.k == locNone: d = getTemp(p, n.typ) genAssignment(p, d, a, {}) @@ -1855,7 +1858,7 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = var b = initLocExpr(p, a[2]) var c = initLocExpr(p, a[3]) if optBoundsCheck in p.options: - genBoundsCheck(p, m, b, c) + genBoundsCheck(p, m, b, c, skipTypes(m.t, abstractVarRange)) if op == mHigh: putIntoDest(p, d, e, ropecg(p.module, "(($2)-($1))", [rdLoc(b), rdLoc(c)])) else: @@ -1890,8 +1893,8 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = var a = initLocExpr(p, e[1]) var x = lenExpr(p, a) if op == mHigh: x = "($1-1)" % [x] - lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.r, x]) - putIntoDest(p, d, e, tmp.r) + lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.snippet, x]) + putIntoDest(p, d, e, tmp.snippet) of tyArray: # YYY: length(sideeffect) is optimized away incorrectly? if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(p.config, typ))) @@ -1913,13 +1916,13 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = var call = initLoc(locCall, e, OnHeap) if not p.module.compileToCpp: const setLenPattern = "($3) #setLengthSeqV2(($1)?&($1)->Sup:NIM_NIL, $4, $2)" - call.r = ropecg(p.module, setLenPattern, [ + call.snippet = ropecg(p.module, setLenPattern, [ rdLoc(a), rdLoc(b), getTypeDesc(p.module, t), genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)]) else: const setLenPattern = "($3) #setLengthSeqV2($1, $4, $2)" - call.r = ropecg(p.module, setLenPattern, [ + call.snippet = ropecg(p.module, setLenPattern, [ rdLoc(a), rdLoc(b), getTypeDesc(p.module, t), genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)]) @@ -1935,7 +1938,7 @@ proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = var b = initLocExpr(p, e[2]) var call = initLoc(locCall, e, OnHeap) - call.r = ropecg(p.module, "#setLengthStr($1, $2)", [ + call.snippet = ropecg(p.module, "#setLengthStr($1, $2)", [ rdLoc(a), rdLoc(b)]) genAssignment(p, a, call, {}) gcUsage(p.config, e) @@ -2012,23 +2015,23 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) = a = initLocExpr(p, ea) b = initLoc(locExpr, e, OnUnknown) if e[1].len > 0: - b.r = rope("(") + b.snippet = rope("(") for i in 0..<e[1].len: let it = e[1][i] if it.kind == nkRange: x = initLocExpr(p, it[0]) y = initLocExpr(p, it[1]) - b.r.addf("$1 >= $2 && $1 <= $3", + b.snippet.addf("$1 >= $2 && $1 <= $3", [rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)]) else: x = initLocExpr(p, it) - b.r.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)]) - if i < e[1].len - 1: b.r.add(" || ") - b.r.add(")") + b.snippet.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)]) + if i < e[1].len - 1: b.snippet.add(" || ") + b.snippet.add(")") else: # handle the case of an empty set - b.r = rope("0") - putIntoDest(p, d, e, b.r) + b.snippet = rope("0") + putIntoDest(p, d, e, b.snippet) else: assert(e[1].typ != nil) assert(e[2].typ != nil) @@ -2166,7 +2169,7 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = inc(p.labels) var lbl = p.labels.rope var tmp: TLoc = default(TLoc) - tmp.r = "LOC$1.source" % [lbl] + tmp.snippet = "LOC$1.source" % [lbl] let destsize = getSize(p.config, destt) let srcsize = getSize(p.config, srct) @@ -2231,12 +2234,16 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc) = raiseInstr(p, p.s(cpsStmts)) linefmt p, cpsStmts, "}$n", [] - putIntoDest(p, d, n, "(($1) ($2))" % + if sameBackendTypeIgnoreRange(dest, n[0].typ): + # don't cast so an address can be taken for `var` conversions + putIntoDest(p, d, n, "($1)" % [rdCharLoc(a)], a.storage) + else: + putIntoDest(p, d, n, "(($1) ($2))" % [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage) proc genConv(p: BProc, e: PNode, d: var TLoc) = let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink}) - if sameBackendType(destType, e[1].typ): + if sameBackendTypeIgnoreRange(destType, e[1].typ): expr(p, e[1], d) else: genSomeCast(p, e, d) @@ -2345,8 +2352,13 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) = else: linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), byRefLoc(p, a)]) else: - let flags = if not canMove(p, n[1], d): {needToCopy} else: {} - genAssignment(p, d, a, flags) + if n[1].kind == nkSym and isSinkParam(n[1].sym): + var tmp = getTemp(p, n[1].typ.skipTypes({tySink})) + genAssignment(p, tmp, a, {needToCopySinkParam}) + genAssignment(p, d, tmp, {}) + resetLoc(p, tmp) + else: + genAssignment(p, d, a, {}) resetLoc(p, a) proc genDestroy(p: BProc; n: PNode) = @@ -2454,7 +2466,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = var call = initLoc(locCall, e, OnHeap) var dest = initLocExpr(p, e[1]) var b = initLocExpr(p, e[2]) - call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)]) + call.snippet = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)]) genAssignment(p, dest, call, {}) of mAppendStrStr: genStrAppend(p, e, d) of mAppendSeqElem: @@ -2518,7 +2530,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = let member = if t.kind == tyTuple: "Field" & rope(dotExpr[1].sym.position) - else: dotExpr[1].sym.loc.r + else: dotExpr[1].sym.loc.snippet putIntoDest(p,d,e, "((NI)offsetof($1, $2))" % [tname, member]) of mChr: genSomeCast(p, e, d) of mOrd: genOrd(p, e, d) @@ -2541,8 +2553,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = # - not sure, and it wouldn't work if the symbol behind the magic isn't # somehow forward-declared from some other usage, but it is *possible* if lfNoDecl notin opr.loc.flags: - let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.r) - assert prc != nil, $opr.loc.r + let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.snippet) + assert prc != nil, $opr.loc.snippet # HACK: # Explicitly add this proc as declared here so the cgsym call doesn't # add a forward declaration - without this we could end up with the same @@ -2555,14 +2567,13 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = let wasDeclared = containsOrIncl(p.module.declaredProtos, prc.id) # Make the function behind the magic get actually generated - this will # not lead to a forward declaration! The genCall will lead to one. - cgsym(p.module, $opr.loc.r) + cgsym(p.module, $opr.loc.snippet) # make sure we have pointer-initialising code for hot code reloading if not wasDeclared and p.hcrOn: p.module.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n", [mangleDynLibProc(prc), getTypeDesc(p.module, prc.loc.t), getModuleDllPath(p.module, prc)]) genCall(p, e, d) of mDefault, mZeroDefault: genDefault(p, e, d) - of mReset: genReset(p, e) of mEcho: genEcho(p, e[1].skipConv) of mArrToSeq: genArrToSeq(p, e, d) of mNLen..mNError, mSlurp..mQuoteAst: @@ -2685,7 +2696,7 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = var it = n[i] if it.kind == nkExprColonExpr: it = it[1] rec = initLoc(locExpr, it, dest[].storage) - rec.r = "$1.Field$2" % [rdLoc(dest[]), rope(i)] + rec.snippet = "$1.Field$2" % [rdLoc(dest[]), rope(i)] rec.flags.incl(lfEnforceDeref) expr(p, it, rec) @@ -2735,12 +2746,12 @@ proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) = arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), d.storage) var lit = newRopeAppender() intLiteral(i, lit) - arr.r = "$1[$2]" % [rdLoc(d), lit] + arr.snippet = "$1[$2]" % [rdLoc(d), lit] expr(p, n[i], arr) proc genComplexConst(p: BProc, sym: PSym, d: var TLoc) = requestConstImpl(p, sym) - assert((sym.loc.r != "") and (sym.loc.t != nil)) + assert((sym.loc.snippet != "") and (sym.loc.t != nil)) putLocIntoDest(p, d, sym.loc) template genStmtListExprImpl(exprOrStmt) {.dirty.} = @@ -2872,24 +2883,24 @@ proc genConstSetup(p: BProc; sym: PSym): bool = result = lfNoDecl notin sym.loc.flags proc genConstHeader(m, q: BModule; p: BProc, sym: PSym) = - if sym.loc.r == "": + if sym.loc.snippet == "": if not genConstSetup(p, sym): return - assert(sym.loc.r != "", $sym.name.s & $sym.itemId) + assert(sym.loc.snippet != "", $sym.name.s & $sym.itemId) if m.hcrOn: - m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.r]); + m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.snippet]); m.initProc.procSec(cpsLocals).addf( - "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r, + "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(q, sym)]) else: let headerDecl = "extern NIM_CONST $1 $2;$n" % - [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.r] + [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.snippet] m.s[cfsData].add(headerDecl) if sfExportc in sym.flags and p.module.g.generatedHeader != nil: p.module.g.generatedHeader.s[cfsData].add(headerDecl) proc genConstDefinition(q: BModule; p: BProc; sym: PSym) = # add a suffix for hcr - will later init the global pointer with this data - let actualConstName = if q.hcrOn: sym.loc.r & "_const" else: sym.loc.r + let actualConstName = if q.hcrOn: sym.loc.snippet & "_const" else: sym.loc.snippet var data = newRopeAppender() data.addf("N_LIB_PRIVATE NIM_CONST $1 $2 = ", [getTypeDesc(q, sym.typ), actualConstName]) @@ -2898,16 +2909,16 @@ proc genConstDefinition(q: BModule; p: BProc; sym: PSym) = q.s[cfsData].add data if q.hcrOn: # generate the global pointer with the real name - q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, dkVar), sym.loc.r]) + q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, dkVar), sym.loc.snippet]) # register it (but ignore the boolean result of hcrRegisterGlobal) q.initProc.procSec(cpsLocals).addf( "\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n", - [getModuleDllPath(q, sym), sym.loc.r, rdLoc(sym.loc)]) + [getModuleDllPath(q, sym), sym.loc.snippet, rdLoc(sym.loc)]) # always copy over the contents of the actual constant with the _const # suffix ==> this means that the constant is reloadable & updatable! q.initProc.procSec(cpsLocals).add(ropecg(q, "\t#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n", - [sym.loc.r, actualConstName, rdLoc(sym.loc)])) + [sym.loc.snippet, actualConstName, rdLoc(sym.loc)])) proc genConstStmt(p: BProc, n: PNode) = # This code is only used in the new DCE implementation. @@ -2947,7 +2958,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = genProcPrototype(p.module, sym) else: genProc(p.module, sym) - if sym.loc.r == "" or sym.loc.lode == nil: + if sym.loc.snippet == "" or sym.loc.lode == nil: internalError(p.config, n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of skConst: @@ -2957,7 +2968,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = putIntoDest(p, d, n, lit, OnStatic) elif useAliveDataFromDce in p.module.flags: genConstHeader(p.module, p.module, p, sym) - assert((sym.loc.r != "") and (sym.loc.t != nil)) + assert((sym.loc.snippet != "") and (sym.loc.t != nil)) putLocIntoDest(p, d, sym.loc) else: genComplexConst(p, sym, d) @@ -2972,14 +2983,14 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if sfCompileTime in sym.flags: genSingleVar(p, sym, n, astdef(sym)) - if sym.loc.r == "" or sym.loc.t == nil: + if sym.loc.snippet == "" or sym.loc.t == nil: #echo "FAILED FOR PRCO ", p.prc.name.s #echo renderTree(p.prc.ast, {renderIds}) internalError p.config, n.info, "expr: var not init " & sym.name.s & "_" & $sym.id if sfThread in sym.flags: accessThreadLocalVar(p, sym) if emulatedThreadVars(p.config): - putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r) + putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.snippet) else: putLocIntoDest(p, d, sym.loc) else: @@ -2987,17 +2998,17 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of skTemp: when false: # this is more harmful than helpful. - if sym.loc.r == "": + if sym.loc.snippet == "": # we now support undeclared 'skTemp' variables for easier # transformations in other parts of the compiler: assignLocalVar(p, n) - if sym.loc.r == "" or sym.loc.t == nil: + if sym.loc.snippet == "" or sym.loc.t == nil: #echo "FAILED FOR PRCO ", p.prc.name.s #echo renderTree(p.prc.ast, {renderIds}) internalError(p.config, n.info, "expr: temp not init " & sym.name.s & "_" & $sym.id) putLocIntoDest(p, d, sym.loc) of skParam: - if sym.loc.r == "" or sym.loc.t == nil: + if sym.loc.snippet == "" or sym.loc.t == nil: # echo "FAILED FOR PRCO ", p.prc.name.s # debug p.prc.typ.n # echo renderTree(p.prc.ast, {renderIds}) @@ -3087,7 +3098,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkLambdaKinds: var sym = n[namePos].sym genProc(p.module, sym) - if sym.loc.r == "" or sym.loc.lode == nil: + if sym.loc.snippet == "" or sym.loc.lode == nil: internalError(p.config, n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of nkClosure: genClosure(p, n, d) @@ -3118,7 +3129,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if ex.kind != nkEmpty: genLineDir(p, n) var a: TLoc = initLocExprSingleUse(p, ex) - line(p, cpsStmts, "(void)(" & a.r & ");\L") + line(p, cpsStmts, "(void)(" & a.snippet & ");\L") of nkAsmStmt: genAsmStmt(p, n) of nkTryStmt, nkHiddenTryStmt: case p.config.exc @@ -3240,7 +3251,8 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, getNullValueAux(p, t, it, constOrNil, result, count, isConst, info) of nkRecCase: getNullValueAux(p, t, obj[0], constOrNil, result, count, isConst, info) - if count > 0: result.add ", " + var res = "" + if count > 0: res.add ", " var branch = Zero if constOrNil != nil: ## find kind value, default is zero if not specified @@ -3254,18 +3266,21 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, break let selectedBranch = caseObjDefaultBranch(obj, branch) - result.add "{" + res.add "{" var countB = 0 let b = lastSon(obj[selectedBranch]) # designated initilization is the only way to init non first element of unions # branches are allowed to have no members (b.len == 0), in this case they don't need initializer if b.kind == nkRecList and not isEmptyCaseObjectBranch(b): - result.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {" - getNullValueAux(p, t, b, constOrNil, result, countB, isConst, info) - result.add "}" + res.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {" + getNullValueAux(p, t, b, constOrNil, res, countB, isConst, info) + res.add "}" elif b.kind == nkSym: - result.add "." & mangleRecFieldName(p.module, b.sym) & " = " - getNullValueAux(p, t, b, constOrNil, result, countB, isConst, info) + res.add "." & mangleRecFieldName(p.module, b.sym) & " = " + getNullValueAux(p, t, b, constOrNil, res, countB, isConst, info) + else: + return + result.add res result.add "}" of nkSym: diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim index 4af690e35..6caeb8084 100644 --- a/compiler/ccgreset.nim +++ b/compiler/ccgreset.nim @@ -24,10 +24,10 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode; of nkRecCase: if (n[0].kind != nkSym): internalError(p.config, n.info, "specializeResetN") let disc = n[0].sym - if disc.loc.r == "": fillObjectFields(p.module, typ) + if disc.loc.snippet == "": fillObjectFields(p.module, typ) if disc.loc.t == nil: internalError(p.config, n.info, "specializeResetN()") - lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r]) + lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet]) for i in 1..<n.len: let branch = n[i] assert branch.kind in {nkOfBranch, nkElse} @@ -38,14 +38,14 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode; specializeResetN(p, accessor, lastSon(branch), typ) lineF(p, cpsStmts, "break;$n", []) lineF(p, cpsStmts, "} $n", []) - specializeResetT(p, "$1.$2" % [accessor, disc.loc.r], disc.loc.t) + specializeResetT(p, "$1.$2" % [accessor, disc.loc.snippet], disc.loc.t) of nkSym: let field = n.sym if field.typ.kind == tyVoid: return - if field.loc.r == "": fillObjectFields(p.module, typ) + if field.loc.snippet == "": fillObjectFields(p.module, typ) if field.loc.t == nil: internalError(p.config, n.info, "specializeResetN()") - specializeResetT(p, "$1.$2" % [accessor, field.loc.r], field.loc.t) + specializeResetT(p, "$1.$2" % [accessor, field.loc.snippet], field.loc.t) else: internalError(p.config, n.info, "specializeResetN()") proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = @@ -59,8 +59,8 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = let arraySize = lengthOrd(p.config, typ.indexType) var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", - [i.r, arraySize]) - specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.r]), typ.elementType) + [i.snippet, arraySize]) + specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType) lineF(p, cpsStmts, "}$n", []) of tyObject: var x = typ.baseClass @@ -96,7 +96,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = raiseAssert "unexpected set type kind" of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation, tyGenericParam, tyOrdinal, tyOpenArray, tyForward, tyVarargs, - tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, + tyUncheckedArray, tyError, tyBuiltInTypeClass, tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable: discard diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 345639d94..883108f2c 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -110,10 +110,10 @@ proc genVarTuple(p: BProc, n: PNode) = initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1])) var field = initLoc(locExpr, vn, tup.storage) if t.kind == tyTuple: - field.r = "$1.Field$2" % [rdLoc(tup), rope(i)] + field.snippet = "$1.Field$2" % [rdLoc(tup), rope(i)] else: if t.n[i].kind != nkSym: internalError(p.config, n.info, "genVarTuple") - field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)] + field.snippet = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)] putLocIntoDest(p, v.loc, field) if forHcr or isGlobalInBlock: hcrGlobals.add((loc: v.loc, tp: "NULL")) @@ -128,7 +128,7 @@ proc genVarTuple(p: BProc, n: PNode) = lineCg(p, cpsLocals, "NIM_BOOL $1 = NIM_FALSE;$n", [hcrCond]) for curr in hcrGlobals: lineCg(p, cpsLocals, "$1 |= hcrRegisterGlobal($4, \"$2\", sizeof($3), $5, (void**)&$2);$N", - [hcrCond, curr.loc.r, rdLoc(curr.loc), getModuleDllPath(p.module, n[0].sym), curr.tp]) + [hcrCond, curr.loc.snippet, rdLoc(curr.loc), getModuleDllPath(p.module, n[0].sym), curr.tp]) proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} = @@ -267,11 +267,11 @@ proc genBreakState(p: BProc, n: PNode, d: var TLoc) = if n[0].kind == nkClosure: a = initLocExpr(p, n[0][1]) - d.r = "(((NI*) $1)[1] < 0)" % [rdLoc(a)] + d.snippet = "(((NI*) $1)[1] < 0)" % [rdLoc(a)] else: a = initLocExpr(p, n[0]) # the environment is guaranteed to contain the 'state' field at offset 1: - d.r = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)] + d.snippet = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)] proc genGotoVar(p: BProc; value: PNode) = if value.kind notin {nkCharLit..nkUInt64Lit}: @@ -289,7 +289,7 @@ proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) = #echo "New code produced for ", v.name.s, " ", p.config $ value.info genBracedInit(p, value, isConst = false, v.typ, result) -proc genCppParamsForCtor(p: BProc; call: PNode): string = +proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): string = result = "" var argsCounter = 0 let typ = skipTypes(call[0].typ, abstractInst) @@ -298,12 +298,23 @@ proc genCppParamsForCtor(p: BProc; call: PNode): string = #if it's a type we can just generate here another initializer as we are in an initializer context if call[i].kind == nkCall and call[i][0].kind == nkSym and call[i][0].sym.kind == skType: if argsCounter > 0: result.add "," - result.add genCppInitializer(p.module, p, call[i][0].sym.typ) + result.add genCppInitializer(p.module, p, call[i][0].sym.typ, didGenTemp) else: + #We need to test for temp in globals, see: #23657 + let param = + if typ[i].kind in {tyVar} and call[i].kind == nkHiddenAddr: + call[i][0] + else: + call[i] + if param.kind != nkBracketExpr or param.typ.kind in + {tyRef, tyPtr, tyUncheckedArray, tyArray, tyOpenArray, + tyVarargs, tySequence, tyString, tyCstring, tyTuple}: + let tempLoc = initLocExprSingleUse(p, param) + didGenTemp = didGenTemp or tempLoc.k == locTemp genOtherArg(p, call, i, typ, result, argsCounter) -proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope) = - let params = genCppParamsForCtor(p, call) +proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope, didGenTemp: var bool) = + let params = genCppParamsForCtor(p, call, didGenTemp) if params.len == 0: decl = runtimeFormat("$#;\n", [decl]) else: @@ -330,7 +341,14 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = # v.owner.kind != skModule: targetProc = p.module.preInitProc if isCppCtorCall and not containsHiddenPointer(v.typ): - callGlobalVarCppCtor(targetProc, v, vn, value) + var didGenTemp = false + callGlobalVarCppCtor(targetProc, v, vn, value, didGenTemp) + if didGenTemp: + message(p.config, vn.info, warnGlobalVarConstructorTemporary, vn.sym.name.s) + #We fail to call the constructor in the global scope so we do the call inside the main proc + assignGlobalVar(targetProc, vn, valueAsRope) + var loc = initLocExprSingleUse(targetProc, value) + genAssignment(targetProc, v.loc, loc, {}) else: assignGlobalVar(targetProc, vn, valueAsRope) @@ -365,7 +383,8 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = var decl = localVarDecl(p, vn) var tmp: TLoc if isCppCtorCall: - genCppVarForCtor(p, value, decl) + var didGenTemp = false + genCppVarForCtor(p, value, decl, didGenTemp) line(p, cpsStmts, decl) else: tmp = initLocExprSingleUse(p, value) @@ -386,7 +405,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = # put it in the locals section - mainly because of loops which # use the var in a call to resetLoc() in the statements section lineCg(targetProc, cpsLocals, "hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1);$n", - [v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc]) + [v.loc.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc]) # nothing special left to do later on - let's avoid closing and reopening blocks forHcr = false @@ -395,7 +414,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = # be able to re-run it but without the top level code - just the init of globals if forHcr: lineCg(targetProc, cpsStmts, "if (hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1))$N", - [v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc]) + [v.loc.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc]) startBlock(targetProc) if value.kind != nkEmpty and valueAsRope.len == 0: genLineDir(targetProc, vn) @@ -736,6 +755,18 @@ proc raiseExit(p: BProc) = lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto LA$1_;$n", [p.nestedTryStmts[^1].label]) +proc raiseExitCleanup(p: BProc, destroy: string) = + assert p.config.exc == excGoto + if nimErrorFlagDisabled notin p.flags: + p.flags.incl nimErrorFlagAccessed + if p.nestedTryStmts.len == 0: + p.flags.incl beforeRetNeeded + # easy case, simply goto 'ret': + lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) {$1; goto BeforeRet_;}$n", [destroy]) + else: + lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) {$2; goto LA$1_;}$n", + [p.nestedTryStmts[^1].label, destroy]) + proc finallyActions(p: BProc) = if p.config.exc != excGoto and p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept: # if the current try stmt have a finally block, @@ -765,10 +796,14 @@ proc genRaiseStmt(p: BProc, t: PNode) = var e = rdLoc(a) discard getTypeDesc(p.module, t[0].typ) var typ = skipTypes(t[0].typ, abstractPtrs) - # XXX For reasons that currently escape me, this is only required by the new - # C++ based exception handling: - if p.config.exc == excCpp: + case p.config.exc + of excCpp: blockLeaveActions(p, howManyTrys = 0, howManyExcepts = p.inExceptBlockLen) + of excGoto: + blockLeaveActions(p, howManyTrys = 0, + howManyExcepts = (if p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept: 1 else: 0)) + else: + discard genLineDir(p, t) if isImportedException(typ, p.config): lineF(p, cpsStmts, "throw $1;$n", [e]) @@ -1510,7 +1545,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) = else: discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs)) fillBackendName(p.module, sym) - res.add($sym.loc.r) + res.add($sym.loc.snippet) of nkTypeOfExpr: res.add($getTypeDesc(p.module, it.typ)) else: @@ -1547,14 +1582,14 @@ proc genAsmStmt(p: BProc, t: PNode) = if whichPragma(i) == wAsmSyntax: asmSyntax = i[1].strVal - if asmSyntax != "" and + if asmSyntax != "" and not ( asmSyntax == "gcc" and hasGnuAsm in CC[p.config.cCompiler].props or asmSyntax == "vcc" and hasGnuAsm notin CC[p.config.cCompiler].props): localError( - p.config, t.info, + p.config, t.info, "Your compiler does not support the specified inline assembler") - + genAsmOrEmitStmt(p, t, isAsmStmt=true, s) # see bug #2362, "top level asm statements" seem to be a mis-feature # but even if we don't do this, the example in #2362 cannot possibly @@ -1592,9 +1627,9 @@ proc genPragma(p: BProc, n: PNode) = case whichPragma(it) of wEmit: genEmit(p, it) of wPush: - processPushBackendOption(p.optionsStack, p.options, n, i+1) + processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1) of wPop: - processPopBackendOption(p.optionsStack, p.options) + processPopBackendOption(p.config, p.optionsStack, p.options) else: discard diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim index abf830b57..1f551f022 100644 --- a/compiler/ccgthreadvars.nim +++ b/compiler/ccgthreadvars.nim @@ -30,7 +30,7 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) = # allocator for it :-( if not containsOrIncl(m.g.nimtvDeclared, s.id): m.g.nimtvDeps.add(s.loc.t) - m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r]) + m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.snippet]) else: if isExtern: m.s[cfsVars].add("extern ") elif lfExportLib in s.loc.flags: m.s[cfsVars].add("N_LIB_EXPORT_VAR ") @@ -41,7 +41,7 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) = m.s[cfsVars].add("NIM_THREAD_LOCAL ") else: m.s[cfsVars].add("NIM_THREADVAR ") m.s[cfsVars].add(getTypeDesc(m, s.loc.t)) - m.s[cfsVars].addf(" $1;$n", [s.loc.r]) + m.s[cfsVars].addf(" $1;$n", [s.loc.snippet]) proc generateThreadLocalStorage(m: BModule) = if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags): diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index 3fd269bc6..ed4c79d9a 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -34,10 +34,10 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode; if (n[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc") var p = c.p let disc = n[0].sym - if disc.loc.r == "": fillObjectFields(c.p.module, typ) + if disc.loc.snippet == "": fillObjectFields(c.p.module, typ) if disc.loc.t == nil: internalError(c.p.config, n.info, "genTraverseProc()") - lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r]) + lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet]) for i in 1..<n.len: let branch = n[i] assert branch.kind in {nkOfBranch, nkElse} @@ -51,10 +51,10 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode; of nkSym: let field = n.sym if field.typ.kind == tyVoid: return - if field.loc.r == "": fillObjectFields(c.p.module, typ) + if field.loc.snippet == "": fillObjectFields(c.p.module, typ) if field.loc.t == nil: internalError(c.p.config, n.info, "genTraverseProc()") - genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t) + genTraverseProc(c, "$1.$2" % [accessor, field.loc.snippet], field.loc.t) else: internalError(c.p.config, n.info, "genTraverseProc()") proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} = @@ -78,9 +78,9 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) = var oldCode = p.s(cpsStmts) freeze oldCode linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", - [i.r, arraySize]) + [i.snippet, arraySize]) let oldLen = p.s(cpsStmts).len - genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.r]), typ.elementType) + genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType) if p.s(cpsStmts).len == oldLen: # do not emit dummy long loops for faster debug builds: p.s(cpsStmts) = oldCode @@ -120,12 +120,12 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) = var i = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt)) var oldCode = p.s(cpsStmts) freeze oldCode - var a = TLoc(r: accessor) + var a = TLoc(snippet: accessor) lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", - [i.r, lenExpr(c.p, a)]) + [i.snippet, lenExpr(c.p, a)]) let oldLen = p.s(cpsStmts).len - genTraverseProc(c, "$1$3[$2]" % [accessor, i.r, dataField(c.p)], typ.elementType) + genTraverseProc(c, "$1$3[$2]" % [accessor, i.snippet, dataField(c.p)], typ.elementType) if p.s(cpsStmts).len == oldLen: # do not emit dummy long loops for faster debug builds: p.s(cpsStmts) = oldCode diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 1366caab3..2c2556336 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -55,24 +55,24 @@ proc mangleField(m: BModule; name: PIdent): string = if isKeyword(name): result.add "_0" -proc mangleProc(m: BModule; s: PSym; makeUnique: bool): string = +proc mangleProc(m: BModule; s: PSym; makeUnique: bool): string = result = "_Z" # Common prefix in Itanium ABI result.add encodeSym(m, s, makeUnique) if s.typ.len > 1: #we dont care about the return param - for i in 1..<s.typ.len: + for i in 1..<s.typ.len: if s.typ[i].isNil: continue result.add encodeType(m, s.typ[i]) - + if result in m.g.mangledPrcs: result = mangleProc(m, s, true) else: m.g.mangledPrcs.incl(result) proc fillBackendName(m: BModule; s: PSym) = - if s.loc.r == "": + if s.loc.snippet == "": var result: Rope if not m.compileToCpp and s.kind in routineKinds and optCDebug in m.g.config.globalOptions and - m.g.config.symbolFiles == disabledSf: + m.g.config.symbolFiles == disabledSf: result = mangleProc(m, s, false).rope else: result = s.name.s.mangle.rope @@ -80,11 +80,11 @@ proc fillBackendName(m: BModule; s: PSym) = if m.hcrOn: result.add '_' result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts, m.config)) - s.loc.r = result + s.loc.snippet = result writeMangledName(m.ndi, s, m.config) proc fillParamName(m: BModule; s: PSym) = - if s.loc.r == "": + if s.loc.snippet == "": var res = s.name.s.mangle res.add mangleParamExt(s) #res.add idOrSig(s, res, m.sigConflicts, m.config) @@ -104,13 +104,13 @@ proc fillParamName(m: BModule; s: PSym) = # and a function called in main or proxy uses `socket` as a parameter name. # That would lead to either needing to reload `proxy` or to overwrite the # executable file for the main module, which is running (or both!) -> error. - s.loc.r = res.rope + s.loc.snippet = res.rope writeMangledName(m.ndi, s, m.config) proc fillLocalName(p: BProc; s: PSym) = assert s.kind in skLocalVars+{skTemp} #assert sfGlobal notin s.flags - if s.loc.r == "": + if s.loc.snippet == "": var key = s.name.s.mangle let counter = p.sigConflicts.getOrDefault(key) var result = key.rope @@ -120,7 +120,7 @@ proc fillLocalName(p: BProc; s: PSym) = elif counter != 0 or isKeyword(s.name) or p.module.g.config.cppDefines.contains(key): result.add "_" & rope(counter+1) p.sigConflicts.inc(key) - s.loc.r = result + s.loc.snippet = result if s.kind != skTemp: writeMangledName(p.module.ndi, s, p.config) proc scopeMangledParam(p: BProc; param: PSym) = @@ -147,23 +147,23 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope = var t = typ while true: if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}: - return t.sym.loc.r + return t.sym.loc.snippet if t.kind in irrelevantForBackend: t = t.skipModifier else: break let typ = if typ.kind in {tyAlias, tySink, tyOwned}: typ.elementType else: typ - if typ.loc.r == "": - typ.typeName(typ.loc.r) - typ.loc.r.add $sig + if typ.loc.snippet == "": + typ.typeName(typ.loc.snippet) + typ.loc.snippet.add $sig else: when defined(debugSigHashes): # check consistency: var tn = newRopeAppender() typ.typeName(tn) - assert($typ.loc.r == $(tn & $sig)) - result = typ.loc.r + assert($typ.loc.snippet == $(tn & $sig)) + result = typ.loc.snippet if result == "": internalError(m.config, "getTypeName: " & $typ.kind) proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind = @@ -189,7 +189,7 @@ proc mapType(conf: ConfigRef; typ: PType; isParam: bool): TCTypeKind = of tyObject, tyTuple: result = ctStruct of tyUserTypeClasses: doAssert typ.isResolvedUserTypeClass - return mapType(conf, typ.skipModifier, isParam) + result = mapType(conf, typ.skipModifier, isParam) of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyInferred, tyOwned: result = mapType(conf, skipModifier(typ), isParam) @@ -277,9 +277,12 @@ proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool = if rettype.isImportedCppType or t.isImportedCppType or (typ.callConv == ccCDecl and conf.selectedGC in {gcArc, gcAtomicArc, gcOrc}): # prevents nrvo for cdecl procs; # bug #23401 - return false - result = containsGarbageCollectedRef(t) or - (t.kind == tyObject and not isObjLackingTypeField(t)) + result = false + else: + result = containsGarbageCollectedRef(t) or + (t.kind == tyObject and not isObjLackingTypeField(t)) or + (getSize(conf, rettype) == szUnknownSize and (t.sym == nil or sfImportc notin t.sym.flags)) + else: result = false const @@ -287,7 +290,7 @@ const "N_STDCALL", "N_CDECL", "N_SAFECALL", "N_SYSCALL", # this is probably not correct for all platforms, # but one can #define it to what one wants - "N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV", + "N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV", "N_NOCONV" #ccMember is N_NOCONV ] @@ -318,7 +321,7 @@ proc fillResult(conf: ConfigRef; param: PNode, proctype: PType) = proc typeNameOrLiteral(m: BModule; t: PType, literal: string): Rope = if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone: useHeader(m, t.sym) - result = t.sym.loc.r + result = t.sym.loc.snippet else: result = rope(literal) @@ -373,13 +376,6 @@ proc getTypePre(m: BModule; typ: PType; sig: SigHash): Rope = result = getSimpleTypeDesc(m, typ) if result == "": result = cacheGetType(m.typeCache, sig) -proc structOrUnion(t: PType): Rope = - let cachedUnion = rope("union") - let cachedStruct = rope("struct") - let t = t.skipTypes({tyAlias, tySink}) - if tfUnion in t.flags: cachedUnion - else: cachedStruct - proc addForwardStructFormat(m: BModule; structOrUnion: Rope, typename: Rope) = if m.compileToCpp: m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename] @@ -478,8 +474,8 @@ macro unrollChars(x: static openArray[char], name, body: untyped) = copy body ))) -proc multiFormat*(frmt: var string, chars : static openArray[char], args: openArray[seq[string]]) = - var res : string +proc multiFormat*(frmt: var string, chars: static openArray[char], args: openArray[seq[string]]) = + var res: string unrollChars(chars, c): res = "" let arg = args[find(chars, c)] @@ -521,7 +517,8 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params weakDep=false;) = let t = prc.typ let isCtor = sfConstructor in prc.flags - if isCtor or (name[0] == '~' and sfMember in prc.flags): #destructors cant have void + if isCtor or (name[0] == '~' and sfMember in prc.flags): + # destructors can't have void rettype = "" elif t.returnType == nil or isInvalidReturnType(m.config, t): rettype = "void" @@ -537,10 +534,10 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params fillLoc(this.loc, locParam, t.n[1], this.paramStorageLoc) if this.typ.kind == tyPtr: - this.loc.r = "this" + this.loc.snippet = "this" else: - this.loc.r = "(*this)" - names.add this.loc.r + this.loc.snippet = "(*this)" + names.add this.loc.snippet types.add getTypeDescWeak(m, this.typ, check, dkParam) let firstParam = if isCtor: 1 else: 2 @@ -553,7 +550,7 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params descKind = dkRefGenericParam else: descKind = dkRefParam - var typ, name : string + var typ, name: string fillParamName(m, param) fillLoc(param.loc, locParam, t.n[i], param.paramStorageLoc) @@ -568,7 +565,7 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params if sfNoalias in param.flags: typ.add("NIM_NOALIAS ") - name = param.loc.r + name = param.loc.snippet types.add typ names.add name if sfCodegenDecl notin param.flags: @@ -628,9 +625,9 @@ proc genProcParams(m: BModule; t: PType, rettype, params: var Rope, typ.add("NIM_NOALIAS ") if sfCodegenDecl notin param.flags: params.add(typ) - params.add(param.loc.r) + params.add(param.loc.snippet) else: - params.add runtimeFormat(param.cgDeclFrmt, [typ, param.loc.r]) + params.add runtimeFormat(param.cgDeclFrmt, [typ, param.loc.snippet]) # declare the len field for open arrays: var arr = param.typ.skipTypes({tyGenericInst}) if arr.kind in {tyVar, tyLent, tySink}: arr = arr.elementType @@ -639,7 +636,7 @@ proc genProcParams(m: BModule; t: PType, rettype, params: var Rope, # this fixes the 'sort' bug: if param.typ.kind in {tyVar, tyLent}: param.loc.storage = OnUnknown # need to pass hidden parameter: - params.addf(", NI $1Len_$2", [param.loc.r, j.rope]) + params.addf(", NI $1Len_$2", [param.loc.snippet, j.rope]) inc(j) arr = arr[0].skipTypes({tySink}) if t.returnType != nil and isInvalidReturnType(m.config, t): @@ -667,7 +664,7 @@ proc genProcParams(m: BModule; t: PType, rettype, params: var Rope, proc mangleRecFieldName(m: BModule; field: PSym): Rope = if {sfImportc, sfExportc} * field.flags != {}: - result = field.loc.r + result = field.loc.snippet else: result = rope(mangleField(m, field.name)) if result == "": internalError(m.config, field.info, "mangleRecFieldName") @@ -679,9 +676,9 @@ proc hasCppCtor(m: BModule; typ: PType): bool = if sfConstructor in prc.flags: return true -proc genCppParamsForCtor(p: BProc; call: PNode): string +proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): string -proc genCppInitializer(m: BModule, prc: BProc; typ: PType): string = +proc genCppInitializer(m: BModule, prc: BProc; typ: PType; didGenTemp: var bool): string = #To avoid creating a BProc per test when called inside a struct nil BProc is allowed result = "{}" if typ.itemId in m.g.graph.initializersPerType: @@ -690,13 +687,13 @@ proc genCppInitializer(m: BModule, prc: BProc; typ: PType): string = var p = prc if p == nil: p = BProc(module: m) - result = "{" & genCppParamsForCtor(p, call) & "}" + result = "{" & genCppParamsForCtor(p, call, didGenTemp) & "}" if prc == nil: assert p.blocks.len == 0, "BProc belongs to a struct doesnt have blocks" proc genRecordFieldsAux(m: BModule; n: PNode, rectype: PType, - check: var IntSet; result: var Rope; unionPrefix = "") = + check: var IntSet; result: var Builder; unionPrefix = "") = case n.kind of nkRecList: for i in 0..<n.len: @@ -713,63 +710,51 @@ proc genRecordFieldsAux(m: BModule; n: PNode, let k = lastSon(n[i]) if k.kind != nkSym: let structName = "_" & mangleRecFieldName(m, n[0].sym) & "_" & $i - var a = newRopeAppender() + var a = newBuilder("") genRecordFieldsAux(m, k, rectype, check, a, unionPrefix & $structName & ".") - if a != "": - if tfPacked notin rectype.flags: - unionBody.add("struct {") - else: - if hasAttribute in CC[m.config.cCompiler].props: - unionBody.add("struct __attribute__((__packed__)){") - else: - unionBody.addf("#pragma pack(push, 1)$nstruct{", []) - unionBody.add(a) - unionBody.addf("} $1;$n", [structName]) - if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props: - unionBody.addf("#pragma pack(pop)$n", []) + if a.len != 0: + unionBody.addFieldWithStructType(m, rectype, structName): + unionBody.add(a) else: genRecordFieldsAux(m, k, rectype, check, unionBody, unionPrefix) else: internalError(m.config, "genRecordFieldsAux(record case branch)") - if unionBody != "": - result.addf("union{\n$1};$n", [unionBody]) + if unionBody.len != 0: + result.addAnonUnion: + result.add(unionBody) of nkSym: let field = n.sym if field.typ.kind == tyVoid: return #assert(field.ast == nil) let sname = mangleRecFieldName(m, field) fillLoc(field.loc, locField, n, unionPrefix & sname, OnUnknown) - if field.alignment > 0: - result.addf "NIM_ALIGN($1) ", [rope(field.alignment)] # for importcpp'ed objects, we only need to set field.loc, but don't # have to recurse via 'getTypeDescAux'. And not doing so prevents problems # with heavily templatized C++ code: if not isImportedCppType(rectype): - let noAlias = if sfNoalias in field.flags: " NIM_NOALIAS" else: "" - let fieldType = field.loc.lode.typ.skipTypes(abstractInst) + var typ: Rope = "" + var isFlexArray = false + var initializer = "" if fieldType.kind == tyUncheckedArray: - result.addf("\t$1 $2[SEQ_DECL_SIZE];$n", - [getTypeDescAux(m, fieldType.elemType, check, dkField), sname]) + typ = getTypeDescAux(m, fieldType.elemType, check, dkField) + isFlexArray = true elif fieldType.kind == tySequence: # we need to use a weak dependency here for trecursive_table. - result.addf("\t$1$3 $2;$n", [getTypeDescWeak(m, field.loc.t, check, dkField), sname, noAlias]) - elif field.bitsize != 0: - result.addf("\t$1$4 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, rope($field.bitsize), noAlias]) + typ = getTypeDescWeak(m, field.loc.t, check, dkField) else: + typ = getTypeDescAux(m, field.loc.t, check, dkField) # don't use fieldType here because we need the # tyGenericInst for C++ template support let noInit = sfNoInit in field.flags or (field.typ.sym != nil and sfNoInit in field.typ.sym.flags) if not noInit and (fieldType.isOrHasImportedCppType() or hasCppCtor(m, field.owner.typ)): - var initializer = genCppInitializer(m, nil, fieldType) - result.addf("\t$1$3 $2$4;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias, initializer]) - else: - result.addf("\t$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias]) + var didGenTemp = false + initializer = genCppInitializer(m, nil, fieldType, didGenTemp) + result.addField(field, sname, typ, isFlexArray, initializer) else: internalError(m.config, n.info, "genRecordFieldsAux()") proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl:bool = false) -proc getRecordFields(m: BModule; typ: PType, check: var IntSet): Rope = - result = newRopeAppender() +proc addRecordFields(result: var Builder; m: BModule; typ: PType, check: var IntSet) = genRecordFieldsAux(m, typ.n, typ, check, result) if typ.itemId in m.g.graph.memberProcsPerType: let procs = m.g.graph.memberProcsPerType[typ.itemId] @@ -791,88 +776,34 @@ proc fillObjectFields*(m: BModule; typ: PType) = # sometimes generic objects are not consistently merged. We patch over # this fact here. var check = initIntSet() - discard getRecordFields(m, typ, check) + var ignored = newBuilder("") + addRecordFields(ignored, m, typ, check) proc mangleDynLibProc(sym: PSym): Rope -proc getRecordDescAux(m: BModule; typ: PType, name, baseType: Rope, - check: var IntSet, hasField:var bool): Rope = - result = "" - if typ.kind == tyObject: - if typ.baseClass == nil: - if lacksMTypeField(typ): - appcg(m, result, " {$n", []) - else: - if optTinyRtti in m.config.globalOptions: - appcg(m, result, " {$n#TNimTypeV2* m_type;$n", []) - else: - appcg(m, result, " {$n#TNimType* m_type;$n", []) - hasField = true - elif m.compileToCpp: - appcg(m, result, " : public $1 {$n", [baseType]) - if typ.isException and m.config.exc == excCpp: - when false: - appcg(m, result, "virtual void raise() { throw *this; }$n", []) # required for polymorphic exceptions - if typ.sym.magic == mException: - # Add cleanup destructor to Exception base class - appcg(m, result, "~$1();$n", [name]) - # define it out of the class body and into the procs section so we don't have to - # artificially forward-declare popCurrentExceptionEx (very VERY troublesome for HCR) - appcg(m, cfsProcs, "inline $1::~$1() {if(this->raiseId) #popCurrentExceptionEx(this->raiseId);}$n", [name]) - hasField = true - else: - appcg(m, result, " {$n $1 Sup;$n", [baseType]) - hasField = true - else: - result.addf(" {$n", [name]) - proc getRecordDesc(m: BModule; typ: PType, name: Rope, check: var IntSet): Rope = # declare the record: - var hasField = false - var structOrUnion: string - if tfPacked in typ.flags: - if hasAttribute in CC[m.config.cCompiler].props: - structOrUnion = structOrUnion(typ) & " __attribute__((__packed__))" - else: - structOrUnion = "#pragma pack(push, 1)\L" & structOrUnion(typ) - else: - structOrUnion = structOrUnion(typ) var baseType: string = "" if typ.baseClass != nil: baseType = getTypeDescAux(m, typ.baseClass.skipTypes(skipPtrs), check, dkField) if typ.sym == nil or sfCodegenDecl notin typ.sym.flags: - result = structOrUnion & " " & name - result.add(getRecordDescAux(m, typ, name, baseType, check, hasField)) - let desc = getRecordFields(m, typ, check) - if not hasField and typ.itemId notin m.g.graph.memberProcsPerType: - if desc == "": - result.add("\tchar dummy;\n") - elif typ.n.len == 1 and typ.n[0].kind == nkSym: - let field = typ.n[0].sym - let fieldType = field.typ.skipTypes(abstractInst) - if fieldType.kind == tyUncheckedArray: - result.add("\tchar dummy;\n") - result.add(desc) - else: - result.add(desc) - result.add("};\L") + result = newBuilder("") + result.addStruct(m, typ, name, baseType): + result.addRecordFields(m, typ, check) else: - let desc = getRecordFields(m, typ, check) + var desc = newBuilder("") + desc.addRecordFields(m, typ, check) result = runtimeFormat(typ.sym.cgDeclFrmt, [name, desc, baseType]) - if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props: - result.add "#pragma pack(pop)\L" proc getTupleDesc(m: BModule; typ: PType, name: Rope, check: var IntSet): Rope = - result = "$1 $2 {$n" % [structOrUnion(typ), name] - var desc: Rope = "" - for i, a in typ.ikids: - desc.addf("$1 Field$2;$n", - [getTypeDescAux(m, a, check, dkField), rope(i)]) - if desc == "": result.add("char dummy;\L") - else: result.add(desc) - result.add("};\L") + result = newBuilder("") + result.addStruct(m, typ, name, ""): + for i, a in typ.ikids: + result.addField( + name = "Field" & $i, + typ = getTypeDescAux(m, a, check, dkField)) proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool = # A helper proc for handling cppimport patterns, involving numeric @@ -929,12 +860,11 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes if t != origTyp and origTyp.sym != nil: useHeader(m, origTyp.sym) let sig = hashType(origTyp, m.config) - result = "" # todo move `result = getTypePre(m, t, sig)` here ? + result = getTypePre(m, t, sig) defer: # defer is the simplest in this case if isImportedType(t) and not m.typeABICache.containsOrIncl(sig): addAbiCheck(m, t, result) - result = getTypePre(m, t, sig) if result != "" and t.kind != tyOpenArray: excl(check, t.id) if kind == dkRefParam or kind == dkRefGenericParam and origTyp.kind == tyGenericInst: @@ -1037,18 +967,14 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes m.typeCache[sig] = result & seqStar(m) if not isImportedType(t): if skipTypes(t.elementType, typedescInst).kind != tyEmpty: - const - cppSeq = "struct $2 : #TGenericSeq {$n" - cSeq = "struct $2 {$n" & - " #TGenericSeq Sup;$n" - if m.compileToCpp: - appcg(m, m.s[cfsSeqTypes], - cppSeq & " $1 data[SEQ_DECL_SIZE];$n" & - "};$n", [getTypeDescAux(m, t.elementType, check, kind), result]) - else: - appcg(m, m.s[cfsSeqTypes], - cSeq & " $1 data[SEQ_DECL_SIZE];$n" & - "};$n", [getTypeDescAux(m, t.elementType, check, kind), result]) + var struct = newBuilder("") + let baseType = cgsymValue(m, "TGenericSeq") + struct.addSimpleStruct(m, name = result, baseType = baseType): + struct.addField( + name = "data", + typ = getTypeDescAux(m, t.elementType, check, kind), + isFlexArray = true) + m.s[cfsSeqTypes].add struct else: result = rope("TGenericSeq") result.add(seqStar(m)) @@ -1220,7 +1146,7 @@ proc parseVFunctionDecl(val: string; name, params, retType, superCall: var strin params = "(" & params & ")" -proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl : bool = false) = +proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl: bool = false) = assert sfCppMember * prc.flags != {} let isCtor = sfConstructor in prc.flags var check = initIntSet() @@ -1257,7 +1183,7 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = superCall = "" else: if not isCtor: - prc.loc.r = "$1$2(@)" % [memberOp, name] + prc.loc.snippet = "$1$2(@)" % [memberOp, name] elif superCall != "": superCall = " : " & superCall @@ -1278,7 +1204,7 @@ proc genProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false) # handle the 2 options for hotcodereloading codegen - function pointer # (instead of forward declaration) or header for function body with "_actual" postfix let asPtrStr = rope(if asPtr: "_PTR" else: "") - var name = prc.loc.r + var name = prc.loc.snippet if not asPtr and isReloadable(m, prc): name.add("_actual") # careful here! don't access ``prc.ast`` as that could reload large parts of @@ -1418,13 +1344,13 @@ proc genObjectFields(m: BModule; typ, origType: PType, n: PNode, expr: Rope; var tmp = discriminatorTableName(m, typ, field) var L = lengthOrd(m.config, field.typ) assert L > 0 - if field.loc.r == "": fillObjectFields(m, typ) + if field.loc.snippet == "": fillObjectFields(m, typ) if field.loc.t == nil: internalError(m.config, n.info, "genObjectFields") m.s[cfsTypeInit3].addf("$1.kind = 3;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & "$1.name = $5;$n" & "$1.sons = &$6[0];$n" & - "$1.len = $7;$n", [expr, getTypeDesc(m, origType, dkVar), field.loc.r, + "$1.len = $7;$n", [expr, getTypeDesc(m, origType, dkVar), field.loc.snippet, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s), tmp, rope(L)]) @@ -1456,13 +1382,13 @@ proc genObjectFields(m: BModule; typ, origType: PType, n: PNode, expr: Rope; # Do not produce code for void types if isEmptyType(field.typ): return if field.bitsize == 0: - if field.loc.r == "": fillObjectFields(m, typ) + if field.loc.snippet == "": fillObjectFields(m, typ) if field.loc.t == nil: internalError(m.config, n.info, "genObjectFields") m.s[cfsTypeInit3].addf("$1.kind = 1;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & "$1.name = $5;$n", [expr, getTypeDesc(m, origType, dkVar), - field.loc.r, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s)]) + field.loc.snippet, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s)]) else: internalError(m.config, n.info, "genObjectFields") proc genObjectInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) = @@ -1472,7 +1398,7 @@ proc genObjectInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo typeToString(typ)) genTypeInfoAux(m, typ, origType, name, info) var tmp = getNimNode(m) - if not isImportedType(typ): + if (not isImportedType(typ)) or tfCompleteStruct in typ.flags: genObjectFields(m, typ, origType, typ.n, tmp, info) m.s[cfsTypeInit3].addf("$1.node = &$2;$n", [tiNameForHcr(m, name), tmp]) var t = typ.baseClass @@ -1567,7 +1493,7 @@ include ccgtrav proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) = genProc(m, s) m.s[cfsTypeInit3].addf("$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n", - [result, s.loc.r]) + [result, s.loc.snippet]) proc declareNimType(m: BModule; name: string; str: Rope, module: int) = let nr = rope(name) @@ -1655,10 +1581,10 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result: let wrapper = generateRttiDestructor(m.g.graph, t, theProc.owner, attachedDestructor, theProc.info, m.idgen, theProc) genProc(m, wrapper) - result.add wrapper.loc.r + result.add wrapper.loc.snippet else: genProc(m, theProc) - result.add theProc.loc.r + result.add theProc.loc.snippet when false: if not canFormAcycle(m.g.graph, t) and op == attachedTrace: @@ -1706,7 +1632,7 @@ proc genVTable(seqs: seq[PSym]): string = result = "{" for i in 0..<seqs.len: if i > 0: result.add ", " - result.add "(void *) " & seqs[i].loc.r + result.add "(void *) " & seqs[i].loc.snippet result.add "}" proc genTypeInfoV2OldImpl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) = @@ -1861,8 +1787,7 @@ proc typeToC(t: PType): string = ## to be unique. let s = typeToString(t) result = newStringOfCap(s.len) - for i in 0..<s.len: - let c = s[i] + for c in s: case c of 'a'..'z': result.add c diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index da55df45d..c0e574186 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -90,6 +90,9 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool = if s.typ.sym != nil and sfForward in s.typ.sym.flags: # forwarded objects are *always* passed by pointers for consistency! result = true + elif s.typ.kind == tySink and conf.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcHooks}: + # bug #23354: + result = false elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3): result = true # requested anyway elif (tfFinal in pt.flags) and (pt[0] == nil): @@ -122,7 +125,7 @@ proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false): string = var name = s.name.s if makeUnique: name = makeUnique(m, s, name) - "N" & encodeName(s.owner.name.s) & encodeName(name) & "E" + "N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E" proc encodeType*(m: BModule; t: PType): string = result = "" diff --git a/compiler/cgen.nim b/compiler/cgen.nim index e4ba41614..091f5c842 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -15,7 +15,7 @@ import ccgutils, ropes, wordrecg, treetab, cgmeth, rodutils, renderer, cgendata, aliases, lowerings, ndi, lineinfos, pathutils, transf, - injectdestructors, astmsgs, modulepaths, backendpragmas, + injectdestructors, astmsgs, modulepaths, pushpoppragmas, mangleutils from expanddefaults import caseObjDefaultBranch @@ -33,6 +33,12 @@ import std/strutils except `%`, addf # collides with ropes.`%` from ic / ic import ModuleBackendFlag import std/[dynlib, math, tables, sets, os, intsets, hashes] +const + # we use some ASCII control characters to insert directives that will be converted to real code in a postprocessing pass + postprocessDirStart = '\1' + postprocessDirSep = '\31' + postprocessDirEnd = '\23' + when not declared(dynlib.libCandidates): proc libCandidates(s: string, dest: var seq[string]) = ## given a library name pattern `s` write possible library names to `dest`. @@ -66,8 +72,7 @@ proc findPendingModule(m: BModule, s: PSym): BModule = proc initLoc(k: TLocKind, lode: PNode, s: TStorageLoc, flags: TLocFlags = {}): TLoc = result = TLoc(k: k, storage: s, lode: lode, - r: "", flags: flags - ) + snippet: "", flags: flags) proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.inline.} = # fills the loc if it is not already initialized @@ -75,7 +80,7 @@ proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.i a.k = k a.lode = lode a.storage = s - if a.r == "": a.r = r + if a.snippet == "": a.snippet = r proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) {.inline.} = # fills the loc if it is not already initialized @@ -267,24 +272,28 @@ proc safeLineNm(info: TLineInfo): int = result = toLinenumber(info) if result < 0: result = 0 # negative numbers are not allowed in #line -proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) = +proc genPostprocessDir(field1, field2, field3: string): string = + result = postprocessDirStart & field1 & postprocessDirSep & field2 & postprocessDirSep & field3 & postprocessDirEnd + +proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; conf: ConfigRef) = assert line >= 0 if optLineDir in conf.options and line > 0: - r.addf("\n#line $2 $1\n", - [rope(makeSingleLineCString(filename)), rope(line)]) + if fileIdx == InvalidFileIdx: + r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n")) + else: + r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n")) -proc genCLineDir(r: var Rope, filename: string, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) = +proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) = assert line >= 0 if optLineDir in p.config.options and line > 0: - if lastFileIndex == info.fileIndex: - r.addf("\n#line $1\n", [rope(line)]) + if fileIdx == InvalidFileIdx: + r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n")) else: - r.addf("\n#line $2 $1\n", - [rope(makeSingleLineCString(filename)), rope(line)]) + r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n")) proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) = if optLineDir in conf.options: - genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf) + genCLineDir(r, info.fileIndex, info.safeLineNm, conf) proc freshLineInfo(p: BProc; info: TLineInfo): bool = if p.lastLineInfo.line != info.line or @@ -299,7 +308,7 @@ proc genCLineDir(r: var Rope, p: BProc, info: TLineInfo; conf: ConfigRef) = if optLineDir in conf.options: let lastFileIndex = p.lastLineInfo.fileIndex if freshLineInfo(p, info): - genCLineDir(r, toFullPath(conf, info), info.safeLineNm, p, info, lastFileIndex) + genCLineDir(r, info.fileIndex, info.safeLineNm, p, info, lastFileIndex) proc genLineDir(p: BProc, t: PNode) = if p == p.module.preInitProc: return @@ -310,16 +319,11 @@ proc genLineDir(p: BProc, t: PNode) = let lastFileIndex = p.lastLineInfo.fileIndex let freshLine = freshLineInfo(p, t.info) if freshLine: - genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p, t.info, lastFileIndex) + genCLineDir(p.s(cpsStmts), t.info.fileIndex, line, p, t.info, lastFileIndex) if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx: if freshLine: - if lastFileIndex == t.info.fileIndex: - linefmt(p, cpsStmts, "nimln_($1);", - [line]) - else: - linefmt(p, cpsStmts, "nimlf_($1, $2);", - [line, quotedFilename(p.config, t.info)]) + line(p, cpsStmts, genPostprocessDir("nimln", $line, $t.info.fileIndex.int32)) proc accessThreadLocalVar(p: BProc, s: PSym) proc emulatedThreadVars(conf: ConfigRef): bool {.inline.} @@ -336,15 +340,15 @@ proc getTempName(m: BModule): Rope = proc rdLoc(a: TLoc): Rope = # 'read' location (deref if indirect) if lfIndirect in a.flags: - result = "(*" & a.r & ")" + result = "(*" & a.snippet & ")" else: - result = a.r + result = a.snippet proc addRdLoc(a: TLoc; result: var Rope) = if lfIndirect in a.flags: - result.add "(*" & a.r & ")" + result.add "(*" & a.snippet & ")" else: - result.add a.r + result.add a.snippet proc lenField(p: BProc): Rope {.inline.} = result = rope(if p.module.compileToCpp: "len" else: "Sup.len") @@ -369,6 +373,7 @@ proc dataField(p: BProc): Rope = proc genProcPrototype(m: BModule, sym: PSym) +include cbuilder include ccgliterals include ccgtypes @@ -381,22 +386,22 @@ template mapTypeChooser(a: TLoc): TSymKind = mapTypeChooser(a.lode) proc addAddrLoc(conf: ConfigRef; a: TLoc; result: var Rope) = if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray: - result.add "(&" & a.r & ")" + result.add "(&" & a.snippet & ")" else: - result.add a.r + result.add a.snippet proc addrLoc(conf: ConfigRef; a: TLoc): Rope = if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray: - result = "(&" & a.r & ")" + result = "(&" & a.snippet & ")" else: - result = a.r + result = a.snippet proc byRefLoc(p: BProc; a: TLoc): Rope = if lfIndirect notin a.flags and mapType(p.config, a.t, mapTypeChooser(a) == skParam) != ctArray and not p.module.compileToCpp: - result = "(&" & a.r & ")" + result = "(&" & a.snippet & ")" else: - result = a.r + result = a.snippet proc rdCharLoc(a: TLoc): Rope = # read a location that may need a char-cast: @@ -407,7 +412,9 @@ proc rdCharLoc(a: TLoc): Rope = type TAssignmentFlag = enum needToCopy + needToCopySinkParam needTempForOpenArray + needAssignCall TAssignmentFlags = set[TAssignmentFlag] proc genObjConstr(p: BProc, e: PNode, d: var TLoc) @@ -476,9 +483,12 @@ include ccgreset proc resetLoc(p: BProc, loc: var TLoc) = let containsGcRef = optSeqDestructors notin p.config.globalOptions and containsGarbageCollectedRef(loc.t) let typ = skipTypes(loc.t, abstractVarRange) - if isImportedCppType(typ): return + if isImportedCppType(typ): + var didGenTemp = false + linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(loc), genCppInitializer(p.module, p, typ, didGenTemp)]) + return if optSeqDestructors in p.config.globalOptions and typ.kind in {tyString, tySequence}: - assert loc.r != "" + assert loc.snippet != "" let atyp = skipTypes(loc.t, abstractInst) if atyp.kind in {tyVar, tyLent}: @@ -488,7 +498,7 @@ proc resetLoc(p: BProc, loc: var TLoc) = elif not isComplexValueType(typ): if containsGcRef: var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack) - nilLoc.r = rope("NIM_NIL") + nilLoc.snippet = rope("NIM_NIL") genRefAssign(p, loc, nilLoc) else: linefmt(p, cpsStmts, "$1 = 0;$n", [rdLoc(loc)]) @@ -526,7 +536,7 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) = elif not isComplexValueType(typ): if containsGarbageCollectedRef(loc.t): var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack) - nilLoc.r = rope("NIM_NIL") + nilLoc.snippet = rope("NIM_NIL") genRefAssign(p, loc, nilLoc) else: linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc), @@ -554,13 +564,14 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = proc getTemp(p: BProc, t: PType, needsInit=false): TLoc = inc(p.labels) - result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, + result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, storage: OnStack, flags: {}) if p.module.compileToCpp and isOrHasImportedCppType(t): - linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.r, - genCppInitializer(p.module, p, t)]) + var didGenTemp = false + linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.snippet, + genCppInitializer(p.module, p, t, didGenTemp)]) else: - linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.r]) + linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.snippet]) constructLoc(p, result, not needsInit) when false: # XXX Introduce a compiler switch in order to detect these easily. @@ -573,16 +584,16 @@ proc getTemp(p: BProc, t: PType, needsInit=false): TLoc = proc getTempCpp(p: BProc, t: PType, value: Rope): TLoc = inc(p.labels) - result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, + result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, storage: OnStack, flags: {}) - linefmt(p, cpsStmts, "auto $1 = $2;$n", [result.r, value]) + linefmt(p, cpsStmts, "auto $1 = $2;$n", [result.snippet, value]) proc getIntTemp(p: BProc): TLoc = inc(p.labels) - result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, + result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, storage: OnStack, lode: lodeTyp getSysType(p.module.g.graph, unknownLineInfo, tyInt), flags: {}) - linefmt(p, cpsLocals, "NI $1;$n", [result.r]) + linefmt(p, cpsLocals, "NI $1;$n", [result.snippet]) proc localVarDecl(p: BProc; n: PNode): Rope = result = "" @@ -604,9 +615,9 @@ proc localVarDecl(p: BProc; n: PNode): Rope = if sfVolatile in s.flags: result.add(" volatile") if sfNoalias in s.flags: result.add(" NIM_NOALIAS") result.add(" ") - result.add(s.loc.r) + result.add(s.loc.snippet) else: - result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.r]) + result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.snippet]) proc assignLocalVar(p: BProc, n: PNode) = #assert(s.loc.k == locNone) # not yet assigned @@ -615,7 +626,8 @@ proc assignLocalVar(p: BProc, n: PNode) = let nl = if optLineDir in p.config.options: "" else: "\n" var decl = localVarDecl(p, n) if p.module.compileToCpp and isOrHasImportedCppType(n.typ): - decl.add genCppInitializer(p.module, p, n.typ) + var didGenTemp = false + decl.add genCppInitializer(p.module, p, n.typ, didGenTemp) decl.add ";" & nl line(p, cpsLocals, decl) @@ -646,22 +658,11 @@ proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) = if sfNoalias in s.flags: decl.add(" NIM_NOALIAS") else: if value != "": - decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.r, value]) + decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.snippet, value]) else: - decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r]) + decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.snippet]) -proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope) - -proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode) = - let s = vn.sym - fillBackendName(p.module, s) - fillLoc(s.loc, locGlobalVar, vn, OnHeap) - var decl: Rope = "" - let td = getTypeDesc(p.module, vn.sym.typ, dkVar) - genGlobalVarDecl(p, vn, td, "", decl) - decl.add " " & $s.loc.r - genCppVarForCtor(p, value, decl) - p.module.s[cfsVars].add decl +proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope; didGenTemp: var bool) proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = let s = n.sym @@ -675,7 +676,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = if q != nil and not containsOrIncl(q.declaredThings, s.id): varInDynamicLib(q, s) else: - s.loc.r = mangleDynLibProc(s) + s.loc.snippet = mangleDynLibProc(s) if value != "": internalError(p.config, n.info, ".dynlib variables cannot have a value") return @@ -706,19 +707,31 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = # [^0]: https://en.cppreference.com/w/cpp/language/aggregate_initialization # [^1]: https://cplusplus.github.io/CWG/issues/1518.html # [^2]: https://eel.is/c++draft/over.match.ctor - decl.addf(" $1;$n", [s.loc.r]) + decl.addf(" $1;$n", [s.loc.snippet]) else: - decl.addf(" $1 = $2;$n", [s.loc.r, value]) + decl.addf(" $1 = $2;$n", [s.loc.snippet, value]) else: - decl.addf(" $1;$n", [s.loc.r]) + decl.addf(" $1;$n", [s.loc.snippet]) p.module.s[cfsVars].add(decl) if p.withinLoop > 0 and value == "": # fixes tests/run/tzeroarray: resetLoc(p, s.loc) +proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode; didGenTemp: var bool) = + let s = vn.sym + fillBackendName(p.module, s) + fillLoc(s.loc, locGlobalVar, vn, OnHeap) + var decl: Rope = "" + let td = getTypeDesc(p.module, vn.sym.typ, dkVar) + genGlobalVarDecl(p, vn, td, "", decl) + decl.add " " & $s.loc.snippet + genCppVarForCtor(p, value, decl, didGenTemp) + if didGenTemp: return # generated in the caller + p.module.s[cfsVars].add decl + proc assignParam(p: BProc, s: PSym, retType: PType) = - assert(s.loc.r != "") + assert(s.loc.snippet != "") scopeMangledParam(p, s) proc fillProcLoc(m: BModule; n: PNode) = @@ -744,6 +757,7 @@ proc intLiteral(i: BiggestInt; result: var Rope) proc genLiteral(p: BProc, n: PNode; result: var Rope) proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope; argsCounter: var int) proc raiseExit(p: BProc) +proc raiseExitCleanup(p: BProc, destroy: string) proc initLocExpr(p: BProc, e: PNode, flags: TLocFlags = {}): TLoc = result = initLoc(locNone, e, OnUnknown, flags) @@ -770,15 +784,11 @@ $1define nimfr_(proc, file) \ TFrame FR_; \ FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; #nimFrame(&FR_); - $1define nimfrs_(proc, file, slots, length) \ - struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename;NI len;VarSlot s[slots];} FR_; \ - FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; #nimFrame((TFrame*)&FR_); +$1define nimln_(n) \ + FR_.line = n; - $1define nimln_(n) \ - FR_.line = n; - - $1define nimlf_(n, file) \ - FR_.line = n; FR_.filename = file; +$1define nimlf_(n, file) \ + FR_.line = n; FR_.filename = file; """ if p.module.s[cfsFrameDefines].len == 0: @@ -842,7 +852,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) = p.options.excl optStackTrace p.flags.incl nimErrorFlagDisabled var dest: TLoc = initLoc(locTemp, lib.path, OnStack) - dest.r = getTempName(m) + dest.snippet = getTempName(m) appcg(m, m.s[cfsDynLibInit],"$1 $2;$n", [getTypeDesc(m, lib.path.typ, dkVar), rdLoc(dest)]) expr(p, lib.path, dest) @@ -860,7 +870,7 @@ proc mangleDynLibProc(sym: PSym): Rope = # we have to build this as a single rope in order not to trip the # optimization in genInfixCall, see test tests/cpp/t8241.nim if sfCompilerProc in sym.flags: - # NOTE: sym.loc.r is the external name! + # NOTE: sym.loc.snippet is the external name! result = rope(sym.name.s) else: result = rope(strutils.`%`("Dl_$1_", $sym.id)) @@ -868,10 +878,10 @@ proc mangleDynLibProc(sym: PSym): Rope = proc symInDynamicLib(m: BModule, sym: PSym) = var lib = sym.annex let isCall = isGetProcAddr(lib) - var extname = sym.loc.r + var extname = sym.loc.snippet if not isCall: loadDynamicLib(m, lib) var tmp = mangleDynLibProc(sym) - sym.loc.r = tmp # from now on we only need the internal name + sym.loc.snippet = tmp # from now on we only need the internal name sym.typ.sym = nil # generate a new name inc(m.labels, 2) if isCall: @@ -898,24 +908,24 @@ proc symInDynamicLib(m: BModule, sym: PSym) = appcg(m, m.s[cfsDynLibInit], "\t$1 = ($2) #nimGetProcAddr($3, $4);$n", [tmp, getTypeDesc(m, sym.typ, dkVar), lib.name, makeCString($extname)]) - m.s[cfsVars].addf("$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t, dkVar)]) + m.s[cfsVars].addf("$2 $1;$n", [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar)]) proc varInDynamicLib(m: BModule, sym: PSym) = var lib = sym.annex - var extname = sym.loc.r + var extname = sym.loc.snippet loadDynamicLib(m, lib) incl(sym.loc.flags, lfIndirect) var tmp = mangleDynLibProc(sym) - sym.loc.r = tmp # from now on we only need the internal name + sym.loc.snippet = tmp # from now on we only need the internal name inc(m.labels, 2) appcg(m, m.s[cfsDynLibInit], "$1 = ($2*) #nimGetProcAddr($3, $4);$n", [tmp, getTypeDesc(m, sym.typ, dkVar), lib.name, makeCString($extname)]) m.s[cfsVars].addf("$2* $1;$n", - [sym.loc.r, getTypeDesc(m, sym.loc.t, dkVar)]) + [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar)]) proc symInDynamicLibPartial(m: BModule, sym: PSym) = - sym.loc.r = mangleDynLibProc(sym) + sym.loc.snippet = mangleDynLibProc(sym) sym.typ.sym = nil # generate a new name proc cgsymImpl(m: BModule; sym: PSym) {.inline.} = @@ -938,7 +948,7 @@ proc cgsymValue(m: BModule, name: string): Rope = cgsymImpl m, sym else: rawMessage(m.config, errGenerated, "system module needs: " & name) - result = sym.loc.r + result = sym.loc.snippet if m.hcrOn and sym != nil and sym.kind in {skProc..skIterator}: result.addActualSuffixForHCR(m.module, sym) @@ -1065,7 +1075,11 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum = if result != Unknown: return result of nkAsgn, nkFastAsgn, nkSinkAsgn: if n[0].kind == nkSym and n[0].sym.kind == skResult: - if not containsResult(n[1]): result = InitSkippable + if not containsResult(n[1]): + if allPathsAsgnResult(p, n[1]) == InitRequired: + result = InitRequired + else: + result = InitSkippable else: result = InitRequired elif containsResult(n): result = InitRequired @@ -1143,6 +1157,10 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum = allPathsInBranch(n[i]) of nkRaiseStmt: result = InitRequired + of nkChckRangeF, nkChckRange64, nkChckRange: + # TODO: more checks might need to be covered like overflow, indexDefect etc. + # bug #22852 + result = InitRequired else: for i in 0..<n.safeLen: allPathsInBranch(n[i]) @@ -1197,7 +1215,7 @@ proc genProcAux*(m: BModule, prc: PSym) = else: # declare the result symbol: assignLocalVar(p, resNode) - assert(res.loc.r != "") + assert(res.loc.snippet != "") if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and allPathsAsgnResult(p, procBody) == InitSkippable: # In an ideal world the codegen could rely on injectdestructors doing its job properly @@ -1209,7 +1227,7 @@ proc genProcAux*(m: BModule, prc: PSym) = elif sfConstructor in prc.flags: resNode.sym.loc.flags.incl lfIndirect fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap) - prc.loc.r = getTypeDesc(m, resNode.sym.loc.t, dkVar) + prc.loc.snippet = getTypeDesc(m, resNode.sym.loc.t, dkVar) else: fillResult(p.config, resNode, prc.typ) assignParam(p, res, prc.typ.returnType) @@ -1274,7 +1292,7 @@ proc genProcAux*(m: BModule, prc: PSym) = m.s[cfsProcs].add(generatedProc) if isReloadable(m, prc): m.s[cfsDynLibInit].addf("\t$1 = ($3) hcrRegisterProc($4, \"$1\", (void*)$2);$n", - [prc.loc.r, prc.loc.r & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)]) + [prc.loc.snippet, prc.loc.snippet & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)]) proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} = result = (sfCompileToCpp in m.module.flags and @@ -1334,7 +1352,7 @@ proc genProcNoForward(m: BModule, prc: PSym) = # the hcr dynlib (also put it in the DynLibInit section - right after it gets loaded) if isReloadable(q, prc): q.s[cfsDynLibInit].addf("\t$1 = ($2) hcrRegisterProc($3, \"$1\", (void*)$1);$n", - [prc.loc.r, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)]) + [prc.loc.snippet, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)]) else: symInDynamicLibPartial(m, prc) elif prc.typ.callConv == ccInline: @@ -1351,8 +1369,8 @@ proc genProcNoForward(m: BModule, prc: PSym) = #elif {sfExportc, sfImportc} * prc.flags == {}: # # reset name to restore consistency in case of hashing collisions: # echo "resetting ", prc.id, " by ", m.module.name.s - # prc.loc.r = nil - # prc.loc.r = mangleName(m, prc) + # prc.loc.snippet = nil + # prc.loc.snippet = mangleName(m, prc) genProcPrototype(m, prc) genProcAux(m, prc) elif sfImportc notin prc.flags: @@ -1364,7 +1382,7 @@ proc genProcNoForward(m: BModule, prc: PSym) = if isReloadable(m, prc) and prc.id notin m.declaredProtos and q != nil and q.module.id != m.module.id: m.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n", - [prc.loc.r, getProcTypeCast(m, prc), getModuleDllPath(m, prc)]) + [prc.loc.snippet, getProcTypeCast(m, prc), getModuleDllPath(m, prc)]) genProcPrototype(m, prc) if q != nil and not containsOrIncl(q.declaredThings, prc.id): # make sure there is a "prototype" in the external module @@ -1417,7 +1435,7 @@ proc genVarPrototype(m: BModule, n: PNode) = return if sym.owner.id != m.module.id: # else we already have the symbol generated! - assert(sym.loc.r != "") + assert(sym.loc.snippet != "") incl(m.declaredThings, sym.id) if sfThread in sym.flags: declareThreadVar(m, sym, true) @@ -1431,9 +1449,9 @@ proc genVarPrototype(m: BModule, n: PNode) = if sfRegister in sym.flags: m.s[cfsVars].add(" register") if sfVolatile in sym.flags: m.s[cfsVars].add(" volatile") if sfNoalias in sym.flags: m.s[cfsVars].add(" NIM_NOALIAS") - m.s[cfsVars].addf(" $1;$n", [sym.loc.r]) + m.s[cfsVars].addf(" $1;$n", [sym.loc.snippet]) if m.hcrOn: m.initProc.procSec(cpsLocals).addf( - "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r, + "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(m, sym)]) proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} = @@ -1466,15 +1484,13 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope = proc getSomeNameForModule(conf: ConfigRef, filename: AbsoluteFile): Rope = ## Returns a mangled module name. - result = "" - result.add mangleModuleName(conf, filename).mangle + result = mangleModuleName(conf, filename).mangle proc getSomeNameForModule(m: BModule): Rope = ## Returns a mangled module name. assert m.module.kind == skModule assert m.module.owner.kind == skPackage - result = "" - result.add mangleModuleName(m.g.config, m.filename).mangle + result = mangleModuleName(m.g.config, m.filename).mangle proc getSomeInitName(m: BModule, suffix: string): Rope = if not m.hcrOn: @@ -1810,7 +1826,7 @@ proc genDatInitCode(m: BModule) = # we don't want to break into such init code - could happen if a line # directive from a function written by the user spills after itself - genCLineDir(prc, "generated_not_to_break_here", 999999, m.config) + genCLineDir(prc, InvalidFileIdx, 999999, m.config) for i in cfsTypeInit1..cfsDynLibInit: if m.s[i].len != 0: @@ -1832,11 +1848,11 @@ proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): R var extname = prefix & sym var tmp = mangleDynLibProc(prc) - prc.loc.r = tmp + prc.loc.snippet = tmp prc.typ.sym = nil if not containsOrIncl(m.declaredThings, prc.id): - m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, getTypeDesc(m, prc.loc.t, dkVar)]) + m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.snippet, getTypeDesc(m, prc.loc.t, dkVar)]) result = "\t$1 = ($2) $3($4, $5);$n" % [tmp, getTypeDesc(m, prc.typ, dkVar), getProcFunc.rope, handle.rope, makeCString(prefix & sym)] @@ -1851,7 +1867,7 @@ proc genInitCode(m: BModule) = [rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), initname] # we don't want to break into such init code - could happen if a line # directive from a function written by the user spills after itself - genCLineDir(prc, "generated_not_to_break_here", 999999, m.config) + genCLineDir(prc, InvalidFileIdx, 999999, m.config) if m.typeNodes > 0: if m.hcrOn: appcg(m, m.s[cfsTypeInit1], "\t#TNimNode* $1;$N", [m.typeNodesName]) @@ -1921,9 +1937,6 @@ proc genInitCode(m: BModule) = if optStackTrace in m.initProc.options and preventStackTrace notin m.flags: prc.add(deinitFrame(m.initProc)) - elif m.config.exc == excGoto: - if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil: - m.appcg(prc, "\t#nimTestErrorFlag();$n", []) prc.addf("}$N", []) @@ -1966,6 +1979,40 @@ proc genInitCode(m: BModule) = registerModuleToMain(m.g, m) +proc postprocessCode(conf: ConfigRef, r: var Rope) = + # find the first directive + var f = r.find(postprocessDirStart) + if f == -1: + return + + var + nimlnDirLastF = "" + + var res: Rope = r.substr(0, f - 1) + while f != -1: + var + e = r.find(postprocessDirEnd, f + 1) + dir = r.substr(f + 1, e - 1).split(postprocessDirSep) + case dir[0] + of "nimln": + if dir[2] == nimlnDirLastF: + res.add("nimln_(" & dir[1] & ");") + else: + res.add("nimlf_(" & dir[1] & ", " & quotedFilename(conf, dir[2].parseInt.FileIndex) & ");") + nimlnDirLastF = dir[2] + else: + raiseAssert "unexpected postprocess directive" + + # find the next directive + f = r.find(postprocessDirStart, e + 1) + # copy the code until the next directive + if f != -1: + res.add(r.substr(e + 1, f - 1)) + else: + res.add(r.substr(e + 1)) + + r = res + proc genModule(m: BModule, cfile: Cfile): Rope = var moduleIsEmpty = true @@ -1994,9 +2041,17 @@ proc genModule(m: BModule, cfile: Cfile): Rope = if m.config.cppCustomNamespace.len > 0: closeNamespaceNim(result) + if optLineDir in m.config.options: + var srcFileDefs = "" + for fi in 0..m.config.m.fileInfos.high: + srcFileDefs.add("#define FX_" & $fi & " " & makeSingleLineCString(toFullPath(m.config, fi.FileIndex)) & "\n") + result = srcFileDefs & result + if moduleIsEmpty: result = "" + postprocessCode(m.config, result) + proc initProcOptions(m: BModule): TOptions = let opts = m.config.options if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts @@ -2202,7 +2257,7 @@ proc generateLibraryDestroyGlobals(graph: ModuleGraph; m: BModule; body: PNode; result.typ = newProcType(m.module.info, m.idgen, m.module.owner) result.typ.callConv = ccCDecl incl result.flags, sfExportc - result.loc.r = "NimDestroyGlobals" + result.loc.snippet = "NimDestroyGlobals" if isDynlib: incl(result.loc.flags, lfExportLib) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 9f71a8292..5368e9dc7 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -88,7 +88,7 @@ type options*: TOptions # options that should be used for code # generation; this is the same as prc.options # unless prc == nil - optionsStack*: seq[TOptions] + optionsStack*: seq[(TOptions, TNoteKinds)] module*: BModule # used to prevent excessive parameter passing withinLoop*: int # > 0 if we are within a loop splitDecls*: int # > 0 if we are in some context for C++ that diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index b132c6049..ca97d0494 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -133,7 +133,7 @@ proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym = if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall disp.ast = copyTree(s.ast) disp.ast[bodyPos] = newNodeI(nkEmpty, s.info) - disp.loc.r = "" + disp.loc.snippet = "" if s.typ.returnType != nil: if disp.ast.len > resultPos: disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen) diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index c1f525488..8bdd04ca7 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -36,13 +36,6 @@ # else: # return -# The transformation should play well with lambdalifting, however depending -# on situation, it can be called either before or after lambdalifting -# transformation. As such we behave slightly differently, when accessing -# iterator state, or using temp variables. If lambdalifting did not happen, -# we just create local variables, so that they will be lifted further on. -# Otherwise, we utilize existing env, created by lambdalifting. - # Lambdalifting treats :state variable specially, it should always end up # as the first field in env. Currently C codegen depends on this behavior. @@ -151,7 +144,6 @@ type Ctx = object g: ModuleGraph fn: PSym - stateVarSym: PSym # :state variable. nil if env already introduced by lambdalifting tmpResultSym: PSym # Used when we return, but finally has to interfere unrollFinallySym: PSym # Indicates that we're unrolling finally states (either exception happened or premature return) curExcSym: PSym # Current exception @@ -168,16 +160,23 @@ type nearestFinally: int # Index of the nearest finally block. For try/except it # is their finally. For finally it is parent finally. Otherwise -1 idgen: IdGenerator + varStates: Table[ItemId, int] # Used to detect if local variable belongs to multiple states + stateVarSym: PSym # :state variable. nil if env already introduced by lambdalifting + # remove if -d:nimOptIters is default, treating it as always nil + nimOptItersEnabled: bool # tracks if -d:nimOptIters is enabled + # should be default when issues are fixed, see #24094 const nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt, nkCommentStmt, nkMixinStmt, nkBindStmt} + procDefs emptyStateLabel = -1 + localNotSeen = -1 + localRequiresLifting = -2 proc newStateAccess(ctx: var Ctx): PNode = if ctx.stateVarSym.isNil: result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), - getStateField(ctx.g, ctx.fn), ctx.fn.info) + getStateField(ctx.g, ctx.fn), ctx.fn.info) else: result = newSymNode(ctx.stateVarSym) @@ -195,7 +194,7 @@ proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym = result = newSym(skVar, getIdent(ctx.g.cache, name), ctx.idgen, ctx.fn, ctx.fn.info) result.typ = typ result.flags.incl sfNoInit - assert(not typ.isNil) + assert(not typ.isNil, "Env var needs a type") if not ctx.stateVarSym.isNil: # We haven't gone through labmda lifting yet, so just create a local var, @@ -214,6 +213,9 @@ proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode = else: result = newSymNode(s) +proc newTempVarAccess(ctx: Ctx, s: PSym): PNode = + result = newSymNode(s, ctx.fn.info) + proc newTmpResultAccess(ctx: var Ctx): PNode = if ctx.tmpResultSym.isNil: ctx.tmpResultSym = ctx.newEnvVar(":tmpResult", ctx.fn.typ.returnType) @@ -255,9 +257,26 @@ proc addGotoOut(n: PNode, gotoOut: PNode): PNode = if result.len == 0 or result[^1].kind != nkGotoState: result.add(gotoOut) -proc newTempVar(ctx: var Ctx, typ: PType): PSym = - result = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ) +proc newTempVarDef(ctx: Ctx, s: PSym, initialValue: PNode): PNode = + var v = initialValue + if v == nil: + v = ctx.g.emptyNode + newTree(nkVarSection, newTree(nkIdentDefs, newSymNode(s), ctx.g.emptyNode, v)) + +proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode + +proc newTempVar(ctx: var Ctx, typ: PType, parent: PNode, initialValue: PNode = nil): PSym = + if ctx.nimOptItersEnabled: + result = newSym(skVar, getIdent(ctx.g.cache, ":tmpSlLower" & $ctx.tempVarId), ctx.idgen, ctx.fn, ctx.fn.info) + else: + result = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ) inc ctx.tempVarId + result.typ = typ + assert(not typ.isNil, "Temp var needs a type") + if ctx.nimOptItersEnabled: + parent.add(ctx.newTempVarDef(result, initialValue)) + elif initialValue != nil: + parent.add(ctx.newEnvVarAsgn(result, initialValue)) proc hasYields(n: PNode): bool = # TODO: This is very inefficient. It traverses the node, looking for nkYieldStmt. @@ -429,8 +448,15 @@ proc exprToStmtList(n: PNode): tuple[s, res: PNode] = result.res = n +proc newTempVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode = + if isEmptyType(v.typ): + result = v + else: + result = newTree(nkFastAsgn, ctx.newTempVarAccess(s), v) + result.info = v.info proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode = + # unused with -d:nimOptIters if isEmptyType(v.typ): result = v else: @@ -438,10 +464,13 @@ proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode = result.info = v.info proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) = + var input = input if input.kind == nkStmtListExpr: let (st, res) = exprToStmtList(input) output.add(st) - output.add(ctx.newEnvVarAsgn(sym, res)) + input = res + if ctx.nimOptItersEnabled: + output.add(ctx.newTempVarAsgn(sym, input)) else: output.add(ctx.newEnvVarAsgn(sym, input)) @@ -457,6 +486,12 @@ proc boolLit(g: ModuleGraph; info: TLineInfo; value: bool): PNode = result = newIntLit(g, info, ord value) result.typ = getSysType(g, info, tyBool) +proc captureVar(c: var Ctx, s: PSym) = + if c.varStates.getOrDefault(s.itemId) != localRequiresLifting: + c.varStates[s.itemId] = localRequiresLifting # Mark this variable for lifting + let e = getEnvParam(c.fn) + discard addField(e.typ.elementType, s, c.g.cache, c.idgen) + proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = result = n case n.kind @@ -513,9 +548,9 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = var tmp: PSym = nil let isExpr = not isEmptyType(n.typ) if isExpr: - tmp = ctx.newTempVar(n.typ) result = newNodeI(nkStmtListExpr, n.info) result.typ = n.typ + tmp = ctx.newTempVar(n.typ, result) else: result = newNodeI(nkStmtList, n.info) @@ -566,7 +601,11 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = else: internalError(ctx.g.config, "lowerStmtListExpr(nkIf): " & $branch.kind) - if isExpr: result.add(ctx.newEnvVarAccess(tmp)) + if isExpr: + if ctx.nimOptItersEnabled: + result.add(ctx.newTempVarAccess(tmp)) + else: + result.add(ctx.newEnvVarAccess(tmp)) of nkTryStmt, nkHiddenTryStmt: var ns = false @@ -580,7 +619,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = if isExpr: result = newNodeI(nkStmtListExpr, n.info) result.typ = n.typ - let tmp = ctx.newTempVar(n.typ) + let tmp = ctx.newTempVar(n.typ, result) n[0] = ctx.convertExprBodyToAsgn(n[0], tmp) for i in 1..<n.len: @@ -596,7 +635,10 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = else: internalError(ctx.g.config, "lowerStmtListExpr(nkTryStmt): " & $branch.kind) result.add(n) - result.add(ctx.newEnvVarAccess(tmp)) + if ctx.nimOptItersEnabled: + result.add(ctx.newTempVarAccess(tmp)) + else: + result.add(ctx.newEnvVarAccess(tmp)) of nkCaseStmt: var ns = false @@ -609,9 +651,9 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = let isExpr = not isEmptyType(n.typ) if isExpr: - let tmp = ctx.newTempVar(n.typ) result = newNodeI(nkStmtListExpr, n.info) result.typ = n.typ + let tmp = ctx.newTempVar(n.typ, result) if n[0].kind == nkStmtListExpr: let (st, ex) = exprToStmtList(n[0]) @@ -628,7 +670,10 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = else: internalError(ctx.g.config, "lowerStmtListExpr(nkCaseStmt): " & $branch.kind) result.add(n) - result.add(ctx.newEnvVarAccess(tmp)) + if ctx.nimOptItersEnabled: + result.add(ctx.newTempVarAccess(tmp)) + else: + result.add(ctx.newEnvVarAccess(tmp)) elif n[0].kind == nkStmtListExpr: result = newNodeI(nkStmtList, n.info) let (st, ex) = exprToStmtList(n[0]) @@ -658,10 +703,14 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = result.add(st) cond = ex - let tmp = ctx.newTempVar(cond.typ) - result.add(ctx.newEnvVarAsgn(tmp, cond)) + let tmp = ctx.newTempVar(cond.typ, result, cond) + # result.add(ctx.newTempVarAsgn(tmp, cond)) - var check = ctx.newEnvVarAccess(tmp) + var check: PNode + if ctx.nimOptItersEnabled: + check = ctx.newTempVarAccess(tmp) + else: + check = ctx.newEnvVarAccess(tmp) if n[0].sym.magic == mOr: check = ctx.g.newNotCall(check) @@ -671,12 +720,18 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = let (st, ex) = exprToStmtList(cond) ifBody.add(st) cond = ex - ifBody.add(ctx.newEnvVarAsgn(tmp, cond)) + if ctx.nimOptItersEnabled: + ifBody.add(ctx.newTempVarAsgn(tmp, cond)) + else: + ifBody.add(ctx.newEnvVarAsgn(tmp, cond)) let ifBranch = newTree(nkElifBranch, check, ifBody) let ifNode = newTree(nkIfStmt, ifBranch) result.add(ifNode) - result.add(ctx.newEnvVarAccess(tmp)) + if ctx.nimOptItersEnabled: + result.add(ctx.newTempVarAccess(tmp)) + else: + result.add(ctx.newEnvVarAccess(tmp)) else: for i in 0..<n.len: if n[i].kind == nkStmtListExpr: @@ -685,9 +740,12 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = n[i] = ex if n[i].kind in nkCallKinds: # XXX: This should better be some sort of side effect tracking - let tmp = ctx.newTempVar(n[i].typ) - result.add(ctx.newEnvVarAsgn(tmp, n[i])) - n[i] = ctx.newEnvVarAccess(tmp) + let tmp = ctx.newTempVar(n[i].typ, result, n[i]) + # result.add(ctx.newTempVarAsgn(tmp, n[i])) + if ctx.nimOptItersEnabled: + n[i] = ctx.newTempVarAccess(tmp) + else: + n[i] = ctx.newEnvVarAccess(tmp) result.add(n) @@ -703,6 +761,12 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = let (st, ex) = exprToStmtList(c[^1]) result.add(st) c[^1] = ex + for i in 0 .. c.len - 3: + if c[i].kind == nkSym: + let s = c[i].sym + if sfForceLift in s.flags: + ctx.captureVar(s) + result.add(varSect) of nkDiscardStmt, nkReturnStmt, nkRaiseStmt: @@ -1433,15 +1497,77 @@ proc preprocess(c: var PreprocessContext; n: PNode): PNode = for i in 0 ..< n.len: result[i] = preprocess(c, n[i]) +proc detectCapturedVars(c: var Ctx, n: PNode, stateIdx: int) = + case n.kind + of nkSym: + let s = n.sym + if s.kind in {skResult, skVar, skLet, skForVar, skTemp} and sfGlobal notin s.flags and s.owner == c.fn: + let vs = c.varStates.getOrDefault(s.itemId, localNotSeen) + if vs == localNotSeen: # First seing this variable + c.varStates[s.itemId] = stateIdx + elif vs == localRequiresLifting: + discard # Sym already marked + elif vs != stateIdx: + c.captureVar(s) + of nkReturnStmt: + if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}: + # we have a `result = result` expression produced by the closure + # transform, let's not touch the LHS in order to make the lifting pass + # correct when `result` is lifted + detectCapturedVars(c, n[0][1], stateIdx) + else: + detectCapturedVars(c, n[0], stateIdx) + else: + for i in 0 ..< n.safeLen: + detectCapturedVars(c, n[i], stateIdx) + +proc detectCapturedVars(c: var Ctx) = + for i, s in c.states: + detectCapturedVars(c, s.body, i) + +proc liftLocals(c: var Ctx, n: PNode): PNode = + result = n + case n.kind + of nkSym: + let s = n.sym + if c.varStates.getOrDefault(s.itemId) == localRequiresLifting: + # lift + let e = getEnvParam(c.fn) + let field = getFieldFromObj(e.typ.elementType, s) + assert(field != nil) + result = rawIndirectAccess(newSymNode(e), field, n.info) + # elif c.varStates.getOrDefault(s.itemId, localNotSeen) != localNotSeen: + # echo "Not lifting ", s.name.s + + of nkReturnStmt: + if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}: + # we have a `result = result` expression produced by the closure + # transform, let's not touch the LHS in order to make the lifting pass + # correct when `result` is lifted + n[0][1] = liftLocals(c, n[0][1]) + else: + n[0] = liftLocals(c, n[0]) + else: + for i in 0 ..< n.safeLen: + n[i] = liftLocals(c, n[i]) + proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: PNode): PNode = - var ctx = Ctx(g: g, fn: fn, idgen: idgen) + var ctx = Ctx(g: g, fn: fn, idgen: idgen, + # should be default when issues are fixed, see #24094: + nimOptItersEnabled: isDefined(g.config, "nimOptIters")) if getEnvParam(fn).isNil: - # Lambda lifting was not done yet. Use temporary :state sym, which will - # be handled specially by lambda lifting. Local temp vars (if needed) - # should follow the same logic. - ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), idgen, fn, fn.info) - ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen) + if ctx.nimOptItersEnabled: + # The transformation should always happen after at least partial lambdalifting + # is performed, so that the closure iter environment is always created upfront. + doAssert(false, "Env param not created before iter transformation") + else: + # Lambda lifting was not done yet. Use temporary :state sym, which will + # be handled specially by lambda lifting. Local temp vars (if needed) + # should follow the same logic. + ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), idgen, fn, fn.info) + ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen) + ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), idgen, fn, fn.info) var pc = PreprocessContext(finallys: @[], config: g.config, idgen: idgen) var n = preprocess(pc, n.toStmtList) @@ -1466,6 +1592,11 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: let caseDispatcher = newTreeI(nkCaseStmt, n.info, ctx.newStateAccess()) + if ctx.nimOptItersEnabled: + # Lamdalifting will not touch our locals, it is our responsibility to lift those that + # need it. + detectCapturedVars(ctx) + for s in ctx.states: let body = ctx.transformStateAssignments(s.body) caseDispatcher.add newTreeI(nkOfBranch, body.info, g.newIntLit(body.info, s.label), body) @@ -1473,6 +1604,8 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: caseDispatcher.add newTreeI(nkElse, n.info, newTreeI(nkReturnStmt, n.info, g.emptyNode)) result = wrapIntoStateLoop(ctx, caseDispatcher) + if ctx.nimOptItersEnabled: + result = liftLocals(ctx, result) when false: echo "TRANSFORM TO STATES: " @@ -1481,3 +1614,5 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: echo "exception table:" for i, e in ctx.exceptionTable: echo i, " -> ", e + + echo "ENV: ", renderTree(getEnvParam(fn).typ.elementType.n) diff --git a/compiler/commands.nim b/compiler/commands.nim index 176b73044..cbf915ca6 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -24,7 +24,7 @@ bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep") bootSwitch(usedGoGC, defined(gogc), "--gc:go") bootSwitch(usedNoGC, defined(nogc), "--gc:none") -import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs] +import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs, enumutils] import msgs, options, nversion, condsyms, extccomp, platform, wordrecg, nimblecmd, lineinfos, pathutils @@ -248,6 +248,7 @@ const errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found" errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found" errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found" + errInvalidFeatureButXFound = Feature.toSeq.map(proc(val:Feature): string = "'$1'" % $val).join(", ") & " expected, but '$1' found" template warningOptionNoop(switch: string) = warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch) @@ -303,6 +304,12 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo else: result = false localError(conf, info, errInvalidExceptionSystem % arg) + of "experimental": + try: + result = conf.features.contains parseEnum[Feature](arg) + except ValueError: + result = false + localError(conf, info, errInvalidFeatureButXFound % arg) else: result = false invalidCmdLineOption(conf, passCmd1, switch, info) @@ -462,7 +469,6 @@ proc handleCmdInput*(conf: ConfigRef) = proc parseCommand*(command: string): Command = case command.normalize of "c", "cc", "compile", "compiletoc": cmdCompileToC - of "nir": cmdCompileToNir of "cpp", "compiletocpp": cmdCompileToCpp of "objc", "compiletooc": cmdCompileToOC of "js", "compiletojs": cmdCompileToJS @@ -500,7 +506,6 @@ proc setCmd*(conf: ConfigRef, cmd: Command) = of cmdCompileToCpp: conf.backend = backendCpp of cmdCompileToOC: conf.backend = backendObjc of cmdCompileToJS: conf.backend = backendJs - of cmdCompileToNir: conf.backend = backendNir else: discard proc setCommandEarly*(conf: ConfigRef, command: string) = diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 085576c6b..5043fc5d4 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -166,3 +166,6 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasWarnStdPrefix") defineSymbol("nimHasVtables") + defineSymbol("nimHasGenericsOpenSym2") + defineSymbol("nimHasGenericsOpenSym3") + defineSymbol("nimHasJsNoLambdaLifting") diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 8ee527952..5534d07e7 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -46,10 +46,10 @@ type case isTryBlock: bool of false: label: PSym - breakFixups: seq[(TPosition, seq[PNode])] #Contains the gotos for the breaks along with their pending finales + breakFixups: seq[(TPosition, seq[PNode])] # Contains the gotos for the breaks along with their pending finales of true: finale: PNode - raiseFixups: seq[TPosition] #Contains the gotos for the raises + raiseFixups: seq[TPosition] # Contains the gotos for the raises Con = object code: ControlFlowGraph @@ -181,14 +181,6 @@ proc genIf(c: var Con, n: PNode) = goto Lend3 L3: D - goto Lend3 # not eliminated to simplify the join generation - Lend3: - join F3 - Lend2: - join F2 - Lend: - join F1 - ]# var endings: seq[TPosition] = @[] let oldInteresting = c.interestingInstructions @@ -213,7 +205,6 @@ proc genAndOr(c: var Con; n: PNode) = # fork lab1 # asgn dest, b # lab1: - # join F1 c.gen(n[1]) forkT: c.gen(n[2]) @@ -324,7 +315,7 @@ proc genRaise(c: var Con; n: PNode) = if c.blocks[i].isTryBlock: genBreakOrRaiseAux(c, i, n) return - assert false #Unreachable + assert false # Unreachable else: genNoReturn(c) @@ -390,7 +381,6 @@ proc genCall(c: var Con; n: PNode) = # fork lab1 # goto exceptionHandler (except or finally) # lab1: - # join F1 forkT: for i in countdown(c.blocks.high, 0): if c.blocks[i].isTryBlock: diff --git a/compiler/docgen.nim b/compiler/docgen.nim index b4c4baa2b..8e5f5e4e7 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -831,7 +831,7 @@ proc getName(n: PNode): string = result = "`" for i in 0..<n.len: result.add(getName(n[i])) result = "`" - of nkOpenSymChoice, nkClosedSymChoice: + of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym: result = getName(n[0]) else: result = "" @@ -849,7 +849,7 @@ proc getNameIdent(cache: IdentCache; n: PNode): PIdent = var r = "" for i in 0..<n.len: r.add(getNameIdent(cache, n[i]).s) result = getIdent(cache, r) - of nkOpenSymChoice, nkClosedSymChoice: + of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym: result = getNameIdent(cache, n[0]) else: result = nil @@ -863,7 +863,7 @@ proc getRstName(n: PNode): PRstNode = of nkAccQuoted: result = getRstName(n[0]) for i in 1..<n.len: result.text.add(getRstName(n[i]).text) - of nkOpenSymChoice, nkClosedSymChoice: + of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym: result = getRstName(n[0]) else: result = nil @@ -1206,7 +1206,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false): var param = %{"name": %($genericParam)} if genericParam.sym.typ.len > 0: param["types"] = newJArray() - param["types"].add %($genericParam.sym.typ.elementType) + param["types"] = %($genericParam.sym.typ.elementType) result.json["signature"]["genericParams"].add param if optGenIndex in d.conf.globalOptions: genItem(d, n, nameNode, k, kForceExport) diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index bd0875213..77c136d63 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -33,6 +33,19 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = let x = param if x.kind == nkArgList: for y in items(x): result.add(y) + elif nfDefaultRefsParam in x.flags: + # value of default param needs to be evaluated like template body + # if it contains other template params + var res: PNode + if isAtom(x): + res = newNodeI(nkPar, x.info) + evalTemplateAux(x, actual, c, res) + if res.len == 1: res = res[0] + else: + res = copyNode(x) + for i in 0..<x.safeLen: + evalTemplateAux(x[i], actual, c, res) + result.add res else: result.add copyTree(x) @@ -56,6 +69,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = # internalAssert c.config, false idTablePut(c.mapping, s, x) if sfGenSym in s.flags: + # TODO: getIdent(c.ic, "`" & x.name.s & "`gensym" & $c.instID) result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $c.instID), if c.instLines: actual.info else: templ.info) else: diff --git a/compiler/expanddefaults.nim b/compiler/expanddefaults.nim index c121f5dea..c520d8849 100644 --- a/compiler/expanddefaults.nim +++ b/compiler/expanddefaults.nim @@ -125,7 +125,7 @@ proc expandDefault(t: PType; info: TLineInfo): PNode = of tyString: result = newZero(t, info, nkStrLit) of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc, - tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass, + tyNil, tyGenericInvocation, tyError, tyBuiltInTypeClass, tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward: result = newZero(t, info, nkEmpty) # bug indicator diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 0ea458e3b..ce25da773 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -172,6 +172,22 @@ compiler vcc: cppXsupport: "", props: {hasCpp, hasAssume, hasDeclspec}) +# Nvidia CUDA NVCC Compiler +compiler nvcc: + result = gcc() + result.name = "nvcc" + result.compilerExe = "nvcc" + result.cppCompiler = "nvcc" + result.compileTmpl = "-c -x cu -Xcompiler=\"$options\" $include -o $objfile $file" + result.linkTmpl = "$buildgui $builddll -o $exefile $objfiles -Xcompiler=\"$options\"" + +# AMD HIPCC Compiler (rocm/cuda) +compiler hipcc: + result = clang() + result.name = "hipcc" + result.compilerExe = "hipcc" + result.cppCompiler = "hipcc" + compiler clangcl: result = vcc() result.name = "clang_cl" @@ -285,7 +301,9 @@ const envcc(), icl(), icc(), - clangcl()] + clangcl(), + hipcc(), + nvcc()] hExt* = ".h" @@ -319,7 +337,7 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string = var fullSuffix = suffix case conf.backend of backendCpp, backendJs, backendObjc: fullSuffix = "." & $conf.backend & suffix - of backendC, backendNir: discard + of backendC: discard of backendInvalid: # during parsing of cfg files; we don't know the backend yet, no point in # guessing wrong thing @@ -982,10 +1000,11 @@ proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile = # works out of the box with `hashMainCompilationParams`. result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json") -const cacheVersion = "D20210525T193831" # update when `BuildCache` spec changes +const cacheVersion = "D20240927T193831" # update when `BuildCache` spec changes type BuildCache = object cacheVersion: string outputFile: string + outputLastModificationTime: string compile: seq[(string, string)] link: seq[string] linkcmd: string @@ -1029,6 +1048,8 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) = bcache.depfiles.add (path, $secureHashFile(path)) bcache.nimexe = hashNimExe() + if fileExists(bcache.outputFile): + bcache.outputLastModificationTime = $getLastModificationTime(bcache.outputFile) conf.jsonBuildFile = conf.jsonBuildInstructionsFile conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty) @@ -1049,6 +1070,8 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: Absolute # xxx optimize by returning false if stdin input was the same for (file, hash) in bcache.depfiles: if $secureHashFile(file) != hash: return true + if bcache.outputLastModificationTime != $getLastModificationTime(bcache.outputFile): + return true proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) = var bcache: BuildCache = default(BuildCache) @@ -1065,7 +1088,7 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) = "jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " % [outputCurrent, output, jsonFile.string]) var cmds: TStringSeq = default(TStringSeq) - var prettyCmds: TStringSeq= default(TStringSeq) + var prettyCmds: TStringSeq = default(TStringSeq) let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx]) for (name, cmd) in bcache.compile: cmds.add cmd diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index c9f546c76..8e81633ef 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -16,7 +16,7 @@ from std/os import removeFile, isAbsolute import ../../dist/checksums/src/checksums/sha1 -import ".." / nir / nirlineinfos +import iclineinfos when defined(nimPreviewSlimSystem): import std/[syncio, assertions, formatfloat] @@ -59,8 +59,8 @@ type emittedTypeInfo*: seq[string] backendFlags*: set[ModuleBackendFlag] - syms*: seq[PackedSym] - types*: seq[PackedType] + syms*: OrderedTable[int32, PackedSym] + types*: OrderedTable[int32, PackedType] strings*: BiTable[string] # we could share these between modules. numbers*: BiTable[BiggestInt] # we also store floats in here so # that we can assure that every bit is kept @@ -362,10 +362,10 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI result = PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item) if t.uniqueId.module == c.thisModule and not c.typeMarker.containsOrIncl(t.uniqueId.item): - if t.uniqueId.item >= m.types.len: - setLen m.types, t.uniqueId.item+1 + #if t.uniqueId.item >= m.types.len: + # setLen m.types, t.uniqueId.item+1 - var p = PackedType(kind: t.kind, flags: t.flags, callConv: t.callConv, + var p = PackedType(id: t.uniqueId.item, kind: t.kind, flags: t.flags, callConv: t.callConv, size: t.size, align: t.align, nonUniqueId: t.itemId.item, paddingAtEnd: t.paddingAtEnd) storeNode(p, t, n) @@ -396,12 +396,12 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item) if s.itemId.module == c.thisModule and not c.symMarker.containsOrIncl(s.itemId.item): - if s.itemId.item >= m.syms.len: - setLen m.syms, s.itemId.item+1 + #if s.itemId.item >= m.syms.len: + # setLen m.syms, s.itemId.item+1 assert sfForward notin s.flags - var p = PackedSym(kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic, + var p = PackedSym(id: s.itemId.item, kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic, position: s.position, offset: s.offset, disamb: s.disamb, options: s.options, name: s.name.s.toLitId(m)) @@ -414,7 +414,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId p.bitsize = s.bitsize p.alignment = s.alignment - p.externalName = toLitId(s.loc.r, m) + p.externalName = toLitId(s.loc.snippet, m) p.locFlags = s.loc.flags c.addMissing s.typ p.typ = s.typ.storeType(c, m) @@ -613,6 +613,10 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef f.loadSection section f.loadSeq data + template loadTableSection(section, data) {.dirty.} = + f.loadSection section + f.loadOrderedTable data + template loadTabSection(section, data) {.dirty.} = f.loadSection section f.load data @@ -645,8 +649,8 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef loadTabSection topLevelSection, m.topLevel loadTabSection bodiesSection, m.bodies - loadSeqSection symsSection, m.syms - loadSeqSection typesSection, m.types + loadTableSection symsSection, m.syms + loadTableSection typesSection, m.types loadSeqSection typeInstCacheSection, m.typeInstCache loadSeqSection procInstCacheSection, m.procInstCache @@ -691,6 +695,10 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac f.storeSection section f.store data + template storeTableSection(section, data) {.dirty.} = + f.storeSection section + f.storeOrderedTable data + storeTabSection stringsSection, m.strings storeSeqSection checkSumsSection, m.includes @@ -714,9 +722,9 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac storeTabSection topLevelSection, m.topLevel storeTabSection bodiesSection, m.bodies - storeSeqSection symsSection, m.syms + storeTableSection symsSection, m.syms - storeSeqSection typesSection, m.types + storeTableSection typesSection, m.types storeSeqSection typeInstCacheSection, m.typeInstCache storeSeqSection procInstCacheSection, m.procInstCache @@ -767,8 +775,8 @@ type status*: ModuleStatus symsInit, typesInit, loadedButAliveSetChanged*: bool fromDisk*: PackedModule - syms: seq[PSym] # indexed by itemId - types: seq[PType] + syms: OrderedTable[int32, PSym] # indexed by itemId + types: OrderedTable[int32, PType] module*: PSym # the one true module symbol. iface, ifaceHidden: Table[PIdent, seq[PackedItemId]] # PackedItemId so that it works with reexported symbols too @@ -829,7 +837,7 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; result.ident = getIdent(c.cache, g[thisModule].fromDisk.strings[n.litId]) of nkSym: result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree[n].soperand)) - if result.typ == nil and nfOpenSym notin result.flags: + if result.typ == nil: result.typ = result.sym.typ of externIntLit: result.intVal = g[thisModule].fromDisk.numbers[n.litId] @@ -843,7 +851,7 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; assert n2.kind == nkNone transitionNoneToSym(result) result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree[n2].soperand)) - if result.typ == nil and nfOpenSym notin result.flags: + if result.typ == nil: result.typ = result.sym.typ else: for n0 in sonsReadonly(tree, n): @@ -937,7 +945,7 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; result.owner = loadSym(c, g, si, s.owner) let externalName = g[si].fromDisk.strings[s.externalName] if externalName != "": - result.loc.r = rope externalName + result.loc.snippet = externalName result.loc.flags = s.locFlags result.instantiatedFrom = loadSym(c, g, si, s.instantiatedFrom) @@ -961,11 +969,11 @@ proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s: loadToReplayNodes(g, c.config, c.cache, m, g[int m]) assert g[si].status in {loaded, storing, stored} - if not g[si].symsInit: - g[si].symsInit = true - setLen g[si].syms, g[si].fromDisk.syms.len + #if not g[si].symsInit: + # g[si].symsInit = true + # setLen g[si].syms, g[si].fromDisk.syms.len - if g[si].syms[s.item] == nil: + if g[si].syms.getOrDefault(s.item) == nil: if g[si].fromDisk.syms[s.item].kind != skModule: result = symHeaderFromPacked(c, g, g[si].fromDisk.syms[s.item], si, s.item) # store it here early on, so that recursions work properly: @@ -1012,11 +1020,11 @@ proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t assert g[si].status in {loaded, storing, stored} assert t.item > 0 - if not g[si].typesInit: - g[si].typesInit = true - setLen g[si].types, g[si].fromDisk.types.len + #if not g[si].typesInit: + # g[si].typesInit = true + # setLen g[si].types, g[si].fromDisk.types.len - if g[si].types[t.item] == nil: + if g[si].types.getOrDefault(t.item) == nil: result = typeHeaderFromPacked(c, g, g[si].fromDisk.types[t.item], si, t.item) # store it here early on, so that recursions work properly: g[si].types[t.item] = result @@ -1155,10 +1163,7 @@ proc loadProcBody*(config: ConfigRef, cache: IdentCache; proc loadTypeFromId*(config: ConfigRef, cache: IdentCache; g: var PackedModuleGraph; module: int; id: PackedItemId): PType = bench g.loadType: - if id.item < g[module].types.len: - result = g[module].types[id.item] - else: - result = nil + result = g[module].types.getOrDefault(id.item) if result == nil: var decoder = PackedDecoder( lastModule: int32(-1), @@ -1171,10 +1176,7 @@ proc loadTypeFromId*(config: ConfigRef, cache: IdentCache; proc loadSymFromId*(config: ConfigRef, cache: IdentCache; g: var PackedModuleGraph; module: int; id: PackedItemId): PSym = bench g.loadSym: - if id.item < g[module].syms.len: - result = g[module].syms[id.item] - else: - result = nil + result = g[module].syms.getOrDefault(id.item) if result == nil: var decoder = PackedDecoder( lastModule: int32(-1), @@ -1190,19 +1192,6 @@ proc translateId*(id: PackedItemId; g: PackedModuleGraph; thisModule: int; confi else: ItemId(module: toFileIndex(id.module, g[thisModule].fromDisk, config).int32, item: id.item) -proc checkForHoles(m: PackedModule; config: ConfigRef; moduleId: int) = - var bugs = 0 - for i in 1 .. high(m.syms): - if m.syms[i].kind == skUnknown: - echo "EMPTY ID ", i, " module ", moduleId, " ", toFullPath(config, FileIndex(moduleId)) - inc bugs - assert bugs == 0 - when false: - var nones = 0 - for i in 1 .. high(m.types): - inc nones, m.types[i].kind == tyNone - assert nones < 1 - proc simulateLoadedModule*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; moduleSym: PSym; m: PackedModule) = # For now only used for heavy debugging. In the future we could use this to reduce the diff --git a/compiler/nir/nirlineinfos.nim b/compiler/ic/iclineinfos.nim index f11ef7c42..74a7d971b 100644 --- a/compiler/nir/nirlineinfos.nim +++ b/compiler/ic/iclineinfos.nim @@ -1,7 +1,7 @@ # # # The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf +# (c) Copyright 2024 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. diff --git a/compiler/ic/integrity.nim b/compiler/ic/integrity.nim index d78e56847..3e8ea2503 100644 --- a/compiler/ic/integrity.nim +++ b/compiler/ic/integrity.nim @@ -10,7 +10,7 @@ ## Integrity checking for a set of .rod files. ## The set must cover a complete Nim project. -import std/sets +import std/[sets, tables] when defined(nimPreviewSlimSystem): import std/assertions @@ -108,18 +108,18 @@ proc checkModule(c: var CheckedContext; m: PackedModule) = # We check that: # - Every symbol references existing types and symbols. # - Every tree node references existing types and symbols. - for i in 0..high(m.syms): - checkLocalSym c, int32(i) + for _, v in pairs(m.syms): + checkLocalSym c, v.id checkTree c, m.toReplay checkTree c, m.topLevel for e in m.exports: - assert e[1] >= 0 and e[1] < m.syms.len + #assert e[1] >= 0 and e[1] < m.syms.len assert e[0] == m.syms[e[1]].name for e in m.compilerProcs: - assert e[1] >= 0 and e[1] < m.syms.len + #assert e[1] >= 0 and e[1] < m.syms.len assert e[0] == m.syms[e[1]].name checkLocalSymIds c, m, m.converters diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim index da8e7e597..39037b94f 100644 --- a/compiler/ic/navigator.nim +++ b/compiler/ic/navigator.nim @@ -11,7 +11,7 @@ ## IDE-like features. It uses the set of .rod files to accomplish ## its task. The set must cover a complete Nim project. -import std/sets +import std/[sets, tables] from std/os import nil from std/private/miscdollars import toLocation @@ -20,7 +20,7 @@ when defined(nimPreviewSlimSystem): import std/assertions import ".." / [ast, modulegraphs, msgs, options] -import ".." / nir / nirlineinfos +import iclineinfos import packed_ast, bitabs, ic type diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 2599e07d1..a39bb7adf 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -16,7 +16,7 @@ import std/[hashes, tables, strtabs] import bitabs, rodfiles import ".." / [ast, options] -import ".." / nir / nirlineinfos +import iclineinfos when defined(nimPreviewSlimSystem): import std/assertions @@ -47,6 +47,7 @@ type path*: NodeId PackedSym* = object + id*: int32 kind*: TSymKind name*: LitId typ*: PackedItemId @@ -71,6 +72,7 @@ type instantiatedFrom*: PackedItemId PackedType* = object + id*: int32 kind*: TTypeKind callConv*: TCallingConvention #nodekind*: TNodeKind diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index 5eef3874a..ac995dd2e 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -19,6 +19,8 @@ from std/typetraits import supportsCopyMem when defined(nimPreviewSlimSystem): import std/[syncio, assertions] +import std / tables + ## Overview ## ======== ## `RodFile` represents a Rod File (versioned binary format), and the @@ -170,6 +172,18 @@ proc storeSeq*[T](f: var RodFile; s: seq[T]) = for i in 0..<s.len: storePrim(f, s[i]) +proc storeOrderedTable*[K, T](f: var RodFile; s: OrderedTable[K, T]) = + if f.err != ok: return + if s.len >= high(int32): + setError f, tooBig + return + var lenPrefix = int32(s.len) + if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix): + setError f, ioFailure + else: + for _, v in s: + storePrim(f, v) + proc loadPrim*(f: var RodFile; s: var string) = ## Read a string, the length was stored as a prefix if f.err != ok: return @@ -211,6 +225,19 @@ proc loadSeq*[T](f: var RodFile; s: var seq[T]) = for i in 0..<lenPrefix: loadPrim(f, s[i]) +proc loadOrderedTable*[K, T](f: var RodFile; s: var OrderedTable[K, T]) = + ## `T` must be compatible with `copyMem`, see `loadPrim` + if f.err != ok: return + var lenPrefix = int32(0) + if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix): + setError f, ioFailure + else: + s = initOrderedTable[K, T](lenPrefix) + for i in 0..<lenPrefix: + var x = default T + loadPrim(f, x) + s[x.id] = x + proc storeHeader*(f: var RodFile; cookie = defaultCookie) = ## stores the header which is described by `cookie`. if f.err != ok: return diff --git a/compiler/importer.nim b/compiler/importer.nim index 176b33b7b..ffb7e0305 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -246,7 +246,8 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool) result = createModuleAliasImpl(realModule.name) if importHidden: result.options.incl optImportHidden - c.unusedImports.add((result, n.info)) + let moduleIdent = if n.kind == nkInfix: n[^1] else: n + c.unusedImports.add((result, moduleIdent.info)) c.importModuleMap[result.id] = realModule.id c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 6b7676653..3dcc364a3 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -317,8 +317,9 @@ proc isCriticalLink(dest: PNode): bool {.inline.} = ]# result = dest.kind != nkSym -proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) = - if c.graph.config.selectedGC == gcOrc: +proc finishCopy(c: var Con; result, dest: PNode; flags: set[MoveOrCopyFlag]; isFromSink: bool) = + if c.graph.config.selectedGC == gcOrc and IsExplicitSink notin flags: + # add cyclic flag, but not to sink calls, which IsExplicitSink generates let t = dest.typ.skipTypes(tyUserTypeClasses + {tyGenericInst, tyAlias, tySink, tyDistinct}) if cyclicType(c.graph, t): result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest)) @@ -464,7 +465,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = var newCall = newTreeIT(nkCall, src.info, src.typ, newSymNode(op), src) - c.finishCopy(newCall, n, isFromSink = true) + c.finishCopy(newCall, n, {}, isFromSink = true) result.add newTreeI(nkFastAsgn, src.info, tmp, newCall @@ -473,7 +474,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = result.add c.genWasMoved(tmp) var m = c.genCopy(tmp, n, {}) m.add p(n, c, s, normal) - c.finishCopy(m, n, isFromSink = true) + c.finishCopy(m, n, {}, isFromSink = true) result.add m if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0: message(c.graph.config, n.info, hintPerformance, @@ -501,7 +502,7 @@ proc containsConstSeq(n: PNode): bool = return true result = false case n.kind - of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv: + of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast: result = containsConstSeq(n[1]) of nkObjConstr, nkClosure: for i in 1..<n.len: @@ -653,7 +654,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false, for j in 0 ..< it.len-1: branch[j] = copyTree(it[j]) var ofScope = nestedScope(s, it.lastSon) - branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt: + branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt: processScope(c, ofScope, maybeVoid(it[^1], ofScope)) else: processScopeExpr(c, ofScope, it[^1], processCall, tmpFlags) @@ -701,7 +702,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false, #Condition needs to be destroyed outside of the condition/branch scope branch[0] = p(it[0], c, s, normal) - branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt: + branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt: processScope(c, branchScope, maybeVoid(it[^1], branchScope)) else: processScopeExpr(c, branchScope, it[^1], processCall, tmpFlags) @@ -761,7 +762,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode = let tmp = c.getTemp(s, n[0].typ, n.info) var m = c.genCopyNoCheck(tmp, n[0], attachedAsgn) m.add p(n[0], c, s, normal) - c.finishCopy(m, n[0], isFromSink = false) + c.finishCopy(m, n[0], {}, isFromSink = false) result = newTree(nkStmtList, c.genWasMoved(tmp), m) var toDisarm = n[0] if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon @@ -829,6 +830,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing elif n.kind in {nkObjDownConv, nkObjUpConv}: result = copyTree(n) result[0] = p(n[0], c, s, sinkArg) + elif n.kind == nkCast and n.typ.skipTypes(abstractInst).kind in {tyString, tySequence}: + result = copyTree(n) + result[1] = p(n[1], c, s, sinkArg) elif n.typ == nil: # 'raise X' can be part of a 'case' expression. Deal with it here: result = p(n, c, s, normal) @@ -872,7 +876,8 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing for i in 1..<n.len: if n[i].kind == nkExprColonExpr: let field = lookupFieldAgain(t, n[i][0].sym) - if field != nil and sfCursor in field.flags: + if field != nil and (sfCursor in field.flags or field.typ.kind in {tyOpenArray, tyVarargs}): + # don't sink fields with openarray types result[i][1] = p(n[i][1], c, s, normal) else: result[i][1] = p(n[i][1], c, s, m) @@ -893,7 +898,8 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing elif c.inSpawn > 0: c.inSpawn.dec - let parameters = n[0].typ + # bug #23907; skips tyGenericInst for generic callbacks + let parameters = if n[0].typ != nil: n[0].typ.skipTypes(abstractInst) else: n[0].typ let L = if parameters != nil: parameters.signatureLen else: 0 when false: @@ -1168,7 +1174,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy result = c.genCopy(dest, ri, flags) dec c.inEnsureMove, isEnsureMove result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) + c.finishCopy(result, dest, flags, isFromSink = false) of nkBracket: # array constructor if ri.len > 0 and isDangerousSeq(ri.typ): @@ -1176,7 +1182,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy result = c.genCopy(dest, ri, flags) dec c.inEnsureMove, isEnsureMove result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) + c.finishCopy(result, dest, flags, isFromSink = false) else: result = c.genSink(s, dest, p(ri, c, s, consumed), flags) of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit: @@ -1186,9 +1192,9 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy # Rule 3: `=sink`(x, z); wasMoved(z) let snk = c.genSink(s, dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) - elif ri.sym.kind != skParam and ri.sym.owner == c.owner and - isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri) and - not ({sfGlobal, sfPure} <= ri.sym.flags): + elif ri.sym.kind != skParam and + isAnalysableFieldAccess(ri, c.owner) and + isLastRead(ri, c, s) and canBeMoved(c, dest.typ): # Rule 3: `=sink`(x, z); wasMoved(z) let snk = c.genSink(s, dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) @@ -1197,7 +1203,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy result = c.genCopy(dest, ri, flags) dec c.inEnsureMove, isEnsureMove result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) + c.finishCopy(result, dest, flags, isFromSink = false) of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast: result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags) of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt: @@ -1217,7 +1223,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy result = c.genCopy(dest, ri, flags) dec c.inEnsureMove, isEnsureMove result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) + c.finishCopy(result, dest, flags, isFromSink = false) when false: proc computeUninit(c: var Con) = diff --git a/compiler/installer.ini b/compiler/installer.ini index 8569d0ef8..54a35dbee 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -6,11 +6,11 @@ Name: "Nim" Version: "$version" Platforms: """ windows: i386;amd64 - linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64 + linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64;loongarch64 macosx: i386;amd64;powerpc64;arm64 solaris: i386;amd64;sparc;sparc64 freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el - netbsd: i386;amd64 + netbsd: i386;amd64;arm64 openbsd: i386;amd64;arm;arm64 dragonfly: i386;amd64 crossos: amd64 diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index baebfe188..713944def 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -33,7 +33,7 @@ import nversion, msgs, idents, types, ropes, wordrecg, renderer, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, - transf, injectdestructors, sourcemap, astmsgs, backendpragmas, + transf, injectdestructors, sourcemap, astmsgs, pushpoppragmas, mangleutils import pipelineutils @@ -103,7 +103,7 @@ type prc: PSym globals, locals, body: Rope options: TOptions - optionsStack: seq[TOptions] + optionsStack: seq[(TOptions, TNoteKinds)] module: BModule g: PGlobals beforeRetNeeded: bool @@ -111,13 +111,25 @@ type blocks: seq[TBlock] extraIndent: int previousFileName: string # For frameInfo inside templates. + # legacy: generatedParamCopies and up fields are used for jsNoLambdaLifting + generatedParamCopies: IntSet + up: PProc # up the call chain; required for closure support template config*(p: PProc): ConfigRef = p.module.config proc indentLine(p: PProc, r: Rope): Rope = var p = p - let ind = p.blocks.len + p.extraIndent - result = repeat(' ', ind*2) & r + if jsNoLambdaLifting in p.config.legacyFeatures: + var ind = 0 + while true: + inc ind, p.blocks.len + p.extraIndent + if p.up == nil or p.up.prc != p.prc.owner: + break + p = p.up + result = repeat(' ', ind*2) & r + else: + let ind = p.blocks.len + p.extraIndent + result = repeat(' ', ind*2) & r template line(p: PProc, added: string) = p.body.add(indentLine(p, rope(added))) @@ -183,7 +195,7 @@ proc mapType(typ: PType): TJSTypeKind = of tyPointer: # treat a tyPointer like a typed pointer to an array of bytes result = etyBaseIndex - of tyRange, tyDistinct, tyOrdinal, tyProxy, tyLent: + of tyRange, tyDistinct, tyOrdinal, tyError, tyLent: # tyLent is no-op as JS has pass-by-reference semantics result = mapType(skipModifier t) of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt @@ -234,7 +246,7 @@ proc mangleName(m: BModule, s: PSym): Rope = for chr in name: if chr notin {'A'..'Z','a'..'z','_','$','0'..'9'}: return false - result = s.loc.r + result = s.loc.snippet if result == "": if s.kind == skField and s.name.s.validJsName: result = rope(s.name.s) @@ -265,7 +277,7 @@ proc mangleName(m: BModule, s: PSym): Rope = else: result.add("_") result.add(rope(s.id)) - s.loc.r = result + s.loc.snippet = result proc escapeJSString(s: string): string = result = newStringOfCap(s.len + s.len shr 2) @@ -599,6 +611,17 @@ template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = r.res = frmt % [a, tmp] r.kind = resExpr +proc genBreakState(p: PProc, n: PNode, r: var TCompRes) = + var a: TCompRes = default(TCompRes) + # mangle `:state` properly somehow + if n.kind == nkClosure: + gen(p, n[1], a) + r.res = "(($1).HEX3Astate < 0)" % [rdLoc(a)] + else: + gen(p, n, a) + r.res = "((($1.ClE_0).HEX3Astate) < 0)" % [rdLoc(a)] + r.kind = resExpr + proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = var x, y: TCompRes = default(TCompRes) @@ -969,7 +992,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = # if isJsObject(throwObj.typ): if isImportedException(throwObj.typ, p.config): orExpr.addf("lastJSError instanceof $1", - [throwObj.typ.sym.loc.r]) + [throwObj.typ.sym.loc.snippet]) else: orExpr.addf("isObj(lastJSError.m_type, $1)", [genTypeInfo(p, throwObj.typ)]) @@ -979,8 +1002,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = # If some branch requires a local alias introduce it here. This is needed # since JS cannot do ``catch x as y``. if excAlias != nil: - excAlias.sym.loc.r = mangleName(p.module, excAlias.sym) - lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.r) + excAlias.sym.loc.snippet = mangleName(p.module, excAlias.sym) + lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.snippet) gen(p, n[i][^1], a) moveInto(p, a, r) lineF(p, "}$n", []) @@ -1086,14 +1109,19 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = lineF(p, "break;$n", []) of nkElse: if transferRange: - lineF(p, "else{$n", []) + if n.len == 2: # a dangling else for a case statement + transferRange = false + lineF(p, "switch ($1) {$n", [cond.rdLoc]) + lineF(p, "default: $n", []) + else: + lineF(p, "else{$n", []) else: lineF(p, "default: $n", []) p.nested: gen(p, it[0], stmt) moveInto(p, stmt, r) if transferRange: - lineF(p, "}$n", []) + lineF(p, "}$n", []) else: lineF(p, "break;$n", []) else: internalError(p.config, it.info, "jsgen.genCaseStmt") @@ -1200,12 +1228,13 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) = proc generateHeader(p: PProc, prc: PSym): Rope = result = "" let typ = prc.typ - if typ.callConv == ccClosure: - # we treat Env as the `this` parameter of the function - # to keep it simple - let env = prc.ast[paramsPos].lastSon - assert env.kind == nkSym, "env is missing" - env.sym.loc.r = "this" + if jsNoLambdaLifting notin p.config.legacyFeatures: + if typ.callConv == ccClosure: + # we treat Env as the `this` parameter of the function + # to keep it simple + let env = prc.ast[paramsPos].lastSon + assert env.kind == nkSym, "env is missing" + env.sym.loc.snippet = "this" for i in 1..<typ.n.len: assert(typ.n[i].kind == nkSym) @@ -1239,9 +1268,10 @@ const proc needsNoCopy(p: PProc; y: PNode): bool = return y.kind in nodeKindsNeedNoCopy or - ((mapType(y.typ) != etyBaseIndex) and + ((mapType(y.typ) != etyBaseIndex or + (jsNoLambdaLifting in p.config.legacyFeatures and y.kind == nkSym and y.sym.kind == skParam)) and (skipTypes(y.typ, abstractInst).kind in - {tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned} + IntegralTypes)) + {tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned, tyOpenArray} + IntegralTypes)) proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = var a, b: TCompRes = default(TCompRes) @@ -1268,7 +1298,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = lineF(p, "$1 = nimCopy(null, $2, $3);$n", [a.rdLoc, b.res, genTypeInfo(p, y.typ)]) of etyObject: - if x.typ.kind in {tyVar, tyLent} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: + if x.typ.kind in {tyVar, tyLent, tyOpenArray, tyVarargs} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") @@ -1346,8 +1376,8 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = else: if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr") var f = b[1].sym - if f.loc.r == "": f.loc.r = mangleName(p.module, f) - r.res = makeJSString($f.loc.r) + if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f) + r.res = makeJSString($f.loc.snippet) internalAssert p.config, a.typ != etyBaseIndex r.address = a.res r.kind = resExpr @@ -1374,8 +1404,8 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = else: if n[1].kind != nkSym: internalError(p.config, n[1].info, "genFieldAccess") var f = n[1].sym - if f.loc.r == "": f.loc.r = mangleName(p.module, f) - r.res = "$1.$2" % [r.res, f.loc.r] + if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f) + r.res = "$1.$2" % [r.res, f.loc.snippet] mkTemp(1) r.kind = resExpr @@ -1395,11 +1425,11 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) = # Field symbol var field = accessExpr[1].sym internalAssert p.config, field.kind == skField - if field.loc.r == "": field.loc.r = mangleName(p.module, field) + if field.loc.snippet == "": field.loc.snippet = mangleName(p.module, field) # Discriminant symbol let disc = checkExpr[2].sym internalAssert p.config, disc.kind == skField - if disc.loc.r == "": disc.loc.r = mangleName(p.module, disc) + if disc.loc.snippet == "": disc.loc.snippet = mangleName(p.module, disc) var setx: TCompRes = default(TCompRes) gen(p, checkExpr[1], setx) @@ -1416,16 +1446,16 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) = useMagic(p, "reprDiscriminant") # no need to offset by firstOrd unlike for cgen let msg = genFieldDefect(p.config, field.name.s, disc) lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError2(makeNimstrLit($5), reprDiscriminant($2.$3, $6)); }$n", - setx.res, tmp, disc.loc.r, if negCheck: "!==" else: "===", + setx.res, tmp, disc.loc.snippet, if negCheck: "!==" else: "===", makeJSString(msg), genTypeInfo(p, disc.typ)) if addrTyp != nil and mapType(p, addrTyp) == etyBaseIndex: r.typ = etyBaseIndex - r.res = makeJSString($field.loc.r) + r.res = makeJSString($field.loc.snippet) r.address = tmp else: r.typ = etyNone - r.res = "$1.$2" % [tmp, field.loc.r] + r.res = "$1.$2" % [tmp, field.loc.snippet] r.kind = resExpr proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = @@ -1456,7 +1486,7 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = r.kind = resExpr proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = - var ty = skipTypes(n[0].typ, abstractVarRange) + var ty = skipTypes(n[0].typ, abstractVarRange+tyUserTypeClasses) if ty.kind in {tyRef, tyPtr, tyLent, tyOwned}: ty = skipTypes(ty.elementType, abstractVarRange) case ty.kind of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs: @@ -1492,10 +1522,10 @@ template isIndirect(x: PSym): bool = proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) = let s = n.sym - if s.loc.r == "": internalError(p.config, n.info, "genAddr: 3") + if s.loc.snippet == "": internalError(p.config, n.info, "genAddr: 3") case s.kind of skParam: - r.res = s.loc.r + r.res = s.loc.snippet r.address = "" r.typ = etyNone of skVar, skLet, skResult: @@ -1509,15 +1539,15 @@ proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) = # make addr() a no-op: r.typ = etyNone if isIndirect(s): - r.res = s.loc.r & "[0]" + r.res = s.loc.snippet & "[0]" else: - r.res = s.loc.r + r.res = s.loc.snippet r.address = "" elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex: # for ease of code generation, we do not distinguish between # sfAddrTaken and sfGlobal. r.typ = etyBaseIndex - r.address = s.loc.r + r.address = s.loc.snippet r.res = rope("0") else: # 'var openArray' for instance produces an 'addr' but this is harmless: @@ -1590,7 +1620,30 @@ proc attachProc(p: PProc; s: PSym) = proc genProcForSymIfNeeded(p: PProc, s: PSym) = if not p.g.generatedSyms.containsOrIncl(s.id): - attachProc(p, s) + if jsNoLambdaLifting in p.config.legacyFeatures: + let newp = genProc(p, s) + var owner = p + while owner != nil and owner.prc != s.owner: + owner = owner.up + if owner != nil: owner.locals.add(newp) + else: attachProc(p, newp, s) + else: + attachProc(p, s) + +proc genCopyForParamIfNeeded(p: PProc, n: PNode) = + let s = n.sym + if p.prc == s.owner or needsNoCopy(p, n): + return + var owner = p.up + while true: + if owner == nil: + internalError(p.config, n.info, "couldn't find the owner proc of the closed over param: " & s.name.s) + if owner.prc == s.owner: + if not owner.generatedParamCopies.containsOrIncl(s.id): + let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.snippet, genTypeInfo(p, s.typ)] + owner.locals.add(owner.indentLine(copy)) + return + owner = owner.up proc genVarInit(p: PProc, v: PSym, n: PNode) @@ -1598,38 +1651,40 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = var s = n.sym case s.kind of skVar, skLet, skParam, skTemp, skResult, skForVar: - if s.loc.r == "": + if s.loc.snippet == "": internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) if sfCompileTime in s.flags: genVarInit(p, s, if s.astdef != nil: s.astdef else: newNodeI(nkEmpty, s.info)) + if jsNoLambdaLifting in p.config.legacyFeatures and s.kind == skParam: + genCopyForParamIfNeeded(p, n) let k = mapType(p, s.typ) if k == etyBaseIndex: r.typ = etyBaseIndex if {sfAddrTaken, sfGlobal} * s.flags != {}: if isIndirect(s): - r.address = "$1[0][0]" % [s.loc.r] - r.res = "$1[0][1]" % [s.loc.r] + r.address = "$1[0][0]" % [s.loc.snippet] + r.res = "$1[0][1]" % [s.loc.snippet] else: - r.address = "$1[0]" % [s.loc.r] - r.res = "$1[1]" % [s.loc.r] + r.address = "$1[0]" % [s.loc.snippet] + r.res = "$1[1]" % [s.loc.snippet] else: - r.address = s.loc.r - r.res = s.loc.r & "_Idx" + r.address = s.loc.snippet + r.res = s.loc.snippet & "_Idx" elif isIndirect(s): - r.res = "$1[0]" % [s.loc.r] + r.res = "$1[0]" % [s.loc.snippet] else: - r.res = s.loc.r + r.res = s.loc.snippet of skConst: genConstant(p, s) - if s.loc.r == "": + if s.loc.snippet == "": internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) - r.res = s.loc.r + r.res = s.loc.snippet of skProc, skFunc, skConverter, skMethod, skIterator: if sfCompileTime in s.flags: localError(p.config, n.info, "request to generate code for .compileTime proc: " & s.name.s) discard mangleName(p.module, s) - r.res = s.loc.r + r.res = s.loc.snippet if lfNoDecl in s.loc.flags or s.magic notin generatedMagics or {sfImportc, sfInfixCall} * s.flags != {}: discard @@ -1641,13 +1696,13 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = else: genProcForSymIfNeeded(p, s) else: - if s.loc.r == "": + if s.loc.snippet == "": internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) if mapType(p, s.typ) == etyBaseIndex: - r.address = s.loc.r - r.res = s.loc.r & "_Idx" + r.address = s.loc.snippet + r.res = s.loc.snippet & "_Idx" else: - r.res = s.loc.r + r.res = s.loc.snippet r.kind = resVal proc genDeref(p: PProc, n: PNode, r: var TCompRes) = @@ -1789,9 +1844,9 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType; proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = # don't call '$' here for efficiency: let f = n[0].sym - if f.loc.r == "": f.loc.r = mangleName(p.module, f) + if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f) if sfInfixCall in f.flags: - let pat = $n[0].sym.loc.r + let pat = $n[0].sym.loc.snippet internalAssert p.config, pat.len > 0 if pat.contains({'#', '(', '@'}): var typ = skipTypes(n[0].typ, abstractInst) @@ -1906,7 +1961,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = of tyInt8..tyInt32, tyUInt8..tyUInt32, tyEnum, tyChar: result = putToSeq("0", indirect) of tyInt, tyUInt: - if $t.sym.loc.r == "bigint": + if $t.sym.loc.snippet == "bigint": result = putToSeq("0n", indirect) else: result = putToSeq("0", indirect) @@ -1966,7 +2021,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = result = putToSeq("null", indirect) of tySequence, tyString: result = putToSeq("[]", indirect) - of tyCstring, tyProc: + of tyCstring, tyProc, tyOpenArray: result = putToSeq("null", indirect) of tyStatic: if t.n != nil: @@ -2018,7 +2073,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = gen(p, n, a) case mapType(p, v.typ) of etyObject, etySeq: - if needsNoCopy(p, n): + if v.typ.kind in {tyOpenArray, tyVarargs} or needsNoCopy(p, n): s = a.res else: useMagic(p, "nimCopy") @@ -2028,28 +2083,28 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = if a.typ == etyBaseIndex: if targetBaseIndex: line(p, runtimeFormat(varCode & " = $3, $2_Idx = $4;$n", - [returnType, v.loc.r, a.address, a.res])) + [returnType, v.loc.snippet, a.address, a.res])) else: if isIndirect(v): line(p, runtimeFormat(varCode & " = [[$3, $4]];$n", - [returnType, v.loc.r, a.address, a.res])) + [returnType, v.loc.snippet, a.address, a.res])) else: line(p, runtimeFormat(varCode & " = [$3, $4];$n", - [returnType, v.loc.r, a.address, a.res])) + [returnType, v.loc.snippet, a.address, a.res])) else: if targetBaseIndex: let tmp = p.getTemp lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n", - [tmp, a.res, v.loc.r]) + [tmp, a.res, v.loc.snippet]) else: - line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, a.res])) + line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.snippet, a.res])) return else: s = a.res if isIndirect(v): - line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.r, s])) + line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.snippet, s])) else: - line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, s])) + line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.snippet, s])) if useReloadingGuard or useGlobalPragmas: dec p.extraIndent @@ -2212,16 +2267,17 @@ proc genDefault(p: PProc, n: PNode; r: var TCompRes) = r.res = createVar(p, n.typ, indirect = false) r.kind = resExpr -proc genReset(p: PProc, n: PNode) = +proc genWasMoved(p: PProc, n: PNode) = + # TODO: it should be done by nir var x: TCompRes = default(TCompRes) - useMagic(p, "genericReset") gen(p, n[1], x) if x.typ == etyBaseIndex: lineF(p, "$1 = null, $2 = 0;$n", [x.address, x.res]) else: - let (a, tmp) = maybeMakeTempAssignable(p, n[1], x) - lineF(p, "$1 = genericReset($3, $2);$n", [a, - genTypeInfo(p, n[1].typ), tmp]) + var y: TCompRes = default(TCompRes) + genDefault(p, n[1], y) + let (a, _) = maybeMakeTempAssignable(p, n[1], x) + lineF(p, "$1 = $2;$n", [a, y.rdLoc]) proc genMove(p: PProc; n: PNode; r: var TCompRes) = var a: TCompRes = default(TCompRes) @@ -2229,7 +2285,7 @@ proc genMove(p: PProc; n: PNode; r: var TCompRes) = r.res = p.getTemp() gen(p, n[1], a) lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc]) - genReset(p, n) + genWasMoved(p, n) #lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc]) proc genDup(p: PProc; n: PNode; r: var TCompRes) = @@ -2410,7 +2466,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]") of mOf: genOf(p, n, r) of mDefault, mZeroDefault: genDefault(p, n, r) - of mReset, mWasMoved: genReset(p, n) + of mWasMoved: genWasMoved(p, n) of mEcho: genEcho(p, n, r) of mNLen..mNError, mSlurp, mStaticExec: localError(p.config, n.info, errXMustBeCompileTime % n[0].sym.name.s) @@ -2523,17 +2579,17 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = let val = it[1] gen(p, val, a) var f = it[0].sym - if f.loc.r == "": f.loc.r = mangleName(p.module, f) + if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f) fieldIDs.incl(lookupFieldAgain(n.typ.skipTypes({tyDistinct}), f).id) let typ = val.typ.skipTypes(abstractInst) if a.typ == etyBaseIndex: - initList.addf("$#: [$#, $#]", [f.loc.r, a.address, a.res]) + initList.addf("$#: [$#, $#]", [f.loc.snippet, a.address, a.res]) else: if not needsNoCopy(p, val): useMagic(p, "nimCopy") a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] - initList.addf("$#: $#", [f.loc.r, a.res]) + initList.addf("$#: $#", [f.loc.snippet, a.res]) let t = skipTypes(n.typ, abstractInst + skipPtrs) createObjInitList(p, t, fieldIDs, initList) r.res = ("{$1}") % [initList] @@ -2592,7 +2648,13 @@ proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) = let src = skipTypes(n[0].typ, abstractVarRange) let dest = skipTypes(n.typ, abstractVarRange) if optRangeCheck notin p.options: - return + if optJsBigInt64 in p.config.globalOptions and + dest.kind in {tyUInt..tyUInt32, tyInt..tyInt32} and + src.kind in {tyInt64, tyUInt64}: + # conversions to Number are kept + r.res = "Number($1)" % [r.res] + else: + discard elif dest.kind in {tyUInt..tyUInt64} and checkUnsignedConversions notin p.config.legacyFeatures: if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: r.res = "BigInt.asUintN($1, $2)" % [$(dest.size * 8), r.res] @@ -2687,6 +2749,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = #if gVerbosity >= 3: # echo "BEGIN generating code for: " & prc.name.s var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options) + p.up = oldProc var returnStmt: Rope = "" var resultAsgn: Rope = "" var name = mangleName(p.module, prc) @@ -2772,9 +2835,9 @@ proc genPragma(p: PProc, n: PNode) = case whichPragma(it) of wEmit: genAsmOrEmitStmt(p, it[1]) of wPush: - processPushBackendOption(p.optionsStack, p.options, n, i+1) + processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1) of wPop: - processPopBackendOption(p.optionsStack, p.options) + processPopBackendOption(p.config, p.optionsStack, p.options) else: discard proc genCast(p: PProc, n: PNode, r: var TCompRes) = @@ -2910,14 +2973,17 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = else: genCall(p, n, r) of nkClosure: - let tmp = getTemp(p) - var a: TCompRes = default(TCompRes) - var b: TCompRes = default(TCompRes) - gen(p, n[0], a) - gen(p, n[1], b) - lineF(p, "$1 = $2.bind($3); $1.ClP_0 = $2; $1.ClE_0 = $3;$n", [tmp, a.rdLoc, b.rdLoc]) - r.res = tmp - r.kind = resVal + if jsNoLambdaLifting in p.config.legacyFeatures: + gen(p, n[0], r) + else: + let tmp = getTemp(p) + var a: TCompRes = default(TCompRes) + var b: TCompRes = default(TCompRes) + gen(p, n[0], a) + gen(p, n[1], b) + lineF(p, "$1 = $2.bind($3); $1.ClP_0 = $2; $1.ClE_0 = $3;$n", [tmp, a.rdLoc, b.rdLoc]) + r.res = tmp + r.kind = resVal of nkCurly: genSetConstr(p, n, r) of nkBracket: genArrayConstr(p, n, r) of nkPar, nkTupleConstr: genTupleConstr(p, n, r) @@ -2948,7 +3014,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkLambdaKinds: let s = n[namePos].sym discard mangleName(p.module, s) - r.res = s.loc.r + r.res = s.loc.snippet if lfNoDecl in s.loc.flags or s.magic notin generatedMagics: discard elif not p.g.generatedSyms.containsOrIncl(s.id): p.locals.add(genProc(p, s)) @@ -2999,16 +3065,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkGotoState, nkState: globalError(p.config, n.info, "not implemented") of nkBreakState: - var a: TCompRes = default(TCompRes) - if n[0].kind == nkClosure: - gen(p, n[0][1], a) - let sym = n[0][1].typ[0].n[0].sym - r.res = "(($1).$2 < 0)" % [rdLoc(a), mangleName(p.module, sym)] - else: - gen(p, n[0], a) - let sym = n[0].typ[0].n[0].sym - r.res = "((($1.ClE_0).$2) < 0)" % [rdLoc(a), mangleName(p.module, sym)] - r.kind = resExpr + genBreakState(p, n[0], r) of nkPragmaBlock: gen(p, n.lastSon, r) of nkComesFrom: discard "XXX to implement for better stack traces" diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index c32a1c614..54cdfc5bc 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -145,11 +145,13 @@ proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym = result = newSym(skField, getIdent(g.cache, ":state"), idgen, iter, iter.info) result.typ = createClosureIterStateType(g, iter, idgen) +template isIterator*(owner: PSym): bool = + owner.kind == skIterator and owner.typ.callConv == ccClosure + proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType = - # YYY meh, just add the state field for every closure for now, it's too - # hard to figure out if it comes from a closure iterator: result = createObj(g, idgen, owner, info, final=false) - rawAddField(result, createStateField(g, owner, idgen)) + if owner.isIterator or not isDefined(g.config, "nimOptIters"): + rawAddField(result, createStateField(g, owner, idgen)) proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym = if resultPos < iter.ast.len: @@ -172,26 +174,22 @@ proc addHiddenParam(routine: PSym, param: PSym) = assert sfFromGeneric in param.flags #echo "produced environment: ", param.id, " for ", routine.id -proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym = +proc getEnvParam*(routine: PSym): PSym = let params = routine.ast[paramsPos] let hidden = lastSon(params) if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName: result = hidden.sym assert sfFromGeneric in result.flags else: + result = nil + +proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym = + result = getEnvParam(routine) + if result.isNil: # writeStackTrace() localError(g.config, routine.info, "internal error: could not find env param for " & routine.name.s) result = routine -proc getEnvParam*(routine: PSym): PSym = - let params = routine.ast[paramsPos] - let hidden = lastSon(params) - if hidden.kind == nkSym and hidden.sym.name.s == paramName: - result = hidden.sym - assert sfFromGeneric in result.flags - else: - result = nil - proc interestingVar(s: PSym): bool {.inline.} = result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and sfGlobal notin s.flags and @@ -231,13 +229,16 @@ proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; inf prc.flags.incl sfInjectDestructors proc interestingIterVar(s: PSym): bool {.inline.} = + # unused with -d:nimOptIters # XXX optimization: Only lift the variable if it lives across # yield/return boundaries! This can potentially speed up # closure iterators quite a bit. result = s.kind in {skResult, skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags -template isIterator*(owner: PSym): bool = - owner.kind == skIterator and owner.typ.callConv == ccClosure +template liftingHarmful(conf: ConfigRef; owner: PSym): bool = + ## lambda lifting can be harmful for JS-like code generators. + let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro + jsNoLambdaLifting in conf.legacyFeatures and conf.backend == backendJs and not isCompileTime proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen: IdGenerator; owner: PSym) = if owner.kind != skMacro: @@ -255,11 +256,11 @@ proc genCreateEnv(env: PNode): PNode = proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode = # transforms (iter) to (let env = newClosure[iter](); (iter, env)) + if liftingHarmful(g.config, owner): return n let iter = n.sym assert iter.isIterator - result = newNodeIT(nkStmtListExpr, n.info, n.typ) - + result = newNodeIT(nkStmtListExpr, n.info, iter.typ) let hp = getHiddenParam(g, iter) var env: PNode if owner.isIterator: @@ -281,6 +282,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN result.add makeClosure(g, idgen, iter, env, n.info) proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode = + # unused with -d:nimOptIters let envParam = getHiddenParam(g, owner) let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr}) let field = addField(obj, s, g.cache, idgen) @@ -298,8 +300,8 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) = localError(g.config, n.info, ("'$1' is of type <$2> which cannot be captured as it would violate memory" & " safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." & - " Consider using a <ref $2> which can be captured.") % - [s.name.s, typeToString(s.typ), g.config$s.info]) + " Consider using a <ref T> which can be captured.") % + [s.name.s, typeToString(s.typ.skipTypes({tyVar})), g.config$s.info]) elif not (owner.typ.isClosure or owner.isNimcall and not owner.isExplicitCallConv or isEnv): localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" % [s.name.s, owner.name.s, $owner.typ.callConv]) @@ -336,9 +338,13 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym; info: TLineInfo): PType = result = c.ownerToType.getOrDefault(owner.id) if result.isNil: - result = newType(tyRef, c.idgen, owner) - let obj = createEnvObj(c.graph, c.idgen, owner, info) - rawAddSon(result, obj) + let env = getEnvParam(owner) + if env.isNil or not owner.isIterator or not isDefined(c.graph.config, "nimOptIters"): + result = newType(tyRef, c.idgen, owner) + let obj = createEnvObj(c.graph, c.idgen, owner, info) + rawAddSon(result, obj) + else: + result = env.typ c.ownerToType[owner.id] = result proc asOwnedRef(c: var DetectionPass; t: PType): PType = @@ -454,7 +460,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = if owner.isIterator: c.somethingToDo = true addClosureParam(c, owner, n.info) - if interestingIterVar(s): + if not isDefined(c.graph.config, "nimOptIters") and interestingIterVar(s): if not c.capturedVars.contains(s.id): if not c.inTypeOf: c.capturedVars.incl(s.id) let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef, tyPtr}) @@ -768,7 +774,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass; elif s.id in d.capturedVars: if s.owner != owner: result = accessViaEnvParam(d.graph, n, owner) - elif owner.isIterator and interestingIterVar(s): + elif owner.isIterator and not isDefined(d.graph.config, "nimOptIters") and interestingIterVar(s): result = accessViaEnvParam(d.graph, n, owner) else: result = accessViaEnvVar(n, owner, d, c) @@ -880,12 +886,16 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool; idgen: IdGenerator; flags: TransformFlags): PNode = let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro - if body.kind == nkEmpty or + if body.kind == nkEmpty or (jsNoLambdaLifting in g.config.legacyFeatures and + g.config.backend == backendJs and not isCompileTime) or (fn.skipGenericOwner.kind != skModule and force notin flags): # ignore forward declaration: result = body tooEarly = true + if fn.isIterator and isDefined(g.config, "nimOptIters"): + var d = initDetectionPass(g, fn, idgen) + addClosureParam(d, fn, body.info) else: var d = initDetectionPass(g, fn, idgen) detectCapturedVars(body, fn, d) @@ -940,6 +950,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym): break ... """ + if liftingHarmful(g.config, owner): return body if not (body.kind == nkForStmt and body[^2].kind in nkCallKinds): localError(g.config, body.info, "ignored invalid for loop") return body diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 350b4cc25..9ff5c0a9d 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -40,7 +40,7 @@ template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink) proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; - info: TLineInfo; idgen: IdGenerator): PSym + info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo; idgen: IdGenerator) @@ -157,7 +157,7 @@ proc genWasMovedCall(c: var TLiftCtx; op: PSym; x: PNode): PNode = result.add(newSymNode(op)) result.add genAddr(c, x) -proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) = +proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool, enforceWasMoved = false) = case n.kind of nkSym: if c.filterDiscriminator != nil: return @@ -167,6 +167,8 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) enforceDefaultOp: defaultOp(c, f.typ, body, x.dotField(f), b) else: + if enforceWasMoved: + body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x.dotField(f)) fillBody(c, f.typ, body, x.dotField(f), b) of nkNilLit: discard of nkRecCase: @@ -205,7 +207,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) branch[^1] = newNodeI(nkStmtList, c.info) fillBodyObj(c, n[i].lastSon, branch[^1], x, y, - enforceDefaultOp = localEnforceDefaultOp) + enforceDefaultOp = localEnforceDefaultOp, enforceWasMoved = c.kind == attachedAsgn) if branch[^1].len == 0: inc emptyBranches caseStmt.add(branch) if emptyBranches != n.len-1: @@ -216,13 +218,22 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false) c.filterDiscriminator = oldfilterDiscriminator of nkRecList: - for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp) + for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp, enforceWasMoved) else: illFormedAstLocal(n, c.g.config) proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) = if t.baseClass != nil: - fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, x, y) + let dest = newNodeIT(nkHiddenSubConv, c.info, t.baseClass) + dest.add newNodeI(nkEmpty, c.info) + dest.add x + var src = y + if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}: + src = newNodeIT(nkHiddenSubConv, c.info, t.baseClass) + src.add newNodeI(nkEmpty, c.info) + src.add y + + fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, dest, src) fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false) proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) = @@ -279,6 +290,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) = c.kind = attachedDestructor fillBodyObjTImpl(c, t, body, blob, y) c.kind = prevKind + else: fillBodyObjTImpl(c, t, body, x, y) @@ -1034,7 +1046,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = else: discard "cannot copy openArray" - of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, + of tyFromExpr, tyError, tyBuiltInTypeClass, tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything, tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped, tyTypeDesc, tyGenericInvocation, tyForward, tyStatic: @@ -1051,7 +1063,9 @@ proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType; assert typ.kind == tyDistinct let baseType = typ.elementType if getAttachedOp(g, baseType, kind) == nil: - discard produceSym(g, c, baseType, kind, info, idgen) + # TODO: fixme `isDistinct` is a fix for #23552; remove it after + # `-d:nimPreviewNonVarDestructor` becomes the default + discard produceSym(g, c, baseType, kind, info, idgen, isDistinct = true) result = getAttachedOp(g, baseType, kind) setAttachedOp(g, idgen.module, typ, kind, result) @@ -1090,7 +1104,7 @@ proc symDupPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttache incl result.flags, sfGeneratedOp proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp; - info: TLineInfo; idgen: IdGenerator; isDiscriminant = false): PSym = + info: TLineInfo; idgen: IdGenerator; isDiscriminant = false; isDistinct = false): PSym = if kind == attachedDup: return symDupPrototype(g, typ, owner, kind, info, idgen) @@ -1101,7 +1115,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp idgen, result, info) if kind == attachedDestructor and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and - ((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or typ.kind in {tyRef, tyString, tySequence}): + ((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence} and not isDistinct)): dest.typ = typ else: dest.typ = makeVarType(typ.owner, typ, idgen) @@ -1143,13 +1157,13 @@ proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add newAsgnStmt(xx, yy) proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; - info: TLineInfo; idgen: IdGenerator): PSym = + info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym = if typ.kind == tyDistinct: return produceSymDistinctType(g, c, typ, kind, info, idgen) result = getAttachedOp(g, typ, kind) if result == nil: - result = symPrototype(g, typ, typ.owner, kind, info, idgen) + result = symPrototype(g, typ, typ.owner, kind, info, idgen, isDistinct = isDistinct) var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType: typ, idgen: idgen, fn: result) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index dc0b6c360..94a483299 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -92,9 +92,10 @@ type warnStmtListLambda = "StmtListLambda", warnBareExcept = "BareExcept", warnImplicitDefaultValue = "ImplicitDefaultValue", - warnGenericsIgnoredInjection = "GenericsIgnoredInjection", + warnIgnoredSymbolInjection = "IgnoredSymbolInjection", warnStdPrefix = "StdPrefix" warnUser = "User", + warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary", # hints hintSuccess = "Success", hintSuccessX = "SuccessX", hintCC = "CC", @@ -197,9 +198,10 @@ const warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead", warnBareExcept: "$1", warnImplicitDefaultValue: "$1", - warnGenericsIgnoredInjection: "$1", + warnIgnoredSymbolInjection: "$1", warnStdPrefix: "$1 needs the 'std' prefix", warnUser: "$1", + warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` hintSuccessX: "$build\n$loc lines; ${sec}s; $mem; proj: $project; out: $output", @@ -266,6 +268,7 @@ const NotesVerbosity* = computeNotesVerbosity() errXMustBeCompileTime* = "'$1' can only be used in compile-time context" errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected" + errFloatToString* = "cannot convert '$1' to '$2'" type TFileInfo* = object diff --git a/compiler/llstream.nim b/compiler/llstream.nim index fa223b373..cc8148483 100644 --- a/compiler/llstream.nim +++ b/compiler/llstream.nim @@ -68,6 +68,7 @@ when not declared(readLineFromStdin): # fallback implementation: proc readLineFromStdin(prompt: string, line: var string): bool = stdout.write(prompt) + stdout.flushFile() result = readLine(stdin, line) if not result: stdout.write("\n") diff --git a/compiler/lookups.nim b/compiler/lookups.nim index e6b4c8f9a..d8fcf73e0 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -50,7 +50,7 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent = case x.kind of nkIdent: id.add(x.ident.s) of nkSym: id.add(x.sym.name.s) - of nkSymChoices: + of nkSymChoices, nkOpenSym: if x[0].kind == nkSym: id.add(x[0].sym.name.s) else: @@ -63,6 +63,8 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent = result = n[0].sym.name else: handleError(n, origin) + of nkOpenSym: + result = considerQuotedIdent(c, n[0], origin) else: handleError(n, origin) @@ -561,23 +563,33 @@ proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) = var amb: bool = false discard errorUseQualifier(c, info, s, amb) -proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]; prefix = "use one of") = - var err = "ambiguous identifier: '" & candidates[0].name.s & "'" +proc ambiguousIdentifierMsg*(candidates: seq[PSym], prefix = "use one of", indent = 0): string = + result = "" + for i in 0 ..< indent: + result.add(' ') + result.add "ambiguous identifier: '" & candidates[0].name.s & "'" var i = 0 for candidate in candidates: - if i == 0: err.add " -- $1 the following:\n" % prefix - else: err.add "\n" - err.add " " & candidate.owner.name.s & "." & candidate.name.s - err.add ": " & typeToString(candidate.typ) + if i == 0: result.add " -- $1 the following:\n" % prefix + else: result.add "\n" + for i in 0 ..< indent: + result.add(' ') + result.add " " & candidate.owner.name.s & "." & candidate.name.s + result.add ": " & typeToString(candidate.typ) inc i - localError(c.config, info, errGenerated, err) -proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) = +proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]) = + localError(c.config, info, errGenerated, ambiguousIdentifierMsg(candidates)) + +proc ambiguousIdentifierMsg*(choices: PNode, indent = 0): string = var candidates = newSeq[PSym](choices.len) let prefix = if choices[0].typ.kind != tyProc: "use one of" else: "you need a helper proc to disambiguate" for i, n in choices: candidates[i] = n.sym - errorUseQualifier(c, info, candidates, prefix) + result = ambiguousIdentifierMsg(candidates, prefix, indent) + +proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) = + localError(c.config, info, errGenerated, ambiguousIdentifierMsg(choices)) proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") = var err: string @@ -630,9 +642,10 @@ type const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage} -proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind]): seq[PSym] = +proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind], + includePureEnum = false): seq[PSym] = result = searchInScopesFilterBy(c, ident, filter) - if result.len == 0: + if skEnumField in filter and (result.len == 0 or includePureEnum): result.add allPureEnumFields(c, ident) proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = @@ -665,24 +678,33 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = c.isAmbiguous = amb of nkSym: result = n.sym + of nkOpenSym: + result = qualifiedLookUp(c, n[0], flags) of nkDotExpr: result = nil var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule}) if m != nil and m.kind == skModule: var ident: PIdent = nil - if n[1].kind == nkIdent: - ident = n[1].ident - elif n[1].kind == nkAccQuoted: + if n[1].kind == nkAccQuoted: ident = considerQuotedIdent(c, n[1]) + else: + # this includes sym and symchoice nodes, but since we are looking in + # a module, it shouldn't matter what was captured + ident = n[1].getPIdent if ident != nil: if m == c.module: - result = strTableGet(c.topLevelScope.symbols, ident) + var ti: TIdentIter = default(TIdentIter) + result = initIdentIter(ti, c.topLevelScope.symbols, ident) + if result != nil and nextIdentIter(ti, c.topLevelScope.symbols) != nil: + # another symbol exists with same name + c.isAmbiguous = true else: + var amb: bool = false if c.importModuleLookup.getOrDefault(m.name.id).len > 1: - var amb: bool = false result = errorUseQualifier(c, n.info, m, amb) else: - result = someSym(c.graph, m, ident) + result = someSymAmb(c.graph, m, ident, amb) + if amb: c.isAmbiguous = true if result == nil and checkUndeclared in flags: result = errorUndeclaredIdentifierHint(c, ident, n[1].info) elif n[1].kind == nkSym: @@ -701,6 +723,10 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = if result != nil and result.kind == skStub: loadStub(result) proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = + if n.kind == nkOpenSym: + # maybe the logic in semexprs should be mirrored here instead + # for now it only seems this is called for `pickSym` in `getTypeIdent` + return initOverloadIter(o, c, n[0]) o.importIdx = -1 o.marked = initIntSet() case n.kind diff --git a/compiler/main.nim b/compiler/main.nim index 0b74162a9..4c52317cf 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -22,8 +22,6 @@ import modules, modulegraphs, lineinfos, pathutils, vmprofiler -# ensure NIR compiles: -import nir / nir when defined(nimPreviewSlimSystem): import std/[syncio, assertions] @@ -48,9 +46,6 @@ proc writeDepsFile(g: ModuleGraph) = f.writeLine(toFullPath(g.config, k)) f.close() -proc writeNinjaFile(g: ModuleGraph) = - discard "to implement" - proc writeCMakeDepsFile(conf: ConfigRef) = ## write a list of C files for build systems like CMake. ## only updated when the C file list changes. @@ -161,26 +156,6 @@ proc commandCompileToC(graph: ModuleGraph) = if optGenCDeps in graph.config.globalOptions: writeCMakeDepsFile(conf) -proc commandCompileToNir(graph: ModuleGraph) = - let conf = graph.config - extccomp.initVars(conf) - if conf.symbolFiles == disabledSf: - if {optRun, optForceFullMake} * conf.globalOptions == {optRun}: - if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile): - # nothing changed - graph.config.notes = graph.config.mainPackageNotes - return - - if not extccomp.ccHasSaneOverflow(conf): - conf.symbols.defineSymbol("nimEmulateOverflowChecks") - - if conf.symbolFiles == disabledSf: - setPipeLinePass(graph, NirPass) - else: - setPipeLinePass(graph, SemPass) - compilePipelineProject(graph) - writeNinjaFile(graph) - proc commandJsonScript(graph: ModuleGraph) = extccomp.runJsonBuildInstructions(graph.config, graph.config.jsonBuildInstructionsFile) @@ -197,16 +172,13 @@ proc commandCompileToJS(graph: ModuleGraph) = if optGenScript in conf.globalOptions: writeDepsFile(graph) -proc commandInteractive(graph: ModuleGraph; useNir: bool) = +proc commandInteractive(graph: ModuleGraph) = graph.config.setErrorMaxHighMaybe initDefines(graph.config.symbols) - if useNir: - defineSymbol(graph.config.symbols, "noSignalHandler") - else: - defineSymbol(graph.config.symbols, "nimscript") + defineSymbol(graph.config.symbols, "nimscript") # note: seems redundant with -d:nimHasLibFFI when hasFFI: defineSymbol(graph.config.symbols, "nimffi") - setPipeLinePass(graph, if useNir: NirReplPass else: InterpreterPass) + setPipeLinePass(graph, InterpreterPass) compilePipelineSystemModule(graph) if graph.config.commandArgs.len > 0: discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {}) @@ -293,8 +265,6 @@ proc mainCommand*(graph: ModuleGraph) = # and it has added this define implictly, so we must undo that here. # A better solution might be to fix system.nim undefSymbol(conf.symbols, "useNimRtl") - of backendNir: - if conf.exc == excNone: conf.exc = excGoto of backendInvalid: raiseAssert "unreachable" proc compileToBackend() = @@ -305,7 +275,6 @@ proc mainCommand*(graph: ModuleGraph) = of backendCpp: commandCompileToC(graph) of backendObjc: commandCompileToC(graph) of backendJs: commandCompileToJS(graph) - of backendNir: commandCompileToNir(graph) of backendInvalid: raiseAssert "unreachable" template docLikeCmd(body) = @@ -444,7 +413,7 @@ proc mainCommand*(graph: ModuleGraph) = wantMainModule(conf) commandView(graph) #msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!") - of cmdInteractive: commandInteractive(graph, isDefined(conf, "nir")) + of cmdInteractive: commandInteractive(graph) of cmdNimscript: if conf.projectIsCmd or conf.projectIsStdin: discard elif not fileExists(conf.projectFull): diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 75f3a3c70..77762d23a 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -11,7 +11,7 @@ ## represents a complete Nim project. Single modules can either be kept in RAM ## or stored in a rod-file. -import std/[intsets, tables, hashes, strtabs, algorithm] +import std/[intsets, tables, hashes, strtabs, algorithm, os, strutils, parseutils] import ../dist/checksums/src/checksums/md5 import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages, suggestsymdb import ic / [packed_ast, ic] @@ -62,8 +62,6 @@ type CgenPass EvalPass InterpreterPass - NirPass - NirReplPass GenDependPass Docgen2TexPass Docgen2JsonPass @@ -276,6 +274,25 @@ proc someSym*(g: ModuleGraph; m: PSym; name: PIdent): PSym = else: result = strTableGet(g.ifaces[m.position].interfSelect(importHidden), name) +proc someSymAmb*(g: ModuleGraph; m: PSym; name: PIdent; amb: var bool): PSym = + let importHidden = optImportHidden in m.options + if isCachedModule(g, m): + result = nil + for s in interfaceSymbols(g.config, g.cache, g.packed, FileIndex(m.position), name, importHidden): + if result == nil: + # set result to the first symbol + result = s + else: + # another symbol found + amb = true + break + else: + var ti: TIdentIter = default(TIdentIter) + result = initIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden), name) + if result != nil and nextIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden)) != nil: + # another symbol exists with same name + amb = true + proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym = result = someSym(g, g.systemModule, name) @@ -311,7 +328,7 @@ proc resolveInst(g: ModuleGraph; t: var LazyInstantiation): PInstantiation = t.inst = result assert result != nil -proc resolveAttachedOp(g: ModuleGraph; t: var LazySym): PSym = +proc resolveAttachedOp*(g: ModuleGraph; t: var LazySym): PSym = result = t.sym if result == nil: result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed) @@ -454,6 +471,49 @@ proc createMagic*(g: ModuleGraph; idgen: IdGenerator; name: string, m: TMagic): proc createMagic(g: ModuleGraph; name: string, m: TMagic): PSym = result = createMagic(g, g.idgen, name, m) +proc uniqueModuleName*(conf: ConfigRef; m: PSym): string = + ## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'} + ## so that it is useful as a C identifier snippet. + let fid = FileIndex(m.position) + let path = AbsoluteFile toFullPath(conf, fid) + var isLib = false + var rel = "" + if path.string.startsWith(conf.libpath.string): + isLib = true + rel = relativeTo(path, conf.libpath).string + else: + rel = relativeTo(path, conf.projectPath).string + + if not isLib and not belongsToProjectPackage(conf, m): + # special handlings for nimble packages + when DirSep == '\\': + let rel2 = replace(rel, '\\', '/') + else: + let rel2 = rel + const pkgs2 = "pkgs2/" + var start = rel2.find(pkgs2) + if start >= 0: + start += pkgs2.len + start += skipUntil(rel2, {'/'}, start) + if start+1 < rel2.len: + rel = "pkg/" & rel2[start+1..<rel.len] # strips paths + + let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len + result = newStringOfCap(trunc) + for i in 0..<trunc: + let c = rel[i] + case c + of 'a'..'z', '0'..'9': + result.add c + of {os.DirSep, os.AltSep}: + result.add 'Z' # because it looks a bit like '/' + of '.': + result.add 'O' # a circle + else: + # We mangle upper letters too so that there cannot + # be clashes with our special meanings of 'Z' and 'O' + result.addInt ord(c) + proc registerModule*(g: ModuleGraph; m: PSym) = assert m != nil assert m.kind == skModule @@ -465,7 +525,7 @@ proc registerModule*(g: ModuleGraph; m: PSym) = setLen(g.packed.pm, m.position + 1) g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[], - uniqueName: rope(uniqueModuleName(g.config, FileIndex(m.position)))) + uniqueName: rope(uniqueModuleName(g.config, m))) initStrTables(g, m) proc registerModuleById*(g: ModuleGraph; m: FileIndex) = @@ -617,7 +677,6 @@ proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) = if m != nil: g.suggestSymbols.del(fileIdx) g.suggestErrors.del(fileIdx) - g.resetForBackend incl m.flags, sfDirty proc unmarkAllDirty*(g: ModuleGraph) = @@ -680,8 +739,6 @@ proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex; proc configComplete*(g: ModuleGraph) = rememberStartupConfig(g.startupPackedConfig, g.config) -from std/strutils import repeat, `%` - proc onProcessing*(graph: ModuleGraph, fileIdx: FileIndex, moduleStatus: string, fromModule: PSym, ) = let conf = graph.config let isNimscript = conf.isDefined("nimscript") diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 1e0a90ebd..c49ca8c9b 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -629,7 +629,7 @@ proc warningDeprecated*(conf: ConfigRef, info: TLineInfo = gCmdLineInfo, msg = " message(conf, info, warnDeprecated, msg) proc internalErrorImpl(conf: ConfigRef; info: TLineInfo, errMsg: string, info2: InstantiationInfo) = - if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return + if conf.cmd in {cmdIdeTools, cmdCheck} and conf.structuredErrorHook.isNil: return writeContext(conf, info) liMessage(conf, info, errInternal, errMsg, doAbort, info2) @@ -651,13 +651,16 @@ template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraM let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName liMessage(conf, info, msg, m, doNothing, instLoc()) -proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = - if i.fileIndex.int32 < 0: +proc quotedFilename*(conf: ConfigRef; fi: FileIndex): Rope = + if fi.int32 < 0: result = makeCString "???" elif optExcessiveStackTrace in conf.globalOptions: - result = conf.m.fileInfos[i.fileIndex.int32].quotedFullName + result = conf.m.fileInfos[fi.int32].quotedFullName else: - result = conf.m.fileInfos[i.fileIndex.int32].quotedName + result = conf.m.fileInfos[fi.int32].quotedName + +proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = + quotedFilename(conf, i.fileIndex) template listMsg(title, r) = msgWriteln(conf, title, {msgNoUnitSep}) @@ -666,31 +669,6 @@ template listMsg(title, r) = proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax) proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax) -proc uniqueModuleName*(conf: ConfigRef; fid: FileIndex): string = - ## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'} - ## so that it is useful as a C identifier snippet. - let path = AbsoluteFile toFullPath(conf, fid) - let rel = - if path.string.startsWith(conf.libpath.string): - relativeTo(path, conf.libpath).string - else: - relativeTo(path, conf.projectPath).string - let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len - result = newStringOfCap(trunc) - for i in 0..<trunc: - let c = rel[i] - case c - of 'a'..'z': - result.add c - of {os.DirSep, os.AltSep}: - result.add 'Z' # because it looks a bit like '/' - of '.': - result.add 'O' # a circle - else: - # We mangle upper letters and digits too so that there cannot - # be clashes with our special meanings of 'Z' and 'O' - result.addInt ord(c) - proc genSuccessX*(conf: ConfigRef) = let mem = when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem" diff --git a/compiler/ndi.nim b/compiler/ndi.nim index a9d9cfe79..cc18ab39f 100644 --- a/compiler/ndi.nim +++ b/compiler/ndi.nim @@ -29,7 +29,7 @@ proc doWrite(f: var NdiFile; s: PSym; conf: ConfigRef) = f.buf.add "\t" f.buf.addInt s.info.col.int f.f.write(s.name.s, "\t") - f.f.writeRope(s.loc.r) + f.f.writeRope(s.loc.snippet) f.f.writeLine("\t", toFullPath(conf, s.info), "\t", f.buf) template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) = diff --git a/compiler/nim.nim b/compiler/nim.nim index 3473ea443..005f11a58 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -116,9 +116,9 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = conf.backend = backendC if conf.selectedGC == gcUnselected: - if conf.backend in {backendC, backendCpp, backendObjc, backendNir} or - (conf.cmd == cmdInteractive and isDefined(conf, "nir")) or - (conf.cmd in cmdDocLike and conf.backend != backendJs): + if conf.backend in {backendC, backendCpp, backendObjc} or + (conf.cmd in cmdDocLike and conf.backend != backendJs) or + conf.cmd == cmdGendepend: initOrcDefines(conf) mainCommand(graph) diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim deleted file mode 100644 index c8954548f..000000000 --- a/compiler/nir/ast2ir.nim +++ /dev/null @@ -1,2638 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -import std / [assertions, tables, sets] -import ".." / [ast, astalgo, types, options, lineinfos, msgs, magicsys, - modulegraphs, renderer, transf, bitsets, trees, nimsets, - expanddefaults] -from ".." / lowerings import lowerSwap, lowerTupleUnpacking -from ".." / pathutils import customPath -import .. / ic / bitabs - -import nirtypes, nirinsts, nirlineinfos, nirslots, types2ir, nirfiles - -when defined(nimCompilerStacktraceHints): - import std/stackframes - -type - ModuleCon* = ref object - nirm*: ref NirModule - types: TypesCon - module*: PSym - graph*: ModuleGraph - nativeIntId, nativeUIntId: TypeId - strPayloadId: (TypeId, TypeId) - idgen: IdGenerator - processedProcs, pendingProcsAsSet: HashSet[ItemId] - pendingProcs: seq[PSym] # procs we still need to generate code for - pendingVarsAsSet: HashSet[ItemId] - pendingVars: seq[PSym] - noModularity*: bool - inProc: int - toSymId: Table[ItemId, SymId] - symIdCounter: int32 - - ProcCon* = object - config*: ConfigRef - lit: Literals - lastFileKey: FileIndex - lastFileVal: LitId - labelGen: int - exitLabel: LabelId - #code*: Tree - blocks: seq[(PSym, LabelId)] - sm: SlotManager - idgen: IdGenerator - m: ModuleCon - prc: PSym - options: TOptions - -template code(c: ProcCon): Tree = c.m.nirm.code - -proc initModuleCon*(graph: ModuleGraph; config: ConfigRef; idgen: IdGenerator; module: PSym; - nirm: ref NirModule): ModuleCon = - #let lit = Literals() # must be shared - result = ModuleCon(graph: graph, types: initTypesCon(config), nirm: nirm, - idgen: idgen, module: module) - case config.target.intSize - of 2: - result.nativeIntId = Int16Id - result.nativeUIntId = UInt16Id - of 4: - result.nativeIntId = Int32Id - result.nativeUIntId = UInt16Id - else: - result.nativeIntId = Int64Id - result.nativeUIntId = UInt16Id - result.strPayloadId = strPayloadPtrType(result.types, result.nirm.types) - nirm.namespace = nirm.lit.strings.getOrIncl(customPath(toFullPath(config, module.info))) - nirm.intbits = uint32(config.target.intSize * 8) - -proc initProcCon*(m: ModuleCon; prc: PSym; config: ConfigRef): ProcCon = - result = ProcCon(m: m, sm: initSlotManager({}), prc: prc, config: config, - lit: m.nirm.lit, idgen: m.idgen, - options: if prc != nil: prc.options - else: config.options) - result.exitLabel = newLabel(result.labelGen) - -proc toLineInfo(c: var ProcCon; i: TLineInfo): PackedLineInfo = - var val: LitId - if c.lastFileKey == i.fileIndex: - val = c.lastFileVal - else: - val = c.lit.strings.getOrIncl(toFullPath(c.config, i.fileIndex)) - # remember the entry: - c.lastFileKey = i.fileIndex - c.lastFileVal = val - result = pack(c.m.nirm.man, val, int32 i.line, int32 i.col) - -proc bestEffort(c: ProcCon): TLineInfo = - if c.prc != nil: - c.prc.info - else: - c.m.module.info - -proc popBlock(c: var ProcCon; oldLen: int) = - c.blocks.setLen(oldLen) - -template withBlock(labl: PSym; info: PackedLineInfo; asmLabl: LabelId; body: untyped) {.dirty.} = - var oldLen {.gensym.} = c.blocks.len - c.blocks.add (labl, asmLabl) - body - popBlock(c, oldLen) - -type - GenFlag = enum - gfAddrOf # load the address of the expression - gfToOutParam # the expression is passed to an `out` parameter - GenFlags = set[GenFlag] - -proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) - -proc genScope(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = - openScope c.sm - gen c, n, d, flags - closeScope c.sm - -proc freeTemp(c: var ProcCon; tmp: Value) = - let s = extractTemp(tmp) - if s != SymId(-1): - freeTemp(c.sm, s) - -proc freeTemps(c: var ProcCon; tmps: openArray[Value]) = - for t in tmps: freeTemp(c, t) - -proc typeToIr(m: ModuleCon; t: PType): TypeId = - typeToIr(m.types, m.nirm.types, t) - -proc allocTemp(c: var ProcCon; t: TypeId): SymId = - if c.m.noModularity: - result = allocTemp(c.sm, t, c.m.symIdCounter) - else: - result = allocTemp(c.sm, t, c.idgen.symId) - -const - ListSymId = -1 - -proc toSymId(c: var ProcCon; s: PSym): SymId = - if c.m.noModularity: - result = c.m.toSymId.getOrDefault(s.itemId, SymId(-1)) - if result.int < 0: - inc c.m.symIdCounter - result = SymId(c.m.symIdCounter) - c.m.toSymId[s.itemId] = result - when ListSymId != -1: - if result.int == ListSymId or s.name.s == "echoBinSafe": - echo result.int, " is ", s.name.s, " ", c.m.graph.config $ s.info, " ", s.flags - writeStackTrace() - else: - result = SymId(s.itemId.item) - -proc getTemp(c: var ProcCon; n: PNode): Value = - let info = toLineInfo(c, n.info) - let t = typeToIr(c.m, n.typ) - let tmp = allocTemp(c, t) - c.code.addSummon info, tmp, t - result = localToValue(info, tmp) - -proc getTemp(c: var ProcCon; t: TypeId; info: PackedLineInfo): Value = - let tmp = allocTemp(c, t) - c.code.addSummon info, tmp, t - result = localToValue(info, tmp) - -proc gen(c: var ProcCon; n: PNode; flags: GenFlags = {}) = - var tmp = default(Value) - gen(c, n, tmp, flags) - freeTemp c, tmp - -proc genScope(c: var ProcCon; n: PNode; flags: GenFlags = {}) = - openScope c.sm - gen c, n, flags - closeScope c.sm - -proc genx(c: var ProcCon; n: PNode; flags: GenFlags = {}): Value = - result = default(Value) - gen(c, n, result, flags) - assert Tree(result).len > 0, $n - -proc clearDest(c: var ProcCon; n: PNode; d: var Value) {.inline.} = - when false: - if n.typ.isNil or n.typ.kind == tyVoid: - let s = extractTemp(d) - if s != SymId(-1): - freeLoc(c.sm, s) - -proc isNotOpr(n: PNode): bool = - n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mNot - -proc jmpBack(c: var ProcCon; n: PNode; lab: LabelId) = - c.code.gotoLabel toLineInfo(c, n.info), GotoLoop, lab - -type - JmpKind = enum opcFJmp, opcTJmp - -proc xjmp(c: var ProcCon; n: PNode; jk: JmpKind; v: Value): LabelId = - result = newLabel(c.labelGen) - let info = toLineInfo(c, n.info) - buildTyped c.code, info, Select, Bool8Id: - c.code.copyTree Tree(v) - build c.code, info, SelectPair: - build c.code, info, SelectValue: - c.code.boolVal(c.lit.numbers, info, jk == opcTJmp) - c.code.gotoLabel info, Goto, result - -proc patch(c: var ProcCon; n: PNode; L: LabelId) = - addLabel c.code, toLineInfo(c, n.info), Label, L - -proc genWhile(c: var ProcCon; n: PNode) = - # lab1: - # cond, tmp - # fjmp tmp, lab2 - # body - # jmp lab1 - # lab2: - let info = toLineInfo(c, n.info) - let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel) - withBlock(nil, info, lab1): - if isTrue(n[0]): - c.gen(n[1]) - c.jmpBack(n, lab1) - elif isNotOpr(n[0]): - var tmp = c.genx(n[0][1]) - let lab2 = c.xjmp(n, opcTJmp, tmp) - c.freeTemp(tmp) - c.gen(n[1]) - c.jmpBack(n, lab1) - c.patch(n, lab2) - else: - var tmp = c.genx(n[0]) - let lab2 = c.xjmp(n, opcFJmp, tmp) - c.freeTemp(tmp) - c.gen(n[1]) - c.jmpBack(n, lab1) - c.patch(n, lab2) - -proc genBlock(c: var ProcCon; n: PNode; d: var Value) = - openScope c.sm - let info = toLineInfo(c, n.info) - let lab1 = newLabel(c.labelGen) - - withBlock(n[0].sym, info, lab1): - c.gen(n[1], d) - - c.code.addLabel(info, Label, lab1) - closeScope c.sm - c.clearDest(n, d) - -proc jumpTo(c: var ProcCon; n: PNode; L: LabelId) = - c.code.addLabel(toLineInfo(c, n.info), Goto, L) - -proc genBreak(c: var ProcCon; n: PNode) = - if n[0].kind == nkSym: - for i in countdown(c.blocks.len-1, 0): - if c.blocks[i][0] == n[0].sym: - c.jumpTo n, c.blocks[i][1] - return - localError(c.config, n.info, "NIR problem: cannot find 'break' target") - else: - c.jumpTo n, c.blocks[c.blocks.high][1] - -proc genIf(c: var ProcCon; n: PNode; d: var Value) = - # if (!expr1) goto lab1; - # thenPart - # goto LEnd - # lab1: - # if (!expr2) goto lab2; - # thenPart2 - # goto LEnd - # lab2: - # elsePart - # Lend: - if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) - var ending = newLabel(c.labelGen) - for i in 0..<n.len: - var it = n[i] - if it.len == 2: - let info = toLineInfo(c, it[0].info) - var elsePos: LabelId - if isNotOpr(it[0]): - let tmp = c.genx(it[0][1]) - elsePos = c.xjmp(it[0][1], opcTJmp, tmp) # if true - c.freeTemp tmp - else: - let tmp = c.genx(it[0]) - elsePos = c.xjmp(it[0], opcFJmp, tmp) # if false - c.freeTemp tmp - c.clearDest(n, d) - if isEmptyType(it[1].typ): # maybe noreturn call, don't touch `d` - c.genScope(it[1]) - else: - c.genScope(it[1], d) # then part - if i < n.len-1: - c.jumpTo it[1], ending - c.patch(it, elsePos) - else: - c.clearDest(n, d) - if isEmptyType(it[0].typ): # maybe noreturn call, don't touch `d` - c.genScope(it[0]) - else: - c.genScope(it[0], d) - c.patch(n, ending) - c.clearDest(n, d) - -proc tempToDest(c: var ProcCon; n: PNode; d: var Value; tmp: Value) = - if isEmpty(d): - d = tmp - else: - let info = toLineInfo(c, n.info) - buildTyped c.code, info, Asgn, typeToIr(c.m, n.typ): - c.code.copyTree d - c.code.copyTree tmp - freeTemp(c, tmp) - -proc genAndOr(c: var ProcCon; n: PNode; opc: JmpKind; d: var Value) = - # asgn d, a - # tjmp|fjmp lab1 - # asgn d, b - # lab1: - var tmp = getTemp(c, n) - c.gen(n[1], tmp) - let lab1 = c.xjmp(n, opc, tmp) - c.gen(n[2], tmp) - c.patch(n, lab1) - tempToDest c, n, d, tmp - -proc unused(c: var ProcCon; n: PNode; x: Value) {.inline.} = - if hasValue(x): - #debug(n) - localError(c.config, n.info, "not unused") - -proc caseValue(c: var ProcCon; n: PNode) = - let info = toLineInfo(c, n.info) - build c.code, info, SelectValue: - let x = genx(c, n) - c.code.copyTree x - freeTemp(c, x) - -proc caseRange(c: var ProcCon; n: PNode) = - let info = toLineInfo(c, n.info) - build c.code, info, SelectRange: - let x = genx(c, n[0]) - let y = genx(c, n[1]) - c.code.copyTree x - c.code.copyTree y - freeTemp(c, y) - freeTemp(c, x) - -proc addUseCodegenProc(c: var ProcCon; dest: var Tree; name: string; info: PackedLineInfo) = - let cp = getCompilerProc(c.m.graph, name) - let theProc = c.genx newSymNode(cp) - copyTree c.code, theProc - -template buildCond(useNegation: bool; cond: typed; body: untyped) = - let lab = newLabel(c.labelGen) - buildTyped c.code, info, Select, Bool8Id: - c.code.copyTree cond - build c.code, info, SelectPair: - build c.code, info, SelectValue: - c.code.boolVal(c.lit.numbers, info, useNegation) - c.code.gotoLabel info, Goto, lab - - body - c.code.addLabel info, Label, lab - -template buildIf(cond: typed; body: untyped) = - buildCond false, cond, body - -template buildIfNot(cond: typed; body: untyped) = - buildCond true, cond, body - -template buildIfThenElse(cond: typed; then, otherwise: untyped) = - let lelse = newLabel(c.labelGen) - let lend = newLabel(c.labelGen) - buildTyped c.code, info, Select, Bool8Id: - c.code.copyTree cond - build c.code, info, SelectPair: - build c.code, info, SelectValue: - c.code.boolVal(c.lit.numbers, info, false) - c.code.gotoLabel info, Goto, lelse - - then() - c.code.gotoLabel info, Goto, lend - c.code.addLabel info, Label, lelse - otherwise() - c.code.addLabel info, Label, lend - -include stringcases - -proc genCase(c: var ProcCon; n: PNode; d: var Value) = - if not isEmptyType(n.typ): - if isEmpty(d): d = getTemp(c, n) - else: - unused(c, n, d) - - if n[0].typ.skipTypes(abstractInst).kind == tyString: - genStringCase(c, n, d) - return - - var sections = newSeqOfCap[LabelId](n.len-1) - let ending = newLabel(c.labelGen) - let info = toLineInfo(c, n.info) - let tmp = c.genx(n[0]) - buildTyped c.code, info, Select, typeToIr(c.m, n[0].typ): - c.code.copyTree tmp - for i in 1..<n.len: - let section = newLabel(c.labelGen) - sections.add section - let it = n[i] - let itinfo = toLineInfo(c, it.info) - build c.code, itinfo, SelectPair: - build c.code, itinfo, SelectList: - for j in 0..<it.len-1: - if it[j].kind == nkRange: - caseRange c, it[j] - else: - caseValue c, it[j] - c.code.addLabel itinfo, Goto, section - c.freeTemp tmp - for i in 1..<n.len: - let it = n[i] - let itinfo = toLineInfo(c, it.info) - c.code.addLabel itinfo, Label, sections[i-1] - c.gen it.lastSon - if i != n.len-1: - c.code.addLabel itinfo, Goto, ending - c.code.addLabel info, Label, ending - -proc rawCall(c: var ProcCon; info: PackedLineInfo; opc: Opcode; t: TypeId; args: var openArray[Value]) = - buildTyped c.code, info, opc, t: - if opc in {CheckedCall, CheckedIndirectCall}: - c.code.addLabel info, CheckedGoto, c.exitLabel - for a in mitems(args): - c.code.copyTree a - freeTemp c, a - -proc canRaiseDisp(c: ProcCon; n: PNode): bool = - # we assume things like sysFatal cannot raise themselves - if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}: - result = false - elif optPanics in c.config.globalOptions or - (n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and - sfSystemRaisesDefect notin n.sym.flags): - # we know we can be strict: - result = canRaise(n) - else: - # we have to be *very* conservative: - result = canRaiseConservative(n) - -proc genCall(c: var ProcCon; n: PNode; d: var Value) = - let canRaise = canRaiseDisp(c, n[0]) - - let opc = if n[0].kind == nkSym and n[0].sym.kind in routineKinds: - (if canRaise: CheckedCall else: Call) - else: - (if canRaise: CheckedIndirectCall else: IndirectCall) - let info = toLineInfo(c, n.info) - - # In the IR we cannot nest calls. Thus we use two passes: - var args: seq[Value] = @[] - var t = n[0].typ - if t != nil: t = t.skipTypes(abstractInst) - args.add genx(c, n[0]) - for i in 1..<n.len: - if t != nil and i < t.len: - if isCompileTimeOnly(t[i]): discard - elif isOutParam(t[i]): args.add genx(c, n[i], {gfToOutParam}) - else: args.add genx(c, n[i]) - else: - args.add genx(c, n[i]) - - let tb = typeToIr(c.m, n.typ) - if not isEmptyType(n.typ): - if isEmpty(d): d = getTemp(c, n) - # XXX Handle problematic aliasing here: `a = f_canRaise(a)`. - buildTyped c.code, info, Asgn, tb: - c.code.copyTree d - rawCall c, info, opc, tb, args - else: - rawCall c, info, opc, tb, args - freeTemps c, args - -proc genRaise(c: var ProcCon; n: PNode) = - let info = toLineInfo(c, n.info) - let tb = typeToIr(c.m, n[0].typ) - - let d = genx(c, n[0]) - buildTyped c.code, info, SetExc, tb: - c.code.copyTree d - c.freeTemp(d) - c.code.addLabel info, Goto, c.exitLabel - -proc genReturn(c: var ProcCon; n: PNode) = - if n[0].kind != nkEmpty: - gen(c, n[0]) - # XXX Block leave actions? - let info = toLineInfo(c, n.info) - c.code.addLabel info, Goto, c.exitLabel - -proc genTry(c: var ProcCon; n: PNode; d: var Value) = - if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) - var endings: seq[LabelId] = @[] - let ehPos = newLabel(c.labelGen) - let oldExitLab = c.exitLabel - c.exitLabel = ehPos - if isEmptyType(n[0].typ): # maybe noreturn call, don't touch `d` - c.gen(n[0]) - else: - c.gen(n[0], d) - c.clearDest(n, d) - - # Add a jump past the exception handling code - let jumpToFinally = newLabel(c.labelGen) - c.jumpTo n, jumpToFinally - # This signals where the body ends and where the exception handling begins - c.patch(n, ehPos) - c.exitLabel = oldExitLab - for i in 1..<n.len: - let it = n[i] - if it.kind != nkFinally: - # first opcExcept contains the end label of the 'except' block: - let endExcept = newLabel(c.labelGen) - for j in 0..<it.len - 1: - assert(it[j].kind == nkType) - let typ = it[j].typ.skipTypes(abstractPtrs-{tyTypeDesc}) - let itinfo = toLineInfo(c, it[j].info) - build c.code, itinfo, TestExc: - c.code.addTyped itinfo, typeToIr(c.m, typ) - if it.len == 1: - let itinfo = toLineInfo(c, it.info) - build c.code, itinfo, TestExc: - c.code.addTyped itinfo, VoidId - let body = it.lastSon - if isEmptyType(body.typ): # maybe noreturn call, don't touch `d` - c.gen(body) - else: - c.gen(body, d) - c.clearDest(n, d) - if i < n.len: - endings.add newLabel(c.labelGen) - c.patch(it, endExcept) - let fin = lastSon(n) - # we always generate an 'opcFinally' as that pops the safepoint - # from the stack if no exception is raised in the body. - c.patch(fin, jumpToFinally) - #c.gABx(fin, opcFinally, 0, 0) - for endPos in endings: c.patch(n, endPos) - if fin.kind == nkFinally: - c.gen(fin[0]) - c.clearDest(n, d) - #c.gABx(fin, opcFinallyEnd, 0, 0) - -template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar -proc isGlobal(n: PNode): bool = n.kind == nkSym and isGlobal(n.sym) - -proc genField(c: var ProcCon; n: PNode; d: var Value) = - var pos: int - if n.kind != nkSym or n.sym.kind != skField: - localError(c.config, n.info, "no field symbol") - pos = 0 - else: - pos = n.sym.position - d.addImmediateVal toLineInfo(c, n.info), pos - -proc genIndex(c: var ProcCon; n: PNode; arr: PType; d: var Value) = - let info = toLineInfo(c, n.info) - if arr.skipTypes(abstractInst).kind == tyArray and - (let offset = firstOrd(c.config, arr); offset != Zero): - let x = c.genx(n) - buildTyped d, info, Sub, c.m.nativeIntId: - copyTree d.Tree, x - d.addImmediateVal toLineInfo(c, n.info), toInt(offset) - else: - c.gen(n, d) - if optBoundsCheck in c.options: - let idx = move d - build d, info, CheckedIndex: - d.Tree.addLabel info, CheckedGoto, c.exitLabel - copyTree d.Tree, idx - let x = toInt64 lengthOrd(c.config, arr) - d.addIntVal c.lit.numbers, info, c.m.nativeIntId, x - -proc rawGenNew(c: var ProcCon; d: Value; refType: PType; ninfo: TLineInfo; needsInit: bool) = - assert refType.kind == tyRef - let baseType = refType.elementType - - let info = toLineInfo(c, ninfo) - let codegenProc = magicsys.getCompilerProc(c.m.graph, - if needsInit: "nimNewObj" else: "nimNewObjUninit") - let refTypeIr = typeToIr(c.m, refType) - buildTyped c.code, info, Asgn, refTypeIr: - copyTree c.code, d - buildTyped c.code, info, Cast, refTypeIr: - buildTyped c.code, info, Call, VoidPtrId: - let theProc = c.genx newSymNode(codegenProc, ninfo) - copyTree c.code, theProc - c.code.addImmediateVal info, int(getSize(c.config, baseType)) - c.code.addImmediateVal info, int(getAlign(c.config, baseType)) - -proc genNew(c: var ProcCon; n: PNode; needsInit: bool) = - # If in doubt, always follow the blueprint of the C code generator for `mm:orc`. - let refType = n[1].typ.skipTypes(abstractInstOwned) - let d = genx(c, n[1]) - rawGenNew c, d, refType, n.info, needsInit - freeTemp c, d - -proc genNewSeqOfCap(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let seqtype = skipTypes(n.typ, abstractVarRange) - let baseType = seqtype.elementType - var a = c.genx(n[1]) - if isEmpty(d): d = getTemp(c, n) - # $1.len = 0 - buildTyped c.code, info, Asgn, c.m.nativeIntId: - buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype): - copyTree c.code, d - c.code.addImmediateVal info, 0 - c.code.addImmediateVal info, 0 - # $1.p = ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3)) - let payloadPtr = seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[0] - buildTyped c.code, info, Asgn, payloadPtr: - # $1.p - buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype): - copyTree c.code, d - c.code.addImmediateVal info, 1 - # ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3)) - buildTyped c.code, info, Cast, payloadPtr: - buildTyped c.code, info, Call, VoidPtrId: - let codegenProc = magicsys.getCompilerProc(c.m.graph, "newSeqPayloadUninit") - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - copyTree c.code, a - c.code.addImmediateVal info, int(getSize(c.config, baseType)) - c.code.addImmediateVal info, int(getAlign(c.config, baseType)) - freeTemp c, a - -proc genNewSeqPayload(c: var ProcCon; info: PackedLineInfo; d, b: Value; seqtype: PType) = - let baseType = seqtype.elementType - # $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3)) - let payloadPtr = seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[0] - - # $1.len = $2 - buildTyped c.code, info, Asgn, c.m.nativeIntId: - buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype): - copyTree c.code, d - c.code.addImmediateVal info, 0 - copyTree c.code, b - - buildTyped c.code, info, Asgn, payloadPtr: - # $1.p - buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype): - copyTree c.code, d - c.code.addImmediateVal info, 1 - # ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3)) - buildTyped c.code, info, Cast, payloadPtr: - buildTyped c.code, info, Call, VoidPtrId: - let codegenProc = magicsys.getCompilerProc(c.m.graph, "newSeqPayload") - let theProc = c.genx newSymNode(codegenProc) - copyTree c.code, theProc - copyTree c.code, b - c.code.addImmediateVal info, int(getSize(c.config, baseType)) - c.code.addImmediateVal info, int(getAlign(c.config, baseType)) - -proc genNewSeq(c: var ProcCon; n: PNode) = - let info = toLineInfo(c, n.info) - let seqtype = skipTypes(n[1].typ, abstractVarRange) - var d = c.genx(n[1]) - var b = c.genx(n[2]) - - genNewSeqPayload(c, info, d, b, seqtype) - - freeTemp c, b - freeTemp c, d - -template intoDest*(d: var Value; info: PackedLineInfo; typ: TypeId; body: untyped) = - if typ == VoidId: - body(c.code) - elif isEmpty(d): - body(Tree(d)) - else: - buildTyped c.code, info, Asgn, typ: - copyTree c.code, d - body(c.code) - -template valueIntoDest(c: var ProcCon; info: PackedLineInfo; d: var Value; typ: PType; body: untyped) = - if isEmpty(d): - body(Tree d) - else: - buildTyped c.code, info, Asgn, typeToIr(c.m, typ): - copyTree c.code, d - body(c.code) - -template constrIntoDest(c: var ProcCon; info: PackedLineInfo; d: var Value; typ: PType; body: untyped) = - var tmp = default(Value) - body(Tree tmp) - if isEmpty(d): - d = tmp - else: - buildTyped c.code, info, Asgn, typeToIr(c.m, typ): - copyTree c.code, d - copyTree c.code, tmp - -proc genBinaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) = - let info = toLineInfo(c, n.info) - let tmp = c.genx(n[1]) - let tmp2 = c.genx(n[2]) - let t = typeToIr(c.m, n.typ) - template body(target) = - buildTyped target, info, opc, t: - if opc in {CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedMod}: - target.addLabel info, CheckedGoto, c.exitLabel - copyTree target, tmp - copyTree target, tmp2 - intoDest d, info, t, body - c.freeTemp(tmp) - c.freeTemp(tmp2) - -proc genCmpOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) = - let info = toLineInfo(c, n.info) - let tmp = c.genx(n[1]) - let tmp2 = c.genx(n[2]) - let t = typeToIr(c.m, n[1].typ) - template body(target) = - buildTyped target, info, opc, t: - copyTree target, tmp - copyTree target, tmp2 - intoDest d, info, Bool8Id, body - c.freeTemp(tmp) - c.freeTemp(tmp2) - -proc genUnaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) = - let info = toLineInfo(c, n.info) - let tmp = c.genx(n[1]) - let t = typeToIr(c.m, n.typ) - template body(target) = - buildTyped target, info, opc, t: - copyTree target, tmp - intoDest d, info, t, body - c.freeTemp(tmp) - -proc genIncDec(c: var ProcCon; n: PNode; opc: Opcode) = - let info = toLineInfo(c, n.info) - let t = typeToIr(c.m, skipTypes(n[1].typ, abstractVar)) - - let d = c.genx(n[1]) - let tmp = c.genx(n[2]) - # we produce code like: i = i + 1 - buildTyped c.code, info, Asgn, t: - copyTree c.code, d - buildTyped c.code, info, opc, t: - if opc in {CheckedAdd, CheckedSub}: - c.code.addLabel info, CheckedGoto, c.exitLabel - copyTree c.code, d - copyTree c.code, tmp - c.freeTemp(tmp) - #c.genNarrow(n[1], d) - c.freeTemp(d) - -proc genArrayLen(c: var ProcCon; n: PNode; d: var Value) = - #echo c.m.graph.config $ n.info, " ", n - let info = toLineInfo(c, n.info) - var a = n[1] - #if a.kind == nkHiddenAddr: a = a[0] - var typ = skipTypes(a.typ, abstractVar + tyUserTypeClasses) - case typ.kind - of tyOpenArray, tyVarargs: - let xa = c.genx(a) - template body(target) = - buildTyped target, info, FieldAt, typeToIr(c.m, typ): - copyTree target, xa - target.addImmediateVal info, 1 # (p, len)-pair so len is at index 1 - intoDest d, info, c.m.nativeIntId, body - - of tyCstring: - let xa = c.genx(a) - if isEmpty(d): d = getTemp(c, n) - buildTyped c.code, info, Call, c.m.nativeIntId: - let codegenProc = magicsys.getCompilerProc(c.m.graph, "nimCStrLen") - assert codegenProc != nil - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - copyTree c.code, xa - - of tyString, tySequence: - let xa = c.genx(a) - - if typ.kind == tySequence: - # we go through a temporary here because people write bullshit code. - if isEmpty(d): d = getTemp(c, n) - - template body(target) = - buildTyped target, info, FieldAt, typeToIr(c.m, typ): - copyTree target, xa - target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 - intoDest d, info, c.m.nativeIntId, body - - of tyArray: - template body(target) = - target.addIntVal(c.lit.numbers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ)) - intoDest d, info, c.m.nativeIntId, body - else: internalError(c.config, n.info, "genArrayLen()") - -proc genUnaryMinus(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let tmp = c.genx(n[1]) - let t = typeToIr(c.m, n.typ) - template body(target) = - buildTyped target, info, Sub, t: - # Little hack: This works because we know that `0.0` is all 0 bits: - target.addIntVal(c.lit.numbers, info, t, 0) - copyTree target, tmp - intoDest d, info, t, body - c.freeTemp(tmp) - -proc genHigh(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let t = typeToIr(c.m, n.typ) - var x = default(Value) - genArrayLen(c, n, x) - template body(target) = - buildTyped target, info, Sub, t: - copyTree target, x - target.addIntVal(c.lit.numbers, info, t, 1) - intoDest d, info, t, body - c.freeTemp x - -proc genBinaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string) = - let info = toLineInfo(c, n.info) - let xa = c.genx(n[1]) - let xb = c.genx(n[2]) - if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) - - let t = typeToIr(c.m, n.typ) - template body(target) = - buildTyped target, info, Call, t: - let codegenProc = magicsys.getCompilerProc(c.m.graph, compilerProc) - #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info) - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree target, theProc - copyTree target, xa - copyTree target, xb - - intoDest d, info, t, body - c.freeTemp xb - c.freeTemp xa - -proc genUnaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string; argAt = 1) = - let info = toLineInfo(c, n.info) - let xa = c.genx(n[argAt]) - if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) - - let t = typeToIr(c.m, n.typ) - template body(target) = - buildTyped target, info, Call, t: - let codegenProc = magicsys.getCompilerProc(c.m.graph, compilerProc) - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree target, theProc - copyTree target, xa - - intoDest d, info, t, body - c.freeTemp xa - -proc genEnumToStr(c: var ProcCon; n: PNode; d: var Value) = - let t = n[1].typ.skipTypes(abstractInst+{tyRange}) - let toStrProc = getToStringProc(c.m.graph, t) - # XXX need to modify this logic for IC. - var nb = copyTree(n) - nb[0] = newSymNode(toStrProc) - gen(c, nb, d) - -proc genOf(c: var ProcCon; n: PNode; d: var Value) = - genUnaryOp c, n, d, TestOf - -template sizeOfLikeMsg(name): string = - "'" & name & "' requires '.importc' types to be '.completeStruct'" - -proc genIsNil(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let tmp = c.genx(n[1]) - let t = typeToIr(c.m, n[1].typ) - template body(target) = - buildTyped target, info, Eq, t: - copyTree target, tmp - addNilVal target, info, t - intoDest d, info, Bool8Id, body - c.freeTemp(tmp) - -proc fewCmps(conf: ConfigRef; s: PNode): bool = - # this function estimates whether it is better to emit code - # for constructing the set or generating a bunch of comparisons directly - if s.kind != nkCurly: - result = false - elif (getSize(conf, s.typ) <= conf.target.intSize) and (nfAllConst in s.flags): - result = false # it is better to emit the set generation code - elif elemType(s.typ).kind in {tyInt, tyInt16..tyInt64}: - result = true # better not emit the set if int is basetype! - else: - result = s.len <= 8 # 8 seems to be a good value - -proc genInBitset(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let a = c.genx(n[1]) - let b = c.genx(n[2]) - - let t = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ) - let setType = typeToIr(c.m, n[1].typ) - let mask = - case t - of UInt8Id: 7 - of UInt16Id: 15 - of UInt32Id: 31 - else: 63 - let expansion = if t == UInt64Id: UInt64Id else: c.m.nativeUIntId - # "(($1 &(1U<<((NU)($2)&7U)))!=0)" - or - - # "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)" - - template body(target) = - buildTyped target, info, BoolNot, Bool8Id: - buildTyped target, info, Eq, t: - buildTyped target, info, BitAnd, t: - if c.m.nirm.types[setType].kind != ArrayTy: - copyTree target, a - else: - buildTyped target, info, ArrayAt, setType: - copyTree target, a - buildTyped target, info, BitShr, t: - buildTyped target, info, Cast, expansion: - copyTree target, b - addIntVal target, c.lit.numbers, info, expansion, 3 - - buildTyped target, info, BitShl, t: - addIntVal target, c.lit.numbers, info, t, 1 - buildTyped target, info, BitAnd, t: - buildTyped target, info, Cast, expansion: - copyTree target, b - addIntVal target, c.lit.numbers, info, expansion, mask - addIntVal target, c.lit.numbers, info, t, 0 - intoDest d, info, t, body - - c.freeTemp(b) - c.freeTemp(a) - -proc genInSet(c: var ProcCon; n: PNode; d: var Value) = - let g {.cursor.} = c.m.graph - if n[1].kind == nkCurly and fewCmps(g.config, n[1]): - # a set constructor but not a constant set: - # do not emit the set, but generate a bunch of comparisons; and if we do - # so, we skip the unnecessary range check: This is a semantical extension - # that code now relies on. :-/ XXX - let elem = if n[2].kind in {nkChckRange, nkChckRange64}: n[2][0] - else: n[2] - let curly = n[1] - var ex: PNode = nil - for it in curly: - var test: PNode - if it.kind == nkRange: - test = newTree(nkCall, g.operators.opAnd.newSymNode, - newTree(nkCall, g.operators.opLe.newSymNode, it[0], elem), # a <= elem - newTree(nkCall, g.operators.opLe.newSymNode, elem, it[1]) - ) - else: - test = newTree(nkCall, g.operators.opEq.newSymNode, elem, it) - test.typ = getSysType(g, it.info, tyBool) - - if ex == nil: ex = test - else: ex = newTree(nkCall, g.operators.opOr.newSymNode, ex, test) - - if ex == nil: - let info = toLineInfo(c, n.info) - template body(target) = - boolVal target, c.lit.numbers, info, false - intoDest d, info, Bool8Id, body - else: - gen c, ex, d - else: - genInBitset c, n, d - -proc genCard(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let a = c.genx(n[1]) - let t = typeToIr(c.m, n.typ) - - let setType = typeToIr(c.m, n[1].typ) - if isEmpty(d): d = getTemp(c, n) - - buildTyped c.code, info, Asgn, t: - copyTree c.code, d - buildTyped c.code, info, Call, t: - if c.m.nirm.types[setType].kind == ArrayTy: - let codegenProc = magicsys.getCompilerProc(c.m.graph, "cardSet") - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, setType): - copyTree c.code, a - c.code.addImmediateVal info, int(getSize(c.config, n[1].typ)) - elif t == UInt64Id: - let codegenProc = magicsys.getCompilerProc(c.m.graph, "countBits64") - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - copyTree c.code, a - else: - let codegenProc = magicsys.getCompilerProc(c.m.graph, "countBits32") - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - buildTyped c.code, info, Cast, UInt32Id: - copyTree c.code, a - freeTemp c, a - -proc genEqSet(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let a = c.genx(n[1]) - let b = c.genx(n[2]) - let t = typeToIr(c.m, n.typ) - - let setType = typeToIr(c.m, n[1].typ) - - if c.m.nirm.types[setType].kind == ArrayTy: - if isEmpty(d): d = getTemp(c, n) - - buildTyped c.code, info, Asgn, t: - copyTree c.code, d - buildTyped c.code, info, Eq, t: - buildTyped c.code, info, Call, t: - let codegenProc = magicsys.getCompilerProc(c.m.graph, "nimCmpMem") - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, setType): - copyTree c.code, a - buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, setType): - copyTree c.code, b - c.code.addImmediateVal info, int(getSize(c.config, n[1].typ)) - c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0 - - else: - template body(target) = - buildTyped target, info, Eq, setType: - copyTree target, a - copyTree target, b - intoDest d, info, Bool8Id, body - - freeTemp c, b - freeTemp c, a - -proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: int): (SymId, LabelId, LabelId) = - let tmp = allocTemp(c, c.m.nativeIntId) - c.code.addSummon info, tmp, c.m.nativeIntId - buildTyped c.code, info, Asgn, c.m.nativeIntId: - c.code.addSymUse info, tmp - c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, first - let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel) - result = (tmp, lab1, newLabel(c.labelGen)) - - buildTyped c.code, info, Select, Bool8Id: - buildTyped c.code, info, Lt, c.m.nativeIntId: - c.code.addSymUse info, tmp - c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, last - build c.code, info, SelectPair: - build c.code, info, SelectValue: - c.code.boolVal(c.lit.numbers, info, false) - c.code.gotoLabel info, Goto, result[2] - -proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: Value): (SymId, LabelId, LabelId) = - let tmp = allocTemp(c, c.m.nativeIntId) - c.code.addSummon info, tmp, c.m.nativeIntId - buildTyped c.code, info, Asgn, c.m.nativeIntId: - c.code.addSymUse info, tmp - copyTree c.code, first - let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel) - result = (tmp, lab1, newLabel(c.labelGen)) - - buildTyped c.code, info, Select, Bool8Id: - buildTyped c.code, info, Le, c.m.nativeIntId: - c.code.addSymUse info, tmp - copyTree c.code, last - build c.code, info, SelectPair: - build c.code, info, SelectValue: - c.code.boolVal(c.lit.numbers, info, false) - c.code.gotoLabel info, Goto, result[2] - -proc endLoop(c: var ProcCon; info: PackedLineInfo; s: SymId; back, exit: LabelId) = - buildTyped c.code, info, Asgn, c.m.nativeIntId: - c.code.addSymUse info, s - buildTyped c.code, info, Add, c.m.nativeIntId: - c.code.addSymUse info, s - c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1 - c.code.addLabel info, GotoLoop, back - c.code.addLabel info, Label, exit - freeTemp(c.sm, s) - -proc genLeSet(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let a = c.genx(n[1]) - let b = c.genx(n[2]) - let t = typeToIr(c.m, n.typ) - - let setType = typeToIr(c.m, n[1].typ) - - if c.m.nirm.types[setType].kind == ArrayTy: - let elemType = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ) - if isEmpty(d): d = getTemp(c, n) - # "for ($1 = 0; $1 < $2; $1++):" - # " $3 = (($4[$1] & ~ $5[$1]) == 0)" - # " if (!$3) break;" - let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, int(getSize(c.config, n[1].typ))) - buildTyped c.code, info, Asgn, Bool8Id: - copyTree c.code, d - buildTyped c.code, info, Eq, elemType: - buildTyped c.code, info, BitAnd, elemType: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, a - c.code.addSymUse info, idx - buildTyped c.code, info, BitNot, elemType: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, b - c.code.addSymUse info, idx - c.code.addIntVal c.lit.numbers, info, elemType, 0 - - # if !$3: break - buildTyped c.code, info, Select, Bool8Id: - c.code.copyTree d - build c.code, info, SelectPair: - build c.code, info, SelectValue: - c.code.boolVal(c.lit.numbers, info, false) - c.code.gotoLabel info, Goto, endLabel - - endLoop(c, info, idx, backLabel, endLabel) - else: - # "(($1 & ~ $2)==0)" - template body(target) = - buildTyped target, info, Eq, setType: - buildTyped target, info, BitAnd, setType: - copyTree target, a - buildTyped target, info, BitNot, setType: - copyTree target, b - target.addIntVal c.lit.numbers, info, setType, 0 - - intoDest d, info, Bool8Id, body - - freeTemp c, b - freeTemp c, a - -proc genLtSet(c: var ProcCon; n: PNode; d: var Value) = - localError(c.m.graph.config, n.info, "`<` for sets not implemented") - -proc genBinarySet(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = - let info = toLineInfo(c, n.info) - let a = c.genx(n[1]) - let b = c.genx(n[2]) - let t = typeToIr(c.m, n.typ) - - let setType = typeToIr(c.m, n[1].typ) - - if c.m.nirm.types[setType].kind == ArrayTy: - let elemType = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ) - if isEmpty(d): d = getTemp(c, n) - # "for ($1 = 0; $1 < $2; $1++):" - # " $3 = (($4[$1] & ~ $5[$1]) == 0)" - # " if (!$3) break;" - let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, int(getSize(c.config, n[1].typ))) - buildTyped c.code, info, Asgn, elemType: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, d - c.code.addSymUse info, idx - buildTyped c.code, info, (if m == mPlusSet: BitOr else: BitAnd), elemType: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, a - c.code.addSymUse info, idx - if m == mMinusSet: - buildTyped c.code, info, BitNot, elemType: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, b - c.code.addSymUse info, idx - else: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, b - c.code.addSymUse info, idx - - endLoop(c, info, idx, backLabel, endLabel) - else: - # "(($1 & ~ $2)==0)" - template body(target) = - buildTyped target, info, (if m == mPlusSet: BitOr else: BitAnd), setType: - copyTree target, a - if m == mMinusSet: - buildTyped target, info, BitNot, setType: - copyTree target, b - else: - copyTree target, b - - intoDest d, info, setType, body - - freeTemp c, b - freeTemp c, a - -proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) = - let info = toLineInfo(c, n.info) - let a = c.genx(n[1]) - let b = c.genx(n[2]) - - let setType = typeToIr(c.m, n[1].typ) - - let t = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ) - let mask = - case t - of UInt8Id: 7 - of UInt16Id: 15 - of UInt32Id: 31 - else: 63 - - buildTyped c.code, info, Asgn, setType: - if c.m.nirm.types[setType].kind == ArrayTy: - if m == mIncl: - # $1[(NU)($2)>>3] |=(1U<<($2&7U)) - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, a - buildTyped c.code, info, BitShr, t: - buildTyped c.code, info, Cast, c.m.nativeUIntId: - copyTree c.code, b - addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 - buildTyped c.code, info, BitOr, t: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, a - buildTyped c.code, info, BitShr, t: - buildTyped c.code, info, Cast, c.m.nativeUIntId: - copyTree c.code, b - addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 - buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.lit.numbers, info, t, 1 - buildTyped c.code, info, BitAnd, t: - copyTree c.code, b - c.code.addIntVal c.lit.numbers, info, t, 7 - else: - # $1[(NU)($2)>>3] &= ~(1U<<($2&7U)) - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, a - buildTyped c.code, info, BitShr, t: - buildTyped c.code, info, Cast, c.m.nativeUIntId: - copyTree c.code, b - addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 - buildTyped c.code, info, BitAnd, t: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, a - buildTyped c.code, info, BitShr, t: - buildTyped c.code, info, Cast, c.m.nativeUIntId: - copyTree c.code, b - addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 - buildTyped c.code, info, BitNot, t: - buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.lit.numbers, info, t, 1 - buildTyped c.code, info, BitAnd, t: - copyTree c.code, b - c.code.addIntVal c.lit.numbers, info, t, 7 - - else: - copyTree c.code, a - if m == mIncl: - # $1 |= ((NU8)1)<<(($2) & 7) - buildTyped c.code, info, BitOr, setType: - copyTree c.code, a - buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.lit.numbers, info, t, 1 - buildTyped c.code, info, BitAnd, t: - copyTree c.code, b - c.code.addIntVal c.lit.numbers, info, t, mask - else: - # $1 &= ~(((NU8)1) << (($2) & 7)) - buildTyped c.code, info, BitAnd, setType: - copyTree c.code, a - buildTyped c.code, info, BitNot, t: - buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.lit.numbers, info, t, 1 - buildTyped c.code, info, BitAnd, t: - copyTree c.code, b - c.code.addIntVal c.lit.numbers, info, t, mask - freeTemp c, b - freeTemp c, a - -proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) = - # example: { a..b, c, d, e, f..g } - # we have to emit an expression of the form: - # nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c); - # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g); - let info = toLineInfo(c, n.info) - let setType = typeToIr(c.m, n.typ) - let size = int(getSize(c.config, n.typ)) - let t = bitsetBasetype(c.m.types, c.m.nirm.types, n.typ) - let mask = - case t - of UInt8Id: 7 - of UInt16Id: 15 - of UInt32Id: 31 - else: 63 - - if isEmpty(d): d = getTemp(c, n) - if c.m.nirm.types[setType].kind != ArrayTy: - buildTyped c.code, info, Asgn, setType: - copyTree c.code, d - c.code.addIntVal c.lit.numbers, info, t, 0 - - for it in n: - if it.kind == nkRange: - let a = genx(c, it[0]) - let b = genx(c, it[1]) - let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b) - buildTyped c.code, info, Asgn, setType: - copyTree c.code, d - buildTyped c.code, info, BitAnd, setType: - copyTree c.code, d - buildTyped c.code, info, BitNot, t: - buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.lit.numbers, info, t, 1 - buildTyped c.code, info, BitAnd, t: - c.code.addSymUse info, idx - c.code.addIntVal c.lit.numbers, info, t, mask - - endLoop(c, info, idx, backLabel, endLabel) - freeTemp c, b - freeTemp c, a - - else: - let a = genx(c, it) - buildTyped c.code, info, Asgn, setType: - copyTree c.code, d - buildTyped c.code, info, BitAnd, setType: - copyTree c.code, d - buildTyped c.code, info, BitNot, t: - buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.lit.numbers, info, t, 1 - buildTyped c.code, info, BitAnd, t: - copyTree c.code, a - c.code.addIntVal c.lit.numbers, info, t, mask - freeTemp c, a - - else: - # init loop: - let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, size) - buildTyped c.code, info, Asgn, t: - copyTree c.code, d - c.code.addIntVal c.lit.numbers, info, t, 0 - endLoop(c, info, idx, backLabel, endLabel) - - # incl elements: - for it in n: - if it.kind == nkRange: - let a = genx(c, it[0]) - let b = genx(c, it[1]) - let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b) - - buildTyped c.code, info, Asgn, t: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, d - buildTyped c.code, info, BitShr, t: - buildTyped c.code, info, Cast, c.m.nativeUIntId: - c.code.addSymUse info, idx - addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 - buildTyped c.code, info, BitOr, t: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, d - buildTyped c.code, info, BitShr, t: - buildTyped c.code, info, Cast, c.m.nativeUIntId: - c.code.addSymUse info, idx - addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 - buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.lit.numbers, info, t, 1 - buildTyped c.code, info, BitAnd, t: - c.code.addSymUse info, idx - c.code.addIntVal c.lit.numbers, info, t, 7 - - endLoop(c, info, idx, backLabel, endLabel) - freeTemp c, b - freeTemp c, a - - else: - let a = genx(c, it) - # $1[(NU)($2)>>3] |=(1U<<($2&7U)) - buildTyped c.code, info, Asgn, t: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, d - buildTyped c.code, info, BitShr, t: - buildTyped c.code, info, Cast, c.m.nativeUIntId: - copyTree c.code, a - addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 - buildTyped c.code, info, BitOr, t: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, d - buildTyped c.code, info, BitShr, t: - buildTyped c.code, info, Cast, c.m.nativeUIntId: - copyTree c.code, a - addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 - buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.lit.numbers, info, t, 1 - buildTyped c.code, info, BitAnd, t: - copyTree c.code, a - c.code.addIntVal c.lit.numbers, info, t, 7 - freeTemp c, a - -proc genSetConstr(c: var ProcCon; n: PNode; d: var Value) = - if isDeepConstExpr(n): - let info = toLineInfo(c, n.info) - let setType = typeToIr(c.m, n.typ) - let size = int(getSize(c.config, n.typ)) - let cs = toBitSet(c.config, n) - - if c.m.nirm.types[setType].kind != ArrayTy: - template body(target) = - target.addIntVal c.lit.numbers, info, setType, cast[BiggestInt](bitSetToWord(cs, size)) - intoDest d, info, setType, body - else: - let t = bitsetBasetype(c.m.types, c.m.nirm.types, n.typ) - template body(target) = - buildTyped target, info, ArrayConstr, setType: - for i in 0..high(cs): - target.addIntVal c.lit.numbers, info, t, int64 cs[i] - intoDest d, info, setType, body - else: - genSetConstrDyn c, n, d - -proc genStrConcat(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - # <Nim code> - # s = "Hello " & name & ", how do you feel?" & 'z' - # - # <generated code> - # { - # string tmp0; - # ... - # tmp0 = rawNewString(6 + 17 + 1 + s2->len); - # // we cannot generate s = rawNewString(...) here, because - # // ``s`` may be used on the right side of the expression - # appendString(tmp0, strlit_1); - # appendString(tmp0, name); - # appendString(tmp0, strlit_2); - # appendChar(tmp0, 'z'); - # asgn(s, tmp0); - # } - var args: seq[Value] = @[] - var argsRuntimeLen: seq[Value] = @[] - - var precomputedLen = 0 - for i in 1 ..< n.len: - let it = n[i] - args.add genx(c, it) - if skipTypes(it.typ, abstractVarRange).kind == tyChar: - inc precomputedLen - elif it.kind in {nkStrLit..nkTripleStrLit}: - inc precomputedLen, it.strVal.len - else: - argsRuntimeLen.add args[^1] - - # generate length computation: - var tmpLen = allocTemp(c, c.m.nativeIntId) - buildTyped c.code, info, Asgn, c.m.nativeIntId: - c.code.addSymUse info, tmpLen - c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, precomputedLen - for a in mitems(argsRuntimeLen): - buildTyped c.code, info, Asgn, c.m.nativeIntId: - c.code.addSymUse info, tmpLen - buildTyped c.code, info, CheckedAdd, c.m.nativeIntId: - c.code.addLabel info, CheckedGoto, c.exitLabel - c.code.addSymUse info, tmpLen - buildTyped c.code, info, FieldAt, typeToIr(c.m, n.typ): - copyTree c.code, a - c.code.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 - - var tmpStr = getTemp(c, n) - # ^ because of aliasing, we always go through a temporary - let t = typeToIr(c.m, n.typ) - buildTyped c.code, info, Asgn, t: - copyTree c.code, tmpStr - buildTyped c.code, info, Call, t: - let codegenProc = magicsys.getCompilerProc(c.m.graph, "rawNewString") - #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info) - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - c.code.addSymUse info, tmpLen - freeTemp c.sm, tmpLen - - for i in 1 ..< n.len: - let it = n[i] - let isChar = skipTypes(it.typ, abstractVarRange).kind == tyChar - buildTyped c.code, info, Call, VoidId: - let codegenProc = magicsys.getCompilerProc(c.m.graph, - (if isChar: "appendChar" else: "appendString")) - #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info) - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, t): - copyTree c.code, tmpStr - copyTree c.code, args[i-1] - freeTemp c, args[i-1] - - if isEmpty(d): - d = tmpStr - else: - # XXX Test that this does not cause memory leaks! - buildTyped c.code, info, Asgn, t: - copyTree c.code, d - copyTree c.code, tmpStr - -proc genDefault(c: var ProcCon; n: PNode; d: var Value) = - let m = expandDefault(n.typ, n.info) - gen c, m, d - -proc genWasMoved(c: var ProcCon; n: PNode) = - let n1 = n[1].skipAddr - # XXX We need a way to replicate this logic or better yet a better - # solution for injectdestructors.nim: - #if c.withinBlockLeaveActions > 0 and notYetAlive(n1): - var d = c.genx(n1) - assert not isEmpty(d) - let m = expandDefault(n1.typ, n1.info) - gen c, m, d - -proc genMove(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let n1 = n[1].skipAddr - var a = c.genx(n1) - if n.len == 4: - # generated by liftdestructors: - let src = c.genx(n[2]) - # if ($1.p == $2.p) goto lab1 - let lab1 = newLabel(c.labelGen) - - let n1t = typeToIr(c.m, n1.typ) - let payloadType = seqPayloadPtrType(c.m.types, c.m.nirm.types, n1.typ)[0] - buildTyped c.code, info, Select, Bool8Id: - buildTyped c.code, info, Eq, payloadType: - buildTyped c.code, info, FieldAt, n1t: - copyTree c.code, a - c.code.addImmediateVal info, 1 # (len, p)-pair - buildTyped c.code, info, FieldAt, n1t: - copyTree c.code, src - c.code.addImmediateVal info, 1 # (len, p)-pair - - build c.code, info, SelectPair: - build c.code, info, SelectValue: - c.code.boolVal(c.lit.numbers, info, true) - c.code.gotoLabel info, Goto, lab1 - - gen(c, n[3]) - c.patch n, lab1 - - buildTyped c.code, info, Asgn, typeToIr(c.m, n1.typ): - copyTree c.code, a - copyTree c.code, src - - else: - if isEmpty(d): d = getTemp(c, n) - buildTyped c.code, info, Asgn, typeToIr(c.m, n1.typ): - copyTree c.code, d - copyTree c.code, a - var op = getAttachedOp(c.m.graph, n.typ, attachedWasMoved) - if op == nil or skipTypes(n1.typ, abstractVar+{tyStatic}).kind in {tyOpenArray, tyVarargs}: - let m = expandDefault(n1.typ, n1.info) - gen c, m, a - else: - var opB = c.genx(newSymNode(op)) - buildTyped c.code, info, Call, typeToIr(c.m, n.typ): - copyTree c.code, opB - buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, typeToIr(c.m, n1.typ)): - copyTree c.code, a - -template fieldAt(x: Value; i: int; t: TypeId): Tree = - var result = default(Tree) - buildTyped result, info, FieldAt, t: - copyTree result, x - result.addImmediateVal info, i - result - -template eqNil(x: Tree; t: TypeId): Tree = - var result = default(Tree) - buildTyped result, info, Eq, t: - copyTree result, x - result.addNilVal info, t - result - -template eqZero(x: Tree): Tree = - var result = default(Tree) - buildTyped result, info, Eq, c.m.nativeIntId: - copyTree result, x - result.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0 - result - -template bitOp(x: Tree; opc: Opcode; y: int): Tree = - var result = default(Tree) - buildTyped result, info, opc, c.m.nativeIntId: - copyTree result, x - result.addIntVal c.lit.numbers, info, c.m.nativeIntId, y - result - -proc genDestroySeq(c: var ProcCon; n: PNode; t: PType) = - let info = toLineInfo(c, n.info) - let strLitFlag = 1 shl (c.m.graph.config.target.intSize * 8 - 2) # see also NIM_STRLIT_FLAG - - let x = c.genx(n[1]) - let baseType = t.elementType - - let seqType = typeToIr(c.m, t) - let p = fieldAt(x, 0, seqType) - - # if $1.p != nil and ($1.p.cap and NIM_STRLIT_FLAG) == 0: - # alignedDealloc($1.p, NIM_ALIGNOF($2)) - buildIfNot p.eqNil(seqType): - buildIf fieldAt(Value(p), 0, seqPayloadPtrType(c.m.types, c.m.nirm.types, t)[0]).bitOp(BitAnd, 0).eqZero(): - let codegenProc = getCompilerProc(c.m.graph, "alignedDealloc") - buildTyped c.code, info, Call, VoidId: - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - copyTree c.code, p - c.code.addImmediateVal info, int(getAlign(c.config, baseType)) - - freeTemp c, x - -proc genDestroy(c: var ProcCon; n: PNode) = - let t = n[1].typ.skipTypes(abstractInst) - case t.kind - of tyString: - var unused = default(Value) - genUnaryCp(c, n, unused, "nimDestroyStrV1") - of tySequence: - genDestroySeq(c, n, t) - else: discard "nothing to do" - -type - IndexFor = enum - ForSeq, ForStr, ForOpenArray, ForArray - -proc genIndexCheck(c: var ProcCon; n: PNode; a: Value; kind: IndexFor; arr: PType): Value = - if optBoundsCheck in c.options: - let info = toLineInfo(c, n.info) - result = default(Value) - let idx = genx(c, n) - build result, info, CheckedIndex: - result.Tree.addLabel info, CheckedGoto, c.exitLabel - copyTree result.Tree, idx - case kind - of ForSeq, ForStr: - buildTyped result, info, FieldAt, typeToIr(c.m, arr): - copyTree result.Tree, a - result.addImmediateVal info, 0 # (len, p)-pair - of ForOpenArray: - buildTyped result, info, FieldAt, typeToIr(c.m, arr): - copyTree result.Tree, a - result.addImmediateVal info, 1 # (p, len)-pair - of ForArray: - let x = toInt64 lengthOrd(c.config, arr) - result.addIntVal c.lit.numbers, info, c.m.nativeIntId, x - freeTemp c, idx - else: - result = genx(c, n) - -proc addSliceFields(c: var ProcCon; target: var Tree; info: PackedLineInfo; - x: Value; n: PNode; arrType: PType) = - let elemType = arrayPtrTypeOf(c.m.nirm.types, typeToIr(c.m, arrType.elementType)) - case arrType.kind - of tyString, tySequence: - let checkKind = if arrType.kind == tyString: ForStr else: ForSeq - let pay = if checkKind == ForStr: c.m.strPayloadId - else: seqPayloadPtrType(c.m.types, c.m.nirm.types, arrType) - - let y = genIndexCheck(c, n[2], x, checkKind, arrType) - let z = genIndexCheck(c, n[3], x, checkKind, arrType) - - buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ): - target.addImmediateVal info, 0 - buildTyped target, info, AddrOf, elemType: - buildTyped target, info, DerefArrayAt, pay[1]: - buildTyped target, info, FieldAt, typeToIr(c.m, arrType): - copyTree target, x - target.addImmediateVal info, 1 # (len, p)-pair - copyTree target, y - - # len: - target.addImmediateVal info, 1 - buildTyped target, info, Add, c.m.nativeIntId: - buildTyped target, info, Sub, c.m.nativeIntId: - copyTree target, z - copyTree target, y - target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1 - - freeTemp c, z - freeTemp c, y - of tyArray: - # XXX This evaluates the index check for `y` twice. - # This check is also still insufficient for non-zero based arrays. - let y = genIndexCheck(c, n[2], x, ForArray, arrType) - let z = genIndexCheck(c, n[3], x, ForArray, arrType) - - buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ): - target.addImmediateVal info, 0 - buildTyped target, info, AddrOf, elemType: - buildTyped target, info, ArrayAt, typeToIr(c.m, arrType): - copyTree target, x - copyTree target, y - - target.addImmediateVal info, 1 - buildTyped target, info, Add, c.m.nativeIntId: - buildTyped target, info, Sub, c.m.nativeIntId: - copyTree target, z - copyTree target, y - target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1 - - freeTemp c, z - freeTemp c, y - of tyOpenArray: - # XXX This evaluates the index check for `y` twice. - let y = genIndexCheck(c, n[2], x, ForOpenArray, arrType) - let z = genIndexCheck(c, n[3], x, ForOpenArray, arrType) - let pay = openArrayPayloadType(c.m.types, c.m.nirm.types, arrType) - - buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ): - target.addImmediateVal info, 0 - buildTyped target, info, AddrOf, elemType: - buildTyped target, info, DerefArrayAt, pay: - buildTyped target, info, FieldAt, typeToIr(c.m, arrType): - copyTree target, x - target.addImmediateVal info, 0 # (p, len)-pair - copyTree target, y - - target.addImmediateVal info, 1 - buildTyped target, info, Add, c.m.nativeIntId: - buildTyped target, info, Sub, c.m.nativeIntId: - copyTree target, z - copyTree target, y - target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1 - - freeTemp c, z - freeTemp c, y - else: - raiseAssert "addSliceFields: " & typeToString(arrType) - -proc genSlice(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - - let x = c.genx(n[1]) - - let arrType = n[1].typ.skipTypes(abstractVar) - - template body(target) = - c.addSliceFields target, info, x, n, arrType - - valueIntoDest c, info, d, arrType, body - freeTemp c, x - -proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = - case m - of mAnd: c.genAndOr(n, opcFJmp, d) - of mOr: c.genAndOr(n, opcTJmp, d) - of mPred, mSubI: c.genBinaryOp(n, d, if optOverflowCheck in c.options: CheckedSub else: Sub) - of mSucc, mAddI: c.genBinaryOp(n, d, if optOverflowCheck in c.options: CheckedAdd else: Add) - of mInc: - unused(c, n, d) - c.genIncDec(n, if optOverflowCheck in c.options: CheckedAdd else: Add) - of mDec: - unused(c, n, d) - c.genIncDec(n, if optOverflowCheck in c.options: CheckedSub else: Sub) - of mOrd, mChr, mUnown: - c.gen(n[1], d) - of generatedMagics: - genCall(c, n, d) - of mNew, mNewFinalize: - unused(c, n, d) - c.genNew(n, needsInit = true) - of mNewSeq: - unused(c, n, d) - c.genNewSeq(n) - of mNewSeqOfCap: c.genNewSeqOfCap(n, d) - of mNewString, mNewStringOfCap, mExit: c.genCall(n, d) - of mLengthOpenArray, mLengthArray, mLengthSeq, mLengthStr: - genArrayLen(c, n, d) - of mMulI: genBinaryOp(c, n, d, if optOverflowCheck in c.options: CheckedMul else: Mul) - of mDivI: genBinaryOp(c, n, d, if optOverflowCheck in c.options: CheckedDiv else: Div) - of mModI: genBinaryOp(c, n, d, if optOverflowCheck in c.options: CheckedMod else: Mod) - of mAddF64: genBinaryOp(c, n, d, Add) - of mSubF64: genBinaryOp(c, n, d, Sub) - of mMulF64: genBinaryOp(c, n, d, Mul) - of mDivF64: genBinaryOp(c, n, d, Div) - of mShrI: genBinaryOp(c, n, d, BitShr) - of mShlI: genBinaryOp(c, n, d, BitShl) - of mAshrI: genBinaryOp(c, n, d, BitShr) - of mBitandI: genBinaryOp(c, n, d, BitAnd) - of mBitorI: genBinaryOp(c, n, d, BitOr) - of mBitxorI: genBinaryOp(c, n, d, BitXor) - of mAddU: genBinaryOp(c, n, d, Add) - of mSubU: genBinaryOp(c, n, d, Sub) - of mMulU: genBinaryOp(c, n, d, Mul) - of mDivU: genBinaryOp(c, n, d, Div) - of mModU: genBinaryOp(c, n, d, Mod) - of mEqI, mEqB, mEqEnum, mEqCh: - genCmpOp(c, n, d, Eq) - of mLeI, mLeEnum, mLeCh, mLeB: - genCmpOp(c, n, d, Le) - of mLtI, mLtEnum, mLtCh, mLtB: - genCmpOp(c, n, d, Lt) - of mEqF64: genCmpOp(c, n, d, Eq) - of mLeF64: genCmpOp(c, n, d, Le) - of mLtF64: genCmpOp(c, n, d, Lt) - of mLePtr, mLeU: genCmpOp(c, n, d, Le) - of mLtPtr, mLtU: genCmpOp(c, n, d, Lt) - of mEqProc, mEqRef: - genCmpOp(c, n, d, Eq) - of mXor: genBinaryOp(c, n, d, BitXor) - of mNot: genUnaryOp(c, n, d, BoolNot) - of mUnaryMinusI, mUnaryMinusI64: - genUnaryMinus(c, n, d) - #genNarrow(c, n, d) - of mUnaryMinusF64: genUnaryMinus(c, n, d) - of mUnaryPlusI, mUnaryPlusF64: gen(c, n[1], d) - of mBitnotI: - genUnaryOp(c, n, d, BitNot) - when false: - # XXX genNarrowU modified, do not narrow signed types - let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) - let size = getSize(c.config, t) - if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8): - c.gABC(n, opcNarrowU, d, TRegister(size*8)) - of mStrToStr, mEnsureMove: c.gen n[1], d - of mBoolToStr: genUnaryCp(c, n, d, "nimBoolToStr") - of mCharToStr: genUnaryCp(c, n, d, "nimCharToStr") - of mCStrToStr: genUnaryCp(c, n, d, "cstrToNimstr") - of mEnumToStr: genEnumToStr(c, n, d) - - of mEqStr: genBinaryCp(c, n, d, "eqStrings") - of mEqCString: genCall(c, n, d) - of mLeStr: genBinaryCp(c, n, d, "leStrings") - of mLtStr: genBinaryCp(c, n, d, "ltStrings") - - of mSetLengthStr: - unused(c, n, d) - let nb = copyTree(n) - nb[1] = makeAddr(nb[1], c.m.idgen) - genBinaryCp(c, nb, d, "setLengthStrV2") - - of mSetLengthSeq: - unused(c, n, d) - let nb = copyTree(n) - nb[1] = makeAddr(nb[1], c.m.idgen) - genCall(c, nb, d) - - of mSwap: - unused(c, n, d) - c.gen(lowerSwap(c.m.graph, n, c.m.idgen, - if c.prc == nil: c.m.module else: c.prc), d) - of mParseBiggestFloat: - genCall c, n, d - of mHigh: - c.genHigh n, d - - of mEcho: - unused(c, n, d) - genUnaryCp c, n, d, "echoBinSafe" - - of mAppendStrCh: - unused(c, n, d) - let nb = copyTree(n) - nb[1] = makeAddr(nb[1], c.m.idgen) - genBinaryCp(c, nb, d, "nimAddCharV1") - of mMinI, mMaxI, mAbsI, mDotDot: - c.genCall(n, d) - of mSizeOf: - localError(c.config, n.info, sizeOfLikeMsg("sizeof")) - of mAlignOf: - localError(c.config, n.info, sizeOfLikeMsg("alignof")) - of mOffsetOf: - localError(c.config, n.info, sizeOfLikeMsg("offsetof")) - of mRunnableExamples: - discard "just ignore any call to runnableExamples" - of mOf: genOf(c, n, d) - of mAppendStrStr: - unused(c, n, d) - let nb = copyTree(n) - nb[1] = makeAddr(nb[1], c.m.idgen) - genBinaryCp(c, nb, d, "nimAddStrV1") - of mAppendSeqElem: - unused(c, n, d) - let nb = copyTree(n) - nb[1] = makeAddr(nb[1], c.m.idgen) - genCall(c, nb, d) - of mIsNil: genIsNil(c, n, d) - of mInSet: genInSet(c, n, d) - of mCard: genCard(c, n, d) - of mEqSet: genEqSet(c, n, d) - of mLeSet: genLeSet(c, n, d) - of mLtSet: genLtSet(c, n, d) - of mMulSet: genBinarySet(c, n, d, m) - of mPlusSet: genBinarySet(c, n, d, m) - of mMinusSet: genBinarySet(c, n, d, m) - of mIncl, mExcl: - unused(c, n, d) - genInclExcl(c, n, m) - of mConStrStr: genStrConcat(c, n, d) - of mDefault, mZeroDefault: - genDefault c, n, d - of mMove: genMove(c, n, d) - of mWasMoved, mReset: - unused(c, n, d) - genWasMoved(c, n) - of mDestroy: genDestroy(c, n) - #of mAccessEnv: unaryExpr(d, n, d, "$1.ClE_0") - #of mAccessTypeField: genAccessTypeField(c, n, d) - of mSlice: genSlice(c, n, d) - of mTrace: discard "no code to generate" - else: - # mGCref, mGCunref: unused by ORC - globalError(c.config, n.info, "cannot generate code for: " & $m) - -proc canElimAddr(n: PNode; idgen: IdGenerator): PNode = - result = nil - case n[0].kind - of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: - var m = n[0][0] - if m.kind in {nkDerefExpr, nkHiddenDeref}: - # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) - result = copyNode(n[0]) - result.add m[0] - if n.typ.skipTypes(abstractVar).kind != tyOpenArray: - result.typ = n.typ - elif n.typ.skipTypes(abstractInst).kind in {tyVar}: - result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen) - of nkHiddenStdConv, nkHiddenSubConv, nkConv: - var m = n[0][1] - if m.kind in {nkDerefExpr, nkHiddenDeref}: - # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) - result = copyNode(n[0]) - result.add n[0][0] - result.add m[0] - if n.typ.skipTypes(abstractVar).kind != tyOpenArray: - result.typ = n.typ - elif n.typ.skipTypes(abstractInst).kind in {tyVar}: - result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen) - else: - if n[0].kind in {nkDerefExpr, nkHiddenDeref}: - # addr ( deref ( x )) --> x - result = n[0][0] - -proc genAddr(c: var ProcCon; n: PNode; d: var Value, flags: GenFlags) = - if (let m = canElimAddr(n, c.m.idgen); m != nil): - gen(c, m, d, flags) - return - - let info = toLineInfo(c, n.info) - let tmp = c.genx(n[0], flags) - template body(target) = - buildTyped target, info, AddrOf, typeToIr(c.m, n.typ): - copyTree target, tmp - - valueIntoDest c, info, d, n.typ, body - freeTemp c, tmp - -proc genDeref(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = - let info = toLineInfo(c, n.info) - let tmp = c.genx(n[0], flags) - template body(target) = - buildTyped target, info, Load, typeToIr(c.m, n.typ): - copyTree target, tmp - - valueIntoDest c, info, d, n.typ, body - freeTemp c, tmp - -proc addAddrOfFirstElem(c: var ProcCon; target: var Tree; info: PackedLineInfo; tmp: Value; typ: PType) = - let arrType = typ.skipTypes(abstractVar) - let elemType = arrayPtrTypeOf(c.m.nirm.types, typeToIr(c.m, arrType.elementType)) - case arrType.kind - of tyString: - let t = typeToIr(c.m, typ) - target.addImmediateVal info, 0 - buildTyped target, info, AddrOf, elemType: - buildTyped target, info, DerefArrayAt, c.m.strPayloadId[1]: - buildTyped target, info, FieldAt, typeToIr(c.m, arrType): - copyTree target, tmp - target.addImmediateVal info, 1 # (len, p)-pair - target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0 - # len: - target.addImmediateVal info, 1 - buildTyped target, info, FieldAt, typeToIr(c.m, arrType): - copyTree target, tmp - target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 - - of tySequence: - let t = typeToIr(c.m, typ) - target.addImmediateVal info, 0 - buildTyped target, info, AddrOf, elemType: - buildTyped target, info, DerefArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, typ)[1]: - buildTyped target, info, FieldAt, typeToIr(c.m, arrType): - copyTree target, tmp - target.addImmediateVal info, 1 # (len, p)-pair - target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0 - # len: - target.addImmediateVal info, 1 - buildTyped target, info, FieldAt, typeToIr(c.m, arrType): - copyTree target, tmp - target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 - - of tyArray: - let t = typeToIr(c.m, arrType) - target.addImmediateVal info, 0 - buildTyped target, info, AddrOf, elemType: - buildTyped target, info, ArrayAt, t: - copyTree target, tmp - target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0 - target.addImmediateVal info, 1 - target.addIntVal(c.lit.numbers, info, c.m.nativeIntId, toInt lengthOrd(c.config, arrType)) - else: - raiseAssert "addAddrOfFirstElem: " & typeToString(typ) - -proc genToOpenArrayConv(c: var ProcCon; arg: PNode; d: var Value; flags: GenFlags; destType: PType) = - let info = toLineInfo(c, arg.info) - let tmp = c.genx(arg, flags) - let arrType = destType.skipTypes(abstractVar) - template body(target) = - buildTyped target, info, ObjConstr, typeToIr(c.m, arrType): - c.addAddrOfFirstElem target, info, tmp, arg.typ - - valueIntoDest c, info, d, arrType, body - freeTemp c, tmp - -proc genConv(c: var ProcCon; n, arg: PNode; d: var Value; flags: GenFlags; opc: Opcode) = - let targetType = n.typ.skipTypes({tyDistinct}) - let argType = arg.typ.skipTypes({tyDistinct}) - - if sameBackendType(targetType, argType) or ( - argType.kind == tyProc and targetType.kind == argType.kind): - # don't do anything for lambda lifting conversions: - gen c, arg, d - return - - if opc != Cast and targetType.skipTypes({tyVar, tyLent}).kind in {tyOpenArray, tyVarargs} and - argType.skipTypes({tyVar, tyLent}).kind notin {tyOpenArray, tyVarargs}: - genToOpenArrayConv c, arg, d, flags, n.typ - return - - let info = toLineInfo(c, n.info) - let tmp = c.genx(arg, flags) - template body(target) = - buildTyped target, info, opc, typeToIr(c.m, n.typ): - if opc == CheckedObjConv: - target.addLabel info, CheckedGoto, c.exitLabel - copyTree target, tmp - - valueIntoDest c, info, d, n.typ, body - freeTemp c, tmp - -proc genObjOrTupleConstr(c: var ProcCon; n: PNode; d: var Value; t: PType) = - # XXX x = (x.old, 22) produces wrong code ... stupid self assignments - let info = toLineInfo(c, n.info) - template body(target) = - buildTyped target, info, ObjConstr, typeToIr(c.m, t): - for i in ord(n.kind == nkObjConstr)..<n.len: - let it = n[i] - if it.kind == nkExprColonExpr: - genField(c, it[0], Value target) - let tmp = c.genx(it[1]) - copyTree target, tmp - c.freeTemp(tmp) - else: - let tmp = c.genx(it) - target.addImmediateVal info, i - copyTree target, tmp - c.freeTemp(tmp) - - if isException(t): - target.addImmediateVal info, 1 # "name" field is at position after the "parent". See system.nim - target.addStrVal c.lit.strings, info, t.skipTypes(abstractInst).sym.name.s - - constrIntoDest c, info, d, t, body - -proc genRefObjConstr(c: var ProcCon; n: PNode; d: var Value) = - if isEmpty(d): d = getTemp(c, n) - let info = toLineInfo(c, n.info) - let refType = n.typ.skipTypes(abstractInstOwned) - let objType = refType.elementType - - rawGenNew(c, d, refType, n.info, needsInit = nfAllFieldsSet notin n.flags) - var deref = default(Value) - deref.buildTyped info, Load, typeToIr(c.m, objType): - deref.Tree.copyTree d - genObjOrTupleConstr c, n, deref, objType - -proc genSeqConstr(c: var ProcCon; n: PNode; d: var Value) = - if isEmpty(d): d = getTemp(c, n) - - let info = toLineInfo(c, n.info) - let seqtype = skipTypes(n.typ, abstractVarRange) - let baseType = seqtype.elementType - - var b = default(Value) - b.addIntVal c.lit.numbers, info, c.m.nativeIntId, n.len - - genNewSeqPayload(c, info, d, b, seqtype) - - for i in 0..<n.len: - var dd = default(Value) - buildTyped dd, info, DerefArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[1]: - buildTyped dd, info, FieldAt, typeToIr(c.m, seqtype): - copyTree Tree(dd), d - dd.addImmediateVal info, 1 # (len, p)-pair - dd.addIntVal c.lit.numbers, info, c.m.nativeIntId, i - gen(c, n[i], dd) - - freeTemp c, d - -proc genArrayConstr(c: var ProcCon; n: PNode, d: var Value) = - let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc}) - if seqType.kind == tySequence: - genSeqConstr(c, n, d) - return - - let info = toLineInfo(c, n.info) - template body(target) = - buildTyped target, info, ArrayConstr, typeToIr(c.m, n.typ): - for i in 0..<n.len: - let tmp = c.genx(n[i]) - copyTree target, tmp - c.freeTemp(tmp) - - constrIntoDest c, info, d, n.typ, body - -proc genAsgn2(c: var ProcCon; a, b: PNode) = - assert a != nil - assert b != nil - var d = c.genx(a) - c.gen b, d - -proc irModule(c: var ProcCon; owner: PSym): string = - #if owner == c.m.module: "" else: - customPath(toFullPath(c.config, owner.info)) - -proc fromForeignModule(c: ProcCon; s: PSym): bool {.inline.} = - result = ast.originatingModule(s) != c.m.module and not c.m.noModularity - -proc genForeignVar(c: var ProcCon; s: PSym) = - var opc: Opcode - if s.kind == skConst: - opc = SummonConst - elif sfThread in s.flags: - opc = SummonThreadLocal - else: - assert sfGlobal in s.flags - opc = SummonGlobal - let t = typeToIr(c.m, s.typ) - let info = toLineInfo(c, s.info) - build c.code, info, ForeignDecl: - buildTyped c.code, info, opc, t: - build c.code, info, ModuleSymUse: - c.code.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(s)) - c.code.addImmediateVal info, s.itemId.item.int - -proc genVarSection(c: var ProcCon; n: PNode) = - for a in n: - if a.kind == nkCommentStmt: continue - #assert(a[0].kind == nkSym) can happen for transformed vars - if a.kind == nkVarTuple: - c.gen(lowerTupleUnpacking(c.m.graph, a, c.m.idgen, c.prc)) - else: - var vn = a[0] - if vn.kind == nkPragmaExpr: vn = vn[0] - if vn.kind == nkSym: - let s = vn.sym - if s.kind == skConst: - if dontInlineConstant(n, s.astdef): - let symId = toSymId(c, s) - c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(s.name.s) - let val = c.genx(s.astdef) - let info = toLineInfo(c, a.info) - buildTyped c.code, info, SummonConst, typeToIr(c.m, s.typ): - c.code.addSymDef info, symId - c.code.copyTree val - freeTemp c, val - else: - var opc: Opcode - if sfThread in s.flags: - opc = SummonThreadLocal - elif sfGlobal in s.flags: - opc = SummonGlobal - else: - opc = Summon - #assert t.int >= 0, typeToString(s.typ) & (c.config $ n.info) - let symId = toSymId(c, s) - c.code.addSummon toLineInfo(c, a.info), symId, typeToIr(c.m, s.typ), opc - c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(s.name.s) - if a[2].kind != nkEmpty: - genAsgn2(c, vn, a[2]) - else: - if a[2].kind == nkEmpty: - genAsgn2(c, vn, expandDefault(vn.typ, vn.info)) - else: - genAsgn2(c, vn, a[2]) - -proc genAsgn(c: var ProcCon; n: PNode) = - var d = c.genx(n[0]) - c.gen n[1], d - -proc convStrToCStr(c: var ProcCon; n: PNode; d: var Value) = - genUnaryCp(c, n, d, "nimToCStringConv", argAt = 0) - -proc convCStrToStr(c: var ProcCon; n: PNode; d: var Value) = - genUnaryCp(c, n, d, "cstrToNimstr", argAt = 0) - -proc genRdVar(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = - let info = toLineInfo(c, n.info) - let s = n.sym - if fromForeignModule(c, s): - if s.kind in {skVar, skConst, skLet} and not c.m.pendingVarsAsSet.containsOrIncl(s.itemId): - c.m.pendingVars.add s - - template body(target) = - build target, info, ModuleSymUse: - target.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(s)) - target.addImmediateVal info, s.itemId.item.int - - valueIntoDest c, info, d, s.typ, body - else: - template body(target) = - target.addSymUse info, toSymId(c, s) - valueIntoDest c, info, d, s.typ, body - -proc genSym(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = - let s = n.sym - case s.kind - of skConst: - if dontInlineConstant(n, s.astdef): - genRdVar(c, n, d, flags) - else: - gen(c, s.astdef, d, flags) - of skVar, skForVar, skTemp, skLet, skResult, skParam: - genRdVar(c, n, d, flags) - of skProc, skFunc, skConverter, skMethod, skIterator: - if not c.m.noModularity: - # anon and generic procs have no AST so we need to remember not to forget - # to emit these: - if not c.m.processedProcs.contains(s.itemId): - if not c.m.pendingProcsAsSet.containsOrIncl(s.itemId): - c.m.pendingProcs.add s - genRdVar(c, n, d, flags) - of skEnumField: - let info = toLineInfo(c, n.info) - template body(target) = - target.addIntVal c.lit.numbers, info, typeToIr(c.m, n.typ), s.position - valueIntoDest c, info, d, n.typ, body - else: - localError(c.config, n.info, "cannot generate code for: " & s.name.s) - -proc genNumericLit(c: var ProcCon; n: PNode; d: var Value; bits: int64) = - let info = toLineInfo(c, n.info) - template body(target) = - target.addIntVal c.lit.numbers, info, typeToIr(c.m, n.typ), bits - valueIntoDest c, info, d, n.typ, body - -proc genStringLit(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - template body(target) = - target.addStrVal c.lit.strings, info, n.strVal - valueIntoDest c, info, d, n.typ, body - -proc genNilLit(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - template body(target) = - target.addNilVal info, typeToIr(c.m, n.typ) - valueIntoDest c, info, d, n.typ, body - -proc genRangeCheck(c: var ProcCon; n: PNode; d: var Value) = - if optRangeCheck in c.options: - let info = toLineInfo(c, n.info) - let tmp = c.genx n[0] - let a = c.genx n[1] - let b = c.genx n[2] - template body(target) = - buildTyped target, info, CheckedRange, typeToIr(c.m, n.typ): - target.addLabel info, CheckedGoto, c.exitLabel - copyTree target, tmp - copyTree target, a - copyTree target, b - valueIntoDest c, info, d, n.typ, body - freeTemp c, tmp - freeTemp c, a - freeTemp c, b - else: - gen c, n[0], d - -proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = - let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}) - let arrayKind = arrayType.kind - let info = toLineInfo(c, n.info) - case arrayKind - of tyString: - let a = genx(c, n[0], flags) - let b = genIndexCheck(c, n[1], a, ForStr, arrayType) - let t = typeToIr(c.m, n.typ) - template body(target) = - buildTyped target, info, DerefArrayAt, c.m.strPayloadId[1]: - buildTyped target, info, FieldAt, typeToIr(c.m, arrayType): - copyTree target, a - target.addImmediateVal info, 1 # (len, p)-pair - copyTree target, b - intoDest d, info, t, body - freeTemp c, b - freeTemp c, a - - of tyCstring, tyPtr, tyUncheckedArray: - let a = genx(c, n[0], flags) - let b = genx(c, n[1]) - template body(target) = - buildTyped target, info, DerefArrayAt, typeToIr(c.m, arrayType): - copyTree target, a - copyTree target, b - valueIntoDest c, info, d, n.typ, body - - freeTemp c, b - freeTemp c, a - of tyTuple: - let a = genx(c, n[0], flags) - let b = int n[1].intVal - template body(target) = - buildTyped target, info, FieldAt, typeToIr(c.m, arrayType): - copyTree target, a - target.addImmediateVal info, b - valueIntoDest c, info, d, n.typ, body - - freeTemp c, a - of tyOpenArray, tyVarargs: - let a = genx(c, n[0], flags) - let b = genIndexCheck(c, n[1], a, ForOpenArray, arrayType) - let t = typeToIr(c.m, n.typ) - template body(target) = - buildTyped target, info, DerefArrayAt, openArrayPayloadType(c.m.types, c.m.nirm.types, n[0].typ): - buildTyped target, info, FieldAt, typeToIr(c.m, arrayType): - copyTree target, a - target.addImmediateVal info, 0 # (p, len)-pair - copyTree target, b - intoDest d, info, t, body - - freeTemp c, b - freeTemp c, a - of tyArray: - let a = genx(c, n[0], flags) - var b = default(Value) - genIndex(c, n[1], n[0].typ, b) - - template body(target) = - buildTyped target, info, ArrayAt, typeToIr(c.m, arrayType): - copyTree target, a - copyTree target, b - valueIntoDest c, info, d, n.typ, body - freeTemp c, b - freeTemp c, a - of tySequence: - let a = genx(c, n[0], flags) - let b = genIndexCheck(c, n[1], a, ForSeq, arrayType) - let t = typeToIr(c.m, n.typ) - template body(target) = - buildTyped target, info, DerefArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, n[0].typ)[1]: - buildTyped target, info, FieldAt, t: - copyTree target, a - target.addImmediateVal info, 1 # (len, p)-pair - copyTree target, b - intoDest d, info, t, body - freeTemp c, b - freeTemp c, a - else: - localError c.config, n.info, "invalid type for nkBracketExpr: " & $arrayKind - -proc genObjAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = - let info = toLineInfo(c, n.info) - - var n0 = n[0] - var opc = FieldAt - if n0.kind == nkDotExpr: - # obj[].a --> DerefFieldAt instead of FieldAt: - n0 = n[0] - opc = DerefFieldAt - - let a = genx(c, n0, flags) - - template body(target) = - buildTyped target, info, opc, typeToIr(c.m, n0.typ): - copyTree target, a - genField c, n[1], Value(target) - - valueIntoDest c, info, d, n.typ, body - freeTemp c, a - -proc genParams(c: var ProcCon; params: PNode; prc: PSym): PSym = - result = nil - if params.len > 0 and resultPos < prc.ast.len: - let resNode = prc.ast[resultPos] - result = resNode.sym # get result symbol - c.code.addSummon toLineInfo(c, result.info), toSymId(c, result), - typeToIr(c.m, result.typ), SummonResult - elif prc.typ.len > 0 and not isEmptyType(prc.typ.returnType) and not isCompileTimeOnly(prc.typ.returnType): - # happens for procs without bodies: - let t = typeToIr(c.m, prc.typ.returnType) - let tmp = allocTemp(c, t) - c.code.addSummon toLineInfo(c, params.info), tmp, t, SummonResult - - for i in 1..<params.len: - let s = params[i].sym - if not isCompileTimeOnly(s.typ): - let t = typeToIr(c.m, s.typ) - assert t.int != -1, typeToString(s.typ) - let symId = toSymId(c, s) - c.code.addSummon toLineInfo(c, params[i].info), symId, t, SummonParam - c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(s.name.s) - -proc addCallConv(c: var ProcCon; info: PackedLineInfo; callConv: TCallingConvention) = - template ann(s: untyped) = c.code.addPragmaId info, s - case callConv - of ccNimCall, ccFastCall, ccClosure: ann FastCall - of ccStdCall: ann StdCall - of ccCDecl: ann CDeclCall - of ccSafeCall: ann SafeCall - of ccSysCall: ann SysCall - of ccInline: ann InlineCall - of ccNoInline: ann NoinlineCall - of ccThisCall: ann ThisCall - of ccNoConvention, ccMember: ann NoCall - -proc genProc(cOuter: var ProcCon; prc: PSym) = - if prc.magic notin generatedMagics: return - if cOuter.m.processedProcs.containsOrIncl(prc.itemId): - return - #assert cOuter.m.inProc == 0, " in nested proc! " & prc.name.s - if cOuter.m.inProc > 0: - if not cOuter.m.pendingProcsAsSet.containsOrIncl(prc.itemId): - cOuter.m.pendingProcs.add prc - return - inc cOuter.m.inProc - - var c = initProcCon(cOuter.m, prc, cOuter.m.graph.config) - let body = - if not fromForeignModule(c, prc): - transformBody(c.m.graph, c.m.idgen, prc, {useCache, keepOpenArrayConversions}) - else: - nil - - let info = toLineInfo(c, prc.info) - build c.code, info, (if body != nil: ProcDecl else: ForeignProcDecl): - if body != nil: - let symId = toSymId(c, prc) - addSymDef c.code, info, symId - c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(prc.name.s) - else: - build c.code, info, ModuleSymUse: - c.code.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(prc)) - c.code.addImmediateVal info, prc.itemId.item.int - addCallConv c, info, prc.typ.callConv - if sfCompilerProc in prc.flags: - build c.code, info, PragmaPair: - c.code.addPragmaId info, CoreName - c.code.addStrVal c.lit.strings, info, prc.name.s - if {sfImportc, sfExportc} * prc.flags != {}: - build c.code, info, PragmaPair: - c.code.addPragmaId info, ExternName - c.code.addStrVal c.lit.strings, info, prc.loc.r - if sfImportc in prc.flags: - if lfHeader in prc. loc.flags: - assert(prc. annex != nil) - let str = getStr(prc. annex.path) - build c.code, info, PragmaPair: - c.code.addPragmaId info, HeaderImport - c.code.addStrVal c.lit.strings, info, str - elif lfDynamicLib in prc. loc.flags: - assert(prc. annex != nil) - let str = getStr(prc. annex.path) - build c.code, info, PragmaPair: - c.code.addPragmaId info, DllImport - c.code.addStrVal c.lit.strings, info, str - elif sfExportc in prc.flags: - if lfDynamicLib in prc. loc.flags: - c.code.addPragmaId info, DllExport - else: - c.code.addPragmaId info, ObjExport - - let resultSym = genParams(c, prc.typ.n, prc) - if body != nil: - gen(c, body) - patch c, body, c.exitLabel - if resultSym != nil: - build c.code, info, Ret: - c.code.addSymUse info, toSymId(c, resultSym) - else: - build c.code, info, Ret: - c.code.addNop info - - #copyTree cOuter.code, c.code - dec cOuter.m.inProc - -proc genProc(cOuter: var ProcCon; n: PNode) = - if n.len == 0 or n[namePos].kind != nkSym: return - let prc = n[namePos].sym - if isGenericRoutineStrict(prc) or isCompileTimeProc(prc) or sfForward in prc.flags: return - genProc cOuter, prc - -proc genClosureCall(c: var ProcCon; n: PNode; d: var Value) = - let typ = skipTypes(n[0].typ, abstractInstOwned) - if tfIterator in typ.flags: - const PatIter = "$1.ClP_0($3, $1.ClE_0)" # we know the env exists - - else: - const PatProc = "$1.ClE_0? $1.ClP_0($3, $1.ClE_0):(($4)($1.ClP_0))($2)" - - -proc genComplexCall(c: var ProcCon; n: PNode; d: var Value) = - if n[0].typ != nil and n[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure: - # XXX genClosureCall p, n, d - genCall c, n, d - else: - genCall c, n, d - -proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = - when defined(nimCompilerStacktraceHints): - setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags - case n.kind - of nkSym: genSym(c, n, d, flags) - of nkCallKinds: - if n[0].kind == nkSym: - let s = n[0].sym - if s.magic != mNone: - genMagic(c, n, d, s.magic) - elif s.kind == skMethod: - localError(c.config, n.info, "cannot call method " & s.name.s & - " at compile time") - else: - genComplexCall(c, n, d) - else: - genComplexCall(c, n, d) - of nkCharLit..nkInt64Lit, nkUIntLit..nkUInt64Lit: - genNumericLit(c, n, d, n.intVal) - of nkFloatLit..nkFloat128Lit: - genNumericLit(c, n, d, cast[int64](n.floatVal)) - of nkStrLit..nkTripleStrLit: - genStringLit(c, n, d) - of nkNilLit: - if not n.typ.isEmptyType: genNilLit(c, n, d) - else: unused(c, n, d) - of nkAsgn, nkFastAsgn, nkSinkAsgn: - unused(c, n, d) - genAsgn(c, n) - of nkDotExpr: genObjAccess(c, n, d, flags) - of nkCheckedFieldExpr: genObjAccess(c, n[0], d, flags) - of nkBracketExpr: genArrAccess(c, n, d, flags) - of nkDerefExpr, nkHiddenDeref: genDeref(c, n, d, flags) - of nkAddr, nkHiddenAddr: genAddr(c, n, d, flags) - of nkIfStmt, nkIfExpr: genIf(c, n, d) - of nkWhenStmt: - # This is "when nimvm" node. Chose the first branch. - gen(c, n[0][1], d) - of nkCaseStmt: genCase(c, n, d) - of nkWhileStmt: - unused(c, n, d) - genWhile(c, n) - of nkBlockExpr, nkBlockStmt: genBlock(c, n, d) - of nkReturnStmt: genReturn(c, n) - of nkRaiseStmt: genRaise(c, n) - of nkBreakStmt: genBreak(c, n) - of nkTryStmt, nkHiddenTryStmt: genTry(c, n, d) - of nkStmtList: - #unused(c, n, d) - # XXX Fix this bug properly, lexim triggers it - for x in n: gen(c, x) - of nkStmtListExpr: - for i in 0..<n.len-1: gen(c, n[i]) - gen(c, n[^1], d, flags) - of nkPragmaBlock: - gen(c, n.lastSon, d, flags) - of nkDiscardStmt: - unused(c, n, d) - gen(c, n[0], d) - of nkHiddenStdConv, nkHiddenSubConv, nkConv: - genConv(c, n, n[1], d, flags, NumberConv) # misnomer? - of nkObjDownConv: - genConv(c, n, n[0], d, flags, ObjConv) - of nkObjUpConv: - genConv(c, n, n[0], d, flags, CheckedObjConv) - of nkVarSection, nkLetSection, nkConstSection: - unused(c, n, d) - genVarSection(c, n) - of nkLambdaKinds: - #let s = n[namePos].sym - #discard genProc(c, s) - gen(c, newSymNode(n[namePos].sym), d) - of nkChckRangeF, nkChckRange64, nkChckRange: - genRangeCheck(c, n, d) - of declarativeDefs - {nkIteratorDef}: - unused(c, n, d) - genProc(c, n) - of nkEmpty, nkCommentStmt, nkTypeSection, nkPragma, - nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt, - nkMixinStmt, nkBindStmt, nkMacroDef, nkIteratorDef: - unused(c, n, d) - of nkStringToCString: convStrToCStr(c, n, d) - of nkCStringToString: convCStrToStr(c, n, d) - of nkBracket: genArrayConstr(c, n, d) - of nkCurly: genSetConstr(c, n, d) - of nkObjConstr: - if n.typ.skipTypes(abstractInstOwned).kind == tyRef: - genRefObjConstr(c, n, d) - else: - genObjOrTupleConstr(c, n, d, n.typ) - of nkPar, nkClosure, nkTupleConstr: - genObjOrTupleConstr(c, n, d, n.typ) - of nkCast: - genConv(c, n, n[1], d, flags, Cast) - of nkComesFrom: - discard "XXX to implement for better stack traces" - #of nkState: genState(c, n) - #of nkGotoState: genGotoState(c, n) - #of nkBreakState: genBreakState(c, n, d) - else: - localError(c.config, n.info, "cannot generate IR code for " & $n) - -proc genPendingProcs(c: var ProcCon) = - while c.m.pendingProcs.len > 0 or c.m.pendingVars.len > 0: - let procs = move(c.m.pendingProcs) - for v in procs: - genProc(c, v) - let vars = move(c.m.pendingVars) - for v in vars: - genForeignVar(c, v) - -proc genStmt*(c: var ProcCon; n: PNode): NodePos = - result = NodePos c.code.len - var d = default(Value) - c.gen(n, d) - unused c, n, d - genPendingProcs c - -proc genExpr*(c: var ProcCon; n: PNode, requiresValue = true): int = - result = c.code.len - var d = default(Value) - c.gen(n, d) - genPendingProcs c - if isEmpty d: - if requiresValue: - globalError(c.config, n.info, "VM problem: d register is not set") diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim deleted file mode 100644 index 01f9c4c9a..000000000 --- a/compiler/nir/cir.nim +++ /dev/null @@ -1,983 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# We produce C code as a list of tokens. - -import std / [assertions, syncio, tables, intsets, formatfloat] -from std / strutils import toOctal -import .. / ic / [bitabs, rodfiles] -import nirtypes, nirinsts, nirfiles -import ../../dist/checksums/src/checksums/md5 - -type - Token = LitId # indexing into the tokens BiTable[string] - - PredefinedToken = enum - IgnoreMe = "<unused>" - EmptyToken = "" - CurlyLe = "{" - CurlyRi = "}" - ParLe = "(" - ParRi = ")" - BracketLe = "[" - BracketRi = "]" - NewLine = "\n" - Semicolon = ";" - Comma = ", " - Space = " " - Colon = ": " - Dot = "." - Arrow = "->" - Star = "*" - Amp = "&" - AsgnOpr = " = " - ScopeOpr = "::" - ConstKeyword = "const " - StaticKeyword = "static " - ExternKeyword = "extern " - WhileKeyword = "while " - IfKeyword = "if (" - ElseKeyword = "else " - SwitchKeyword = "switch " - CaseKeyword = "case " - DefaultKeyword = "default:" - BreakKeyword = "break" - NullPtr = "nullptr" - IfNot = "if (!(" - ReturnKeyword = "return " - TypedefStruct = "typedef struct " - TypedefUnion = "typedef union " - IncludeKeyword = "#include " - -proc fillTokenTable(tab: var BiTable[string]) = - for e in EmptyToken..high(PredefinedToken): - let id = tab.getOrIncl $e - assert id == LitId(e), $(id, " ", ord(e)) - -type - GeneratedCode* = object - m: NirModule - includes: seq[LitId] - includedHeaders: IntSet - data: seq[LitId] - protos: seq[LitId] - code: seq[LitId] - init: seq[LitId] - tokens: BiTable[string] - emittedStrings: IntSet - needsPrefix: IntSet - generatedTypes: IntSet - mangledModules: Table[LitId, LitId] - -proc initGeneratedCode*(m: sink NirModule): GeneratedCode = - result = GeneratedCode(m: m, code: @[], tokens: initBiTable[string]()) - fillTokenTable(result.tokens) - -proc add*(g: var GeneratedCode; t: PredefinedToken) {.inline.} = - g.code.add Token(t) - -proc add*(g: var GeneratedCode; s: string) {.inline.} = - g.code.add g.tokens.getOrIncl(s) - -proc mangleModuleName(c: var GeneratedCode; key: LitId): LitId = - result = c.mangledModules.getOrDefault(key, LitId(0)) - if result == LitId(0): - let u {.cursor.} = c.m.lit.strings[key] - var last = u.len - len(".nim") - 1 - var start = last - while start >= 0 and u[start] != '/': dec start - var sum = getMD5(u) - sum.setLen(8) - let dest = u.substr(start+1, last) & sum - result = c.tokens.getOrIncl(dest) - c.mangledModules[key] = result - -type - CppFile = object - f: File - -proc write(f: var CppFile; s: string) = write(f.f, s) -proc write(f: var CppFile; c: char) = write(f.f, c) - -proc writeTokenSeq(f: var CppFile; s: seq[Token]; c: GeneratedCode) = - var indent = 0 - for i in 0..<s.len: - let x = s[i] - case x - of Token(CurlyLe): - inc indent - write f, c.tokens[x] - write f, "\n" - for i in 1..indent*2: write f, ' ' - of Token(CurlyRi): - dec indent - write f, c.tokens[x] - if i+1 < s.len and s[i+1] == Token(CurlyRi): - discard - else: - write f, "\n" - for i in 1..indent*2: write f, ' ' - of Token(Semicolon): - write f, c.tokens[x] - if i+1 < s.len and s[i+1] == Token(CurlyRi): - discard "no newline before }" - else: - write f, "\n" - for i in 1..indent*2: write f, ' ' - of Token(NewLine): - write f, c.tokens[x] - for i in 1..indent*2: write f, ' ' - else: - write f, c.tokens[x] - - -# Type graph - -type - TypeList = object - processed: IntSet - s: seq[(TypeId, PredefinedToken)] - -proc add(dest: var TypeList; elem: TypeId; decl: PredefinedToken) = - if not containsOrIncl(dest.processed, int(elem)): - dest.s.add (elem, decl) - -type - TypeOrder = object - forwardedDecls, ordered: TypeList - typeImpls: Table[string, TypeId] - lookedAt: IntSet - -proc traverseObject(types: TypeGraph; lit: Literals; c: var TypeOrder; t: TypeId) - -proc recordDependency(types: TypeGraph; lit: Literals; c: var TypeOrder; parent, child: TypeId) = - var ch = child - var viaPointer = false - while true: - case types[ch].kind - of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy: - viaPointer = true - ch = elementType(types, ch) - of LastArrayTy: - ch = elementType(types, ch) - else: - break - - case types[ch].kind - of ObjectTy, UnionTy: - let decl = if types[ch].kind == ObjectTy: TypedefStruct else: TypedefUnion - let obj = c.typeImpls.getOrDefault(lit.strings[types[ch].litId]) - if viaPointer: - c.forwardedDecls.add obj, decl - else: - if not containsOrIncl(c.lookedAt, obj.int): - traverseObject(types, lit, c, obj) - c.ordered.add obj, decl - of ArrayTy: - if viaPointer: - c.forwardedDecls.add ch, TypedefStruct - else: - if not containsOrIncl(c.lookedAt, ch.int): - traverseObject(types, lit, c, ch) - c.ordered.add ch, TypedefStruct - else: - discard "uninteresting type as we only focus on the required struct declarations" - -proc traverseObject(types: TypeGraph; lit: Literals; c: var TypeOrder; t: TypeId) = - for x in sons(types, t): - case types[x].kind - of FieldDecl: - recordDependency types, lit, c, t, x.firstSon - of ObjectTy: - # inheritance - recordDependency types, lit, c, t, x - else: discard - -proc traverseTypes(types: TypeGraph; lit: Literals; c: var TypeOrder) = - for t in allTypes(types): - if types[t].kind in {ObjectDecl, UnionDecl}: - assert types[t.firstSon].kind == NameVal - c.typeImpls[lit.strings[types[t.firstSon].litId]] = t - - for t in allTypesIncludingInner(types): - case types[t].kind - of ObjectDecl, UnionDecl: - traverseObject types, lit, c, t - let decl = if types[t].kind == ObjectDecl: TypedefStruct else: TypedefUnion - c.ordered.add t, decl - of ArrayTy: - traverseObject types, lit, c, t - c.ordered.add t, TypedefStruct - else: discard - -when false: - template emitType(s: string) = c.types.add c.tokens.getOrIncl(s) - template emitType(t: Token) = c.types.add t - template emitType(t: PredefinedToken) = c.types.add Token(t) - -proc genType(g: var GeneratedCode; types: TypeGraph; lit: Literals; t: TypeId; name = "") = - template maybeAddName = - if name != "": - g.add Space - g.add name - - template atom(s: string) = - g.add s - maybeAddName() - case types[t].kind - of VoidTy: atom "void" - of IntTy: atom "NI" & $types[t].integralBits - of UIntTy: atom "NU" & $types[t].integralBits - of FloatTy: atom "NF" & $types[t].integralBits - of BoolTy: atom "NB" & $types[t].integralBits - of CharTy: atom "NC" & $types[t].integralBits - of ObjectTy, UnionTy, NameVal, AnnotationVal: - atom lit.strings[types[t].litId] - of VarargsTy: - g.add "..." - of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy: - genType g, types, lit, elementType(types, t) - g.add Star - maybeAddName() - of ArrayTy: - genType g, types, lit, arrayName(types, t) - maybeAddName() - of LastArrayTy: - genType g, types, lit, elementType(types, t) - maybeAddName() - g.add BracketLe - g.add BracketRi - of ProcTy: - let (retType, callConv) = returnType(types, t) - genType g, types, lit, retType - g.add Space - g.add ParLe - genType g, types, lit, callConv - g.add Star # "(*fn)" - maybeAddName() - g.add ParRi - g.add ParLe - var i = 0 - for ch in params(types, t): - if i > 0: g.add Comma - genType g, types, lit, ch - inc i - g.add ParRi - of ObjectDecl, UnionDecl: - atom lit.strings[types[t.firstSon].litId] - of IntVal, SizeVal, AlignVal, OffsetVal, FieldDecl: - #raiseAssert "did not expect: " & $types[t].kind - g.add "BUG " - atom $types[t].kind - -proc generateTypes(g: var GeneratedCode; types: TypeGraph; lit: Literals; c: TypeOrder) = - for (t, declKeyword) in c.forwardedDecls.s: - let name = if types[t].kind == ArrayTy: arrayName(types, t) else: t.firstSon - let s {.cursor.} = lit.strings[types[name].litId] - g.add declKeyword - g.add s - g.add Space - g.add s - g.add Semicolon - - for (t, declKeyword) in c.ordered.s: - let name = if types[t].kind == ArrayTy: arrayName(types, t) else: t.firstSon - let litId = types[name].litId - if not g.generatedTypes.containsOrIncl(litId.int): - let s {.cursor.} = lit.strings[litId] - g.add declKeyword - g.add CurlyLe - if types[t].kind == ArrayTy: - genType g, types, lit, elementType(types, t), "a" - g.add BracketLe - g.add $arrayLen(types, t) - g.add BracketRi - g.add Semicolon - else: - var i = 0 - for x in sons(types, t): - case types[x].kind - of FieldDecl: - genType g, types, lit, x.firstSon, "F" & $i - g.add Semicolon - inc i - of ObjectTy: - genType g, types, lit, x, "P" - g.add Semicolon - else: discard - g.add CurlyRi - g.add s - g.add Semicolon - -# Procs - -proc toCChar*(c: char; result: var string) {.inline.} = - case c - of '\0'..'\x1F', '\x7F'..'\xFF': - result.add '\\' - result.add toOctal(c) - of '\'', '\"', '\\', '?': - result.add '\\' - result.add c - else: - result.add c - -proc makeCString(s: string): string = - result = newStringOfCap(s.len + 10) - result.add('"') - for c in s: toCChar(c, result) - result.add('"') - -template emitData(s: string) = c.data.add c.tokens.getOrIncl(s) -template emitData(t: Token) = c.data.add t -template emitData(t: PredefinedToken) = c.data.add Token(t) - -proc genStrLit(c: var GeneratedCode; lit: Literals; litId: LitId): Token = - result = Token(c.tokens.getOrIncl "QStr" & $litId) - if not containsOrIncl(c.emittedStrings, int(litId)): - let s {.cursor.} = lit.strings[litId] - emitData "static const struct " - emitData CurlyLe - emitData "NI cap" - emitData Semicolon - emitData "NC8 data" - emitData BracketLe - emitData $s.len - emitData "+1" - emitData BracketRi - emitData Semicolon - emitData CurlyRi - emitData result - emitData AsgnOpr - emitData CurlyLe - emitData $s.len - emitData " | NIM_STRLIT_FLAG" - emitData Comma - emitData makeCString(s) - emitData CurlyRi - emitData Semicolon - -proc genIntLit(c: var GeneratedCode; lit: Literals; litId: LitId) = - let i = lit.numbers[litId] - if i > low(int32) and i <= high(int32): - c.add $i - elif i == low(int32): - # Nim has the same bug for the same reasons :-) - c.add "(-2147483647 -1)" - elif i > low(int64): - c.add "IL64(" - c.add $i - c.add ")" - else: - c.add "(IL64(-9223372036854775807) - IL64(1))" - -proc gen(c: var GeneratedCode; t: Tree; n: NodePos) - -proc genDisplayName(c: var GeneratedCode; symId: SymId) = - let displayName = c.m.symnames[symId] - if displayName != LitId(0): - c.add "/*" - c.add c.m.lit.strings[displayName] - c.add "*/" - -proc genSymDef(c: var GeneratedCode; t: Tree; n: NodePos) = - if t[n].kind == SymDef: - let symId = t[n].symId - c.needsPrefix.incl symId.int - genDisplayName c, symId - gen c, t, n - -proc genGlobal(c: var GeneratedCode; t: Tree; name, typ: NodePos; annotation: string) = - c.add annotation - let m: string - if t[name].kind == SymDef: - let symId = t[name].symId - m = c.tokens[mangleModuleName(c, c.m.namespace)] & "__" & $symId - genDisplayName c, symId - else: - assert t[name].kind == ModuleSymUse - let (x, y) = sons2(t, name) - m = c.tokens[mangleModuleName(c, t[x].litId)] & "__" & $t[y].immediateVal - genType c, c.m.types, c.m.lit, t[typ].typeId, m - -proc genLocal(c: var GeneratedCode; t: Tree; name, typ: NodePos; annotation: string) = - assert t[name].kind == SymDef - c.add annotation - let symId = t[name].symId - genType c, c.m.types, c.m.lit, t[typ].typeId, "q" & $symId - genDisplayName c, symId - -proc genProcDecl(c: var GeneratedCode; t: Tree; n: NodePos; isExtern: bool) = - let signatureBegin = c.code.len - let name = n.firstSon - - var prc = n.firstSon - next t, prc - - while true: - case t[prc].kind - of PragmaPair: - let (x, y) = sons2(t, prc) - let key = cast[PragmaKey](t[x].rawOperand) - case key - of HeaderImport: - let lit = t[y].litId - let headerAsStr {.cursor.} = c.m.lit.strings[lit] - let header = c.tokens.getOrIncl(headerAsStr) - # headerAsStr can be empty, this has the semantics of the `nodecl` pragma: - if headerAsStr.len > 0 and not c.includedHeaders.containsOrIncl(int header): - if headerAsStr[0] == '#': - discard "skip the #include" - else: - c.includes.add Token(IncludeKeyword) - c.includes.add header - c.includes.add Token NewLine - # do not generate code for importc'ed procs: - return - of DllImport: - let lit = t[y].litId - raiseAssert "cannot eval: " & c.m.lit.strings[lit] - else: discard - of PragmaId: discard - else: break - next t, prc - - var resultDeclPos = NodePos(-1) - if t[prc].kind == SummonResult: - resultDeclPos = prc - gen c, t, prc.firstSon - next t, prc - else: - c.add "void" - c.add Space - genSymDef c, t, name - c.add ParLe - var params = 0 - while t[prc].kind == SummonParam: - if params > 0: c.add Comma - let (typ, sym) = sons2(t, prc) - genLocal c, t, sym, typ, "" - next t, prc - inc params - if params == 0: - c.add "void" - c.add ParRi - - for i in signatureBegin ..< c.code.len: - c.protos.add c.code[i] - c.protos.add Token Semicolon - - if isExtern: - c.code.setLen signatureBegin - else: - c.add CurlyLe - if resultDeclPos.int >= 0: - gen c, t, resultDeclPos - for ch in sonsRest(t, n, prc): - gen c, t, ch - c.add CurlyRi - -template triop(opr) = - let (typ, a, b) = sons3(t, n) - c.add ParLe - c.add ParLe - gen c, t, typ - c.add ParRi - gen c, t, a - c.add opr - gen c, t, b - c.add ParRi - -template cmpop(opr) = - let (_, a, b) = sons3(t, n) - c.add ParLe - gen c, t, a - c.add opr - gen c, t, b - c.add ParRi - -template binaryop(opr) = - let (typ, a) = sons2(t, n) - c.add ParLe - c.add ParLe - gen c, t, typ - c.add ParRi - c.add opr - gen c, t, a - c.add ParRi - -template checkedBinaryop(opr) = - let (typ, labIdx, a, b) = sons4(t, n) - let bits = integralBits(c.m.types[t[typ].typeId]) - let lab = t[labIdx].label - - c.add (opr & $bits) - c.add ParLe - c.gen t, a - c.add Comma - c.gen t, b - c.add Comma - c.add "L" & $lab.int - c.add ParRi - -proc genNumberConv(c: var GeneratedCode; t: Tree; n: NodePos) = - let (typ, arg) = sons2(t, n) - if t[arg].kind == IntVal: - let litId = t[arg].litId - c.add ParLe - c.add ParLe - gen c, t, typ - c.add ParRi - case c.m.types[t[typ].typeId].kind - of UIntTy: - let x = cast[uint64](c.m.lit.numbers[litId]) - c.add $x - of FloatTy: - let x = cast[float64](c.m.lit.numbers[litId]) - c.add $x - else: - gen c, t, arg - c.add ParRi - else: - binaryop "" - -template moveToDataSection(body: untyped) = - let oldLen = c.code.len - body - for i in oldLen ..< c.code.len: - c.data.add c.code[i] - setLen c.code, oldLen - -proc gen(c: var GeneratedCode; t: Tree; n: NodePos) = - case t[n].kind - of Nop: - discard "nothing to emit" - of ImmediateVal: - c.add $t[n].immediateVal - of IntVal: - genIntLit c, c.m.lit, t[n].litId - of StrVal: - c.code.add genStrLit(c, c.m.lit, t[n].litId) - of Typed: - genType c, c.m.types, c.m.lit, t[n].typeId - of SymDef, SymUse: - let s = t[n].symId - if c.needsPrefix.contains(s.int): - c.code.add mangleModuleName(c, c.m.namespace) - c.add "__" - c.add $s - else: - # XXX Use proper names here - c.add "q" - c.add $s - - of ModuleSymUse: - let (x, y) = sons2(t, n) - let u = mangleModuleName(c, t[x].litId) - let s = t[y].immediateVal - c.code.add u - c.add "__" - c.add $s - - of NilVal: - c.add NullPtr - of LoopLabel: - c.add WhileKeyword - c.add ParLe - c.add "1" - c.add ParRi - c.add CurlyLe - of GotoLoop: - c.add CurlyRi - of Label: - let lab = t[n].label - c.add "L" - c.add $lab.int - c.add Colon - c.add Semicolon - of Goto: - let lab = t[n].label - c.add "goto L" - c.add $lab.int - c.add Semicolon - of CheckedGoto: - discard "XXX todo" - of ArrayConstr: - c.add CurlyLe - c.add ".a = " - c.add CurlyLe - var i = 0 - for ch in sonsFrom1(t, n): - if i > 0: c.add Comma - c.gen t, ch - inc i - c.add CurlyRi - c.add CurlyRi - of ObjConstr: - c.add CurlyLe - var i = 0 - for ch in sonsFrom1(t, n): - if i mod 2 == 0: - if i > 0: c.add Comma - c.add ".F" & $t[ch].immediateVal - c.add AsgnOpr - else: - c.gen t, ch - inc i - c.add CurlyRi - of Ret: - c.add ReturnKeyword - c.gen t, n.firstSon - c.add Semicolon - of Select: - c.add SwitchKeyword - c.add ParLe - let (_, selector) = sons2(t, n) - c.gen t, selector - c.add ParRi - c.add CurlyLe - for ch in sonsFromN(t, n, 2): - c.gen t, ch - c.add CurlyRi - of SelectPair: - let (le, ri) = sons2(t, n) - c.gen t, le - c.gen t, ri - of SelectList: - for ch in sons(t, n): - c.gen t, ch - of SelectValue: - c.add CaseKeyword - c.gen t, n.firstSon - c.add Colon - of SelectRange: - let (le, ri) = sons2(t, n) - c.add CaseKeyword - c.gen t, le - c.add " ... " - c.gen t, ri - c.add Colon - of ForeignDecl: - c.data.add LitId(ExternKeyword) - c.gen t, n.firstSon - of SummonGlobal: - moveToDataSection: - let (typ, sym) = sons2(t, n) - c.genGlobal t, sym, typ, "" - c.add Semicolon - of SummonThreadLocal: - moveToDataSection: - let (typ, sym) = sons2(t, n) - c.genGlobal t, sym, typ, "__thread " - c.add Semicolon - of SummonConst: - moveToDataSection: - let (typ, sym, val) = sons3(t, n) - c.genGlobal t, sym, typ, "const " - c.add AsgnOpr - c.gen t, val - c.add Semicolon - of Summon, SummonResult: - let (typ, sym) = sons2(t, n) - c.genLocal t, sym, typ, "" - c.add Semicolon - - of SummonParam: - raiseAssert "SummonParam should have been handled in genProc" - of Kill: - discard "we don't care about Kill instructions" - of AddrOf: - let (_, arg) = sons2(t, n) - c.add "&" - gen c, t, arg - of DerefArrayAt: - let (_, a, i) = sons3(t, n) - gen c, t, a - c.add BracketLe - gen c, t, i - c.add BracketRi - of ArrayAt: - let (_, a, i) = sons3(t, n) - gen c, t, a - c.add Dot - c.add "a" - c.add BracketLe - gen c, t, i - c.add BracketRi - of FieldAt: - let (_, a, b) = sons3(t, n) - gen c, t, a - let field = t[b].immediateVal - c.add Dot - c.add "F" & $field - of DerefFieldAt: - let (_, a, b) = sons3(t, n) - gen c, t, a - let field = t[b].immediateVal - c.add Arrow - c.add "F" & $field - of Load: - let (_, arg) = sons2(t, n) - c.add ParLe - c.add "*" - gen c, t, arg - c.add ParRi - of Store: - raiseAssert "Assumption was that Store is unused!" - of Asgn: - let (_, dest, src) = sons3(t, n) - gen c, t, dest - c.add AsgnOpr - gen c, t, src - c.add Semicolon - of CheckedRange: - c.add "nimCheckRange" - c.add ParLe - let (_, gotoInstr, x, a, b) = sons5(t, n) - gen c, t, x - c.add Comma - gen c, t, a - c.add Comma - gen c, t, b - c.add Comma - c.add "L" & $t[gotoInstr].label.int - c.add ParRi - of CheckedIndex: - c.add "nimCheckIndex" - c.add ParLe - let (gotoInstr, x, a) = sons3(t, n) - gen c, t, x - c.add Comma - gen c, t, a - c.add Comma - c.add "L" & $t[gotoInstr].label.int - c.add ParRi - of Call, IndirectCall: - let (typ, fn) = sons2(t, n) - gen c, t, fn - c.add ParLe - var i = 0 - for ch in sonsFromN(t, n, 2): - if i > 0: c.add Comma - gen c, t, ch - inc i - c.add ParRi - if c.m.types[t[typ].typeId].kind == VoidTy: - c.add Semicolon - of CheckedCall, CheckedIndirectCall: - let (typ, gotoInstr, fn) = sons3(t, n) - gen c, t, fn - c.add ParLe - var i = 0 - for ch in sonsFromN(t, n, 3): - if i > 0: c.add Comma - gen c, t, ch - inc i - c.add ParRi - if c.m.types[t[typ].typeId].kind == VoidTy: - c.add Semicolon - - of CheckedAdd: checkedBinaryop "nimAddInt" - of CheckedSub: checkedBinaryop "nimSubInt" - of CheckedMul: checkedBinaryop "nimMulInt" - of CheckedDiv: checkedBinaryop "nimDivInt" - of CheckedMod: checkedBinaryop "nimModInt" - of Add: triop " + " - of Sub: triop " - " - of Mul: triop " * " - of Div: triop " / " - of Mod: triop " % " - of BitShl: triop " << " - of BitShr: triop " >> " - of BitAnd: triop " & " - of BitOr: triop " | " - of BitXor: triop " ^ " - of BitNot: binaryop " ~ " - of BoolNot: binaryop " !" - of Eq: cmpop " == " - of Le: cmpop " <= " - of Lt: cmpop " < " - of Cast: binaryop "" - of NumberConv: genNumberConv c, t, n - of CheckedObjConv: binaryop "" - of ObjConv: binaryop "" - of Emit: raiseAssert "cannot interpret: Emit" - of ProcDecl: genProcDecl c, t, n, false - of ForeignProcDecl: genProcDecl c, t, n, true - of PragmaPair, PragmaId, TestOf, Yld, SetExc, TestExc: - c.add "cannot interpret: " & $t[n].kind - -const - Prelude = """ -/* GENERATED CODE. DO NOT EDIT. */ - -#ifdef __cplusplus -#define NB8 bool -#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901) -// see #13798: to avoid conflicts for code emitting `#include <stdbool.h>` -#define NB8 _Bool -#else -typedef unsigned char NB8; // best effort -#endif - -typedef unsigned char NC8; - -typedef float NF32; -typedef double NF64; -#if defined(__BORLANDC__) || defined(_MSC_VER) -typedef signed char NI8; -typedef signed short int NI16; -typedef signed int NI32; -typedef __int64 NI64; -/* XXX: Float128? */ -typedef unsigned char NU8; -typedef unsigned short int NU16; -typedef unsigned int NU32; -typedef unsigned __int64 NU64; -#elif defined(HAVE_STDINT_H) -#ifndef USE_NIM_NAMESPACE -# include <stdint.h> -#endif -typedef int8_t NI8; -typedef int16_t NI16; -typedef int32_t NI32; -typedef int64_t NI64; -typedef uint8_t NU8; -typedef uint16_t NU16; -typedef uint32_t NU32; -typedef uint64_t NU64; -#elif defined(HAVE_CSTDINT) -#ifndef USE_NIM_NAMESPACE -# include <cstdint> -#endif -typedef std::int8_t NI8; -typedef std::int16_t NI16; -typedef std::int32_t NI32; -typedef std::int64_t NI64; -typedef std::uint8_t NU8; -typedef std::uint16_t NU16; -typedef std::uint32_t NU32; -typedef std::uint64_t NU64; -#else -/* Unknown compiler/version, do our best */ -#ifdef __INT8_TYPE__ -typedef __INT8_TYPE__ NI8; -#else -typedef signed char NI8; -#endif -#ifdef __INT16_TYPE__ -typedef __INT16_TYPE__ NI16; -#else -typedef signed short int NI16; -#endif -#ifdef __INT32_TYPE__ -typedef __INT32_TYPE__ NI32; -#else -typedef signed int NI32; -#endif -#ifdef __INT64_TYPE__ -typedef __INT64_TYPE__ NI64; -#else -typedef long long int NI64; -#endif -/* XXX: Float128? */ -#ifdef __UINT8_TYPE__ -typedef __UINT8_TYPE__ NU8; -#else -typedef unsigned char NU8; -#endif -#ifdef __UINT16_TYPE__ -typedef __UINT16_TYPE__ NU16; -#else -typedef unsigned short int NU16; -#endif -#ifdef __UINT32_TYPE__ -typedef __UINT32_TYPE__ NU32; -#else -typedef unsigned int NU32; -#endif -#ifdef __UINT64_TYPE__ -typedef __UINT64_TYPE__ NU64; -#else -typedef unsigned long long int NU64; -#endif -#endif - -#ifdef NIM_INTBITS -# if NIM_INTBITS == 64 -typedef NI64 NI; -typedef NU64 NU; -# elif NIM_INTBITS == 32 -typedef NI32 NI; -typedef NU32 NU; -# elif NIM_INTBITS == 16 -typedef NI16 NI; -typedef NU16 NU; -# elif NIM_INTBITS == 8 -typedef NI8 NI; -typedef NU8 NU; -# else -# error "invalid bit width for int" -# endif -#endif - -#define NIM_STRLIT_FLAG ((NU)(1) << ((NIM_INTBITS) - 2)) /* This has to be the same as system.strlitFlag! */ - -#define nimAddInt64(a, b, L) ({long long int res; if(__builtin_saddll_overflow(a, b, &res)) goto L; res}) -#define nimSubInt64(a, b, L) ({long long int res; if(__builtin_ssubll_overflow(a, b, &res)) goto L; res}) -#define nimMulInt64(a, b, L) ({long long int res; if(__builtin_smulll_overflow(a, b, &res)) goto L; res}) - -#define nimAddInt32(a, b, L) ({long int res; if(__builtin_sadd_overflow(a, b, &res)) goto L; res}) -#define nimSubInt32(a, b, L) ({long int res; if(__builtin_ssub_overflow(a, b, &res)) goto L; res}) -#define nimMulInt32(a, b, L) ({long int res; if(__builtin_smul_overflow(a, b, &res)) goto L; res}) - -#define nimCheckRange(x, a, b, L) ({if (x < a || x > b) goto L; x}) -#define nimCheckIndex(x, a, L) ({if (x >= a) goto L; x}) - -""" - -proc traverseCode(c: var GeneratedCode) = - const AllowedInToplevelC = {SummonConst, SummonGlobal, SummonThreadLocal, - ProcDecl, ForeignDecl, ForeignProcDecl} - var i = NodePos(0) - while i.int < c.m.code.len: - let oldLen = c.code.len - let moveToInitSection = c.m.code[NodePos(i)].kind notin AllowedInToplevelC - - gen c, c.m.code, NodePos(i) - next c.m.code, i - - if moveToInitSection: - for i in oldLen ..< c.code.len: - c.init.add c.code[i] - setLen c.code, oldLen - -proc generateCode*(inp, outp: string) = - var c = initGeneratedCode(load(inp)) - - var co = TypeOrder() - traverseTypes(c.m.types, c.m.lit, co) - - generateTypes(c, c.m.types, c.m.lit, co) - let typeDecls = move c.code - - traverseCode c - var f = CppFile(f: open(outp, fmWrite)) - f.write "#define NIM_INTBITS " & $c.m.intbits & "\n" - f.write Prelude - writeTokenSeq f, c.includes, c - writeTokenSeq f, typeDecls, c - writeTokenSeq f, c.data, c - writeTokenSeq f, c.protos, c - writeTokenSeq f, c.code, c - if c.init.len > 0: - f.write "void __attribute__((constructor)) init(void) {" - writeTokenSeq f, c.init, c - f.write "}\n\n" - f.f.close diff --git a/compiler/nir/nir.nim b/compiler/nir/nir.nim deleted file mode 100644 index 6f7077fb0..000000000 --- a/compiler/nir/nir.nim +++ /dev/null @@ -1,105 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Nim Intermediate Representation, designed to capture all of Nim's semantics without losing too much -## precious information. Can easily be translated into C. And to JavaScript, hopefully. - -from std/os import addFileExt, `/`, createDir - -import std / assertions -import ".." / [ast, modulegraphs, renderer, transf, options, msgs, lineinfos] -import nirtypes, nirinsts, ast2ir, nirlineinfos, nirfiles, nirvm - -import ".." / ic / [rodfiles, bitabs] - -type - PCtx* = ref object of TPassContext - m: ModuleCon - c: ProcCon - oldErrorCount: int - bytecode: Bytecode - -proc newCtx*(module: PSym; g: ModuleGraph; idgen: IdGenerator): PCtx = - var lit = Literals() - var nirm = (ref NirModule)(types: initTypeGraph(lit), lit: lit) - var m = initModuleCon(g, g.config, idgen, module, nirm) - m.noModularity = true - PCtx(m: m, c: initProcCon(m, nil, g.config), idgen: idgen, bytecode: initBytecode(nirm)) - -proc refresh*(c: PCtx; module: PSym; idgen: IdGenerator) = - #c.m = initModuleCon(c.m.graph, c.m.graph.config, idgen, module, c.m.nirm) - #c.m.noModularity = true - c.c = initProcCon(c.m, nil, c.m.graph.config) - c.idgen = idgen - -proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) = - if graph.repl.isNil: - graph.repl = newCtx(module, graph, idgen) - #registerAdditionalOps(PCtx graph.repl) - else: - refresh(PCtx graph.repl, module, idgen) - -proc setupNirReplGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = - setupGlobalCtx(module, graph, idgen) - result = PCtx graph.repl - -proc evalStmt(c: PCtx; n: PNode) = - let n = transformExpr(c.m.graph, c.idgen, c.m.module, n) - let pc = genStmt(c.c, n) - #var res = "" - #toString c.m.nirm.code, NodePos(pc), c.m.nirm.lit.strings, c.m.nirm.lit.numbers, c.m.symnames, res - #res.add "\n--------------------------\n" - #toString res, c.m.types.g - if pc.int < c.m.nirm.code.len: - c.bytecode.interactive = c.m.graph.interactive - execCode c.bytecode, c.m.nirm.code, pc - #echo res - -proc runCode*(c: PPassContext; n: PNode): PNode = - let c = PCtx(c) - # don't eval errornous code: - if c.oldErrorCount == c.m.graph.config.errorCounter: - evalStmt(c, n) - result = newNodeI(nkEmpty, n.info) - else: - result = n - c.oldErrorCount = c.m.graph.config.errorCounter - -type - NirPassContext* = ref object of TPassContext - m: ModuleCon - c: ProcCon - -proc openNirBackend*(g: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = - var lit = Literals() - var nirm = (ref NirModule)(types: initTypeGraph(lit), lit: lit) - let m = initModuleCon(g, g.config, idgen, module, nirm) - NirPassContext(m: m, c: initProcCon(m, nil, g.config), idgen: idgen) - -proc gen(c: NirPassContext; n: PNode) = - let n = transformExpr(c.m.graph, c.idgen, c.m.module, n) - let pc = genStmt(c.c, n) - -proc nirBackend*(c: PPassContext; n: PNode): PNode = - gen(NirPassContext(c), n) - result = n - -proc closeNirBackend*(c: PPassContext; finalNode: PNode) = - discard nirBackend(c, finalNode) - - let c = NirPassContext(c) - let nimcache = getNimcacheDir(c.c.config).string - createDir nimcache - let outp = nimcache / c.m.module.name.s.addFileExt("nir") - #c.m.nirm.code = move c.c.code - try: - store c.m.nirm[], outp - echo "created: ", outp - except IOError: - rawMessage(c.c.config, errFatal, "serialization failed: " & outp) diff --git a/compiler/nir/nirc.nim b/compiler/nir/nirc.nim deleted file mode 100644 index a2cf69988..000000000 --- a/compiler/nir/nirc.nim +++ /dev/null @@ -1,52 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Nir Compiler. - -import ".." / ic / [bitabs, rodfiles] -import nirinsts, nirtypes, nirlineinfos, nirfiles, cir - -proc view(filename: string) = - let m = load(filename) - var res = "" - allTreesToString m.code, m.lit.strings, m.lit.numbers, m.symnames, res - res.add "\n# TYPES\n" - nirtypes.toString res, m.types - echo res - -import std / [syncio, parseopt] - -proc writeHelp = - echo """Usage: nirc view|c <file.nir>""" - quit 0 - -proc main = - var inp = "" - var cmd = "" - for kind, key, val in getopt(): - case kind - of cmdArgument: - if cmd.len == 0: cmd = key - elif inp.len == 0: inp = key - else: quit "Error: too many arguments" - of cmdLongOption, cmdShortOption: - case key - of "help", "h": writeHelp() - of "version", "v": stdout.write "1.0\n" - of cmdEnd: discard - if inp.len == 0: - quit "Error: no input file specified" - case cmd - of "", "view": - view inp - of "c": - let outp = inp & ".c" - cir.generateCode inp, outp - -main() diff --git a/compiler/nir/nirfiles.nim b/compiler/nir/nirfiles.nim deleted file mode 100644 index cd5a79f06..000000000 --- a/compiler/nir/nirfiles.nim +++ /dev/null @@ -1,83 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -import ".." / ic / [bitabs, rodfiles] -import nirinsts, nirtypes, nirlineinfos - -type - NirModule* = object - code*: Tree - man*: LineInfoManager - types*: TypeGraph - lit*: Literals - namespace*: LitId - intbits*: uint32 - symnames*: SymNames - -proc load*(filename: string): NirModule = - let lit = Literals() - result = NirModule(lit: lit, types: initTypeGraph(lit)) - var r = rodfiles.open(filename) - try: - r.loadHeader(nirCookie) - r.loadSection stringsSection - r.load result.lit.strings - - r.loadSection numbersSection - r.load result.lit.numbers - - r.loadSection bodiesSection - r.load result.code - - r.loadSection typesSection - r.load result.types - - r.loadSection sideChannelSection - r.load result.man - - r.loadSection namespaceSection - r.loadPrim result.namespace - r.loadPrim result.intbits - - r.loadSection symnamesSection - r.load result.symnames - - finally: - r.close() - -proc store*(m: NirModule; outp: string) = - var r = rodfiles.create(outp) - try: - r.storeHeader(nirCookie) - r.storeSection stringsSection - r.store m.lit.strings - - r.storeSection numbersSection - r.store m.lit.numbers - - r.storeSection bodiesSection - r.store m.code - - r.storeSection typesSection - r.store m.types - - r.storeSection sideChannelSection - r.store m.man - - r.storeSection namespaceSection - r.storePrim m.namespace - r.storePrim m.intbits - - r.storeSection symnamesSection - r.store m.symnames - - finally: - r.close() - if r.err != ok: - raise newException(IOError, "could store into: " & outp) diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim deleted file mode 100644 index 6cffc1a89..000000000 --- a/compiler/nir/nirinsts.nim +++ /dev/null @@ -1,582 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## NIR instructions. Somewhat inspired by LLVM's instructions. - -import std / [assertions, hashes] -import .. / ic / [bitabs, rodfiles] -import nirlineinfos, nirtypes - -const - NirVersion = 1 - nirCookie* = [byte(0), byte('N'), byte('I'), byte('R'), - byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(NirVersion)] - -type - SymId* = distinct int - -proc `$`*(s: SymId): string {.borrow.} -proc hash*(s: SymId): Hash {.borrow.} -proc `==`*(a, b: SymId): bool {.borrow.} - -type - Opcode* = enum - Nop, - ImmediateVal, - IntVal, - StrVal, - SymDef, - SymUse, - Typed, # with type ID - PragmaId, # with Pragma ID, possible values: see PragmaKey enum - NilVal, - Label, - Goto, - CheckedGoto, - LoopLabel, - GotoLoop, # last atom - - ModuleSymUse, # `"module".x` - - ArrayConstr, - ObjConstr, - Ret, - Yld, - - Select, - SelectPair, # ((values...), Label) - SelectList, # (values...) - SelectValue, # (value) - SelectRange, # (valueA..valueB) - ForeignDecl, # Can wrap SummonGlobal, SummonThreadLocal, SummonConst - SummonGlobal, - SummonThreadLocal, - Summon, # x = Summon Typed <Type ID>; x begins to live - SummonResult, - SummonParam, - SummonConst, - Kill, # `Kill x`: scope end for `x` - - AddrOf, - ArrayAt, # a[i] - DerefArrayAt, # a[i] where `a` is a PtrArray; `a[][i]` - FieldAt, # obj.field - DerefFieldAt, # obj[].field - - Load, # a[] - Store, # a[] = b - Asgn, # a = b - SetExc, - TestExc, - - CheckedRange, - CheckedIndex, - - Call, - IndirectCall, - CheckedCall, # call that can raise - CheckedIndirectCall, # call that can raise - CheckedAdd, # with overflow checking etc. - CheckedSub, - CheckedMul, - CheckedDiv, - CheckedMod, - Add, - Sub, - Mul, - Div, - Mod, - BitShl, - BitShr, - BitAnd, - BitOr, - BitXor, - BitNot, - BoolNot, - Eq, - Le, - Lt, - Cast, - NumberConv, - CheckedObjConv, - ObjConv, - TestOf, - Emit, - ProcDecl, - ForeignProcDecl, - PragmaPair - -type - PragmaKey* = enum - FastCall, StdCall, CDeclCall, SafeCall, SysCall, InlineCall, NoinlineCall, ThisCall, NoCall, - CoreName, - ExternName, - HeaderImport, - DllImport, - DllExport, - ObjExport - -const - LastAtomicValue = GotoLoop - - OpcodeBits = 8'u32 - OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32 - - ValueProducingAtoms = {ImmediateVal, IntVal, StrVal, SymUse, NilVal} - - ValueProducing* = { - ImmediateVal, - IntVal, - StrVal, - SymUse, - NilVal, - ModuleSymUse, - ArrayConstr, - ObjConstr, - CheckedAdd, - CheckedSub, - CheckedMul, - CheckedDiv, - CheckedMod, - Add, - Sub, - Mul, - Div, - Mod, - BitShl, - BitShr, - BitAnd, - BitOr, - BitXor, - BitNot, - BoolNot, - Eq, - Le, - Lt, - Cast, - NumberConv, - CheckedObjConv, - ObjConv, - AddrOf, - Load, - ArrayAt, - DerefArrayAt, - FieldAt, - DerefFieldAt, - TestOf - } - -type - Instr* = object # 8 bytes - x: uint32 - info*: PackedLineInfo - -template kind*(n: Instr): Opcode = Opcode(n.x and OpcodeMask) -template operand(n: Instr): uint32 = (n.x shr OpcodeBits) - -template rawOperand*(n: Instr): uint32 = (n.x shr OpcodeBits) - -template toX(k: Opcode; operand: uint32): uint32 = - uint32(k) or (operand shl OpcodeBits) - -template toX(k: Opcode; operand: LitId): uint32 = - uint32(k) or (operand.uint32 shl OpcodeBits) - -type - Tree* = object - nodes: seq[Instr] - - Values* = object - numbers: BiTable[int64] - strings: BiTable[string] - -type - PatchPos* = distinct int - NodePos* = distinct int - -const - InvalidPatchPos* = PatchPos(-1) - -proc isValid(p: PatchPos): bool {.inline.} = p.int != -1 - -proc prepare*(tree: var Tree; info: PackedLineInfo; kind: Opcode): PatchPos = - result = PatchPos tree.nodes.len - tree.nodes.add Instr(x: toX(kind, 1'u32), info: info) - -proc isAtom(tree: Tree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue -proc isAtom(tree: Tree; pos: NodePos): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue - -proc patch*(tree: var Tree; pos: PatchPos) = - let pos = pos.int - let k = tree.nodes[pos].kind - assert k > LastAtomicValue - let distance = int32(tree.nodes.len - pos) - assert distance > 0 - tree.nodes[pos].x = toX(k, cast[uint32](distance)) - -template build*(tree: var Tree; info: PackedLineInfo; kind: Opcode; body: untyped) = - let pos = prepare(tree, info, kind) - body - patch(tree, pos) - -template buildTyped*(tree: var Tree; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) = - let pos = prepare(tree, info, kind) - tree.addTyped info, typ - body - patch(tree, pos) - -proc len*(tree: Tree): int {.inline.} = tree.nodes.len - -template rawSpan(n: Instr): int = int(operand(n)) - -proc nextChild(tree: Tree; pos: var int) {.inline.} = - if tree.nodes[pos].kind > LastAtomicValue: - assert tree.nodes[pos].operand > 0'u32 - inc pos, tree.nodes[pos].rawSpan - else: - inc pos - -proc next*(tree: Tree; pos: var NodePos) {.inline.} = nextChild tree, int(pos) - -template firstSon*(n: NodePos): NodePos = NodePos(n.int+1) - -template skipTyped*(n: NodePos): NodePos = NodePos(n.int+2) - -iterator sons*(tree: Tree; n: NodePos): NodePos = - var pos = n.int - assert tree.nodes[pos].kind > LastAtomicValue - let last = pos + tree.nodes[pos].rawSpan - inc pos - while pos < last: - yield NodePos pos - nextChild tree, pos - -iterator sonsFrom1*(tree: Tree; n: NodePos): NodePos = - var pos = n.int - assert tree.nodes[pos].kind > LastAtomicValue - let last = pos + tree.nodes[pos].rawSpan - inc pos - nextChild tree, pos - while pos < last: - yield NodePos pos - nextChild tree, pos - -iterator sonsFromN*(tree: Tree; n: NodePos; toSkip = 2): NodePos = - var pos = n.int - assert tree.nodes[pos].kind > LastAtomicValue - let last = pos + tree.nodes[pos].rawSpan - inc pos - for i in 1..toSkip: - nextChild tree, pos - while pos < last: - yield NodePos pos - nextChild tree, pos - -template `[]`*(t: Tree; n: NodePos): Instr = t.nodes[n.int] - -iterator sonsRest*(tree: Tree; parent, n: NodePos): NodePos = - var pos = n.int - assert tree[parent].kind > LastAtomicValue - let last = parent.int + tree[parent].rawSpan - while pos < last: - yield NodePos pos - nextChild tree, pos - -proc span(tree: Tree; pos: int): int {.inline.} = - if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand) - -proc copyTree*(dest: var Tree; src: Tree) = - let pos = 0 - let L = span(src, pos) - let d = dest.nodes.len - dest.nodes.setLen(d + L) - assert L > 0 - for i in 0..<L: - dest.nodes[d+i] = src.nodes[pos+i] - -proc sons2*(tree: Tree; n: NodePos): (NodePos, NodePos) {.inline.} = - assert(not isAtom(tree, n.int)) - let a = n.int+1 - let b = a + span(tree, a) - result = (NodePos a, NodePos b) - -proc sons3*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos) {.inline.} = - assert(not isAtom(tree, n.int)) - let a = n.int+1 - let b = a + span(tree, a) - let c = b + span(tree, b) - result = (NodePos a, NodePos b, NodePos c) - -proc sons4*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos) {.inline.} = - assert(not isAtom(tree, n.int)) - let a = n.int+1 - let b = a + span(tree, a) - let c = b + span(tree, b) - let d = c + span(tree, c) - result = (NodePos a, NodePos b, NodePos c, NodePos d) - -proc sons5*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos, NodePos) {.inline.} = - assert(not isAtom(tree, n.int)) - let a = n.int+1 - let b = a + span(tree, a) - let c = b + span(tree, b) - let d = c + span(tree, c) - let e = d + span(tree, d) - result = (NodePos a, NodePos b, NodePos c, NodePos d, NodePos e) - -proc typeId*(ins: Instr): TypeId {.inline.} = - assert ins.kind == Typed - result = TypeId(ins.operand) - -proc symId*(ins: Instr): SymId {.inline.} = - assert ins.kind in {SymUse, SymDef} - result = SymId(ins.operand) - -proc immediateVal*(ins: Instr): int {.inline.} = - assert ins.kind == ImmediateVal - result = cast[int](ins.operand) - -proc litId*(ins: Instr): LitId {.inline.} = - assert ins.kind in {StrVal, IntVal} - result = LitId(ins.operand) - - -type - LabelId* = distinct int - -proc `==`*(a, b: LabelId): bool {.borrow.} -proc hash*(a: LabelId): Hash {.borrow.} - -proc label*(ins: Instr): LabelId {.inline.} = - assert ins.kind in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto} - result = LabelId(ins.operand) - -proc newLabel*(labelGen: var int): LabelId {.inline.} = - result = LabelId labelGen - inc labelGen - -proc newLabels*(labelGen: var int; n: int): LabelId {.inline.} = - result = LabelId labelGen - inc labelGen, n - -proc addNewLabel*(t: var Tree; labelGen: var int; info: PackedLineInfo; k: Opcode): LabelId = - assert k in {Label, LoopLabel} - result = LabelId labelGen - t.nodes.add Instr(x: toX(k, uint32(result)), info: info) - inc labelGen - -proc gotoLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) = - assert k in {Goto, GotoLoop, CheckedGoto} - t.nodes.add Instr(x: toX(k, uint32(L)), info: info) - -proc addLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) {.inline.} = - assert k in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto} - t.nodes.add Instr(x: toX(k, uint32(L)), info: info) - -proc addSymUse*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} = - t.nodes.add Instr(x: toX(SymUse, uint32(s)), info: info) - -proc addSymDef*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} = - t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info) - -proc addNop*(t: var Tree; info: PackedLineInfo) {.inline.} = - t.nodes.add Instr(x: toX(Nop, 0'u32), info: info) - -proc addTyped*(t: var Tree; info: PackedLineInfo; typ: TypeId) {.inline.} = - assert typ.int >= 0 - t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info) - -proc addSummon*(t: var Tree; info: PackedLineInfo; s: SymId; typ: TypeId; opc = Summon) {.inline.} = - assert typ.int >= 0 - assert opc in {Summon, SummonConst, SummonGlobal, SummonThreadLocal, SummonParam, SummonResult} - let x = prepare(t, info, opc) - t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info) - t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info) - patch t, x - -proc addImmediateVal*(t: var Tree; info: PackedLineInfo; x: int) = - assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int) - t.nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info) - -proc addPragmaId*(t: var Tree; info: PackedLineInfo; x: PragmaKey) = - t.nodes.add Instr(x: toX(PragmaId, uint32(x)), info: info) - -proc addIntVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) = - buildTyped t, info, NumberConv, typ: - t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(x))), info: info) - -proc boolVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; b: bool) = - buildTyped t, info, NumberConv, Bool8Id: - t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(ord b))), info: info) - -proc addStrVal*(t: var Tree; strings: var BiTable[string]; info: PackedLineInfo; s: string) = - t.nodes.add Instr(x: toX(StrVal, uint32(strings.getOrIncl(s))), info: info) - -proc addStrLit*(t: var Tree; info: PackedLineInfo; s: LitId) = - t.nodes.add Instr(x: toX(StrVal, uint32(s)), info: info) - -proc addNilVal*(t: var Tree; info: PackedLineInfo; typ: TypeId) = - buildTyped t, info, NumberConv, typ: - t.nodes.add Instr(x: toX(NilVal, uint32(0)), info: info) - -proc store*(r: var RodFile; t: Tree) = storeSeq r, t.nodes -proc load*(r: var RodFile; t: var Tree) = loadSeq r, t.nodes - -proc escapeToNimLit*(s: string; result: var string) = - result.add '"' - for c in items s: - if c < ' ' or int(c) >= 128: - result.add '\\' - result.addInt int(c) - elif c == '\\': - result.add r"\\" - elif c == '\n': - result.add r"\n" - elif c == '\r': - result.add r"\r" - elif c == '\t': - result.add r"\t" - else: - result.add c - result.add '"' - -type - SymNames* = object - s: seq[LitId] - -proc `[]=`*(t: var SymNames; key: SymId; val: LitId) = - let k = int(key) - if k >= t.s.len: t.s.setLen k+1 - t.s[k] = val - -proc `[]`*(t: SymNames; key: SymId): LitId = - let k = int(key) - if k < t.s.len: result = t.s[k] - else: result = LitId(0) - -template localName(s: SymId): string = - let name = names[s] - if name != LitId(0): - strings[name] - else: - $s.int - -proc store*(r: var RodFile; t: SymNames) = storeSeq(r, t.s) -proc load*(r: var RodFile; t: var SymNames) = loadSeq(r, t.s) - -proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTable[int64]; - names: SymNames; - r: var string; nesting = 0) = - if r.len > 0 and r[r.len-1] notin {' ', '\n', '(', '[', '{'}: - r.add ' ' - - case t[pos].kind - of Nop: r.add "Nop" - of ImmediateVal: - r.add $t[pos].operand - of IntVal: - r.add "IntVal " - r.add $integers[LitId t[pos].operand] - of StrVal: - escapeToNimLit(strings[LitId t[pos].operand], r) - of SymDef: - r.add "SymDef " - r.add localName(SymId t[pos].operand) - of SymUse: - r.add "SymUse " - r.add localName(SymId t[pos].operand) - of PragmaId: - r.add $cast[PragmaKey](t[pos].operand) - of Typed: - r.add "T<" - r.add $t[pos].operand - r.add ">" - of NilVal: - r.add "NilVal" - of Label: - # undo the nesting: - var spaces = r.len-1 - while spaces >= 0 and r[spaces] == ' ': dec spaces - r.setLen spaces+1 - r.add "\n L" - r.add $t[pos].operand - of Goto, CheckedGoto, LoopLabel, GotoLoop: - r.add $t[pos].kind - r.add " L" - r.add $t[pos].operand - else: - r.add $t[pos].kind - r.add "{\n" - for i in 0..<(nesting+1)*2: r.add ' ' - for p in sons(t, pos): - toString t, p, strings, integers, names, r, nesting+1 - r.add "\n" - for i in 0..<nesting*2: r.add ' ' - r.add "}" - -proc allTreesToString*(t: Tree; strings: BiTable[string]; integers: BiTable[int64]; - names: SymNames; - r: var string) = - var i = 0 - while i < t.len: - toString t, NodePos(i), strings, integers, names, r - nextChild t, i - -type - Value* = distinct Tree - -proc prepare*(dest: var Value; info: PackedLineInfo; k: Opcode): PatchPos {.inline.} = - assert k in ValueProducing - ValueProducingAtoms - result = prepare(Tree(dest), info, k) - -proc patch*(dest: var Value; pos: PatchPos) {.inline.} = - patch(Tree(dest), pos) - -proc localToValue*(info: PackedLineInfo; s: SymId): Value = - result = Value(Tree()) - Tree(result).addSymUse info, s - -proc hasValue*(v: Value): bool {.inline.} = Tree(v).len > 0 - -proc isEmpty*(v: Value): bool {.inline.} = Tree(v).len == 0 - -proc extractTemp*(v: Value): SymId = - if hasValue(v) and Tree(v)[NodePos 0].kind == SymUse: - result = SymId(Tree(v)[NodePos 0].operand) - else: - result = SymId(-1) - -proc copyTree*(dest: var Tree; src: Value) = copyTree dest, Tree(src) - -proc addImmediateVal*(t: var Value; info: PackedLineInfo; x: int) = - assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int) - Tree(t).nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info) - -template build*(tree: var Value; info: PackedLineInfo; kind: Opcode; body: untyped) = - let pos = prepare(Tree(tree), info, kind) - body - patch(tree, pos) - -proc addTyped*(t: var Value; info: PackedLineInfo; typ: TypeId) {.inline.} = - addTyped(Tree(t), info, typ) - -template buildTyped*(tree: var Value; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) = - let pos = prepare(tree, info, kind) - tree.addTyped info, typ - body - patch(tree, pos) - -proc addStrVal*(t: var Value; strings: var BiTable[string]; info: PackedLineInfo; s: string) = - addStrVal(Tree(t), strings, info, s) - -proc addNilVal*(t: var Value; info: PackedLineInfo; typ: TypeId) = - addNilVal Tree(t), info, typ - -proc addIntVal*(t: var Value; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) = - addIntVal Tree(t), integers, info, typ, x diff --git a/compiler/nir/nirslots.nim b/compiler/nir/nirslots.nim deleted file mode 100644 index a01e7a633..000000000 --- a/compiler/nir/nirslots.nim +++ /dev/null @@ -1,104 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Management of slots. Similar to "register allocation" -## in lower level languages. - -import std / [assertions, tables] -import nirtypes, nirinsts - -type - SlotManagerFlag* = enum - ReuseTemps, - ReuseVars - SlotKind* = enum - Temp, Perm - SlotManager* = object # "register allocator" - live: Table[SymId, (SlotKind, TypeId)] - dead: Table[TypeId, seq[SymId]] - flags: set[SlotManagerFlag] - inScope: seq[SymId] - -proc initSlotManager*(flags: set[SlotManagerFlag]): SlotManager {.inline.} = - SlotManager(flags: flags) - -proc allocRaw(m: var SlotManager; t: TypeId; f: SlotManagerFlag; k: SlotKind; - symIdgen: var int32): SymId {.inline.} = - if f in m.flags and m.dead.hasKey(t) and m.dead[t].len > 0: - result = m.dead[t].pop() - else: - inc symIdgen - result = SymId(symIdgen) - m.inScope.add result - m.live[result] = (k, t) - -proc allocTemp*(m: var SlotManager; t: TypeId; symIdgen: var int32): SymId {.inline.} = - result = allocRaw(m, t, ReuseTemps, Temp, symIdgen) - -proc allocVar*(m: var SlotManager; t: TypeId; symIdgen: var int32): SymId {.inline.} = - result = allocRaw(m, t, ReuseVars, Perm, symIdgen) - -proc freeLoc*(m: var SlotManager; s: SymId) = - let t = m.live.getOrDefault(s) - assert t[1].int != 0 - m.live.del s - m.dead.mgetOrPut(t[1], @[]).add s - -proc freeTemp*(m: var SlotManager; s: SymId) = - let t = m.live.getOrDefault(s) - if t[1].int != 0 and t[0] == Temp: - m.live.del s - m.dead.mgetOrPut(t[1], @[]).add s - -iterator stillAlive*(m: SlotManager): (SymId, TypeId) = - for k, v in pairs(m.live): - yield (k, v[1]) - -proc getType*(m: SlotManager; s: SymId): TypeId {.inline.} = m.live[s][1] - -proc openScope*(m: var SlotManager) = - m.inScope.add SymId(-1) # add marker - -proc closeScope*(m: var SlotManager) = - var i = m.inScope.len - 1 - while i >= 0: - if m.inScope[i] == SymId(-1): - m.inScope.setLen i - break - dec i - -when isMainModule: - var symIdgen: int32 - var m = initSlotManager({ReuseTemps}) - - var g = initTypeGraph(Literals()) - - let a = g.openType ArrayTy - g.addBuiltinType Int8Id - g.addArrayLen 5 - let finalArrayType = finishType(g, a) - - let obj = g.openType ObjectDecl - g.addName "MyType" - - g.addField "p", finalArrayType, 0 - let objB = finishType(g, obj) - - let x = m.allocTemp(objB, symIdgen) - assert x.int == 0 - - let y = m.allocTemp(objB, symIdgen) - assert y.int == 1 - - let z = m.allocTemp(Int8Id, symIdgen) - assert z.int == 2 - - m.freeLoc y - let y2 = m.allocTemp(objB, symIdgen) - assert y2.int == 1 diff --git a/compiler/nir/nirtypes.nim b/compiler/nir/nirtypes.nim deleted file mode 100644 index a79bf6d01..000000000 --- a/compiler/nir/nirtypes.nim +++ /dev/null @@ -1,475 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Type system for NIR. Close to C's type system but without its quirks. - -import std / [assertions, hashes] -import .. / ic / [bitabs, rodfiles] - -type - NirTypeKind* = enum - VoidTy, IntTy, UIntTy, FloatTy, BoolTy, CharTy, NameVal, - IntVal, SizeVal, AlignVal, OffsetVal, - AnnotationVal, - ObjectTy, - UnionTy, - VarargsTy, # the `...` in a C prototype; also the last "atom" - APtrTy, # pointer to aliasable memory - UPtrTy, # pointer to unique/unaliasable memory - AArrayPtrTy, # pointer to array of aliasable memory - UArrayPtrTy, # pointer to array of unique/unaliasable memory - ArrayTy, - LastArrayTy, # array of unspecified size as a last field inside an object - ProcTy, - ObjectDecl, - UnionDecl, - FieldDecl - -const - TypeKindBits = 8'u32 - TypeKindMask = (1'u32 shl TypeKindBits) - 1'u32 - -type - TypeNode* = object # 4 bytes - x: uint32 - -template kind*(n: TypeNode): NirTypeKind = NirTypeKind(n.x and TypeKindMask) -template operand(n: TypeNode): uint32 = (n.x shr TypeKindBits) - -proc integralBits*(n: TypeNode): int {.inline.} = - # Number of bits in the IntTy, etc. Only valid for integral types. - assert n.kind in {IntTy, UIntTy, FloatTy, BoolTy, CharTy} - result = int(n.operand) - -template toX(k: NirTypeKind; operand: uint32): uint32 = - uint32(k) or (operand shl TypeKindBits) - -template toX(k: NirTypeKind; operand: LitId): uint32 = - uint32(k) or (operand.uint32 shl TypeKindBits) - -type - TypeId* = distinct int - -proc `==`*(a, b: TypeId): bool {.borrow.} -proc hash*(a: TypeId): Hash {.borrow.} - -type - Literals* = ref object - strings*: BiTable[string] - numbers*: BiTable[int64] - - TypeGraph* = object - nodes: seq[TypeNode] - lit: Literals - -const - VoidId* = TypeId 0 - Bool8Id* = TypeId 1 - Char8Id* = TypeId 2 - Int8Id* = TypeId 3 - Int16Id* = TypeId 4 - Int32Id* = TypeId 5 - Int64Id* = TypeId 6 - UInt8Id* = TypeId 7 - UInt16Id* = TypeId 8 - UInt32Id* = TypeId 9 - UInt64Id* = TypeId 10 - Float32Id* = TypeId 11 - Float64Id* = TypeId 12 - VoidPtrId* = TypeId 13 - LastBuiltinId* = 13 - -proc initTypeGraph*(lit: Literals): TypeGraph = - result = TypeGraph(nodes: @[ - TypeNode(x: toX(VoidTy, 0'u32)), - TypeNode(x: toX(BoolTy, 8'u32)), - TypeNode(x: toX(CharTy, 8'u32)), - TypeNode(x: toX(IntTy, 8'u32)), - TypeNode(x: toX(IntTy, 16'u32)), - TypeNode(x: toX(IntTy, 32'u32)), - TypeNode(x: toX(IntTy, 64'u32)), - TypeNode(x: toX(UIntTy, 8'u32)), - TypeNode(x: toX(UIntTy, 16'u32)), - TypeNode(x: toX(UIntTy, 32'u32)), - TypeNode(x: toX(UIntTy, 64'u32)), - TypeNode(x: toX(FloatTy, 32'u32)), - TypeNode(x: toX(FloatTy, 64'u32)), - TypeNode(x: toX(APtrTy, 2'u32)), - TypeNode(x: toX(VoidTy, 0'u32)) - ], lit: lit) - assert result.nodes.len == LastBuiltinId+2 - -type - TypePatchPos* = distinct int - -const - InvalidTypePatchPos* = TypePatchPos(-1) - LastAtomicValue = VarargsTy - -proc isValid(p: TypePatchPos): bool {.inline.} = p.int != -1 - -proc prepare(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos = - result = TypePatchPos tree.nodes.len - tree.nodes.add TypeNode(x: toX(kind, 1'u32)) - -proc isAtom(tree: TypeGraph; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue -proc isAtom(tree: TypeGraph; pos: TypeId): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue - -proc patch(tree: var TypeGraph; pos: TypePatchPos) = - let pos = pos.int - let k = tree.nodes[pos].kind - assert k > LastAtomicValue - let distance = int32(tree.nodes.len - pos) - assert distance > 0 - tree.nodes[pos].x = toX(k, cast[uint32](distance)) - -proc len*(tree: TypeGraph): int {.inline.} = tree.nodes.len - -template rawSpan(n: TypeNode): int = int(operand(n)) - -proc nextChild(tree: TypeGraph; pos: var int) {.inline.} = - if tree.nodes[pos].kind > LastAtomicValue: - assert tree.nodes[pos].operand > 0'u32 - inc pos, tree.nodes[pos].rawSpan - else: - inc pos - -iterator sons*(tree: TypeGraph; n: TypeId): TypeId = - var pos = n.int - assert tree.nodes[pos].kind > LastAtomicValue - let last = pos + tree.nodes[pos].rawSpan - inc pos - while pos < last: - yield TypeId pos - nextChild tree, pos - -template `[]`*(t: TypeGraph; n: TypeId): TypeNode = t.nodes[n.int] - -proc elementType*(tree: TypeGraph; n: TypeId): TypeId {.inline.} = - assert tree[n].kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, ArrayTy, LastArrayTy} - result = TypeId(n.int+1) - -proc litId*(n: TypeNode): LitId {.inline.} = - assert n.kind in {NameVal, IntVal, SizeVal, AlignVal, OffsetVal, AnnotationVal, ObjectTy, UnionTy} - result = LitId(n.operand) - -proc kind*(tree: TypeGraph; n: TypeId): NirTypeKind {.inline.} = tree[n].kind - -proc span(tree: TypeGraph; pos: int): int {.inline.} = - if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand) - -proc sons2(tree: TypeGraph; n: TypeId): (TypeId, TypeId) = - assert(not isAtom(tree, n.int)) - let a = n.int+1 - let b = a + span(tree, a) - result = (TypeId a, TypeId b) - -proc sons3(tree: TypeGraph; n: TypeId): (TypeId, TypeId, TypeId) = - assert(not isAtom(tree, n.int)) - let a = n.int+1 - let b = a + span(tree, a) - let c = b + span(tree, b) - result = (TypeId a, TypeId b, TypeId c) - -proc arrayName*(tree: TypeGraph; n: TypeId): TypeId {.inline.} = - assert tree[n].kind == ArrayTy - let (_, _, c) = sons3(tree, n) - result = c - -proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestInt = - assert tree[n].kind == ArrayTy - let (_, b) = sons2(tree, n) - result = tree.lit.numbers[LitId tree[b].operand] - -proc returnType*(tree: TypeGraph; n: TypeId): (TypeId, TypeId) = - # Returns the positions of the return type + calling convention. - var pos = n.int - assert tree.nodes[pos].kind == ProcTy - let a = n.int+1 - let b = a + span(tree, a) - result = (TypeId b, TypeId a) # not a typo, order is reversed - -iterator params*(tree: TypeGraph; n: TypeId): TypeId = - var pos = n.int - assert tree.nodes[pos].kind == ProcTy - let last = pos + tree.nodes[pos].rawSpan - inc pos - nextChild tree, pos - nextChild tree, pos - while pos < last: - yield TypeId pos - nextChild tree, pos - -proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos = - assert kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, - ArrayTy, LastArrayTy, ProcTy, ObjectDecl, UnionDecl, - FieldDecl} - result = prepare(tree, kind) - -template typeInvariant(p: TypePatchPos) = - when false: - if tree[TypeId(p)].kind == FieldDecl: - var k = 0 - for ch in sons(tree, TypeId(p)): - inc k - assert k > 2, "damn! " & $k - -proc sealType*(tree: var TypeGraph; p: TypePatchPos) = - patch tree, p - typeInvariant(p) - -proc finishType*(tree: var TypeGraph; p: TypePatchPos): TypeId = - # Search for an existing instance of this type in - # order to reduce memory consumption: - patch tree, p - typeInvariant(p) - - let s = span(tree, p.int) - var i = 0 - while i < p.int: - if tree.nodes[i].x == tree.nodes[p.int].x: - var isMatch = true - for j in 1..<s: - if tree.nodes[j+i].x == tree.nodes[j+p.int].x: - discard "still a match" - else: - isMatch = false - break - if isMatch: - if p.int+s == tree.len: - setLen tree.nodes, p.int - return TypeId(i) - nextChild tree, i - result = TypeId(p) - -proc nominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string): TypeId = - assert kind in {ObjectTy, UnionTy} - let content = TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name))) - for i in 0..<tree.len: - if tree.nodes[i].x == content.x: - return TypeId(i) - result = TypeId tree.nodes.len - tree.nodes.add content - -proc addNominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string) = - assert kind in {ObjectTy, UnionTy} - tree.nodes.add TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name))) - -proc getTypeTag*(tree: TypeGraph; t: TypeId): string = - assert tree[t].kind in {ObjectTy, UnionTy} - result = tree.lit.strings[LitId tree[t].operand] - -proc addVarargs*(tree: var TypeGraph) = - tree.nodes.add TypeNode(x: toX(VarargsTy, 0'u32)) - -proc getFloat128Type*(tree: var TypeGraph): TypeId = - result = TypeId tree.nodes.len - tree.nodes.add TypeNode(x: toX(FloatTy, 128'u32)) - -proc addBuiltinType*(g: var TypeGraph; id: TypeId) = - g.nodes.add g[id] - -template firstSon*(n: TypeId): TypeId = TypeId(n.int+1) - -proc addType*(g: var TypeGraph; t: TypeId) = - # We cannot simply copy `*Decl` nodes. We have to introduce `*Ty` nodes instead: - if g[t].kind in {ObjectDecl, UnionDecl}: - assert g[t.firstSon].kind == NameVal - let name = LitId g[t.firstSon].operand - if g[t].kind == ObjectDecl: - g.nodes.add TypeNode(x: toX(ObjectTy, name)) - else: - g.nodes.add TypeNode(x: toX(UnionTy, name)) - else: - let pos = t.int - let L = span(g, pos) - let d = g.nodes.len - g.nodes.setLen(d + L) - assert L > 0 - for i in 0..<L: - g.nodes[d+i] = g.nodes[pos+i] - -proc addArrayLen*(g: var TypeGraph; len: int64) = - g.nodes.add TypeNode(x: toX(IntVal, g.lit.numbers.getOrIncl(len))) - -proc addSize*(g: var TypeGraph; s: int64) = - g.nodes.add TypeNode(x: toX(SizeVal, g.lit.numbers.getOrIncl(s))) - -proc addOffset*(g: var TypeGraph; offset: int64) = - g.nodes.add TypeNode(x: toX(OffsetVal, g.lit.numbers.getOrIncl(offset))) - -proc addAlign*(g: var TypeGraph; a: int64) = - g.nodes.add TypeNode(x: toX(AlignVal, g.lit.numbers.getOrIncl(a))) - -proc addName*(g: var TypeGraph; name: string) = - g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl(name))) - -proc addAnnotation*(g: var TypeGraph; name: string) = - g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl(name))) - -proc addField*(g: var TypeGraph; name: string; typ: TypeId; offset: int64) = - let f = g.openType FieldDecl - g.addType typ - g.addOffset offset - g.addName name - sealType(g, f) - -proc ptrTypeOf*(g: var TypeGraph; t: TypeId): TypeId = - let f = g.openType APtrTy - g.addType t - result = finishType(g, f) - -proc arrayPtrTypeOf*(g: var TypeGraph; t: TypeId): TypeId = - let f = g.openType AArrayPtrTy - g.addType t - result = finishType(g, f) - -proc store*(r: var RodFile; g: TypeGraph) = - storeSeq r, g.nodes - -proc load*(r: var RodFile; g: var TypeGraph) = - loadSeq r, g.nodes - -proc toString*(dest: var string; g: TypeGraph; i: TypeId) = - case g[i].kind - of VoidTy: dest.add "void" - of IntTy: - dest.add "i" - dest.addInt g[i].operand - of UIntTy: - dest.add "u" - dest.addInt g[i].operand - of FloatTy: - dest.add "f" - dest.addInt g[i].operand - of BoolTy: - dest.add "b" - dest.addInt g[i].operand - of CharTy: - dest.add "c" - dest.addInt g[i].operand - of NameVal, AnnotationVal: - dest.add g.lit.strings[LitId g[i].operand] - of IntVal, SizeVal, AlignVal, OffsetVal: - dest.add $g[i].kind - dest.add ' ' - dest.add $g.lit.numbers[LitId g[i].operand] - of VarargsTy: - dest.add "..." - of APtrTy: - dest.add "aptr[" - toString(dest, g, g.elementType(i)) - dest.add "]" - of UPtrTy: - dest.add "uptr[" - toString(dest, g, g.elementType(i)) - dest.add "]" - of AArrayPtrTy: - dest.add "aArrayPtr[" - toString(dest, g, g.elementType(i)) - dest.add "]" - of UArrayPtrTy: - dest.add "uArrayPtr[" - toString(dest, g, g.elementType(i)) - dest.add "]" - of ArrayTy: - dest.add "Array[" - let (elems, len, name) = g.sons3(i) - toString(dest, g, elems) - dest.add ", " - toString(dest, g, len) - dest.add ", " - toString(dest, g, name) - dest.add "]" - of LastArrayTy: - # array of unspecified size as a last field inside an object - dest.add "LastArrayTy[" - toString(dest, g, g.elementType(i)) - dest.add "]" - of ObjectTy: - dest.add "object " - dest.add g.lit.strings[LitId g[i].operand] - of UnionTy: - dest.add "union " - dest.add g.lit.strings[LitId g[i].operand] - of ProcTy: - dest.add "proc[" - for t in sons(g, i): - dest.add ' ' - toString(dest, g, t) - dest.add "]" - of ObjectDecl: - dest.add "object[" - for t in sons(g, i): - toString(dest, g, t) - dest.add '\n' - dest.add "]" - of UnionDecl: - dest.add "union[" - for t in sons(g, i): - toString(dest, g, t) - dest.add '\n' - dest.add "]" - of FieldDecl: - dest.add "field[" - for t in sons(g, i): - toString(dest, g, t) - dest.add ' ' - dest.add "]" - - when false: - let (typ, offset, name) = g.sons3(i) - toString(dest, g, typ) - dest.add ' ' - toString(dest, g, offset) - dest.add ' ' - toString(dest, g, name) - -proc toString*(dest: var string; g: TypeGraph) = - var i = 0 - while i < g.len: - dest.add "T<" - dest.addInt i - dest.add "> " - toString(dest, g, TypeId i) - dest.add '\n' - nextChild g, i - -iterator allTypes*(g: TypeGraph; start = 0): TypeId = - var i = start - while i < g.len: - yield TypeId i - nextChild g, i - -iterator allTypesIncludingInner*(g: TypeGraph; start = 0): TypeId = - var i = start - while i < g.len: - yield TypeId i - inc i - -proc `$`(g: TypeGraph): string = - result = "" - toString(result, g) - -when isMainModule: - var g = initTypeGraph(Literals()) - - let a = g.openType ArrayTy - g.addBuiltinType Int8Id - g.addArrayLen 5 - g.addName "SomeArray" - let finalArrayType = finishType(g, a) - - let obj = g.openType ObjectDecl - g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl("MyType"))) - - g.addField "p", finalArrayType, 0 - sealType(g, obj) - - echo g diff --git a/compiler/nir/nirvm.nim b/compiler/nir/nirvm.nim deleted file mode 100644 index faa7a1fb7..000000000 --- a/compiler/nir/nirvm.nim +++ /dev/null @@ -1,1175 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -##[ NIR is a little too high level to interpret it efficiently. Thus -we compute `addresses` for SymIds, labels and offsets for object fields -in a preprocessing step. - -We also split the instruction stream into separate (code, debug) seqs while -we're at it. -]## - -import std / [syncio, assertions, tables, intsets] -import ".." / ic / bitabs -import nirinsts, nirtypes, nirfiles, nirlineinfos - -type - OpcodeM = enum - ImmediateValM, - IntValM, - StrValM, - LoadLocalM, # with local ID - LoadGlobalM, - LoadProcM, - TypedM, # with type ID - PragmaIdM, # with Pragma ID, possible values: see PragmaKey enum - NilValM, - AllocLocals, - SummonParamM, - GotoM, - CheckedGotoM, # last atom - - ArrayConstrM, - ObjConstrM, - RetM, - YldM, - - SelectM, - SelectPairM, # ((values...), Label) - SelectListM, # (values...) - SelectValueM, # (value) - SelectRangeM, # (valueA..valueB) - - AddrOfM, - ArrayAtM, # (elemSize, addr(a), i) - DerefArrayAtM, - FieldAtM, # addr(obj.field) - DerefFieldAtM, - - LoadM, # a[] - AsgnM, # a = b - StoreM, # a[] = b - SetExcM, - TestExcM, - - CheckedRangeM, - CheckedIndexM, - - CallM, - CheckedAddM, # with overflow checking etc. - CheckedSubM, - CheckedMulM, - CheckedDivM, - CheckedModM, - AddM, - SubM, - MulM, - DivM, - ModM, - BitShlM, - BitShrM, - BitAndM, - BitOrM, - BitXorM, - BitNotM, - BoolNotM, - EqM, - LeM, - LtM, - CastM, - NumberConvM, - CheckedObjConvM, - ObjConvM, - TestOfM, - ProcDeclM, - PragmaPairM - -const - LastAtomicValue = CheckedGotoM - - OpcodeBits = 8'u32 - OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32 - -type - Instr = distinct uint32 - -template kind(n: Instr): OpcodeM = OpcodeM(n.uint32 and OpcodeMask) -template operand(n: Instr): uint32 = (n.uint32 shr OpcodeBits) - -template toIns(k: OpcodeM; operand: uint32): Instr = - Instr(uint32(k) or (operand shl OpcodeBits)) - -template toIns(k: OpcodeM; operand: LitId): Instr = - Instr(uint32(k) or (operand.uint32 shl OpcodeBits)) - -type - NimStrPayloadVM = object - cap: int - data: UncheckedArray[char] - NimStringVM = object - len: int - p: ptr NimStrPayloadVM - -const - GlobalsSize = 1024*24 - -type - PatchPos = distinct int - CodePos = distinct int - - Bytecode* = object - code: seq[Instr] - debug: seq[PackedLineInfo] - m: ref NirModule - procs: Table[SymId, CodePos] - globals: Table[SymId, (uint32, int)] - strings: Table[LitId, NimStringVM] - globalData: pointer - globalsAddr: uint32 - typeImpls: Table[string, TypeId] - offsets: Table[TypeId, seq[(int, TypeId)]] - sizes: Table[TypeId, (int, int)] # (size, alignment) - oldTypeLen: int - procUsagesToPatch: Table[SymId, seq[CodePos]] - interactive*: bool - - Universe* = object ## all units: For interpretation we need that - units: seq[Bytecode] - unitNames: Table[string, int] - current: int - -proc initBytecode*(m: ref NirModule): Bytecode = Bytecode(m: m, globalData: alloc0(GlobalsSize)) - -proc debug(bc: Bytecode; t: TypeId) = - var buf = "" - toString buf, bc.m.types, t - echo buf - -proc debug(bc: Bytecode; info: PackedLineInfo) = - let (litId, line, col) = bc.m.man.unpack(info) - echo bc.m.lit.strings[litId], ":", line, ":", col - -proc debug(bc: Bytecode; t: Tree; n: NodePos) = - var buf = "" - toString(t, n, bc.m.lit.strings, bc.m.lit.numbers, bc.m.symnames, buf) - echo buf - -template `[]`(t: seq[Instr]; n: CodePos): Instr = t[n.int] - -proc traverseObject(b: var Bytecode; t, offsetKey: TypeId) = - var size = -1 - var align = -1 - for x in sons(b.m.types, t): - case b.m.types[x].kind - of FieldDecl: - var offset = -1 - for y in sons(b.m.types, x): - if b.m.types[y].kind == OffsetVal: - offset = int(b.m.lit.numbers[b.m.types[y].litId]) - break - b.offsets.mgetOrPut(offsetKey, @[]).add (offset, x.firstSon) - of SizeVal: - size = int(b.m.lit.numbers[b.m.types[x].litId]) - of AlignVal: - align = int(b.m.lit.numbers[b.m.types[x].litId]) - of ObjectTy: - # inheritance - let impl = b.typeImpls.getOrDefault(b.m.lit.strings[b.m.types[x].litId]) - assert impl.int > 0 - traverseObject b, impl, offsetKey - else: discard - if t == offsetKey: - b.sizes[t] = (size, align) - -proc computeSize(b: var Bytecode; t: TypeId): (int, int) = - case b.m.types[t].kind - of ObjectDecl, UnionDecl: - result = b.sizes[t] - of ObjectTy, UnionTy: - let impl = b.typeImpls[b.m.lit.strings[b.m.types[t].litId]] - result = computeSize(b, impl) - of IntTy, UIntTy, FloatTy, BoolTy, CharTy: - let s = b.m.types[t].integralBits div 8 - result = (s, s) - of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, ProcTy: - result = (sizeof(pointer), sizeof(pointer)) - of ArrayTy: - let e = elementType(b.m.types, t) - let n = arrayLen(b.m.types, t) - let inner = computeSize(b, e) - result = (inner[0] * n.int, inner[1]) - else: - result = (0, 0) - -proc computeElemSize(b: var Bytecode; t: TypeId): int = - case b.m.types[t].kind - of ArrayTy, APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, LastArrayTy: - result = computeSize(b, elementType(b.m.types, t))[0] - else: - raiseAssert "not an array type" - -proc traverseTypes(b: var Bytecode) = - for t in allTypes(b.m.types, b.oldTypeLen): - if b.m.types[t].kind in {ObjectDecl, UnionDecl}: - assert b.m.types[t.firstSon].kind == NameVal - b.typeImpls[b.m.lit.strings[b.m.types[t.firstSon].litId]] = t - - for t in allTypes(b.m.types, b.oldTypeLen): - if b.m.types[t].kind in {ObjectDecl, UnionDecl}: - assert b.m.types[t.firstSon].kind == NameVal - traverseObject b, t, t - b.oldTypeLen = b.m.types.len - -const - InvalidPatchPos* = PatchPos(-1) - -proc isValid(p: PatchPos): bool {.inline.} = p.int != -1 - -proc prepare(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM): PatchPos = - result = PatchPos bc.code.len - bc.code.add toIns(kind, 1'u32) - bc.debug.add info - -proc add(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; raw: uint32) = - bc.code.add toIns(kind, raw) - bc.debug.add info - -proc add(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; lit: LitId) = - add bc, info, kind, uint32(lit) - -proc isAtom(bc: Bytecode; pos: int): bool {.inline.} = bc.code[pos].kind <= LastAtomicValue -proc isAtom(bc: Bytecode; pos: CodePos): bool {.inline.} = bc.code[pos.int].kind <= LastAtomicValue - -proc patch(bc: var Bytecode; pos: PatchPos) = - let pos = pos.int - let k = bc.code[pos].kind - assert k > LastAtomicValue - let distance = int32(bc.code.len - pos) - assert distance > 0 - bc.code[pos] = toIns(k, cast[uint32](distance)) - -template build(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; body: untyped) = - let pos = prepare(bc, info, kind) - body - patch(bc, pos) - -proc len*(bc: Bytecode): int {.inline.} = bc.code.len - -template rawSpan(n: Instr): int = int(operand(n)) - -proc nextChild(bc: Bytecode; pos: var int) {.inline.} = - if bc.code[pos].kind > LastAtomicValue: - assert bc.code[pos].operand > 0'u32 - inc pos, bc.code[pos].rawSpan - else: - inc pos - -proc next(bc: Bytecode; pos: var CodePos) {.inline.} = nextChild bc, int(pos) - -iterator sons(bc: Bytecode; n: CodePos): CodePos = - var pos = n.int - assert bc.code[pos].kind > LastAtomicValue - let last = pos + bc.code[pos].rawSpan - inc pos - while pos < last: - yield CodePos pos - nextChild bc, pos - -iterator sonsFrom1(bc: Bytecode; n: CodePos): CodePos = - var pos = n.int - assert bc.code[pos].kind > LastAtomicValue - let last = pos + bc.code[pos].rawSpan - inc pos - nextChild bc, pos - while pos < last: - yield CodePos pos - nextChild bc, pos - -iterator sonsFrom2(bc: Bytecode; n: CodePos): CodePos = - var pos = n.int - assert bc.code[pos].kind > LastAtomicValue - let last = pos + bc.code[pos].rawSpan - inc pos - nextChild bc, pos - nextChild bc, pos - while pos < last: - yield CodePos pos - nextChild bc, pos - -template firstSon(n: CodePos): CodePos = CodePos(n.int+1) - -template `[]`(t: Bytecode; n: CodePos): Instr = t.code[n.int] - -proc span(bc: Bytecode; pos: int): int {.inline.} = - if bc.code[pos].kind <= LastAtomicValue: 1 else: int(bc.code[pos].operand) - -iterator triples*(bc: Bytecode; n: CodePos): (uint32, int, CodePos) = - var pos = n.int - assert bc.code[pos].kind > LastAtomicValue - let last = pos + bc.code[pos].rawSpan - inc pos - while pos < last: - let offset = bc.code[pos].operand - nextChild bc, pos - let size = bc.code[pos].operand.int - nextChild bc, pos - let val = CodePos pos - yield (offset, size, val) - nextChild bc, pos - -proc toString*(t: Bytecode; pos: CodePos; - r: var string; nesting = 0) = - if r.len > 0 and r[r.len-1] notin {' ', '\n', '(', '[', '{'}: - r.add ' ' - - case t[pos].kind - of ImmediateValM: - r.add $t[pos].operand - of IntValM: - r.add "IntVal " - r.add $t.m.lit.numbers[LitId t[pos].operand] - of StrValM: - escapeToNimLit(t.m.lit.strings[LitId t[pos].operand], r) - of LoadLocalM, LoadGlobalM, LoadProcM, AllocLocals, SummonParamM: - r.add $t[pos].kind - r.add ' ' - r.add $t[pos].operand - of PragmaIdM: - r.add $cast[PragmaKey](t[pos].operand) - of TypedM: - r.add "T<" - r.add $t[pos].operand - r.add ">" - of NilValM: - r.add "NilVal" - of GotoM, CheckedGotoM: - r.add $t[pos].kind - r.add " L" - r.add $t[pos].operand - else: - r.add $t[pos].kind - r.add "{\n" - for i in 0..<(nesting+1)*2: r.add ' ' - for p in sons(t, pos): - toString t, p, r, nesting+1 - r.add "\n" - for i in 0..<nesting*2: r.add ' ' - r.add "}" - -proc debug(b: Bytecode; pos: CodePos) = - var buf = "" - toString(b, pos, buf) - echo buf - -type - Preprocessing = object - u: ref Universe - known: Table[LabelId, CodePos] - toPatch: Table[LabelId, seq[CodePos]] - locals: Table[SymId, (uint32, int)] # address, size - thisModule: uint32 - localsAddr: uint32 - markedWithLabel: IntSet - -proc align(address, alignment: uint32): uint32 = - result = (address + (alignment - 1'u32)) and not (alignment - 1'u32) - -proc genGoto(c: var Preprocessing; bc: var Bytecode; info: PackedLineInfo; lab: LabelId; opc: OpcodeM) = - let dest = c.known.getOrDefault(lab, CodePos(-1)) - if dest.int >= 0: - bc.add info, opc, uint32 dest - else: - let here = CodePos(bc.code.len) - c.toPatch.mgetOrPut(lab, @[]).add here - bc.add info, opc, 1u32 # will be patched once we traversed the label - -type - AddrMode = enum - InDotExpr, WantAddr - -template maybeDeref(doDeref: bool; size: int; body: untyped) = - var pos = PatchPos(-1) - if doDeref: - pos = prepare(bc, info, LoadM) - bc.add info, ImmediateValM, uint32 size - body - if doDeref: - patch(bc, pos) - -proc toReadonlyString(s: string): NimStringVM = - if s.len == 0: - result = NimStringVM(len: 0, p: nil) - else: - result = NimStringVM(len: s.len, p: cast[ptr NimStrPayloadVM](alloc(s.len+1+sizeof(int)))) - copyMem(addr result.p.data[0], addr s[0], s.len+1) - result.p.cap = s.len or (1 shl (8 * 8 - 2)) # see also NIM_STRLIT_FLAG - -const - ForwardedProc = 10_000_000'u32 - -proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; flags: set[AddrMode]) = - let info = t[n].info - - template recurse(opc) = - build bc, info, opc: - for ch in sons(t, n): preprocess(c, bc, t, ch, {WantAddr}) - - case t[n].kind - of Nop, ForeignDecl, ForeignProcDecl: - discard "don't use Nop" - of ImmediateVal: - bc.add info, ImmediateValM, t[n].rawOperand - of IntVal: - bc.add info, IntValM, t[n].rawOperand - of StrVal: - let litId = LitId t[n].rawOperand - if not bc.strings.hasKey(litId): - bc.strings[litId] = toReadonlyString(bc.m.lit.strings[litId]) - bc.add info, StrValM, t[n].rawOperand - of SymDef: - discard "happens for proc decls. Don't copy the node as we don't need it" - of SymUse: - let s = t[n].symId - if c.locals.hasKey(s): - let (address, size) = c.locals[s] - maybeDeref(WantAddr notin flags, size): - bc.add info, LoadLocalM, address - elif bc.procs.hasKey(s): - bc.add info, LoadProcM, uint32 bc.procs[s] - elif bc.globals.hasKey(s): - let (address, size) = bc.globals[s] - maybeDeref(WantAddr notin flags, size): - bc.add info, LoadGlobalM, address - else: - let here = CodePos(bc.code.len) - bc.add info, LoadProcM, ForwardedProc + uint32(s) - bc.procUsagesToPatch.mgetOrPut(s, @[]).add here - #raiseAssert "don't understand SymUse ID " & $int(s) - - of ModuleSymUse: - when false: - let (x, y) = sons2(t, n) - let unit = c.u.unitNames.getOrDefault(bc.m.lit.strings[t[x].litId], -1) - let s = t[y].symId - if c.u.units[unit].procs.hasKey(s): - bc.add info, LoadProcM, uint32 c.u.units[unit].procs[s] - elif bc.globals.hasKey(s): - maybeDeref(WantAddr notin flags): - build bc, info, LoadGlobalM: - bc.add info, ImmediateValM, uint32 unit - bc.add info, LoadLocalM, uint32 s - else: - raiseAssert "don't understand ModuleSymUse ID" - - raiseAssert "don't understand ModuleSymUse ID" - of Typed: - bc.add info, TypedM, t[n].rawOperand - of PragmaId: - bc.add info, PragmaIdM, t[n].rawOperand - of NilVal: - bc.add info, NilValM, t[n].rawOperand - of LoopLabel, Label: - let lab = t[n].label - let here = CodePos(bc.code.len) - c.known[lab] = here - var p: seq[CodePos] = @[] - if c.toPatch.take(lab, p): - for x in p: (bc.code[x]) = toIns(bc.code[x].kind, uint32 here) - c.markedWithLabel.incl here.int # for toString() - of Goto, GotoLoop: - c.genGoto(bc, info, t[n].label, GotoM) - of CheckedGoto: - c.genGoto(bc, info, t[n].label, CheckedGotoM) - of ArrayConstr: - let typ = t[n.firstSon].typeId - let s = computeElemSize(bc, typ) - build bc, info, ArrayConstrM: - bc.add info, ImmediateValM, uint32 s - for ch in sonsFrom1(t, n): - preprocess(c, bc, t, ch, {WantAddr}) - of ObjConstr: - #debug bc, t, n - var i = 0 - let typ = t[n.firstSon].typeId - build bc, info, ObjConstrM: - for ch in sons(t, n): - if i > 0: - if (i mod 2) == 1: - let (offset, typ) = bc.offsets[typ][t[ch].immediateVal] - let size = computeSize(bc, typ)[0] - bc.add info, ImmediateValM, uint32(offset) - bc.add info, ImmediateValM, uint32(size) - else: - preprocess(c, bc, t, ch, {WantAddr}) - inc i - of Ret: - recurse RetM - of Yld: - recurse YldM - of Select: - recurse SelectM - of SelectPair: - recurse SelectPairM - of SelectList: - recurse SelectListM - of SelectValue: - recurse SelectValueM - of SelectRange: - recurse SelectRangeM - of SummonGlobal, SummonThreadLocal, SummonConst: - let (typ, sym) = sons2(t, n) - - let s = t[sym].symId - let tid = t[typ].typeId - let (size, alignment) = computeSize(bc, tid) - - let global = align(bc.globalsAddr, uint32 alignment) - bc.globals[s] = (global, size) - bc.globalsAddr += uint32 size - assert bc.globalsAddr < GlobalsSize - - of Summon: - let (typ, sym) = sons2(t, n) - - let s = t[sym].symId - let tid = t[typ].typeId - let (size, alignment) = computeSize(bc, tid) - - let local = align(c.localsAddr, uint32 alignment) - c.locals[s] = (local, size) - c.localsAddr += uint32 size - # allocation is combined into the frame allocation so there is no - # instruction to emit - of SummonParam, SummonResult: - let (typ, sym) = sons2(t, n) - - let s = t[sym].symId - let tid = t[typ].typeId - let (size, alignment) = computeSize(bc, tid) - - let local = align(c.localsAddr, uint32 alignment) - c.locals[s] = (local, size) - c.localsAddr += uint32 size - bc.add info, SummonParamM, local - bc.add info, ImmediateValM, uint32 size - of Kill: - discard "we don't care about Kill instructions" - of AddrOf: - let (_, arg) = sons2(t, n) - preprocess(c, bc, t, arg, {WantAddr}) - # the address of x is what the VM works with all the time so there is - # nothing to compute. - of ArrayAt: - let (arrayType, a, i) = sons3(t, n) - let tid = t[arrayType].typeId - let size = uint32 computeElemSize(bc, tid) - build bc, info, ArrayAtM: - bc.add info, ImmediateValM, size - preprocess(c, bc, t, a, {WantAddr}) - preprocess(c, bc, t, i, {WantAddr}) - of DerefArrayAt: - let (arrayType, a, i) = sons3(t, n) - let tid = t[arrayType].typeId - let size = uint32 computeElemSize(bc, tid) - build bc, info, DerefArrayAtM: - bc.add info, ImmediateValM, size - preprocess(c, bc, t, a, {WantAddr}) - preprocess(c, bc, t, i, {WantAddr}) - of FieldAt: - let (typ, a, b) = sons3(t, n) - let offset = bc.offsets[t[typ].typeId][t[b].immediateVal][0] - build bc, info, FieldAtM: - preprocess(c, bc, t, a, flags+{WantAddr}) - bc.add info, ImmediateValM, uint32(offset) - of DerefFieldAt: - let (typ, a, b) = sons3(t, n) - let offset = bc.offsets[t[typ].typeId][t[b].immediateVal][0] - build bc, info, DerefFieldAtM: - preprocess(c, bc, t, a, flags+{WantAddr}) - bc.add info, ImmediateValM, uint32(offset) - of Load: - let (elemType, a) = sons2(t, n) - let tid = t[elemType].typeId - build bc, info, LoadM: - bc.add info, ImmediateValM, uint32 computeSize(bc, tid)[0] - preprocess(c, bc, t, a, {}) - - of Store: - raiseAssert "Assumption was that Store is unused!" - of Asgn: - let (elemType, dest, src) = sons3(t, n) - let tid = t[elemType].typeId - if t[src].kind in {Call, IndirectCall}: - # No support for return values, these are mapped to `var T` parameters! - build bc, info, CallM: - preprocess(c, bc, t, src.skipTyped, {WantAddr}) - preprocess(c, bc, t, dest, {WantAddr}) - for ch in sonsFromN(t, src, 2): preprocess(c, bc, t, ch, {WantAddr}) - elif t[src].kind in {CheckedCall, CheckedIndirectCall}: - let (_, gotoInstr, fn) = sons3(t, src) - build bc, info, CallM: - preprocess(c, bc, t, fn, {WantAddr}) - preprocess(c, bc, t, dest, {WantAddr}) - for ch in sonsFromN(t, src, 3): preprocess(c, bc, t, ch, {WantAddr}) - preprocess c, bc, t, gotoInstr, {} - elif t[dest].kind == Load: - let (typ, a) = sons2(t, dest) - let s = computeSize(bc, tid)[0] - build bc, info, StoreM: - bc.add info, ImmediateValM, uint32 s - preprocess(c, bc, t, a, {WantAddr}) - preprocess(c, bc, t, src, {}) - else: - let s = computeSize(bc, tid)[0] - build bc, info, AsgnM: - bc.add info, ImmediateValM, uint32 s - preprocess(c, bc, t, dest, {WantAddr}) - preprocess(c, bc, t, src, {}) - of SetExc: - recurse SetExcM - of TestExc: - recurse TestExcM - of CheckedRange: - recurse CheckedRangeM - of CheckedIndex: - recurse CheckedIndexM - of Call, IndirectCall: - # avoid the Typed thing at position 0: - build bc, info, CallM: - for ch in sonsFrom1(t, n): preprocess(c, bc, t, ch, {WantAddr}) - of CheckedCall, CheckedIndirectCall: - # avoid the Typed thing at position 0: - let (_, gotoInstr, fn) = sons3(t, n) - build bc, info, CallM: - preprocess(c, bc, t, fn, {WantAddr}) - for ch in sonsFromN(t, n, 3): preprocess(c, bc, t, ch, {WantAddr}) - preprocess c, bc, t, gotoInstr, {WantAddr} - of CheckedAdd: - recurse CheckedAddM - of CheckedSub: - recurse CheckedSubM - of CheckedMul: - recurse CheckedMulM - of CheckedDiv: - recurse CheckedDivM - of CheckedMod: - recurse CheckedModM - of Add: - recurse AddM - of Sub: - recurse SubM - of Mul: - recurse MulM - of Div: - recurse DivM - of Mod: - recurse ModM - of BitShl: - recurse BitShlM - of BitShr: - recurse BitShrM - of BitAnd: - recurse BitAndM - of BitOr: - recurse BitOrM - of BitXor: - recurse BitXorM - of BitNot: - recurse BitNotM - of BoolNot: - recurse BoolNotM - of Eq: - recurse EqM - of Le: - recurse LeM - of Lt: - recurse LtM - of Cast: - recurse CastM - of NumberConv: - recurse NumberConvM - of CheckedObjConv: - recurse CheckedObjConvM - of ObjConv: - recurse ObjConvM - of TestOf: - recurse TestOfM - of Emit: - raiseAssert "cannot interpret: Emit" - of ProcDecl: - var c2 = Preprocessing(u: c.u, thisModule: c.thisModule) - let sym = t[n.firstSon].symId - let here = CodePos(bc.len) - var p: seq[CodePos] = @[] - if bc.procUsagesToPatch.take(sym, p): - for x in p: (bc.code[x]) = toIns(bc.code[x].kind, uint32 here) - bc.procs[sym] = here - build bc, info, ProcDeclM: - let toPatch = bc.code.len - bc.add info, AllocLocals, 0'u32 - for ch in sons(t, n): preprocess(c2, bc, t, ch, {}) - bc.code[toPatch] = toIns(AllocLocals, c2.localsAddr) - when false: - if here.int == 39850: - debug bc, t, n - debug bc, here - - of PragmaPair: - recurse PragmaPairM - -const PayloadSize = 128 - -type - StackFrame = ref object - locals: pointer # usually points into `payload` if size is small enough, otherwise it's `alloc`'ed. - payload: array[PayloadSize, byte] - caller: StackFrame - returnAddr: CodePos - jumpTo: CodePos # exception handling - u: ref Universe - -proc newStackFrame(size: int; caller: StackFrame; returnAddr: CodePos): StackFrame = - result = StackFrame(caller: caller, returnAddr: returnAddr, u: caller.u) - if size <= PayloadSize: - result.locals = addr(result.payload) - else: - result.locals = alloc0(size) - -proc popStackFrame(s: StackFrame): StackFrame = - if s.locals != addr(s.payload): - dealloc s.locals - result = s.caller - -template `+!`(p: pointer; diff: uint): pointer = cast[pointer](cast[uint](p) + diff) - -proc isAtom(tree: seq[Instr]; pos: CodePos): bool {.inline.} = tree[pos.int].kind <= LastAtomicValue - -proc span(bc: seq[Instr]; pos: int): int {.inline.} = - if bc[pos].kind <= LastAtomicValue: 1 else: int(bc[pos].operand) - -proc sons2(tree: seq[Instr]; n: CodePos): (CodePos, CodePos) = - assert(not isAtom(tree, n)) - let a = n.int+1 - let b = a + span(tree, a) - result = (CodePos a, CodePos b) - -proc sons3(tree: seq[Instr]; n: CodePos): (CodePos, CodePos, CodePos) = - assert(not isAtom(tree, n)) - let a = n.int+1 - let b = a + span(tree, a) - let c = b + span(tree, b) - result = (CodePos a, CodePos b, CodePos c) - -proc sons4(tree: seq[Instr]; n: CodePos): (CodePos, CodePos, CodePos, CodePos) = - assert(not isAtom(tree, n)) - let a = n.int+1 - let b = a + span(tree, a) - let c = b + span(tree, b) - let d = c + span(tree, c) - result = (CodePos a, CodePos b, CodePos c, CodePos d) - -proc typeId*(ins: Instr): TypeId {.inline.} = - assert ins.kind == TypedM - result = TypeId(ins.operand) - -proc immediateVal*(ins: Instr): int {.inline.} = - assert ins.kind == ImmediateValM - result = cast[int](ins.operand) - -proc litId*(ins: Instr): LitId {.inline.} = - assert ins.kind in {StrValM, IntValM} - result = LitId(ins.operand) - -proc eval(c: Bytecode; pc: CodePos; s: StackFrame; result: pointer; size: int) - -proc evalAddr(c: Bytecode; pc: CodePos; s: StackFrame): pointer = - case c.code[pc].kind - of LoadLocalM: - result = s.locals +! c.code[pc].operand - of FieldAtM: - let (x, offset) = sons2(c.code, pc) - result = evalAddr(c, x, s) - result = result +! c.code[offset].operand - of DerefFieldAtM: - let (x, offset) = sons2(c.code, pc) - let p = evalAddr(c, x, s) - result = cast[ptr pointer](p)[] +! c.code[offset].operand - of ArrayAtM: - let (e, a, i) = sons3(c.code, pc) - let elemSize = c.code[e].operand - result = evalAddr(c, a, s) - var idx: int = 0 - eval(c, i, s, addr idx, sizeof(int)) - result = result +! (uint32(idx) * elemSize) - of DerefArrayAtM: - let (e, a, i) = sons3(c.code, pc) - let elemSize = c.code[e].operand - var p = evalAddr(c, a, s) - var idx: int = 0 - eval(c, i, s, addr idx, sizeof(int)) - result = cast[ptr pointer](p)[] +! (uint32(idx) * elemSize) - of LoadGlobalM: - result = c.globalData +! c.code[pc].operand - else: - raiseAssert("unimplemented addressing mode") - -proc `div`(x, y: float32): float32 {.inline.} = x / y -proc `div`(x, y: float64): float64 {.inline.} = x / y - -from std / math import `mod` - -template binop(opr) {.dirty.} = - template impl(typ) {.dirty.} = - var x = default(typ) - var y = default(typ) - eval c, a, s, addr x, sizeof(typ) - eval c, b, s, addr y, sizeof(typ) - cast[ptr typ](result)[] = opr(x, y) - - let (t, a, b) = sons3(c.code, pc) - let tid = TypeId c.code[t].operand - case tid - of Bool8Id, Char8Id, UInt8Id: impl uint8 - of Int8Id: impl int8 - of Int16Id: impl int16 - of Int32Id: impl int32 - of Int64Id: impl int64 - of UInt16Id: impl uint16 - of UInt32Id: impl uint32 - of UInt64Id: impl uint64 - of Float32Id: impl float32 - of Float64Id: impl float64 - else: discard - -template checkedBinop(opr) {.dirty.} = - template impl(typ) {.dirty.} = - var x = default(typ) - var y = default(typ) - eval c, a, s, addr x, sizeof(typ) - eval c, b, s, addr y, sizeof(typ) - try: - cast[ptr typ](result)[] = opr(x, y) - except OverflowDefect, DivByZeroDefect: - s.jumpTo = CodePos c.code[j].operand - - let (t, j, a, b) = sons4(c.code, pc) - let tid = TypeId c.code[t].operand - case tid - of Bool8Id, Char8Id, UInt8Id: impl uint8 - of Int8Id: impl int8 - of Int16Id: impl int16 - of Int32Id: impl int32 - of Int64Id: impl int64 - of UInt16Id: impl uint16 - of UInt32Id: impl uint32 - of UInt64Id: impl uint64 - of Float32Id: impl float32 - of Float64Id: impl float64 - else: discard - -template bitop(opr) {.dirty.} = - template impl(typ) {.dirty.} = - var x = default(typ) - var y = default(typ) - eval c, a, s, addr x, sizeof(typ) - eval c, b, s, addr y, sizeof(typ) - cast[ptr typ](result)[] = opr(x, y) - - let (t, a, b) = sons3(c.code, pc) - let tid = c.code[t].typeId - case tid - of Bool8Id, Char8Id, UInt8Id: impl uint8 - of Int8Id: impl int8 - of Int16Id: impl int16 - of Int32Id: impl int32 - of Int64Id: impl int64 - of UInt16Id: impl uint16 - of UInt32Id: impl uint32 - of UInt64Id: impl uint64 - else: discard - -template cmpop(opr) {.dirty.} = - template impl(typ) {.dirty.} = - var x = default(typ) - var y = default(typ) - eval c, a, s, addr x, sizeof(typ) - eval c, b, s, addr y, sizeof(typ) - cast[ptr bool](result)[] = opr(x, y) - - let (t, a, b) = sons3(c.code, pc) - let tid = c.code[t].typeId - case tid - of Bool8Id, Char8Id, UInt8Id: impl uint8 - of Int8Id: impl int8 - of Int16Id: impl int16 - of Int32Id: impl int32 - of Int64Id: impl int64 - of UInt16Id: impl uint16 - of UInt32Id: impl uint32 - of UInt64Id: impl uint64 - of Float32Id: impl float32 - of Float64Id: impl float64 - else: discard - -proc evalSelect(c: Bytecode; pc: CodePos; s: StackFrame): CodePos = - template impl(typ) {.dirty.} = - var selector = default(typ) - eval c, sel, s, addr selector, sizeof(typ) - for pair in sonsFrom2(c, pc): - assert c.code[pair].kind == SelectPairM - let (values, action) = sons2(c.code, pair) - if c.code[values].kind == SelectValueM: - var a = default(typ) - eval c, values.firstSon, s, addr a, sizeof(typ) - if selector == a: - return CodePos c.code[action].operand - else: - assert c.code[values].kind == SelectListM, $c.code[values].kind - for v in sons(c, values): - case c.code[v].kind - of SelectValueM: - var a = default(typ) - eval c, v.firstSon, s, addr a, sizeof(typ) - if selector == a: - return CodePos c.code[action].operand - of SelectRangeM: - let (va, vb) = sons2(c.code, v) - var a = default(typ) - eval c, va, s, addr a, sizeof(typ) - var b = default(typ) - eval c, vb, s, addr a, sizeof(typ) - if a <= selector and selector <= b: - return CodePos c.code[action].operand - else: raiseAssert "unreachable" - result = CodePos(-1) - - let (t, sel) = sons2(c.code, pc) - let tid = c.code[t].typeId - case tid - of Bool8Id, Char8Id, UInt8Id: impl uint8 - of Int8Id: impl int8 - of Int16Id: impl int16 - of Int32Id: impl int32 - of Int64Id: impl int64 - of UInt16Id: impl uint16 - of UInt32Id: impl uint32 - of UInt64Id: impl uint64 - else: raiseAssert "unreachable" - -proc eval(c: Bytecode; pc: CodePos; s: StackFrame; result: pointer; size: int) = - case c.code[pc].kind - of LoadLocalM: - let src = s.locals +! c.code[pc].operand - copyMem result, src, size - of FieldAtM, DerefFieldAtM, ArrayAtM, DerefArrayAtM, LoadGlobalM: - let src = evalAddr(c, pc, s) - copyMem result, src, size - of LoadProcM: - let procAddr = c.code[pc].operand - cast[ptr pointer](result)[] = cast[pointer](procAddr) - of LoadM: - let (_, arg) = sons2(c.code, pc) - let src = evalAddr(c, arg, s) - copyMem result, src, size - of CheckedAddM: checkedBinop `+` - of CheckedSubM: checkedBinop `-` - of CheckedMulM: checkedBinop `*` - of CheckedDivM: checkedBinop `div` - of CheckedModM: checkedBinop `mod` - of AddM: binop `+` - of SubM: binop `-` - of MulM: binop `*` - of DivM: binop `div` - of ModM: binop `mod` - of BitShlM: bitop `shl` - of BitShrM: bitop `shr` - of BitAndM: bitop `and` - of BitOrM: bitop `or` - of BitXorM: bitop `xor` - of EqM: cmpop `==` - of LeM: cmpop `<=` - of LtM: cmpop `<` - - of StrValM: - # binary compatible and no deep copy required: - copyMem(cast[ptr string](result), addr(c.strings[c[pc].litId]), sizeof(string)) - of ObjConstrM: - for offset, size, val in triples(c, pc): - eval c, val, s, result+!offset, size - of ArrayConstrM: - let elemSize = c.code[pc.firstSon].operand - var r = result - for ch in sonsFrom1(c, pc): - eval c, ch, s, r, elemSize.int - r = r+!elemSize # can even do strength reduction here! - of NumberConvM: - let (t, x) = sons2(c.code, pc) - let word = if c[x].kind == NilValM: 0'i64 else: c.m.lit.numbers[c[x].litId] - - template impl(typ: typedesc) {.dirty.} = - cast[ptr typ](result)[] = cast[typ](word) - - let tid = c.code[t].typeId - case tid - of Bool8Id, Char8Id, UInt8Id: impl uint8 - of Int8Id: impl int8 - of Int16Id: impl int16 - of Int32Id: impl int32 - of Int64Id: impl int64 - of UInt16Id: impl uint16 - of UInt32Id: impl uint32 - of UInt64Id: impl uint64 - of Float32Id: impl float32 - of Float64Id: impl float64 - else: - case c.m.types[tid].kind - of ProcTy, UPtrTy, APtrTy, AArrayPtrTy, UArrayPtrTy: - # the VM always uses 64 bit pointers: - impl uint64 - else: - raiseAssert "cannot happen: " & $c.m.types[tid].kind - else: - #debug c, c.debug[pc.int] - raiseAssert "cannot happen: " & $c.code[pc].kind - -proc evalProc(c: Bytecode; pc: CodePos; s: StackFrame): CodePos = - assert c.code[pc].kind == LoadProcM - let procSym = c[pc].operand - when false: - if procSym >= ForwardedProc: - for k, v in c.procUsagesToPatch: - if uint32(k) == procSym - ForwardedProc: - echo k.int, " ", v.len, " <-- this one" - else: - echo k.int, " ", v.len - - assert procSym < ForwardedProc - result = CodePos(procSym) - -proc echoImpl(c: Bytecode; pc: CodePos; frame: StackFrame) = - var s = default(NimStringVM) - for a in sonsFrom1(c, pc): - assert c[a].kind == ArrayConstrM - let elemSize = c.code[a.firstSon].operand.int - for ch in sonsFrom1(c, a): - eval c, ch, frame, addr s, elemSize - if s.len > 0: - discard stdout.writeBuffer(addr(s.p.data[0]), s.len) - stdout.write "\n" - stdout.flushFile() - -type - EvalBuiltinState = enum - DidNothing, DidEval, DidError - -proc evalBuiltin(c: Bytecode; pc: CodePos; s: StackFrame; prc: CodePos; state: var EvalBuiltinState): CodePos = - var prc = prc - while true: - case c[prc].kind - of PragmaPairM: - let (x, y) = sons2(c.code, prc) - let key = cast[PragmaKey](c[x].operand) - case key - of CoreName: - let lit = c[y].litId - case c.m.lit.strings[lit] - of "echoBinSafe": echoImpl(c, pc, s) - else: - raiseAssert "cannot eval: " & c.m.lit.strings[lit] - state = DidEval - of HeaderImport, DllImport: - let lit = c[y].litId - raiseAssert "cannot eval: " & c.m.lit.strings[lit] - else: discard - of PragmaIdM, AllocLocals: discard - else: break - next c, prc - result = prc - -proc exec(c: Bytecode; pc: CodePos; u: ref Universe) = - var pc = pc - var frame = StackFrame(u: u) - while pc.int < c.code.len: - when false: # c.interactive: - echo "running: ", pc.int - debug c, pc - - case c.code[pc].kind - of GotoM: - pc = CodePos(c.code[pc].operand) - of AsgnM: - let (sz, a, b) = sons3(c.code, pc) - let dest = evalAddr(c, a, frame) - eval(c, b, frame, dest, c.code[sz].operand.int) - next c, pc - of StoreM: - let (sz, a, b) = sons3(c.code, pc) - let destPtr = evalAddr(c, a, frame) - let dest = cast[ptr pointer](destPtr)[] - eval(c, b, frame, dest, c.code[sz].operand.int) - next c, pc - of CallM: - # No support for return values, these are mapped to `var T` parameters! - var prc = evalProc(c, pc.firstSon, frame) - assert c.code[prc.firstSon].kind == AllocLocals - let frameSize = int c.code[prc.firstSon].operand - # skip stupid stuff: - var evalState = DidNothing - prc = evalBuiltin(c, pc, frame, prc.firstSon, evalState) - if evalState != DidNothing: - next c, pc - if pc.int < c.code.len and c.code[pc].kind == CheckedGotoM: - if evalState == DidEval: - next c, pc - else: - pc = CodePos(c.code[pc].operand) - else: - # setup storage for the proc already: - let callInstr = pc - next c, pc - let s2 = newStackFrame(frameSize, frame, pc) - for a in sonsFrom1(c, callInstr): - assert c[prc].kind == SummonParamM - let paramAddr = c[prc].operand - next c, prc - assert c[prc].kind == ImmediateValM - let paramSize = c[prc].operand.int - next c, prc - eval(c, a, s2, s2.locals +! paramAddr, paramSize) - frame = s2 - pc = prc - of RetM: - pc = frame.returnAddr - if c.code[pc].kind == CheckedGotoM: - pc = frame.jumpTo - frame = popStackFrame(frame) - of SelectM: - let pc2 = evalSelect(c, pc, frame) - if pc2.int >= 0: - pc = pc2 - else: - next c, pc - of ProcDeclM: - next c, pc - else: - #debug c, c.debug[pc.int] - raiseAssert "unreachable: " & $c.code[pc].kind - -proc execCode*(bc: var Bytecode; t: Tree; n: NodePos) = - traverseTypes bc - var c = Preprocessing(u: nil, thisModule: 1'u32) - let start = CodePos(bc.code.len) - var pc = n - while pc.int < t.len: - #if bc.interactive: - # echo "RUnning: " - # debug bc, t, pc - preprocess c, bc, t, pc, {} - next t, pc - exec bc, start, nil diff --git a/compiler/nir/stringcases.nim b/compiler/nir/stringcases.nim deleted file mode 100644 index afdf8fda4..000000000 --- a/compiler/nir/stringcases.nim +++ /dev/null @@ -1,200 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## included from ast2ir.nim - -#[ - -case s -of "abc", "abbd": - echo 1 -of "hah": - echo 2 -of "gah": - echo 3 - -# we produce code like this: - -if s[0] <= 'a': - if s == "abc: goto L1 - elif s == "abbd": goto L1 -else: - if s[2] <= 'h': - if s == "hah": goto L2 - elif s == "gah": goto L3 -goto afterCase - -L1: - echo 1 - goto afterCase -L2: - echo 2 - goto afterCase -L3: - echo 3 - goto afterCase - -afterCase: ... - -]# - -# We split the set of strings into 2 sets of roughly the same size. -# The condition used for splitting is a (position, char) tuple. -# Every string of length > position for which s[position] <= char is in one -# set else it is in the other set. - -from std/sequtils import addUnique - -type - Key = (LitId, LabelId) - -proc splitValue(strings: BiTable[string]; a: openArray[Key]; position: int): (char, float) = - var cand: seq[char] = @[] - for t in items a: - let s = strings[t[0]] - if s.len > position: cand.addUnique s[position] - - result = ('\0', -1.0) - for disc in items cand: - var hits = 0 - for t in items a: - let s = strings[t[0]] - if s.len > position and s[position] <= disc: - inc hits - # the split is the better, the more `hits` is close to `a.len / 2`: - let grade = 100000.0 - abs(hits.float - a.len.float / 2.0) - if grade > result[1]: - result = (disc, grade) - -proc tryAllPositions(strings: BiTable[string]; a: openArray[Key]): (char, int) = - var m = 0 - for t in items a: - m = max(m, strings[t[0]].len) - - result = ('\0', -1) - var best = -1.0 - for i in 0 ..< m: - let current = splitValue(strings, a, i) - if current[1] > best: - best = current[1] - result = (current[0], i) - -type - SearchKind = enum - LinearSearch, SplitSearch - SearchResult* = object - case kind: SearchKind - of LinearSearch: - a: seq[Key] - of SplitSearch: - span: int - best: (char, int) - -proc emitLinearSearch(strings: BiTable[string]; a: openArray[Key]; dest: var seq[SearchResult]) = - var d = SearchResult(kind: LinearSearch, a: @[]) - for x in a: d.a.add x - dest.add d - -proc split(strings: BiTable[string]; a: openArray[Key]; dest: var seq[SearchResult]) = - if a.len <= 4: - emitLinearSearch strings, a, dest - else: - let best = tryAllPositions(strings, a) - var groupA: seq[Key] = @[] - var groupB: seq[Key] = @[] - for t in items a: - let s = strings[t[0]] - if s.len > best[1] and s[best[1]] <= best[0]: - groupA.add t - else: - groupB.add t - if groupA.len == 0 or groupB.len == 0: - emitLinearSearch strings, a, dest - else: - let toPatch = dest.len - dest.add SearchResult(kind: SplitSearch, span: 1, best: best) - split strings, groupA, dest - split strings, groupB, dest - let dist = dest.len - toPatch - assert dist > 0 - dest[toPatch].span = dist - -proc toProblemDescription(c: var ProcCon; n: PNode): (seq[Key], LabelId) = - result = (@[], newLabels(c.labelGen, n.len)) - assert n.kind == nkCaseStmt - for i in 1..<n.len: - let it = n[i] - let thisBranch = LabelId(result[1].int + i - 1) - if it.kind == nkOfBranch: - for j in 0..<it.len-1: - assert it[j].kind in {nkStrLit..nkTripleStrLit} - result[0].add (c.lit.strings.getOrIncl(it[j].strVal), thisBranch) - -proc decodeSolution(c: var ProcCon; dest: var Tree; s: seq[SearchResult]; i: int; - selector: Value; info: PackedLineInfo) = - case s[i].kind - of SplitSearch: - let thenA = i+1 - let elseA = thenA + (if s[thenA].kind == LinearSearch: 1 else: s[thenA].span) - let best = s[i].best - - let tmp = getTemp(c, Bool8Id, info) - buildTyped dest, info, Asgn, Bool8Id: - dest.copyTree tmp - buildTyped dest, info, Call, Bool8Id: - c.addUseCodegenProc dest, "nimStrAtLe", info - dest.copyTree selector - dest.addIntVal c.lit.numbers, info, c.m.nativeIntId, best[1] - dest.addIntVal c.lit.numbers, info, Char8Id, best[0].int - - template then() = - c.decodeSolution dest, s, thenA, selector, info - template otherwise() = - c.decodeSolution dest, s, elseA, selector, info - buildIfThenElse tmp, then, otherwise - freeTemp c, tmp - - of LinearSearch: - let tmp = getTemp(c, Bool8Id, info) - for x in s[i].a: - buildTyped dest, info, Asgn, Bool8Id: - dest.copyTree tmp - buildTyped dest, info, Call, Bool8Id: - c.addUseCodegenProc dest, "eqStrings", info - dest.copyTree selector - dest.addStrLit info, x[0] - buildIf tmp: - c.code.gotoLabel info, Goto, x[1] - freeTemp c, tmp - -proc genStringCase(c: var ProcCon; n: PNode; d: var Value) = - let (problem, firstBranch) = toProblemDescription(c, n) - var solution: seq[SearchResult] = @[] - split c.lit.strings, problem, solution - - # XXX Todo move complex case selector into a temporary. - let selector = c.genx(n[0]) - - let info = toLineInfo(c, n.info) - decodeSolution c, c.code, solution, 0, selector, info - - let lend = newLabel(c.labelGen) - c.code.addLabel info, Goto, lend - for i in 1..<n.len: - let it = n[i] - let thisBranch = LabelId(firstBranch.int + i - 1) - c.code.addLabel info, Label, thisBranch - if it.kind == nkOfBranch: - gen(c, it.lastSon, d) - c.code.addLabel info, Goto, lend - else: - gen(c, it.lastSon, d) - - c.code.addLabel info, Label, lend - freeTemp c, selector diff --git a/compiler/nir/types2ir.nim b/compiler/nir/types2ir.nim deleted file mode 100644 index 8d9583486..000000000 --- a/compiler/nir/types2ir.nim +++ /dev/null @@ -1,525 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -import std / [assertions, tables, sets] -import ".." / [ast, types, options, sighashes, modulegraphs] -import nirtypes - -type - TypesCon* = object - processed: Table[ItemId, TypeId] - processedByName: Table[string, TypeId] - recursionCheck: HashSet[ItemId] - conf: ConfigRef - stringType: TypeId - -proc initTypesCon*(conf: ConfigRef): TypesCon = - TypesCon(conf: conf, stringType: TypeId(-1)) - -proc mangle(c: var TypesCon; t: PType): string = - result = $sighashes.hashType(t, c.conf) - -template cached(c: var TypesCon; t: PType; body: untyped) = - result = c.processed.getOrDefault(t.itemId) - if result.int == 0: - body - c.processed[t.itemId] = result - -template cachedByName(c: var TypesCon; t: PType; body: untyped) = - let key = mangle(c, t) - result = c.processedByName.getOrDefault(key) - if result.int == 0: - body - c.processedByName[key] = result - -proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId - -proc collectFieldTypes(c: var TypesCon; g: var TypeGraph; n: PNode; dest: var Table[ItemId, TypeId]) = - case n.kind - of nkRecList: - for i in 0..<n.len: - collectFieldTypes(c, g, n[i], dest) - of nkRecCase: - assert(n[0].kind == nkSym) - collectFieldTypes(c, g, n[0], dest) - for i in 1..<n.len: - case n[i].kind - of nkOfBranch, nkElse: - collectFieldTypes c, g, lastSon(n[i]), dest - else: discard - of nkSym: - dest[n.sym.itemId] = typeToIr(c, g, n.sym.typ) - else: - assert false, "unknown node kind: " & $n.kind - -proc objectToIr(c: var TypesCon; g: var TypeGraph; n: PNode; fieldTypes: Table[ItemId, TypeId]; unionId: var int) = - case n.kind - of nkRecList: - for i in 0..<n.len: - objectToIr(c, g, n[i], fieldTypes, unionId) - of nkRecCase: - assert(n[0].kind == nkSym) - objectToIr(c, g, n[0], fieldTypes, unionId) - let u = openType(g, UnionDecl) - g.addName "u_" & $unionId - inc unionId - for i in 1..<n.len: - case n[i].kind - of nkOfBranch, nkElse: - let subObj = openType(g, ObjectDecl) - g.addName "uo_" & $unionId & "_" & $i - objectToIr c, g, lastSon(n[i]), fieldTypes, unionId - sealType(g, subObj) - else: discard - sealType(g, u) - of nkSym: - g.addField n.sym.name.s & "_" & $n.sym.position, fieldTypes[n.sym.itemId], n.sym.offset - else: - assert false, "unknown node kind: " & $n.kind - -proc objectToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - if t.baseClass != nil: - # ensure we emitted the base type: - discard typeToIr(c, g, t.baseClass) - - var unionId = 0 - var fieldTypes = initTable[ItemId, TypeId]() - collectFieldTypes c, g, t.n, fieldTypes - let obj = openType(g, ObjectDecl) - g.addName mangle(c, t) - g.addSize c.conf.getSize(t) - g.addAlign c.conf.getAlign(t) - - if t.baseClass != nil: - g.addNominalType(ObjectTy, mangle(c, t.baseClass)) - else: - g.addBuiltinType VoidId # object does not inherit - if not lacksMTypeField(t): - let f2 = g.openType FieldDecl - let voidPtr = openType(g, APtrTy) - g.addBuiltinType(VoidId) - sealType(g, voidPtr) - g.addOffset 0 # type field is always at offset 0 - g.addName "m_type" - sealType(g, f2) # FieldDecl - - objectToIr c, g, t.n, fieldTypes, unionId - result = finishType(g, obj) - -proc objectHeaderToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - result = g.nominalType(ObjectTy, mangle(c, t)) - -proc tupleToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - var fieldTypes = newSeq[TypeId](t.len) - for i in 0..<t.len: - fieldTypes[i] = typeToIr(c, g, t[i]) - let obj = openType(g, ObjectDecl) - g.addName mangle(c, t) - g.addSize c.conf.getSize(t) - g.addAlign c.conf.getAlign(t) - - var accum = OffsetAccum(maxAlign: 1) - for i in 0..<t.len: - let child = t[i] - g.addField "f_" & $i, fieldTypes[i], accum.offset - - computeSizeAlign(c.conf, child) - accum.align(child.align) - accum.inc(int32(child.size)) - result = finishType(g, obj) - -proc procToIr(c: var TypesCon; g: var TypeGraph; t: PType; addEnv = false): TypeId = - var fieldTypes = newSeq[TypeId](0) - for i in 0..<t.len: - if t[i] == nil or not isCompileTimeOnly(t[i]): - fieldTypes.add typeToIr(c, g, t[i]) - let obj = openType(g, ProcTy) - - case t.callConv - of ccNimCall, ccFastCall, ccClosure: g.addAnnotation "__fastcall" - of ccStdCall: g.addAnnotation "__stdcall" - of ccCDecl: g.addAnnotation "__cdecl" - of ccSafeCall: g.addAnnotation "__safecall" - of ccSysCall: g.addAnnotation "__syscall" - of ccInline: g.addAnnotation "__inline" - of ccNoInline: g.addAnnotation "__noinline" - of ccThisCall: g.addAnnotation "__thiscall" - of ccNoConvention, ccMember: g.addAnnotation "" - - for i in 0..<fieldTypes.len: - g.addType fieldTypes[i] - - if addEnv: - let a = openType(g, APtrTy) - g.addBuiltinType(VoidId) - sealType(g, a) - - if tfVarargs in t.flags: - g.addVarargs() - result = finishType(g, obj) - -proc nativeInt(c: TypesCon): TypeId = - case c.conf.target.intSize - of 2: result = Int16Id - of 4: result = Int32Id - else: result = Int64Id - -proc openArrayPayloadType*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - let e = elementType(t) - let elementType = typeToIr(c, g, e) - let arr = g.openType AArrayPtrTy - g.addType elementType - result = finishType(g, arr) # LastArrayTy - -proc openArrayToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - # object (a: ArrayPtr[T], len: int) - let e = elementType(t) - let mangledBase = mangle(c, e) - let typeName = "NimOpenArray" & mangledBase - - let elementType = typeToIr(c, g, e) - #assert elementType.int >= 0, typeToString(t) - - let p = openType(g, ObjectDecl) - g.addName typeName - g.addSize c.conf.target.ptrSize*2 - g.addAlign c.conf.target.ptrSize - - let f = g.openType FieldDecl - let arr = g.openType AArrayPtrTy - g.addType elementType - sealType(g, arr) # LastArrayTy - g.addOffset 0 - g.addName "data" - sealType(g, f) # FieldDecl - - g.addField "len", c.nativeInt, c.conf.target.ptrSize - - result = finishType(g, p) # ObjectDecl - -proc strPayloadType(c: var TypesCon; g: var TypeGraph): (string, TypeId) = - result = ("NimStrPayload", TypeId(-1)) - let p = openType(g, ObjectDecl) - g.addName result[0] - g.addSize c.conf.target.ptrSize*2 - g.addAlign c.conf.target.ptrSize - - g.addField "cap", c.nativeInt, 0 - - let f = g.openType FieldDecl - let arr = g.openType LastArrayTy - g.addBuiltinType Char8Id - result[1] = finishType(g, arr) # LastArrayTy - g.addOffset c.conf.target.ptrSize # comes after the len field - g.addName "data" - sealType(g, f) # FieldDecl - - sealType(g, p) - -proc strPayloadPtrType*(c: var TypesCon; g: var TypeGraph): (TypeId, TypeId) = - let (mangled, arrayType) = strPayloadType(c, g) - let ffp = g.openType APtrTy - g.addNominalType ObjectTy, mangled - result = (finishType(g, ffp), arrayType) # APtrTy - -proc stringToIr(c: var TypesCon; g: var TypeGraph): TypeId = - #[ - - NimStrPayload = object - cap: int - data: UncheckedArray[char] - - NimStringV2 = object - len: int - p: ptr NimStrPayload - - ]# - let payload = strPayloadType(c, g) - - let str = openType(g, ObjectDecl) - g.addName "NimStringV2" - g.addSize c.conf.target.ptrSize*2 - g.addAlign c.conf.target.ptrSize - - g.addField "len", c.nativeInt, 0 - - let fp = g.openType FieldDecl - let ffp = g.openType APtrTy - g.addNominalType ObjectTy, "NimStrPayload" - sealType(g, ffp) # APtrTy - g.addOffset c.conf.target.ptrSize # comes after 'len' field - g.addName "p" - sealType(g, fp) # FieldDecl - - result = finishType(g, str) # ObjectDecl - -proc seqPayloadType(c: var TypesCon; g: var TypeGraph; t: PType): (string, TypeId) = - #[ - NimSeqPayload[T] = object - cap: int - data: UncheckedArray[T] - ]# - let e = elementType(t) - result = (mangle(c, e), TypeId(-1)) - let payloadName = "NimSeqPayload" & result[0] - - let elementType = typeToIr(c, g, e) - - let p = openType(g, ObjectDecl) - g.addName payloadName - g.addSize c.conf.target.intSize - g.addAlign c.conf.target.intSize - - g.addField "cap", c.nativeInt, 0 - - let f = g.openType FieldDecl - let arr = g.openType LastArrayTy - g.addType elementType - # DO NOT USE `finishType` here as it is an inner type. This is subtle and we - # probably need an even better API here. - sealType(g, arr) - result[1] = TypeId(arr) - - g.addOffset c.conf.target.ptrSize - g.addName "data" - sealType(g, f) # FieldDecl - - sealType(g, p) - -proc seqPayloadPtrType*(c: var TypesCon; g: var TypeGraph; t: PType): (TypeId, TypeId) = - let (mangledBase, arrayType) = seqPayloadType(c, g, t) - let ffp = g.openType APtrTy - g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase - result = (finishType(g, ffp), arrayType) # APtrTy - -proc seqToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - #[ - NimSeqV2*[T] = object - len: int - p: ptr NimSeqPayload[T] - ]# - let (mangledBase, _) = seqPayloadType(c, g, t) - - let sq = openType(g, ObjectDecl) - g.addName "NimSeqV2" & mangledBase - g.addSize c.conf.getSize(t) - g.addAlign c.conf.getAlign(t) - - g.addField "len", c.nativeInt, 0 - - let fp = g.openType FieldDecl - let ffp = g.openType APtrTy - g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase - sealType(g, ffp) # APtrTy - g.addOffset c.conf.target.ptrSize - g.addName "p" - sealType(g, fp) # FieldDecl - - result = finishType(g, sq) # ObjectDecl - - -proc closureToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - # struct {fn(args, void* env), env} - # typedef struct {$n" & - # "N_NIMCALL_PTR($2, ClP_0) $3;$n" & - # "void* ClE_0;$n} $1;$n" - let mangledBase = mangle(c, t) - let typeName = "NimClosure" & mangledBase - - let procType = procToIr(c, g, t, addEnv=true) - - let p = openType(g, ObjectDecl) - g.addName typeName - g.addSize c.conf.getSize(t) - g.addAlign c.conf.getAlign(t) - - let f = g.openType FieldDecl - g.addType procType - g.addOffset 0 - g.addName "ClP_0" - sealType(g, f) # FieldDecl - - let f2 = g.openType FieldDecl - let voidPtr = openType(g, APtrTy) - g.addBuiltinType(VoidId) - sealType(g, voidPtr) - - g.addOffset c.conf.target.ptrSize - g.addName "ClE_0" - sealType(g, f2) # FieldDecl - - result = finishType(g, p) # ObjectDecl - -proc bitsetBasetype*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - let s = int(getSize(c.conf, t)) - case s - of 1: result = UInt8Id - of 2: result = UInt16Id - of 4: result = UInt32Id - of 8: result = UInt64Id - else: result = UInt8Id - -proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - if t == nil: return VoidId - case t.kind - of tyInt: - case int(getSize(c.conf, t)) - of 2: result = Int16Id - of 4: result = Int32Id - else: result = Int64Id - of tyInt8: result = Int8Id - of tyInt16: result = Int16Id - of tyInt32: result = Int32Id - of tyInt64: result = Int64Id - of tyFloat: - case int(getSize(c.conf, t)) - of 4: result = Float32Id - else: result = Float64Id - of tyFloat32: result = Float32Id - of tyFloat64: result = Float64Id - of tyFloat128: result = getFloat128Type(g) - of tyUInt: - case int(getSize(c.conf, t)) - of 2: result = UInt16Id - of 4: result = UInt32Id - else: result = UInt64Id - of tyUInt8: result = UInt8Id - of tyUInt16: result = UInt16Id - of tyUInt32: result = UInt32Id - of tyUInt64: result = UInt64Id - of tyBool: result = Bool8Id - of tyChar: result = Char8Id - of tyVoid: result = VoidId - of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned, tyRange: - result = typeToIr(c, g, t.skipModifier) - of tyEnum: - if firstOrd(c.conf, t) < 0: - result = Int32Id - else: - case int(getSize(c.conf, t)) - of 1: result = UInt8Id - of 2: result = UInt16Id - of 4: result = Int32Id - of 8: result = Int64Id - else: result = Int32Id - of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic: - if t.len > 0: - result = typeToIr(c, g, t.skipModifier) - else: - result = TypeId(-1) - of tyFromExpr: - if t.n != nil and t.n.typ != nil: - result = typeToIr(c, g, t.n.typ) - else: - result = TypeId(-1) - of tyArray: - cached(c, t): - var n = toInt64(lengthOrd(c.conf, t)) - if n <= 0: n = 1 # make an array of at least one element - let elemType = typeToIr(c, g, t.elementType) - let a = openType(g, ArrayTy) - g.addType(elemType) - g.addArrayLen n - g.addName mangle(c, t) - result = finishType(g, a) - of tyPtr, tyRef: - cached(c, t): - let e = t.elementType - if e.kind == tyUncheckedArray: - let elemType = typeToIr(c, g, e.elementType) - let a = openType(g, AArrayPtrTy) - g.addType(elemType) - result = finishType(g, a) - else: - let elemType = typeToIr(c, g, t.elementType) - let a = openType(g, APtrTy) - g.addType(elemType) - result = finishType(g, a) - of tyVar, tyLent: - cached(c, t): - let e = t.elementType - if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}: - # skip the modifier, `var openArray` is a (ptr, len) pair too: - result = typeToIr(c, g, e) - else: - let elemType = typeToIr(c, g, e) - let a = openType(g, APtrTy) - g.addType(elemType) - result = finishType(g, a) - of tySet: - let s = int(getSize(c.conf, t)) - case s - of 1: result = UInt8Id - of 2: result = UInt16Id - of 4: result = UInt32Id - of 8: result = UInt64Id - else: - # array[U8, s] - cached(c, t): - let a = openType(g, ArrayTy) - g.addType(UInt8Id) - g.addArrayLen s - g.addName mangle(c, t) - result = finishType(g, a) - of tyPointer, tyNil: - # tyNil can happen for code like: `const CRAP = nil` which we have in posix.nim - let a = openType(g, APtrTy) - g.addBuiltinType(VoidId) - result = finishType(g, a) - of tyObject: - # Objects are special as they can be recursive in Nim. This is easily solvable. - # We check if we are already "processing" t. If so, we produce `ObjectTy` - # instead of `ObjectDecl`. - cached(c, t): - if not c.recursionCheck.containsOrIncl(t.itemId): - result = objectToIr(c, g, t) - else: - result = objectHeaderToIr(c, g, t) - of tyTuple: - cachedByName(c, t): - result = tupleToIr(c, g, t) - of tyProc: - cached(c, t): - if t.callConv == ccClosure: - result = closureToIr(c, g, t) - else: - result = procToIr(c, g, t) - of tyVarargs, tyOpenArray: - cached(c, t): - result = openArrayToIr(c, g, t) - of tyString: - if c.stringType.int < 0: - result = stringToIr(c, g) - c.stringType = result - else: - result = c.stringType - of tySequence: - cachedByName(c, t): - result = seqToIr(c, g, t) - of tyCstring: - cached(c, t): - let a = openType(g, AArrayPtrTy) - g.addBuiltinType Char8Id - result = finishType(g, a) - of tyUncheckedArray: - # We already handled the `ptr UncheckedArray` in a special way. - cached(c, t): - let elemType = typeToIr(c, g, t.elementType) - let a = openType(g, LastArrayTy) - g.addType(elemType) - result = finishType(g, a) - of tyUntyped, tyTyped: - # this avoids a special case for system.echo which is not a generic but - # uses `varargs[typed]`: - result = VoidId - of tyNone, tyEmpty, tyTypeDesc, - tyGenericInvocation, tyProxy, tyBuiltInTypeClass, - tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, - tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward: - result = TypeId(-1) diff --git a/compiler/nodekinds.nim b/compiler/nodekinds.nim index 98ae9405d..ccdbbd26d 100644 --- a/compiler/nodekinds.nim +++ b/compiler/nodekinds.nim @@ -204,6 +204,7 @@ type nkModuleRef # for .rod file support: A (moduleId, itemId) pair nkReplayAction # for .rod file support: A replay action nkNilRodNode # for .rod file support: a 'nil' PNode + nkOpenSym # container for captured sym that can be overriden by local symbols const nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix, diff --git a/compiler/options.nim b/compiler/options.nim index 356aa6cc8..b77bdd2a3 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -139,7 +139,6 @@ type backendCpp = "cpp" backendJs = "js" backendObjc = "objc" - backendNir = "nir" # backendNimscript = "nimscript" # this could actually work # backendLlvm = "llvm" # probably not well supported; was cmdCompileToLLVM @@ -147,7 +146,6 @@ type cmdNone # not yet processed command cmdUnknown # command unmapped cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS, - cmdCompileToNir, cmdCrun # compile and run in nimache cmdTcc # run the project via TCC backend cmdCheck # semantic checking for whole project @@ -176,12 +174,11 @@ type const cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, - cmdCompileToJS, cmdCrun, cmdCompileToNir} + cmdCompileToJS, cmdCrun} cmdDocLike* = {cmdDoc0, cmdDoc, cmdDoc2tex, cmdJsondoc0, cmdJsondoc, cmdCtags, cmdBuildindex} type - NimVer* = tuple[major: int, minor: int, patch: int] TStringSeq* = seq[string] TGCMode* = enum # the selected GC gcUnselected = "unselected" @@ -228,7 +225,9 @@ type strictDefs, strictCaseObjects, inferGenericTypes, - genericsOpenSym, + openSym, # remove nfDisabledOpenSym when this is default + # alternative to above: + genericsOpenSym vtables LegacyFeature* = enum @@ -246,13 +245,15 @@ type emitGenerics ## generics are emitted in the module that contains them. ## Useful for libraries that rely on local passC + jsNoLambdaLifting + ## Old transformation for closures in JS backend SymbolFilesOption* = enum disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest TSystemCC* = enum ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccBcc, ccVcc, - ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl + ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl, ccHipcc, ccNvcc ExceptionSystem* = enum excNone, # no exception system selected yet @@ -368,7 +369,6 @@ type arguments*: string ## the arguments to be passed to the program that ## should be run ideCmd*: IdeCmd - oldNewlines*: bool cCompiler*: TSystemCC # the used compiler modifiedyNotes*: TNoteKinds # notes that have been set/unset from either cmdline/configs cmdlineNotes*: TNoteKinds # notes that have been set/unset from cmdline @@ -395,8 +395,7 @@ type outDir*: AbsoluteDir jsonBuildFile*: AbsoluteFile prefixDir*, libpath*, nimcacheDir*: AbsoluteDir - nimStdlibVersion*: NimVer - dllOverrides, moduleOverrides*, cfileSpecificOptions*: StringTableRef + dllOverrides*, moduleOverrides*, cfileSpecificOptions*: StringTableRef projectName*: string # holds a name like 'nim' projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/ projectFull*: AbsoluteFile # projectPath/projectName @@ -408,7 +407,6 @@ type commandArgs*: seq[string] # any arguments after the main command commandLine*: string extraCmds*: seq[string] # for writeJsonBuildInstructions - keepComments*: bool # whether the parser needs to keep comments implicitImports*: seq[string] # modules that are to be implicitly imported implicitIncludes*: seq[string] # modules that are to be implicitly included docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \ @@ -449,16 +447,6 @@ type clientProcessId*: int -proc parseNimVersion*(a: string): NimVer = - # could be moved somewhere reusable - result = default(NimVer) - if a.len > 0: - let b = a.split(".") - assert b.len == 3, a - template fn(i) = result[i] = b[i].parseInt # could be optimized if needed - fn(0) - fn(1) - fn(2) proc assignIfDefault*[T](result: var T, val: T, def = default(T)) = ## if `result` was already assigned to a value (that wasn't `def`), this is a noop. @@ -592,7 +580,6 @@ proc newConfigRef*(): ConfigRef = command: "", # the main command (e.g. cc, check, scan, etc) commandArgs: @[], # any arguments after the main command commandLine: "", - keepComments: true, # whether the parser needs to keep comments implicitImports: @[], # modules that are to be implicitly imported implicitIncludes: @[], # modules that are to be implicitly included docSeeSrcUrl: "", @@ -633,12 +620,6 @@ proc newPartialConfigRef*(): ConfigRef = proc cppDefine*(c: ConfigRef; define: string) = c.cppDefines.incl define -proc getStdlibVersion*(conf: ConfigRef): NimVer = - if conf.nimStdlibVersion == (0,0,0): - let s = conf.symbols.getOrDefault("nimVersion", "") - conf.nimStdlibVersion = s.parseNimVersion - result = conf.nimStdlibVersion - proc isDefined*(conf: ConfigRef; symbol: string): bool = if conf.symbols.hasKey(symbol): result = true diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index 84c2980c4..e8ec22fe1 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -266,7 +266,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in {tyOpenArray, tyTuple, tyObject}: result = isAssignable(owner, n[1]) - elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct): + elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct, {IgnoreRangeShallow}): # types that are equal modulo distinction preserve l-value: result = isAssignable(owner, n[1]) of nkHiddenDeref: @@ -283,8 +283,15 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: result = isAssignable(owner, n[0]) of nkCallKinds: - # builtin slice keeps lvalue-ness: - if getMagic(n) in {mArrGet, mSlice}: + let m = getMagic(n) + if m == mSlice: + # builtin slice keeps l-value-ness + # except for pointers because slice dereferences + if n[1].typ.kind == tyPtr: + result = arLValue + else: + result = isAssignable(owner, n[1]) + elif m == mArrGet: result = isAssignable(owner, n[1]) elif n.typ != nil: case n.typ.kind diff --git a/compiler/parser.nim b/compiler/parser.nim index eb7b8c289..747505097 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1401,7 +1401,7 @@ proc primary(p: var Parser, mode: PrimaryMode): PNode = result = primarySuffix(p, result, baseInd, mode) proc binaryNot(p: var Parser; a: PNode): PNode = - if p.tok.tokType == tkNot: + if p.tok.tokType == tkNot and p.tok.indent < 0: let notOpr = newIdentNodeP(p.tok.ident, p) getTok(p) optInd(p, notOpr) diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim index a44edbb7f..55e7fe892 100644 --- a/compiler/pipelines.nim +++ b/compiler/pipelines.nim @@ -13,7 +13,6 @@ when not defined(leanCompiler): import std/[syncio, objectdollar, assertions, tables, strutils, strtabs] import renderer import ic/replayer -import nir/nir proc setPipeLinePass*(graph: ModuleGraph; pass: PipelinePass) = graph.pipelinePass = pass @@ -45,10 +44,6 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext): result = nil of EvalPass, InterpreterPass: result = interpreterCode(bModule, semNode) - of NirReplPass: - result = runCode(bModule, semNode) - of NirPass: - result = nirBackend(bModule, semNode) of NonePass: raiseAssert "use setPipeLinePass to set a proper PipelinePass" @@ -111,8 +106,6 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator case graph.pipelinePass of CgenPass: setupCgen(graph, module, idgen) - of NirPass: - openNirBackend(graph, module, idgen) of JSgenPass: when not defined(leanCompiler): setupJSgen(graph, module, idgen) @@ -120,8 +113,6 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator nil of EvalPass, InterpreterPass: setupEvalGen(graph, module, idgen) - of NirReplPass: - setupNirReplGen(graph, module, idgen) of GenDependPass: setupDependPass(graph, module, idgen) of Docgen2Pass: @@ -209,10 +200,6 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator discard finalJSCodeGen(graph, bModule, finalNode) of EvalPass, InterpreterPass: discard interpreterCode(bModule, finalNode) - of NirReplPass: - discard runCode(bModule, finalNode) - of NirPass: - closeNirBackend(bModule, finalNode) of SemPass, GenDependPass: discard of Docgen2Pass, Docgen2TexPass: diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index a644639fe..9a298cd90 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -81,14 +81,14 @@ const wRequiresInit, wNoalias, wAlign, wNoInit} - {wExportNims, wNodecl} # why exclude these? varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar, wMagic, wHeader, wCompilerProc, wCore, wDynlib, - wNoInit, wCompileTime, wGlobal, + wNoInit, wCompileTime, wGlobal, wLiftLocals, wGensym, wInject, wCodegenDecl, wGuard, wGoto, wCursor, wNoalias, wAlign} constPragmas* = declPragmas + {wHeader, wMagic, wGensym, wInject, wIntDefine, wStrDefine, wBoolDefine, wDefine, wCompilerProc, wCore} - paramPragmas* = {wNoalias, wInject, wGensym, wByRef, wByCopy, wCodegenDecl} + paramPragmas* = {wNoalias, wInject, wGensym, wByRef, wByCopy, wCodegenDecl, wExportc, wExportCpp} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect, wThread, wRaises, wEffectsOf, wLocks, wTags, wForbids, wGcSafe, @@ -156,16 +156,16 @@ proc pragmaEnsures(c: PContext, n: PNode) = proc setExternName(c: PContext; s: PSym, extname: string, info: TLineInfo) = # special cases to improve performance: if extname == "$1": - s.loc.r = rope(s.name.s) + s.loc.snippet = rope(s.name.s) elif '$' notin extname: - s.loc.r = rope(extname) + s.loc.snippet = rope(extname) else: try: - s.loc.r = rope(extname % s.name.s) + s.loc.snippet = rope(extname % s.name.s) except ValueError: localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)") when hasFFI: - s.cname = $s.loc.r + s.cname = $s.loc.snippet proc makeExternImport(c: PContext; s: PSym, extname: string, info: TLineInfo) = @@ -370,7 +370,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) = proc processNote(c: PContext, n: PNode) = template handleNote(enumVals, notes) = let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown) - if x != errUnknown: + if x != errUnknown: nk = TNoteKind(x) let x = c.semConstBoolExpr(c, n[1]) n[1] = x @@ -480,6 +480,18 @@ proc processOption(c: PContext, n: PNode, resOptions: var TOptions) = # calling conventions (boring...): localError(c.config, n.info, "option expected") +proc checkPushedPragma(c: PContext, n: PNode) = + let keyDeep = n.kind in nkPragmaCallKinds and n.len > 1 + var key = if keyDeep: n[0] else: n + if key.kind in nkIdentKinds: + let ident = considerQuotedIdent(c, key) + var userPragma = strTableGet(c.userPragmas, ident) + if userPragma == nil: + let k = whichKeyword(ident) + # TODO: might as well make a list which is not accepted by `push`: emit, cast etc. + if k == wEmit: + localError(c.config, n.info, "an 'emit' pragma cannot be pushed") + proc processPush(c: PContext, n: PNode, start: int) = if n[start-1].kind in nkPragmaCallKinds: localError(c.config, n.info, "'push' cannot have arguments") @@ -487,6 +499,7 @@ proc processPush(c: PContext, n: PNode, start: int) = for i in start..<n.len: if not tryProcessOption(c, n[i], c.config.options): # simply store it somewhere: + checkPushedPragma(c, n[i]) if x.otherPragmas.isNil: x.otherPragmas = newNodeI(nkPragma, n.info) x.otherPragmas.add n[i] @@ -787,13 +800,14 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym = proc semCustomPragma(c: PContext, n: PNode, sym: PSym): PNode = var callNode: PNode - if n.kind in {nkIdent, nkSym}: + case n.kind + of nkIdentKinds: # pragma -> pragma() callNode = newTree(nkCall, n) - elif n.kind == nkExprColonExpr: + of nkExprColonExpr: # pragma: arg -> pragma(arg) callNode = newTree(nkCall, n[0], n[1]) - elif n.kind in nkPragmaCallKinds: + of nkPragmaCallKinds - {nkExprColonExpr}: callNode = n else: invalidPragma(c, n) @@ -869,6 +883,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, # number of pragmas increase/decrease with user pragma expansion inc c.instCounter + defer: dec c.instCounter if c.instCounter > 100: globalError(c.config, it.info, "recursive dependency: " & userPragma.name.s) @@ -878,7 +893,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, pragma(c, sym, userPragma.ast, validPragmas, isStatement) n.sons[i..i] = userPragma.ast.sons # expand user pragma with its content i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty - dec c.instCounter else: let k = whichKeyword(ident) if k in validPragmas: @@ -1001,7 +1015,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, incl(sym.loc.flags, lfHeader) incl(sym.loc.flags, lfNoDecl) # implies nodecl, because otherwise header would not make sense - if sym.loc.r == "": sym.loc.r = rope(sym.name.s) + if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s) of wNoSideEffect: noVal(c, it) if sym != nil: @@ -1295,7 +1309,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, noVal(c, it) if sym == nil: invalidPragma(c, it) else: sym.flags.incl sfUsed - of wLiftLocals: discard + of wLiftLocals: + sym.flags.incl(sfForceLift) of wRequires, wInvariant, wAssume, wAssert: pragmaProposition(c, it) of wEnsures: @@ -1329,6 +1344,16 @@ proc mergePragmas(n, pragmas: PNode) = else: for p in pragmas: n[pragmasPos].add p +proc mergeValidPragmas(n, pragmas: PNode, validPragmas: TSpecialWords) = + if n[pragmasPos].kind == nkEmpty: + n[pragmasPos] = newNodeI(nkPragma, n.info) + for p in pragmas: + let prag = whichPragma(p) + if prag in validPragmas: + let copy = copyTree(p) + overwriteLineInfo copy, n.info + n[pragmasPos].add copy + proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo, validPragmas: TSpecialWords) = if sym != nil and sym.kind != skModule: @@ -1342,7 +1367,8 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo, internalError(c.config, info, "implicitPragmas") inc i 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: + mergeValidPragmas(sym.ast, o, validPragmas) if lfExportLib in sym.loc.flags and sfExportc notin sym.flags: localError(c.config, info, ".dynlib requires .exportc") @@ -1351,7 +1377,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo, sfImportc in sym.flags and lib != nil: incl(sym.loc.flags, lfDynamicLib) addToLib(lib, sym) - if sym.loc.r == "": sym.loc.r = rope(sym.name.s) + if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s) proc hasPragma*(n: PNode, pragma: TSpecialWord): bool = if n == nil: return false diff --git a/compiler/pushpoppragmas.nim b/compiler/pushpoppragmas.nim new file mode 100644 index 000000000..773e7013b --- /dev/null +++ b/compiler/pushpoppragmas.nim @@ -0,0 +1,54 @@ +import pragmas, options, ast, trees, lineinfos, idents, wordrecg +import std/assertions + +import renderer + + +proc processNote(config: ConfigRef, n: PNode) = + template handleNote(enumVals, notes) = + let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown) + assert x != errUnknown + assert n[1].kind == nkIntLit + + nk = TNoteKind(x) + if n[1].intVal != 0: incl(notes, nk) + else: excl(notes, nk) + + var nk: TNoteKind + case whichKeyword(n[0][0].ident) + of wHint: handleNote(hintMin .. hintMax, config.notes) + of wWarning: handleNote(warnMin .. warnMax, config.notes) + of wWarningAsError: handleNote(warnMin .. warnMax, config.warningAsErrors) + of wHintAsError: handleNote(hintMin .. hintMax, config.warningAsErrors) + else: discard + +proc pushBackendOption(optionsStack: var seq[(TOptions, TNoteKinds)], options: TOptions, notes: TNoteKinds) = + optionsStack.add (options, notes) + +proc popBackendOption(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions) = + let entry = optionsStack[^1] + options = entry[0] + config.notes = entry[1] + optionsStack.setLen(optionsStack.len-1) + +proc processPushBackendOption*(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions, + n: PNode, start: int) = + pushBackendOption(optionsStack, options, config.notes) + for i in start..<n.len: + let it = n[i] + if it.kind in nkPragmaCallKinds and it.len == 2: + if it[0].kind == nkBracketExpr and + it[0].len == 2 and + it[0][1].kind == nkIdent and it[0][0].kind == nkIdent: + processNote(config, it) + elif it[1].kind == nkIntLit: + let sw = whichPragma(it[0]) + let opts = pragmaToOptions(sw) + if opts != {}: + if it[1].intVal != 0: + options.incl opts + else: + options.excl opts + +template processPopBackendOption*(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions) = + popBackendOption(config, optionsStack, options) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 3a7c60953..cc07c0c2d 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -442,6 +442,11 @@ proc atom(g: TSrcGen; n: PNode): string = result = $n.floatVal & "\'f64" else: result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f64" + of nkFloat128Lit: + if n.flags * {nfBase2, nfBase8, nfBase16} == {}: + result = $n.floatVal & "\'f128" + else: + result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f128" of nkNilLit: result = "nil" of nkType: if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s @@ -514,6 +519,7 @@ proc lsub(g: TSrcGen; n: PNode): int = result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}") of nkClosedSymChoice, nkOpenSymChoice: if n.len > 0: result += lsub(g, n[0]) + of nkOpenSym: result = lsub(g, n[0]) of nkTupleTy: result = lcomma(g, n) + len("tuple[]") of nkTupleClassTy: result = len("tuple") of nkDotExpr: result = lsons(g, n) + 1 @@ -1013,7 +1019,7 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind = proc skipHiddenNodes(n: PNode): PNode = result = n while result != nil: - if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv} and result.len > 1: + if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv, nkOpenSym} and result.len > 1: result = result[1] elif result.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString} and result.len > 0: @@ -1275,6 +1281,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = put(g, tkParRi, if n.kind == nkOpenSymChoice: "|...)" else: ")") else: gsub(g, n, 0) + of nkOpenSym: gsub(g, n, 0) of nkPar, nkClosure: put(g, tkParLe, "(") gcomma(g, n, c) diff --git a/compiler/reorder.nim b/compiler/reorder.nim index 31c8befe2..2f7c04af1 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -322,6 +322,14 @@ proc intersects(s1, s2: IntSet): bool = if s2.contains(a): return true +proc hasPushOrPopPragma(n: DepN): bool = + # Checks if the tree node has some pragmas that do not + # play well with reordering, like the push/pop pragma + # no crossing for push/pop barrier + let a = n.pnode + result = a.kind == nkPragma and a[0].kind == nkIdent and + (a[0].ident.s == "push" or a[0].ident.s == "pop") + proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG = # Build a dependency graph result = newSeqOfCap[DepN](deps.len) @@ -363,6 +371,13 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG = for dep in deps[i][0]: if dep in declares: ni.expls.add "one declares \"" & idNames[dep] & "\" and the other defines it" + elif hasPushOrPopPragma(nj): + # Every node that comes after a push/pop pragma must + # depend on it; vice versa + if j < i: + ni.kids.add nj + else: + nj.kids.add ni else: for d in declares: if uses.contains(d): @@ -403,18 +418,7 @@ proc getStrongComponents(g: var DepG): seq[seq[DepN]] = if v.idx < 0: strongConnect(v, idx, s, result) -proc hasForbiddenPragma(n: PNode): bool = - # Checks if the tree node has some pragmas that do not - # play well with reordering, like the push/pop pragma - result = false - for a in n: - if a.kind == nkPragma and a[0].kind == nkIdent and - a[0].ident.s == "push": - return true - proc reorder*(graph: ModuleGraph, n: PNode, module: PSym): PNode = - if n.hasForbiddenPragma: - return n var includedFiles = initIntSet() let mpath = toFullPath(graph.config, module.fileIdx) let n = expandIncludes(graph, module, n, mpath, diff --git a/compiler/sem.nim b/compiler/sem.nim index 92c21aece..2cf93d365 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -21,7 +21,7 @@ import extccomp import vtables -import std/[strtabs, math, tables, intsets, strutils] +import std/[strtabs, math, tables, intsets, strutils, packedsets] when not defined(leanCompiler): import spawn @@ -103,7 +103,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = result = nil for ch in arg: if sameType(ch.typ, formal): - return getConstExpr(c.module, ch, c.idgen, c.graph) + return ch typeMismatch(c.config, info, formal, arg.typ, arg) else: result = indexTypesMatch(c, formal, arg.typ, arg) @@ -222,66 +222,7 @@ proc shouldCheckCaseCovered(caseTyp: PType): bool = 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 endsInNoReturn(n: PNode): bool proc commonType*(c: PContext; x: PType, y: PNode): PType = # ignore exception raising branches in case/if expressions @@ -313,6 +254,8 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = 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): @@ -322,7 +265,7 @@ 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 + allowed: TSymFlags, fromTopLevel = false): PSym proc typeAllowedCheck(c: PContext; info: TLineInfo; typ: PType; kind: TSymKind; flags: TTypeAllowedFlags = {}) = @@ -554,7 +497,6 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, 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 = @@ -708,7 +650,8 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode = let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes) - if aTypSkip.kind == tyObject: + 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)) @@ -717,7 +660,7 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): P result = semExpr(c, asgnExpr) else: result = nil - elif aTypSkip.kind == tyArray: + of tyArray: let child = defaultNodeField(c, a, aTypSkip[1], checkDefault) if child != nil: @@ -730,7 +673,7 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): P result.typ = aTyp else: result = nil - elif aTypSkip.kind == tyTuple: + of tyTuple: var hasDefault = false if aTypSkip.n != nil: let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault, checkDefault) @@ -743,6 +686,11 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): P result = nil else: result = nil + of tyRange: + if c.graph.config.isDefined("nimPreviewRangeDefault"): + result = firstRange(c.config, aTypSkip) + else: + result = nil else: result = nil @@ -779,6 +727,7 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo result.semOverloadedCall = semOverloadedCall result.semInferredLambda = semInferredLambda result.semGenerateInstance = generateInstance + result.instantiateOnlyProcType = instantiateOnlyProcType result.semTypeNode = semTypeNode result.instTypeBoundOp = sigmatch.instTypeBoundOp result.hasUnresolvedArgs = hasUnresolvedArgs diff --git a/compiler/semcall.nim b/compiler/semcall.nim index a136cf4fe..13f2273a9 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -246,7 +246,16 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): candidates.add(getProcHeader(c.config, err.sym, prefer)) candidates.addDeclaredLocMaybe(c.config, err.sym) candidates.add("\n") - let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil + const genericParamMismatches = {kGenericParamTypeMismatch, kExtraGenericParam, kMissingGenericParam} + let isGenericMismatch = err.firstMismatch.kind in genericParamMismatches + var argList = n + if isGenericMismatch and n[0].kind == nkBracketExpr: + argList = n[0] + let nArg = + if err.firstMismatch.arg < argList.len: + argList[err.firstMismatch.arg] + else: + nil let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: "" if n.len > 1: if verboseTypeMismatch notin c.config.legacyFeatures: @@ -269,6 +278,12 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): of kMissingParam: candidates.add(" missing parameter: " & nameParam) candidates.add "\n" + of kExtraGenericParam: + candidates.add(" extra generic param given") + candidates.add "\n" + of kMissingGenericParam: + candidates.add(" missing generic parameter: " & nameParam) + candidates.add "\n" of kVarNeeded: doAssert nArg != nil doAssert err.firstMismatch.formal != nil @@ -278,6 +293,8 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): candidates.add "\n" of kTypeMismatch: doAssert nArg != nil + if nArg.kind in nkSymChoices: + candidates.add ambiguousIdentifierMsg(nArg, indent = 2) let wanted = err.firstMismatch.formal.typ doAssert err.firstMismatch.formal != nil doAssert wanted != nil @@ -292,9 +309,39 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): candidates.addPragmaAndCallConvMismatch(wanted, got, c.config) effectProblem(wanted, got, candidates, c) candidates.add "\n" + of kGenericParamTypeMismatch: + let pos = err.firstMismatch.arg + doAssert n[0].kind == nkBracketExpr and pos < n[0].len + let arg = n[0][pos] + doAssert arg != nil + var wanted = err.firstMismatch.formal.typ + if wanted.kind == tyGenericParam and wanted.genericParamHasConstraints: + wanted = wanted.genericConstraint + let got = arg.typ.skipTypes({tyTypeDesc}) + doAssert err.firstMismatch.formal != nil + doAssert wanted != nil + doAssert got != nil + candidates.add " generic parameter mismatch, expected " + candidates.addTypeDeclVerboseMaybe(c.config, wanted) + candidates.add " but got '" + candidates.add renderTree(arg) + candidates.add "' of type: " + candidates.addTypeDeclVerboseMaybe(c.config, got) + if nArg.kind in nkSymChoices: + candidates.add "\n" + candidates.add ambiguousIdentifierMsg(nArg, indent = 2) + if got != nil and got.kind == tyProc and wanted.kind == tyProc: + # These are proc mismatches so, + # add the extra explict detail of the mismatch + candidates.addPragmaAndCallConvMismatch(wanted, got, c.config) + if got != nil: + effectProblem(wanted, got, candidates, c) + candidates.add "\n" of kUnknown: discard "do not break 'nim check'" else: candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg) + if err.firstMismatch.kind in genericParamMismatches: + candidates.add(" in generic parameters") # candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging case err.firstMismatch.kind of kUnknownNamedParam: @@ -306,9 +353,16 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): of kPositionalAlreadyGiven: candidates.add("\n positional param was already given as named param") of kExtraArg: candidates.add("\n extra argument given") of kMissingParam: candidates.add("\n missing parameter: " & nameParam) - of kTypeMismatch, kVarNeeded: + of kExtraGenericParam: + candidates.add("\n extra generic param given") + of kMissingGenericParam: + candidates.add("\n missing generic parameter: " & nameParam) + of kTypeMismatch, kGenericParamTypeMismatch, kVarNeeded: doAssert nArg != nil - let wanted = err.firstMismatch.formal.typ + var wanted = err.firstMismatch.formal.typ + if isGenericMismatch and wanted.kind == tyGenericParam and + wanted.genericParamHasConstraints: + wanted = wanted.genericConstraint doAssert err.firstMismatch.formal != nil candidates.add("\n required type for " & nameParam & ": ") candidates.addTypeDeclVerboseMaybe(c.config, wanted) @@ -319,8 +373,12 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): else: candidates.add renderTree(nArg) candidates.add "' is of type: " - let got = nArg.typ + var got = nArg.typ + if isGenericMismatch: got = got.skipTypes({tyTypeDesc}) candidates.addTypeDeclVerboseMaybe(c.config, got) + if nArg.kind in nkSymChoices: + candidates.add "\n" + candidates.add ambiguousIdentifierMsg(nArg, indent = 2) doAssert wanted != nil if got != nil: if got.kind == tyProc and wanted.kind == tyProc: @@ -331,8 +389,8 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): of kUnknown: discard "do not break 'nim check'" candidates.add "\n" - if err.firstMismatch.arg == 1 and nArg.kind == nkTupleConstr and - n.kind == nkCommand: + if err.firstMismatch.arg == 1 and nArg != nil and + nArg.kind == nkTupleConstr and n.kind == nkCommand: maybeWrongSpace = true for diag in err.diagnostics: candidates.add(diag & "\n") @@ -409,23 +467,6 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = result.add("\n" & errExpectedPosition & "\n" & candidates) localError(c.config, n.info, result) -proc bracketNotFoundError(c: PContext; n: PNode) = - var errors: CandidateErrors = @[] - var o: TOverloadIter = default(TOverloadIter) - let headSymbol = n[0] - var symx = initOverloadIter(o, c, headSymbol) - while symx != nil: - if symx.kind in routineKinds: - errors.add(CandidateError(sym: symx, - firstMismatch: MismatchInfo(), - diagnostics: @[], - enabled: false)) - symx = nextOverloadIter(o, c, headSymbol) - if errors.len == 0: - localError(c.config, n.info, "could not resolve: " & $n) - else: - notFoundError(c, n, errors) - proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string = result = "" if c.compilesContextId > 0: @@ -518,7 +559,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode, if overloadsState == csEmpty and result.state == csEmpty: if efNoUndeclared notin flags: # for tests/pragmas/tcustom_pragma.nim result.state = csNoMatch - if efNoDiagnostics in flags: + if c.inGenericContext > 0 and nfExprCall in n.flags: + # untyped expression calls end up here, see #24099 return # xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident) localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f)) @@ -554,6 +596,39 @@ proc resolveOverloads(c: PContext, n, orig: PNode, getProcHeader(c.config, alt.calleeSym), args]) +proc bracketNotFoundError(c: PContext; n: PNode; flags: TExprFlags) = + var errors: CandidateErrors = @[] + let headSymbol = n[0] + block: + # we build a closed symchoice of all `[]` overloads for their errors, + # except add a custom error for the magics which always match + var choice = newNodeIT(nkClosedSymChoice, headSymbol.info, newTypeS(tyNone, c)) + var o: TOverloadIter = default(TOverloadIter) + var symx = initOverloadIter(o, c, headSymbol) + while symx != nil: + if symx.kind in routineKinds: + if symx.magic in {mArrGet, mArrPut}: + errors.add(CandidateError(sym: symx, + firstMismatch: MismatchInfo(), + diagnostics: @[], + enabled: false)) + else: + choice.add newSymNode(symx, headSymbol.info) + symx = nextOverloadIter(o, c, headSymbol) + n[0] = choice + # copied from semOverloadedCallAnalyzeEffects, might be overkill: + const baseFilter = {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate} + let filter = + if flags*{efInTypeof, efWantIterator, efWantIterable} != {}: + baseFilter + {skIterator} + else: baseFilter + # this will add the errors: + var r = resolveOverloads(c, n, n, filter, flags, errors, true) + if errors.len == 0: + localError(c.config, n.info, "could not resolve: " & $n) + else: + notFoundError(c, n, errors) + proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) = let a = if a.kind == nkHiddenDeref: a[0] else: a if a.kind == nkHiddenCallConv and a[0].kind == nkSym: @@ -595,7 +670,7 @@ proc inferWithMetatype(c: PContext, formal: PType, result = copyTree(arg) result.typ = formal -proc updateDefaultParams(call: PNode) = +proc updateDefaultParams(c: PContext, call: PNode) = # In generic procs, the default parameter may be unique for each # instantiation (see tlateboundgenericparams). # After a call is resolved, we need to re-assign any default value @@ -605,8 +680,18 @@ proc updateDefaultParams(call: PNode) = let calleeParams = call[0].sym.typ.n for i in 1..<call.len: if nfDefaultParam in call[i].flags: - let def = calleeParams[i].sym.ast + let formal = calleeParams[i].sym + let def = formal.ast if nfDefaultRefsParam in def.flags: call.flags.incl nfDefaultRefsParam + # mirrored with sigmatch: + if def.kind == nkEmpty: + # The default param value is set to empty in `instantiateProcType` + # when the type of the default expression doesn't match the type + # of the instantiated proc param: + pushInfoContext(c.config, call.info, call[0].sym.detailedInfo) + typeMismatch(c.config, def.info, formal.typ, def.typ, formal.ast) + popInfoContext(c.config) + def.typ = errorType(c) call[i] = def proc getCallLineInfo(n: PNode): TLineInfo = @@ -686,12 +771,12 @@ proc semResolvedCall(c: PContext, x: var TCandidate, markUsed(c, info, finalCallee) onUse(info, finalCallee) assert finalCallee.ast != nil - if x.hasFauxMatch: + if x.matchedErrorType: result = x.call result[0] = newSymNode(finalCallee, getCallLineInfo(result[0])) - if containsGenericType(result.typ) or x.fauxMatch == tyUnknown: - result.typ = newTypeS(x.fauxMatch, c) - if result.typ.kind == tyError: incl result.typ.flags, tfCheckedForDestructor + if containsGenericType(result.typ): + result.typ = newTypeS(tyError, c) + incl result.typ.flags, tfCheckedForDestructor return let gp = finalCallee.ast[genericParamsPos] if gp.isGenericParams: @@ -725,8 +810,9 @@ proc semResolvedCall(c: PContext, x: var TCandidate, result = x.call instGenericConvertersSons(c, result, x) result[0] = newSymNode(finalCallee, getCallLineInfo(result[0])) - result.typ = finalCallee.typ.returnType - updateDefaultParams(result) + if finalCallee.magic notin {mArrGet, mArrPut}: + result.typ = finalCallee.typ.returnType + updateDefaultParams(c, result) proc canDeref(n: PNode): bool {.inline.} = result = n.len >= 2 and (let t = n[1].typ; @@ -751,7 +837,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, candidates) result = semResolvedCall(c, r, n, flags, expectedType) else: - if efDetermineType in flags and c.inGenericContext > 0 and c.matchedConcept == nil: + if c.inGenericContext > 0 and c.matchedConcept == nil: result = semGenericStmt(c, n) result.typ = makeTypeFromExpr(c, result.copyTree) elif efExplain notin flags: @@ -769,21 +855,16 @@ proc explicitGenericInstError(c: PContext; n: PNode): PNode = result = n proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = + if s.kind in {skTemplate, skMacro}: + internalError c.config, n.info, "cannot get explicitly instantiated symbol of " & + (if s.kind == skTemplate: "template" else: "macro") # binding has to stay 'nil' for this to work! var m = newCandidate(c, s, nil) - - for i in 1..<n.len: - let formal = s.ast[genericParamsPos][i-1].typ - var arg = n[i].typ - # try transforming the argument into a static one before feeding it into - # typeRel - if formal.kind == tyStatic and arg.kind != tyStatic: - let evaluated = c.semTryConstExpr(c, n[i], n[i].typ) - if evaluated != nil: - arg = newTypeS(tyStatic, c, son = evaluated.typ) - arg.n = evaluated - let tm = typeRel(m, formal, arg) - if tm in {isNone, isConvertible}: return nil + matchGenericParams(m, n, s) + if m.state != csMatch: + # state is csMatch only if *all* generic params were matched, + # including implicit parameters + return nil var newInst = generateInstance(c, s, m.bindings, n.info) newInst.typ.flags.excl tfUnresolved let info = getCallLineInfo(n) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 12930feca..ca35ddc53 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -73,9 +73,9 @@ type efNoUndeclared, efIsDotCall, efCannotBeDotCall, # Use this if undeclared identifiers should not raise an error during # overload resolution. - efNoDiagnostics, efTypeAllowed # typeAllowed will be called after efWantNoDefaults + efIgnoreDefaults # var statements without initialization efAllowSymChoice # symchoice node should not be resolved TExprFlags* = set[TExprFlag] @@ -139,6 +139,9 @@ type semInferredLambda*: proc(c: PContext, pt: Table[ItemId, PType], n: PNode): PNode semGenerateInstance*: proc (c: PContext, fn: PSym, pt: Table[ItemId, PType], info: TLineInfo): PSym + instantiateOnlyProcType*: proc (c: PContext, pt: TypeMapping, + prc: PSym, info: TLineInfo): PType + # used by sigmatch for explicit generic instantiations includedFiles*: IntSet # used to detect recursive include files pureEnumFields*: TStrTable # pure enum fields that can be used unambiguously userPragmas*: TStrTable @@ -168,6 +171,7 @@ type inUncheckedAssignSection*: int importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id]) skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance, sets and generic bodies. + inTypeofContext*: int TBorrowState* = enum bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 17a3082ff..2885142a7 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -138,7 +138,71 @@ proc resolveSymChoice(c: PContext, n: var PNode, flags: TExprFlags = {}, expecte # to mirror behavior before overloadable enums n = n[0] +proc semOpenSym(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType, + warnDisabled = false): PNode = + ## sem the child of an `nkOpenSym` node, that is, captured symbols that can be + ## replaced by newly injected symbols in generics. `s` must be the captured + ## symbol if the original node is an `nkSym` node; and `nil` if it is an + ## `nkOpenSymChoice`, in which case only non-overloadable injected symbols + ## will be considered. + let isSym = n.kind == nkSym + let ident = n.getPIdent + assert ident != nil + let id = newIdentNode(ident, n.info) + c.isAmbiguous = false + let s2 = qualifiedLookUp(c, id, {}) + # for `nkSym`, the first found symbol being different and unambiguous is + # enough to replace the original + # for `nkOpenSymChoice`, the first found symbol must be non-overloadable, + # since otherwise we have to use regular `nkOpenSymChoice` functionality + # but of the overloadable sym kinds, semExpr does not handle skModule, skMacro, skTemplate + # as overloaded in the case where `nkIdent` finds them first + if s2 != nil and not c.isAmbiguous and + ((isSym and s2 != n.sym) or + (not isSym and s2.kind notin OverloadableSyms-{skModule, skMacro, skTemplate})): + # only consider symbols defined under current proc: + var o = s2.owner + while o != nil: + if o == c.p.owner: + if not warnDisabled: + result = semExpr(c, id, flags, expectedType) + return + else: + var msg = + "a new symbol '" & ident.s & "' has been injected during " & + # msgContext should show what is being instantiated: + "template or generic instantiation, however " + if isSym: + msg.add( + getSymRepr(c.config, n.sym) & " captured at " & + "the proc declaration will be used instead; " & + "either enable --experimental:openSym to use the injected symbol, " & + "or `bind` this captured symbol explicitly") + else: + msg.add( + "overloads of " & ident.s & " will be used instead; " & + "either enable --experimental:openSym to use the injected symbol, " & + "or `bind` this symbol explicitly") + message(c.config, n.info, warnIgnoredSymbolInjection, msg) + break + o = o.owner + # nothing found + n.flags.excl nfDisabledOpenSym + if not warnDisabled and isSym: + result = semExpr(c, n, flags, expectedType) + else: + result = nil + if not isSym: + # set symchoice node type back to None + n.typ = newTypeS(tyNone, c) + proc semSymChoice(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode = + if n.kind == nkOpenSymChoice: + result = semOpenSym(c, n, flags, expectedType, + warnDisabled = nfDisabledOpenSym in n.flags and + genericsOpenSym notin c.features) + if result != nil: + return result = n resolveSymChoice(c, result, flags, expectedType) if isSymChoice(result) and result.len == 1: @@ -218,14 +282,16 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus = result = checkConversionBetweenObjects(d.skipTypes(abstractInst), s.skipTypes(abstractInst), pointers) elif (targetBaseTyp.kind in IntegralTypes) and (srcBaseTyp.kind in IntegralTypes): - if targetTyp.kind == tyEnum and srcBaseTyp.kind == tyEnum: + if targetTyp.kind == tyEnum and srcBaseTyp.kind == tyEnum and + not sameType(targetTyp, srcBaseTyp): message(c.config, src.info, warnSuspiciousEnumConv, "suspicious code: enum to enum conversion") # `elif` would be incorrect here if targetTyp.kind == tyBool: discard "convOk" elif targetTyp.isOrdinalType: if src.kind in nkCharLit..nkUInt64Lit and - src.getInt notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp): + src.getInt notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp) and + targetTyp.kind notin {tyUInt..tyUInt64}: result = convNotInRange elif src.kind in nkFloatLit..nkFloat64Lit and (classify(src.floatVal) in {fcNan, fcNegInf, fcInf} or @@ -554,15 +620,14 @@ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode = n[1] = makeTypeSymNode(c, lhsType, n[1].info) lhsType = n[1].typ else: - if lhsType.base.kind == tyNone or - (c.inGenericContext > 0 and lhsType.base.containsGenericType): + if c.inGenericContext > 0 and lhsType.base.containsUnresolvedType: # BUGFIX: don't evaluate this too early: ``T is void`` return result = isOpImpl(c, n, flags) proc semOpAux(c: PContext, n: PNode) = - const flags = {efDetermineType} + const flags = {efDetermineType, efAllowSymChoice} for i in 1..<n.len: var a = n[i] if a.kind == nkExprEqExpr and a.len == 2: @@ -587,7 +652,14 @@ proc overloadedCallOpr(c: PContext, n: PNode): PNode = proc changeType(c: PContext; n: PNode, newType: PType, check: bool) = case n.kind - of nkCurly, nkBracket: + of nkCurly: + for i in 0..<n.len: + if n[i].kind == nkRange: + changeType(c, n[i][0], elemType(newType), check) + changeType(c, n[i][1], elemType(newType), check) + else: + changeType(c, n[i], elemType(newType), check) + of nkBracket: for i in 0..<n.len: changeType(c, n[i], elemType(newType), check) of nkPar, nkTupleConstr: @@ -624,10 +696,16 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) = let value = n.intVal if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType): localError(c.config, n.info, "cannot convert " & $value & - " to " & typeToString(newType)) + " to " & typeNameAndDesc(newType)) of nkFloatLit..nkFloat64Lit: if check and not floatRangeCheck(n.floatVal, newType): - localError(c.config, n.info, errFloatToString % [$n.floatVal, typeToString(newType)]) + localError(c.config, n.info, errFloatToString % [$n.floatVal, typeNameAndDesc(newType)]) + of nkSym: + if check and n.sym.kind == skEnumField and not sameTypeOrNil(n.sym.typ, newType): + let value = n.sym.position + if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType): + localError(c.config, n.info, "cannot convert '" & n.sym.name.s & + "' to '" & typeNameAndDesc(newType) & "'") else: discard n.typ = newType @@ -644,29 +722,41 @@ proc arrayConstrType(c: PContext, n: PNode): PType = proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = result = newNodeI(nkBracket, n.info) - result.typ = newTypeS(tyArray, c) + # nkBracket nodes can also be produced by the VM as seq constant nodes + # in which case, we cannot produce a new array type for the node, + # as this might lose type info even when the node has array type + let constructType = n.typ.isNil var expectedElementType, expectedIndexType: PType = nil - if expectedType != nil: - let expected = expectedType.skipTypes(abstractRange-{tyDistinct}) - case expected.kind + var expectedBase: PType = nil + if constructType: + result.typ = newTypeS(tyArray, c) + rawAddSon(result.typ, nil) # index type + if expectedType != nil: + expectedBase = expectedType.skipTypes(abstractRange-{tyDistinct}) + else: + result.typ = n.typ + expectedBase = n.typ.skipTypes(abstractRange) # include tyDistinct this time + if expectedBase != nil: + case expectedBase.kind of tyArray: - expectedIndexType = expected[0] - expectedElementType = expected[1] - of tyOpenArray: - expectedElementType = expected[0] + expectedIndexType = expectedBase[0] + expectedElementType = expectedBase[1] + of tyOpenArray, tySequence: + # typed bracket expressions can also have seq type + expectedElementType = expectedBase[0] else: discard - rawAddSon(result.typ, nil) # index type var firstIndex, lastIndex: Int128 = Zero indexType = getSysType(c.graph, n.info, tyInt) lastValidIndex = lastOrd(c.config, indexType) if n.len == 0: - rawAddSon(result.typ, - if expectedElementType != nil and - typeAllowed(expectedElementType, skLet, c) == nil: - expectedElementType - else: - newTypeS(tyEmpty, c)) # needs an empty basetype! + if constructType: + rawAddSon(result.typ, + if expectedElementType != nil and + typeAllowed(expectedElementType, skLet, c) == nil: + expectedElementType + else: + newTypeS(tyEmpty, c)) # needs an empty basetype! lastIndex = toInt128(-1) else: var x = n[0] @@ -683,9 +773,13 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp x = x[1] let yy = semExprWithType(c, x, {efTypeAllowed}, expectedElementType) - var typ = yy.typ - if expectedElementType == nil: - expectedElementType = typ + var typ: PType + if constructType: + typ = yy.typ + if expectedElementType == nil: + expectedElementType = typ + else: + typ = expectedElementType result.add yy #var typ = skipTypes(result[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal}) for i in 1..<n.len: @@ -705,15 +799,20 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp let xx = semExprWithType(c, x, {efTypeAllowed}, expectedElementType) result.add xx - typ = commonType(c, typ, xx.typ) + if constructType: + typ = commonType(c, typ, xx.typ) #n[i] = semExprWithType(c, x, {}) #result.add fitNode(c, typ, n[i]) inc(lastIndex) - addSonSkipIntLit(result.typ, typ, c.idgen) + if constructType: + addSonSkipIntLit(result.typ, typ, c.idgen) for i in 0..<result.len: result[i] = fitNode(c, typ, result[i], result[i].info) - result.typ.setIndexType makeRangeType(c, toInt64(firstIndex), toInt64(lastIndex), n.info, - indexType) + if constructType: + result.typ.setIndexType( + makeRangeType(c, + toInt64(firstIndex), toInt64(lastIndex), + n.info, indexType)) proc fixAbstractType(c: PContext, n: PNode) = for i in 1..<n.len: @@ -738,7 +837,7 @@ proc isUnresolvedSym(s: PSym): bool = result = s.kind == skGenericParam if not result and s.typ != nil: result = tfInferrableStatic in s.typ.flags or - (s.kind == skParam and s.typ.isMetaType) or + (s.kind == skParam and (s.typ.isMetaType or sfTemplateParam in s.flags)) or (s.kind == skType and s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {}) @@ -812,10 +911,13 @@ proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode = proc analyseIfAddressTakenInCall(c: PContext, n: PNode, isConverter = false) = checkMinSonsLen(n, 1, c.config) + if n[0].typ == nil: + # n[0] might be erroring node in nimsuggest + return const FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl, mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap, - mAppendSeqElem, mNewSeq, mReset, mShallowCopy, mDeepCopy, mMove, + mAppendSeqElem, mNewSeq, mShallowCopy, mDeepCopy, mMove, mWasMoved} template checkIfConverterCalled(c: PContext, n: PNode) = @@ -914,7 +1016,8 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = if callee.magic notin ctfeWhitelist: return - if callee.kind notin {skProc, skFunc, skConverter, skConst} or callee.isGenericRoutine: + if callee.kind notin {skProc, skFunc, skConverter, skConst} or + callee.isGenericRoutineStrict: return if n.typ != nil and typeAllowed(n.typ, skConst, c) != nil: return @@ -971,7 +1074,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, if result != nil: if result[0].kind != nkSym: - if not (efDetermineType in flags and c.inGenericContext > 0): + if not (c.inGenericContext > 0): # see generic context check in semOverloadedCall internalError(c.config, "semOverloadedCallAnalyseEffects") return let callee = result[0].sym @@ -995,14 +1098,6 @@ proc resolveIndirectCall(c: PContext; n, nOrig: PNode; result = initCandidate(c, t) matches(c, n, nOrig, result) -proc bracketedMacro(n: PNode): PSym = - if n.len >= 1 and n[0].kind == nkSym: - result = n[0].sym - if result.kind notin {skMacro, skTemplate}: - result = nil - else: - result = nil - proc finishOperand(c: PContext, a: PNode): PNode = if a.typ.isNil: result = c.semOperand(c, a, {efDetermineType}) @@ -1021,10 +1116,14 @@ proc finishOperand(c: PContext, a: PNode): PNode = localError(c.config, a.info, err) considerGenSyms(c, result) -proc semFinishOperands(c: PContext; n: PNode) = +proc semFinishOperands(c: PContext; n: PNode; isBracketExpr = false) = # this needs to be called to ensure that after overloading resolution every - # argument has been sem'checked: - for i in 1..<n.len: + # argument has been sem'checked + + # skip the first argument for operands of `[]` since it may be an unresolved + # generic proc, which is handled in semMagic + let start = 1 + ord(isBracketExpr) + for i in start..<n.len: n[i] = finishOperand(c, n[i]) proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = @@ -1047,10 +1146,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy of skMacro: result = semMacroExpr(c, result, orig, callee, flags, expectedType) of skTemplate: result = semTemplateExpr(c, result, callee, flags, expectedType) else: - if callee.magic notin {mArrGet, mArrPut, mNBindSym}: - # calls to `[]` can be explicit generic instantiations, - # don't sem every operand now, leave it to semmagic - semFinishOperands(c, result) + semFinishOperands(c, result, isBracketExpr = callee.magic in {mArrGet, mArrPut}) activate(c, result) fixAbstractType(c, result) analyseIfAddressTakenInCall(c, result) @@ -1061,7 +1157,8 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy not (result.typ.kind == tySequence and result.elementType.kind == tyEmpty): liftTypeBoundOps(c, result.typ, n.info) #result = patchResolvedTypeBoundOp(c, result) - if c.matchedConcept == nil: + if c.matchedConcept == nil and (c.inTypeofContext == 0 or callee.magic != mNone): + # don't fold calls in concepts and typeof result = evalAtCompileTime(c, result) proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = @@ -1078,6 +1175,11 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType result.flags.incl nfExplicitCall for i in 1..<n.len: result.add n[i] return semExpr(c, result, flags, expectedType) + elif n0.typ.kind == tyFromExpr and c.inGenericContext > 0: + # don't make assumptions, entire expression needs to be tyFromExpr + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, result.copyTree) + return else: n[0] = n0 else: @@ -1085,11 +1187,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType let t = n[0].typ if t != nil and t.kind in {tyVar, tyLent}: n[0] = newDeref(n[0]) - elif n[0].kind == nkBracketExpr: - let s = bracketedMacro(n[0]) - if s != nil: - setGenericParams(c, n[0], s.ast[genericParamsPos]) - return semDirectOp(c, n, flags, expectedType) elif isSymChoice(n[0]) and nfDotField notin n.flags: # overloaded generic procs e.g. newSeq[int] can end up here return semDirectOp(c, n, flags, expectedType) @@ -1418,20 +1515,34 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = proc tryReadingGenericParam(c: PContext, n: PNode, i: PIdent, t: PType): PNode = case t.kind - of tyTypeParamsHolders: + of tyGenericInst: result = readTypeParameter(c, t, i, n.info) if result == c.graph.emptyNode: - result = n - n.typ = makeTypeFromExpr(c, n.copyTree) + if c.inGenericContext > 0: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, result.copyTree) + else: + result = nil of tyUserTypeClasses: if t.isResolvedUserTypeClass: result = readTypeParameter(c, t, i, n.info) + elif c.inGenericContext > 0: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, copyTree(result)) else: - n.typ = makeTypeFromExpr(c, copyTree(n)) - result = n - of tyGenericParam, tyAnything: - n.typ = makeTypeFromExpr(c, copyTree(n)) - result = n + result = nil + of tyGenericBody, tyCompositeTypeClass: + if c.inGenericContext > 0: + result = readTypeParameter(c, t, i, n.info) + if result != nil: + # generic parameter exists, stop here but delay until instantiation + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, copyTree(result)) + else: + result = nil + elif c.inGenericContext > 0 and t.containsUnresolvedType: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, copyTree(result)) else: result = nil @@ -1595,18 +1706,20 @@ proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode = result.add(newIdentNode(ident, n.info)) for s in n: result.add s -proc semDeref(c: PContext, n: PNode): PNode = +proc semDeref(c: PContext, n: PNode, flags: TExprFlags): PNode = checkSonsLen(n, 1, c.config) n[0] = semExprWithType(c, n[0]) let a = getConstExpr(c.module, n[0], c.idgen, c.graph) if a != nil: - if a.kind == nkNilLit: + if a.kind == nkNilLit and efInTypeof notin flags: localError(c.config, n.info, "nil dereference is not allowed") n[0] = a result = n var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink, tyOwned}) case t.kind of tyRef, tyPtr: n.typ = t.elementType + of tyMetaTypes, tyFromExpr: + n.typ = makeTypeFromExpr(c, n.copyTree) else: result = nil #GlobalError(n[0].info, errCircumNeedsPointer) @@ -1629,16 +1742,17 @@ proc maybeInstantiateGeneric(c: PContext, n: PNode, s: PSym): PNode = result = explicitGenericInstantiation(c, n, s) if result == n: n[0] = copyTree(result[0]) - else: - n[0] = result proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = ## returns nil if not a built-in subscript operator; also called for the ## checking of assignments result = nil if n.len == 1: - let x = semDeref(c, n) + let x = semDeref(c, n, flags) if x == nil: return nil + if x.typ.kind == tyFromExpr: + # depends on generic type + return x result = newNodeIT(nkDerefExpr, x.info, x.typ) result.add(x[0]) return @@ -1703,7 +1817,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = result = nil else: let s = if n[0].kind == nkSym: n[0].sym - elif n[0].kind in nkSymChoices: n[0][0].sym + elif n[0].kind in nkSymChoices + {nkOpenSym}: n[0][0].sym else: nil if s != nil: case s.kind @@ -1784,6 +1898,8 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode = else: localError(c.config, n.info, errExprHasNoAddress) result = newNodeIT(nkHiddenAddr, n.info, if n.typ.kind in {tyVar, tyLent}: n.typ else: makePtrType(c, n.typ)) + if n.typ.kind in {tyVar, tyLent}: + n.typ = n.typ.elementType result.add(n) proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = @@ -1910,7 +2026,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "[]=")) result.add(n[1]) if mode == noOverloadedSubscript: - bracketNotFoundError(c, result) + bracketNotFoundError(c, result, {}) return errorNode(c, n) else: result = semExprNoType(c, result) @@ -1957,7 +2073,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = if rhsTyp.kind in tyUserTypeClasses and rhsTyp.isResolvedUserTypeClass: rhsTyp = rhsTyp.last if lhs.sym.typ.kind == tyAnything: - rhsTyp = rhsTyp.skipIntLit(c.idgen) + rhsTyp = rhsTyp.skipTypes({tySink}).skipIntLit(c.idgen) if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual}: internalAssert c.config, c.p.resultSym != nil # Make sure the type is valid for the result variable @@ -2139,6 +2255,8 @@ proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = result = n.sym of nkOpenSymChoice, nkClosedSymChoice: result = n[0].sym + of nkOpenSym: + result = lookUpForDeclared(c, n[0], onlyCurrentScope) else: localError(c.config, n.info, "identifier expected, but got: " & renderTree(n)) result = nil @@ -2426,7 +2544,7 @@ proc instantiateCreateFlowVarCall(c: PContext; t: PType; # codegen would fail: if sfCompilerProc in result.flags: result.flags.excl {sfCompilerProc, sfExportc, sfImportc} - result.loc.r = "" + result.loc.snippet = "" proc setMs(n: PNode, s: PSym): PNode = result = n @@ -2591,13 +2709,16 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = var typ = commonTypeBegin if n.len in 1..2 and n[0].kind == nkElifBranch and ( n.len == 1 or n[1].kind == nkElse): - let exprNode = n[0][0] + var exprNode = n[0][0] + if exprNode.kind == nkOpenSym: + exprNode = exprNode[0] if exprNode.kind == nkIdent: whenNimvm = lookUp(c, exprNode).magic == mNimvm elif exprNode.kind == nkSym: whenNimvm = exprNode.sym.magic == mNimvm if whenNimvm: n.flags.incl nfLL + var cannotResolve = false for i in 0..<n.len: var it = n[i] case it.kind @@ -2608,6 +2729,19 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = it[1] = semExpr(c, it[1], flags) typ = commonType(c, typ, it[1].typ) result = n # when nimvm is not elimited until codegen + elif c.inGenericContext > 0: + let e = semExprWithType(c, it[0]) + if e.typ.kind == tyFromExpr: + it[0] = makeStaticExpr(c, e) + cannotResolve = true + else: + it[0] = forceBool(c, e) + let val = getConstExpr(c.module, it[0], c.idgen, c.graph) + if val == nil or val.kind != nkIntLit: + cannotResolve = true + elif not cannotResolve and val.intVal != 0 and result == nil: + setResult(it[1]) + return # we're not in nimvm and we already have a result else: let e = forceBool(c, semConstExpr(c, it[0])) if e.kind != nkIntLit: @@ -2619,13 +2753,21 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = return # we're not in nimvm and we already have a result of nkElse, nkElseExpr: checkSonsLen(it, 1, c.config) - if result == nil or whenNimvm: + if cannotResolve: + discard + elif result == nil or whenNimvm: if semCheck: it[0] = semExpr(c, it[0], flags) typ = commonType(c, typ, it[0].typ) + if typ != nil and typ.kind != tyUntyped: + it[0] = fitNode(c, typ, it[0], it[0].info) if result == nil: result = it[0] else: illFormedAst(n, c.config) + if cannotResolve: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, result.copyTree) + return if result == nil: result = newNodeI(nkEmpty, n.info) if whenNimvm: @@ -2779,7 +2921,7 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType n[i][1].typ = errorType(c) var f = newSymS(skField, n[i][0], c) - f.typ = skipIntLit(n[i][1].typ, c.idgen) + f.typ = skipIntLit(n[i][1].typ.skipTypes({tySink}), c.idgen) f.position = i rawAddSon(typ, f.typ) typ.n.add newSymNode(f) @@ -2805,7 +2947,7 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedT # `const foo = [(1, {}), (2, {false})]`, # `const foo = if true: (0, nil) else: (1, new(int))` n[i] = fitNode(c, expectedElemType, n[i], n[i].info) - addSonSkipIntLit(typ, n[i].typ, c.idgen) + addSonSkipIntLit(typ, n[i].typ.skipTypes({tySink}), c.idgen) result.typ = typ include semobjconstr @@ -2910,19 +3052,42 @@ proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp else: result = tupexp -proc shouldBeBracketExpr(n: PNode): bool = - result = false +proc isExplicitGenericCall(c: PContext, n: PNode): bool = + ## checks if a call node `n` is a routine call with explicit generic params + ## + ## the callee node needs to be either an nkBracketExpr or a call to a + ## symchoice of `[]` in which case it will be transformed into nkBracketExpr + ## + ## the LHS of the bracket expr has to either be a symchoice or resolve to + ## a routine symbol + template checkCallee(n: PNode) = + # check subscript LHS, `n` must be mutable + if isSymChoice(n): + result = true + else: + let s = qualifiedLookUp(c, n, {}) + if s != nil and s.kind in routineKinds: + result = true + n = semSymGenericInstantiation(c, n, s) assert n.kind in nkCallKinds + result = false let a = n[0] - if a.kind in nkCallKinds: + case a.kind + of nkBracketExpr: + checkCallee(a[0]) + of nkCallKinds: let b = a[0] if b.kind in nkSymChoices: - for i in 0..<b.len: - if b[i].kind == nkSym and b[i].sym.magic == mArrGet: - let be = newNodeI(nkBracketExpr, n.info) + let name = b.getPIdent + if name != nil and name.s == "[]": + checkCallee(a[1]) + if result: + # transform callee into normal bracket expr, only on success + let be = newNodeI(nkBracketExpr, a.info) for i in 1..<a.len: be.add(a[i]) n[0] = be - return true + else: + result = false proc asBracketExpr(c: PContext; n: PNode): PNode = proc isGeneric(c: PContext; n: PNode): bool = @@ -2943,6 +3108,18 @@ proc asBracketExpr(c: PContext; n: PNode): PNode = return result return nil +proc isOpenArraySym(x: PNode): bool = + var x = x + while true: + case x.kind + of {nkAddr, nkHiddenAddr}: + x = x[0] + of {nkHiddenStdConv, nkHiddenDeref}: + x = x[1] + else: + break + result = x.kind == nkSym + proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) = # This takes care of complicated signatures such as: # proc foo(a: int, b = a) @@ -2960,10 +3137,17 @@ proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) # duty is activated by returning a non-nil value. The caller is responsible # for replacing the input to the function with the returned non-nil value. # (which is the hoisted symbol) - if defExpr.kind == nkSym and defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym: + if defExpr.kind == nkSym and defExpr.sym.kind == skParam and + (defExpr.sym.owner == call[0].sym or + # symbol was resolved before proc was instantiated: + (sfFromGeneric in call[0].sym.flags and + defExpr.sym.owner == call[0].sym.instantiatedFrom)): let paramPos = defExpr.sym.position + 1 - if call[paramPos].skipAddr.kind != nkSym: + if call[paramPos].skipAddr.kind != nkSym and not ( + skipTypes(call[paramPos].typ, abstractVar).kind in {tyOpenArray, tyVarargs} and + isOpenArraySym(call[paramPos]) + ): let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), c.idgen, c.p.owner, letSection.info, c.p.owner.options) hoistedVarSym.typ = call[paramPos].typ @@ -2989,7 +3173,7 @@ proc getNilType(c: PContext): PType = result.align = c.config.target.ptrSize.int16 c.nilTypeCache = result -proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode = +proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym; flags: TExprFlags): PNode = var o: TOverloadIter = default(TOverloadIter) var i = 0 var a = initOverloadIter(o, c, n) @@ -3002,7 +3186,7 @@ proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode = if i <= 1: if sfGenSym notin s.flags: result = newSymNode(s, info) - markUsed(c, info, s) + markUsed(c, info, s, efInCall notin flags) onUse(info, s) else: result = n @@ -3027,30 +3211,30 @@ proc resolveIdentToSym(c: PContext, n: PNode, resultNode: var PNode, flags: TExprFlags, expectedType: PType): PSym = # result is nil on error or if a node that can't produce a sym is resolved let ident = considerQuotedIdent(c, n) - if expectedType != nil and ( - let expected = expectedType.skipTypes(abstractRange-{tyDistinct}); - expected.kind == tyEnum): - let nameId = ident.id - for f in expected.n: - if f.kind == nkSym and f.sym.name.id == nameId: - return f.sym var filter = {low(TSymKind)..high(TSymKind)} - if efNoEvaluateGeneric in flags: + if efNoEvaluateGeneric in flags or expectedType != nil: # `a[...]` where `a` is a module or package is not possible filter.excl {skModule, skPackage} - let candidates = lookUpCandidates(c, ident, filter) + let includePureEnum = expectedType != nil and + expectedType.skipTypes(abstractRange-{tyDistinct}).kind == tyEnum + let candidates = lookUpCandidates(c, ident, filter, + includePureEnum = includePureEnum) if candidates.len == 0: result = errorUndeclaredIdentifierHint(c, ident, n.info) elif candidates.len == 1 or {efNoEvaluateGeneric, efInCall} * flags != {}: # unambiguous, or we don't care about ambiguity result = candidates[0] else: - # ambiguous symbols have 1 last chance as a symchoice, - # but type symbols cannot participate in symchoices + # ambiguous symbols have 1 last chance as a symchoice var choice = newNodeIT(nkClosedSymChoice, n.info, newTypeS(tyNone, c)) - for c in candidates: - if c.kind notin {skType, skModule, skPackage}: - choice.add newSymNode(c, n.info) + for cand in candidates: + case cand.kind + of skModule, skPackage: + discard + of skType: + choice.add newSymNodeTypeDesc(cand, c.idgen, n.info) + else: + choice.add newSymNode(cand, n.info) if choice.len == 0: # we know candidates.len > 1, we just couldn't put any in a symchoice errorUseQualifier(c, n.info, candidates) @@ -3126,46 +3310,35 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType if optOwnedRefs in c.config.globalOptions: result.typ = makeVarType(c, result.typ, tyOwned) of skEnumField: - result = enumFieldSymChoice(c, n, s) + result = enumFieldSymChoice(c, n, s, flags) else: result = semSym(c, n, s, flags) if isSymChoice(result): result = semSymChoice(c, result, flags, expectedType) of nkClosedSymChoice, nkOpenSymChoice: - result = semSymChoice(c, result, flags, expectedType) + result = semSymChoice(c, n, flags, expectedType) of nkSym: let s = n.sym - if nfOpenSym in n.flags: - let id = newIdentNode(s.name, n.info) - c.isAmbiguous = false - let s2 = qualifiedLookUp(c, id, {}) - if s2 != nil and s2 != s and not c.isAmbiguous: - # only consider symbols defined under current proc: - var o = s2.owner - while o != nil: - if o == c.p.owner: - if genericsOpenSym in c.features: - result = semExpr(c, id, flags, expectedType) - return - else: - message(c.config, n.info, warnGenericsIgnoredInjection, - "a new symbol '" & s.name.s & "' has been injected during " & - "instantiation of " & c.p.owner.name.s & ", " & - "however " & getSymRepr(c.config, s) & " captured at " & - "the proc declaration will be used instead; " & - "either enable --experimental:genericsOpenSym to use the " & - "injected symbol or `bind` this captured symbol explicitly") - break - o = o.owner + if nfDisabledOpenSym in n.flags: + let override = genericsOpenSym in c.features + let res = semOpenSym(c, n, flags, expectedType, + warnDisabled = not override) + if res != nil: + assert override + return res # because of the changed symbol binding, this does not mean that we # don't have to check the symbol for semantics here again! result = semSym(c, n, s, flags) + of nkOpenSym: + assert n.len == 1 + let inner = n[0] + result = semOpenSym(c, inner, flags, expectedType) of nkEmpty, nkNone, nkCommentStmt, nkType: discard of nkNilLit: if result.typ == nil: result.typ = getNilType(c) - if expectedType != nil: + if expectedType != nil and expectedType.kind notin {tyUntyped, tyTyped}: var m = newCandidate(c, result.typ) if typeRel(m, expectedType, result.typ) >= isSubtype: result.typ = expectedType @@ -3247,12 +3420,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType of skType: # XXX think about this more (``set`` procs) let ambig = c.isAmbiguous - if not (n[0].kind in {nkClosedSymChoice, nkOpenSymChoice, nkIdent} and ambig) and n.len == 2: + if not (n[0].kind in nkSymChoices + {nkIdent, nkDotExpr} and ambig) and n.len == 2: result = semConv(c, n, flags, expectedType) - elif ambig and n.len == 1: - errorUseQualifier(c, n.info, s) elif n.len == 1: - result = semObjConstr(c, n, flags, expectedType) + if ambig: + errorUseQualifier(c, n.info, s) + else: + result = semObjConstr(c, n, flags, expectedType) elif s.magic == mNone: result = semDirectOp(c, n, flags, expectedType) else: result = semMagic(c, n, s, flags, expectedType) of skProc, skFunc, skMethod, skConverter, skIterator: @@ -3261,11 +3435,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType else: #liMessage(n.info, warnUser, renderTree(n)); result = semIndirectOp(c, n, flags, expectedType) - elif (n[0].kind == nkBracketExpr or shouldBeBracketExpr(n)) and - isSymChoice(n[0][0]): - # indirectOp can deal with explicit instantiations; the fixes - # the 'newSeq[T](x)' bug - setGenericParams(c, n[0], nil) + elif isExplicitGenericCall(c, n): # this modifies `n` if true result = semDirectOp(c, n, flags, expectedType) elif nfDotField in n.flags: result = semDirectOp(c, n, flags, expectedType) @@ -3330,7 +3500,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType result = semArrayConstr(c, n, flags, expectedType) of nkObjConstr: result = semObjConstr(c, n, flags, expectedType) of nkLambdaKinds: result = semProcAux(c, n, skProc, lambdaPragmas, flags) - of nkDerefExpr: result = semDeref(c, n) + of nkDerefExpr: result = semDeref(c, n, flags) of nkAddr: result = n checkSonsLen(n, 1, c.config) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index f7da4ea75..80144ccc0 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -420,14 +420,17 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P result = newIntNodeT(toInt128(getFloat(a)), n, idgen, g) of tyChar, tyUInt..tyUInt64, tyInt..tyInt64: var val = a.getOrdValue - if check: rangeCheck(n, val, g) - result = newIntNodeT(val, n, idgen, g) if dstTyp.kind in {tyUInt..tyUInt64}: + result = newIntNodeT(maskBytes(val, int getSize(g.config, dstTyp)), n, idgen, g) result.transitionIntKind(nkUIntLit) + else: + if check: rangeCheck(n, val, g) + result = newIntNodeT(val, n, idgen, g) else: result = a result.typ = n.typ - if check and result.kind in {nkCharLit..nkUInt64Lit}: + if check and result.kind in {nkCharLit..nkUInt64Lit} and + dstTyp.kind notin {tyUInt..tyUInt64}: rangeCheck(n, getInt(result), g) of tyFloat..tyFloat64: case srcTyp.kind @@ -701,10 +704,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode except DivByZeroDefect: localError(g.config, n.info, "division by zero") of nkAddr: - var a = getConstExpr(m, n[0], idgen, g) - if a != nil: - result = n - n[0] = a + result = nil # don't fold paths containing nkAddr of nkBracket, nkCurly: result = copyNode(n) for son in n.items: @@ -750,6 +750,8 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode if leValueConv(n[1], a) and leValueConv(a, n[2]): result = a # a <= x and x <= b result.typ = n.typ + elif n.typ.kind in {tyUInt..tyUInt64}: + discard "don't check uints" else: localError(g.config, n.info, "conversion from $1 to $2 is invalid" % @@ -771,7 +773,8 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode of nkCast: var a = getConstExpr(m, n[1], idgen, g) if a == nil: return - if n.typ != nil and n.typ.kind in NilableTypes: + if n.typ != nil and n.typ.kind in NilableTypes and + not (n.typ.kind == tyProc and a.typ.kind == tyProc): # we allow compile-time 'cast' for pointer types: result = a result.typ = n.typ diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 638b4311b..2639aba6c 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -61,6 +61,7 @@ template canOpenSym(s): bool = proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, ctx: var GenericCtx; flags: TSemGenericFlags, + isAmbiguous: bool, fromDotExpr=false): PNode = result = nil semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody) @@ -72,9 +73,15 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, result.transitionSonsKind(nkClosedSymChoice) else: result = symChoice(c, n, s, scOpen) - if result.kind == nkSym and canOpenSym(result.sym): - result.flags.incl nfOpenSym - result.typ = nil + if canOpenSym(s): + if openSym in c.features: + if result.kind == nkSym: + result = newOpenSym(result) + else: + result.typ = nil + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil case s.kind of skUnknown: # Introduced in this pass! Leave it as an identifier. @@ -98,13 +105,28 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, if s.typ != nil and s.typ.kind == tyStatic: if s.typ.n != nil: result = s.typ.n + elif c.inGenericContext > 0 and withinConcept notin flags: + # don't leave generic param as identifier node in generic type, + # sigmatch will try to instantiate generic type AST without all params + # fine to give a symbol node a generic type here since + # we are in a generic context and `prepareNode` will be called + result = newSymNodeTypeDesc(s, c.idgen, n.info) + if canOpenSym(result.sym): + if openSym in c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil else: result = n else: result = newSymNodeTypeDesc(s, c.idgen, n.info) if canOpenSym(result.sym): - result.flags.incl nfOpenSym - result.typ = nil + if openSym in c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil onUse(n.info, s) of skParam: result = n @@ -112,18 +134,41 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, of skType: if (s.typ != nil) and (s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}): + if isAmbiguous: + # ambiguous types should be symchoices since lookup behaves + # differently for them in regular expressions + maybeDotChoice(c, n, s, fromDotExpr) + return result = newSymNodeTypeDesc(s, c.idgen, n.info) if canOpenSym(result.sym): - result.flags.incl nfOpenSym - result.typ = nil + if openSym in c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil + elif c.inGenericContext > 0 and withinConcept notin flags: + # don't leave generic param as identifier node in generic type, + # sigmatch will try to instantiate generic type AST without all params + # fine to give a symbol node a generic type here since + # we are in a generic context and `prepareNode` will be called + result = newSymNodeTypeDesc(s, c.idgen, n.info) + if canOpenSym(result.sym): + if openSym in c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil else: result = n onUse(n.info, s) else: result = newSymNode(s, n.info) if canOpenSym(result.sym): - result.flags.incl nfOpenSym - result.typ = nil + if openSym in c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil onUse(n.info, s) proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, @@ -145,7 +190,7 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, elif s.isMixedIn: result = symChoice(c, n, s, scForceOpen) else: - result = semGenericStmtSymbol(c, n, s, ctx, flags) + result = semGenericStmtSymbol(c, n, s, ctx, flags, amb) # else: leave as nkIdent proc newDot(n, b: PNode): PNode = @@ -161,10 +206,11 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, let luf = if withinMixin notin flags: {checkUndeclared, checkModule} else: {checkModule} + c.isAmbiguous = false var s = qualifiedLookUp(c, n, luf) if s != nil: isMacro = s.kind in {skTemplate, skMacro} - result = semGenericStmtSymbol(c, n, s, ctx, flags) + result = semGenericStmtSymbol(c, n, s, ctx, flags, c.isAmbiguous) else: n[0] = semGenericStmt(c, n[0], flags, ctx) result = n @@ -178,24 +224,21 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, isMacro = s.kind in {skTemplate, skMacro} if withinBind in flags or s.id in ctx.toBind: if s.kind == skType: # don't put types in sym choice - result = newDot(result, semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true)) + var ambig = false + if candidates.len > 1: + let s2 = searchInScopes(c, ident, ambig) + result = newDot(result, semGenericStmtSymbol(c, n, s, ctx, flags, + isAmbiguous = ambig, fromDotExpr = true)) else: result = newDot(result, symChoice(c, n, s, scClosed)) elif s.isMixedIn: result = newDot(result, symChoice(c, n, s, scForceOpen)) else: + var ambig = false if s.kind == skType and candidates.len > 1: - var ambig = false - let s2 = searchInScopes(c, ident, ambig) - if ambig: - # this is a type conversion like a.T where T is ambiguous with - # other types or routines - # in regular code, this never considers a type conversion and - # skips to routine overloading - # so symchoices are used which behave similarly with type symbols - result = newDot(result, symChoice(c, n, s, scForceOpen)) - return - let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true) + discard searchInScopes(c, ident, ambig) + let syms = semGenericStmtSymbol(c, n, s, ctx, flags, + isAmbiguous = ambig, fromDotExpr = true) result = newDot(result, syms) proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) = @@ -262,7 +305,9 @@ proc semGenericStmt(c: PContext, n: PNode, # check if it is an expression macro: checkMinSonsLen(n, 1, c.config) let fn = n[0] + c.isAmbiguous = false var s = qualifiedLookUp(c, fn, {}) + let ambig = c.isAmbiguous if s == nil and {withinMixin, withinConcept}*flags == {} and fn.kind in {nkIdent, nkAccQuoted} and @@ -315,7 +360,12 @@ proc semGenericStmt(c: PContext, n: PNode, of skType: # bad hack for generics: if (s.typ != nil) and (s.typ.kind != tyGenericParam): - result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info) + if ambig: + # ambiguous types should be symchoices since lookup behaves + # differently for them in regular expressions + result[0] = sc + else: + result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info) onUse(fn.info, s) first = 1 else: @@ -563,7 +613,8 @@ proc semGenericStmt(c: PContext, n: PNode, else: body = getBody(c.graph, s) else: body = n[bodyPos] - n[bodyPos] = semGenericStmtScope(c, body, flags, ctx) + let bodyFlags = if n.kind == nkTemplateDef: flags + {withinMixin} else: flags + n[bodyPos] = semGenericStmtScope(c, body, bodyFlags, ctx) closeScope(c) of nkPragma, nkPragmaExpr: discard of nkExprColonExpr, nkExprEqExpr: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index e9b46c382..1bc6d31a2 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -53,11 +53,14 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PS if q.typ.kind != tyCompositeTypeClass: localError(c.config, a.info, errCannotInstantiateX % s.name.s) t = errorType(c) - elif t.kind in {tyGenericParam, tyConcept}: + elif t.kind in {tyGenericParam, tyConcept, tyFromExpr}: localError(c.config, a.info, errCannotInstantiateX % q.name.s) t = errorType(c) - elif isUnresolvedStatic(t) and c.inGenericContext == 0 and - c.matchedConcept == nil: + elif isUnresolvedStatic(t) and (q.typ.kind == tyStatic or + (q.typ.kind == tyGenericParam and + q.typ.genericParamHasConstraints and + q.typ.genericConstraint.kind == tyStatic)) and + c.inGenericContext == 0 and c.matchedConcept == nil: # generic/concept type bodies will try to instantiate static values but # won't actually use them localError(c.config, a.info, errCannotInstantiateX % q.name.s) @@ -250,9 +253,15 @@ proc instantiateProcType(c: PContext, pt: TypeMapping, var typeToFit = resulti let needsStaticSkipping = resulti.kind == tyFromExpr + let needsTypeDescSkipping = resulti.kind == tyTypeDesc and tfUnresolved in resulti.flags + if resulti.kind == tyFromExpr: + resulti.flags.incl tfNonConstExpr result[i] = replaceTypeVarsT(cl, resulti) if needsStaticSkipping: result[i] = result[i].skipTypes({tyStatic}) + if needsTypeDescSkipping: + result[i] = result[i].skipTypes({tyTypeDesc}) + typeToFit = result[i] # ...otherwise, we use the instantiated type in `fitNode` if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and @@ -270,9 +279,10 @@ proc instantiateProcType(c: PContext, pt: TypeMapping, # call head symbol, because this leads to infinite recursion. if oldParam.ast != nil: var def = oldParam.ast.copyTree - if def.kind in nkCallKinds: - for i in 1..<def.len: - def[i] = replaceTypeVarsN(cl, def[i], 1) + if def.typ.kind == tyFromExpr: + def.typ.flags.incl tfNonConstExpr + if not isIntLit(def.typ): + def = prepareNode(cl, def) # allow symchoice since node will be fit later # although expectedType should cover it @@ -289,6 +299,8 @@ proc instantiateProcType(c: PContext, pt: TypeMapping, # the user calls an explicit instantiation of the proc (this is # the only way the default value might be inserted). param.ast = errorNode(c, def) + # we know the node is empty, we need the actual type for error message + param.ast.typ = def.typ else: param.ast = fitNodePostMatch(c, typeToFit, converted) param.typ = result[i] @@ -312,6 +324,22 @@ proc instantiateProcType(c: PContext, pt: TypeMapping, prc.typ = result popInfoContext(c.config) +proc instantiateOnlyProcType(c: PContext, pt: TypeMapping, prc: PSym, info: TLineInfo): PType = + # instantiates only the type of a given proc symbol + # used by sigmatch for explicit generics + # wouldn't be needed if sigmatch could handle complex cases, + # examples are in texplicitgenerics + # might be buggy, see rest of generateInstance if problems occur + let fakeSym = copySym(prc, c.idgen) + incl(fakeSym.flags, sfFromGeneric) + fakeSym.instantiatedFrom = prc + openScope(c) + for s in instantiateGenericParamList(c, prc.ast[genericParamsPos], pt): + addDecl(c, s) + instantiateProcType(c, pt, fakeSym, info) + closeScope(c) + result = fakeSym.typ + proc fillMixinScope(c: PContext) = var p = c.p while p != nil: @@ -342,7 +370,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping, # generates an instantiated proc if c.instCounter > 50: globalError(c.config, info, "generic instantiation too nested") - inc(c.instCounter) + inc c.instCounter + defer: dec c.instCounter # careful! we copy the whole AST including the possibly nil body! var n = copyTree(fn.ast) # NOTE: for access of private fields within generics from a different module @@ -396,6 +425,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping, for _, param in paramTypes(result.typ): entry.concreteTypes[i] = param inc i + #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " ", entry.concreteTypes.len if tfTriggersCompileTime in result.typ.flags: incl(result.flags, sfCompileTime) n[genericParamsPos] = c.graph.emptyNode @@ -424,7 +454,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping, if result.magic notin {mSlice, mTypeOf}: # 'toOpenArray' is special and it is allowed to return 'openArray': paramsTypeCheck(c, result.typ) + #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- NEW PROC!", " ", entry.concreteTypes.len else: + #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- CACHED! ", typeToString(oldPrc.typ), " ", entry.concreteTypes.len result = oldPrc popProcCon(c) popInfoContext(c.config) @@ -433,7 +465,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping, popOwner(c) c.currentScope = oldScope discard c.friendModules.pop() - dec(c.instCounter) c.matchedConcept = oldMatchedConcept if result.kind == skMethod: finishMethod(c, result) diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim index 4aab216c7..727f36470 100644 --- a/compiler/semmacrosanity.nim +++ b/compiler/semmacrosanity.nim @@ -51,6 +51,7 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) = of nkObjConstr: let x = t.skipTypes(abstractPtrs) n.typ = t + n[0].typ = t for i in 1..<n.len: var j = i-1 let field = x.ithField(j) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 8db62a9c8..a12e933e7 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -49,8 +49,12 @@ proc semTypeOf(c: PContext; n: PNode): PNode = else: m = mode.intVal result = newNodeI(nkTypeOfExpr, n.info) + inc c.inTypeofContext + defer: dec c.inTypeofContext # compiles can raise an exception let typExpr = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {}) result.add typExpr + if typExpr.typ.kind == tyFromExpr: + typExpr.typ.flags.incl tfNonConstExpr result.typ = makeTypeDesc(c, typExpr.typ) type @@ -66,7 +70,16 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode = if result.isNil: let x = copyTree(n) x[0] = newIdentNode(getIdent(c.cache, "[]"), n.info) - bracketNotFoundError(c, x) + if c.inGenericContext > 0: + for i in 0..<n.len: + let a = n[i] + if a.typ != nil and a.typ.kind in {tyGenericParam, tyFromExpr}: + # expression is compiled early in a generic body + result = semGenericStmt(c, x) + result.typ = makeTypeFromExpr(c, copyTree(result)) + result.typ.flags.incl tfNonConstExpr + return + bracketNotFoundError(c, x, flags) #localError(c.config, n.info, "could not resolve: " & $n) result = errorNode(c, n) @@ -224,8 +237,9 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym) of "rangeBase": # return the base type of a range type var arg = operand.skipTypes({tyGenericInst}) - assert arg.kind == tyRange - result = getTypeDescNode(c, arg.base, operand.owner, traitCall.info) + if arg.kind == tyRange: + arg = arg.base + result = getTypeDescNode(c, arg, operand.owner, traitCall.info) of "isCyclic": var operand = operand.skipTypes({tyGenericInst}) let isCyclic = canFormAcycle(c.graph, operand) @@ -237,7 +251,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym) proc semTypeTraits(c: PContext, n: PNode): PNode = checkMinSonsLen(n, 2, c.config) let t = n[1].typ - internalAssert c.config, t != nil and t.kind == tyTypeDesc + internalAssert c.config, t != nil and t.skipTypes({tyAlias}).kind == tyTypeDesc if t.len > 0: # This is either a type known to sem or a typedesc # param to a regular proc (again, known at instantiation) diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 96b2d702d..048053115 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -387,10 +387,13 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, if e != nil: result.status = initFull elif field.ast != nil: - result.status = initUnknown - result.defaults.add newTree(nkExprColonExpr, n, field.ast) + if efIgnoreDefaults notin flags: + result.status = initUnknown + result.defaults.add newTree(nkExprColonExpr, n, field.ast) + else: + result.status = initNone else: - if efWantNoDefaults notin flags: # cannot compute defaults at the typeRightPass + if {efWantNoDefaults, efIgnoreDefaults} * flags == {}: # cannot compute defaults at the typeRightPass let defaultExpr = defaultNodeField(c, n, constrCtx.checkDefault) if defaultExpr != nil: result.status = initUnknown @@ -443,7 +446,7 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) = assert objType != nil if objType.kind == tyObject: var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info)) - let initResult = semConstructTypeAux(c, constrCtx, {efWantNoDefaults}) + let initResult = semConstructTypeAux(c, constrCtx, {efIgnoreDefaults}) if constrCtx.missingFields.len > 0: localError(c.config, info, "The $1 type doesn't have a default value. The following fields must be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)]) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index f611ee8fe..0a160897f 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -11,7 +11,7 @@ import ast, astalgo, msgs, renderer, magicsys, types, idents, trees, wordrecg, options, guards, lineinfos, semfold, semdata, modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling, - semstrictfuncs, suggestsymdb + semstrictfuncs, suggestsymdb, pushpoppragmas import std/[tables, intsets, strutils, sequtils] @@ -85,6 +85,7 @@ type isInnerProc: bool inEnforcedNoSideEffects: bool currOptions: TOptions + optionsStack: seq[(TOptions, TNoteKinds)] config: ConfigRef graph: ModuleGraph c: PContext @@ -615,9 +616,16 @@ proc trackPragmaStmt(tracked: PEffects, n: PNode) = for i in 0..<n.len: var it = n[i] let pragma = whichPragma(it) - if pragma == wEffects: + case pragma + of wEffects: # list the computed effects up to here: listEffects(tracked) + of wPush: + processPushBackendOption(tracked.c.config, tracked.optionsStack, tracked.currOptions, n, i+1) + of wPop: + processPopBackendOption(tracked.c.config, tracked.optionsStack, tracked.currOptions) + else: + discard template notGcSafe(t): untyped = {tfGcSafe, tfNoSideEffect} * t.flags == {} @@ -1202,7 +1210,7 @@ proc track(tracked: PEffects, n: PNode) = if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags: tracked.owner.flags.incl sfInjectDestructors # bug #15038: ensure consistency - if not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ): n.typ = n.sym.typ + if n.typ == nil or (not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ)): n.typ = n.sym.typ of nkHiddenAddr, nkAddr: if n[0].kind == nkSym and isLocalSym(tracked, n[0].sym) and n.typ.kind notin {tyVar, tyLent}: @@ -1594,7 +1602,7 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; c: PContext): TEffects result = TEffects(exc: effects[exceptionEffects], tags: effects[tagEffects], forbids: effects[forbiddenEffects], owner: s, ownerModule: s.getModule, init: @[], locked: @[], graph: g, config: g.config, c: c, - currentBlock: 1 + currentBlock: 1, optionsStack: @[(g.config.options, g.config.notes)] ) result.guards.s = @[] result.guards.g = g diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 7ccc29d7e..f5f8fea0c 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -132,17 +132,141 @@ proc semExprBranchScope(c: PContext, n: PNode; expectedType: PType = nil): PNode closeScope(c) const - skipForDiscardable = {nkIfStmt, nkIfExpr, nkCaseStmt, nkOfBranch, - nkElse, nkStmtListExpr, nkTryStmt, nkFinally, nkExceptBranch, + skipForDiscardable = {nkStmtList, nkStmtListExpr, + nkOfBranch, nkElse, nkFinally, nkExceptBranch, nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr, - nkHiddenStdConv, nkHiddenDeref} + nkHiddenStdConv, nkHiddenSubConv, nkHiddenDeref} proc implicitlyDiscardable(n: PNode): bool = - var n = n - while n.kind in skipForDiscardable: n = n.lastSon - result = n.kind in nkLastBlockStmts or - (isCallExpr(n) and n[0].kind == nkSym and - sfDiscardable in n[0].sym.flags) + # same traversal as endsInNoReturn + template checkBranch(branch) = + if not implicitlyDiscardable(branch): + return false + + var it = n + # skip these beforehand, no special handling needed + while it.kind in skipForDiscardable and it.len > 0: + it = it.lastSon + + case it.kind + of nkIfExpr, nkIfStmt: + for branch in it: + checkBranch: + if branch.len == 2: + branch[1] + elif branch.len == 1: + branch[0] + else: + raiseAssert "Malformed `if` statement during implicitlyDiscardable" + # all branches are discardable + result = true + of nkCaseStmt: + for i in 1 ..< it.len: + let branch = it[i] + checkBranch: + case branch.kind + of nkOfBranch: + branch[^1] + of nkElifBranch: + branch[1] + of nkElse: + branch[0] + else: + raiseAssert "Malformed `case` statement in implicitlyDiscardable" + # all branches are discardable + result = true + of nkTryStmt: + checkBranch(it[0]) + for i in 1 ..< it.len: + let branch = it[i] + if branch.kind != nkFinally: + checkBranch(branch[^1]) + # all branches are discardable + result = true + of nkCallKinds: + result = it[0].kind == nkSym and {sfDiscardable, sfNoReturn} * it[0].sym.flags != {} + of nkLastBlockStmts: + result = true + else: + result = false + +proc endsInNoReturn(n: PNode, returningNode: var PNode; discardableCheck = false): 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, returningNode, discardableCheck): + # proved a branch returns + return false + + var it = n + # skip these beforehand, no special handling needed + let skips = if discardableCheck: skipForDiscardable else: skipForDiscardable-{nkBlockExpr, nkBlockStmt} + while it.kind in skips and it.len > 0: + it = it.lastSon + + case it.kind + of nkIfExpr, 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]) + var lastIndex = it.len - 1 + if it[lastIndex].kind == nkFinally: + # if finally is noreturn, then the entire statement is noreturn + if endsInNoReturn(it[lastIndex][^1], returningNode, discardableCheck): + return true + dec lastIndex + for i in 1 .. lastIndex: + let branch = it[i] + checkBranch(branch[^1]) + # none of the branches returned + result = true + of nkLastBlockStmts: + result = true + of nkCallKinds: + result = it[0].kind == nkSym and sfNoReturn in it[0].sym.flags + if not result: + returningNode = it + else: + result = false + returningNode = it + +proc endsInNoReturn(n: PNode): bool = + var dummy: PNode = nil + result = endsInNoReturn(n, dummy) proc fixNilType(c: PContext; n: PNode) = if isAtom(n): @@ -160,18 +284,15 @@ proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) = if implicitlyDiscardable(result): var n = newNodeI(nkDiscardStmt, result.info, 1) n[0] = result + # notes that it doesn't transform nodes into discard statements elif result.typ.kind != tyError and c.config.cmd != cmdInteractive: if result.typ.kind == tyNone: localError(c.config, result.info, "expression has no type: " & renderTree(result, {renderNoComments})) else: - var n = result - while n.kind in skipForDiscardable: - if n.kind == nkTryStmt: n = n[0] - else: n = n.lastSon - # Ignore noreturn procs since they don't have a type - if n.endsInNoReturn: + var n = result + if result.endsInNoReturn(n, discardableCheck = true): return var s = "expression '" & $n & "' is of type '" & @@ -361,7 +482,7 @@ proc identWithin(n: PNode, s: PIdent): bool = proc semIdentDef(c: PContext, n: PNode, kind: TSymKind, reportToNimsuggest = true): PSym = if isTopLevel(c): - result = semIdentWithPragma(c, kind, n, {sfExported}) + result = semIdentWithPragma(c, kind, n, {sfExported}, fromTopLevel = true) incl(result.flags, sfGlobal) #if kind in {skVar, skLet}: # echo "global variable here ", n.info, " ", result.name.s @@ -501,15 +622,15 @@ proc setVarType(c: PContext; v: PSym, typ: PType) = proc isPossibleMacroPragma(c: PContext, it: PNode, key: PNode): bool = # make sure it's not a normal pragma, and calls an identifier # considerQuotedIdent below will fail on non-identifiers - result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds + result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds+{nkDotExpr} if result: # make sure it's not a user pragma - let ident = considerQuotedIdent(c, key) - result = strTableGet(c.userPragmas, ident) == nil + if key.kind != nkDotExpr: + let ident = considerQuotedIdent(c, key) + result = strTableGet(c.userPragmas, ident) == nil if result: # make sure it's not a custom pragma - var amb = false - let sym = searchInScopes(c, ident, amb) + let sym = qualifiedLookUp(c, key, {}) result = sym == nil or sfCustomPragma notin sym.flags proc copyExcept(n: PNode, i: int): PNode = @@ -708,6 +829,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var typFlags: TTypeAllowedFlags = {} var def: PNode = c.graph.emptyNode + if typ != nil and typ.kind == tyRange and + c.graph.config.isDefined("nimPreviewRangeDefault") and + a[^1].kind == nkEmpty: + a[^1] = firstRange(c.config, typ) + if a[^1].kind != nkEmpty: def = semExprWithType(c, a[^1], {efTypeAllowed}, typ) @@ -859,6 +985,7 @@ proc semConst(c: PContext, n: PNode): PNode = var typFlags: TTypeAllowedFlags = {} # don't evaluate here since the type compatibility check below may add a converter + openScope(c) var def = semExprWithType(c, a[^1], {efTypeAllowed}, typ) if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}: @@ -885,6 +1012,7 @@ proc semConst(c: PContext, n: PNode): PNode = if c.matchedConcept != nil: typFlags.incl taConcept typeAllowedCheck(c, a.info, typ, skConst, typFlags) + closeScope(c) if a.kind == nkVarTuple: # generate new section from tuple unpacking and embed it into this one @@ -1103,7 +1231,7 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode = toResolve.add n[0] var errors: CandidateErrors = @[] - var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {efNoDiagnostics}, + var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {efNoUndeclared}, errors, false) if r.state == csMatch: var match = r.calleeSym @@ -1117,8 +1245,6 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode = of skMacro: result = semMacroExpr(c, toExpand, toExpand, match, flags) of skTemplate: result = semTemplateExpr(c, toExpand, match, flags) else: result = errorNode(c, n[0]) - elif r.state == csNoMatch: - result = errorNode(c, n[0]) else: result = errorNode(c, n[0]) if result.kind == nkEmpty: @@ -1589,21 +1715,27 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = for sk in c.skipTypes: discard semTypeNode(c, sk, nil) c.skipTypes = @[] -proc checkForMetaFields(c: PContext; n: PNode) = - proc checkMeta(c: PContext; n: PNode; t: PType) = - if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags: + +proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) = + proc checkMeta(c: PContext; n: PNode; t: PType; hasError: var bool; parent: PType) = + if t != nil and (t.isMetaType or t.kind == tyNone) and tfGenericTypeParam notin t.flags: if t.kind == tyBuiltInTypeClass and t.len == 1 and t.elementType.kind == tyProc: localError(c.config, n.info, ("'$1' is not a concrete type; " & "for a callback without parameters use 'proc()'") % t.typeToString) + elif t.kind == tyNone and parent != nil: + # TODO: openarray has the `tfGenericTypeParam` flag & generics + # TODO: handle special cases (sink etc.) and views + localError(c.config, n.info, errTIsNotAConcreteType % parent.typeToString) else: localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString) + hasError = true if n.isNil: return case n.kind of nkRecList, nkRecCase: - for s in n: checkForMetaFields(c, s) + for s in n: checkForMetaFields(c, s, hasError) of nkOfBranch, nkElse: - checkForMetaFields(c, n.lastSon) + checkForMetaFields(c, n.lastSon, hasError) of nkSym: let t = n.sym.typ case t.kind @@ -1611,9 +1743,9 @@ proc checkForMetaFields(c: PContext; n: PNode) = tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink, tyOwned: let start = ord(t.kind in {tyGenericInvocation, tyGenericInst}) for i in start..<t.len: - checkMeta(c, n, t[i]) + checkMeta(c, n, t[i], hasError, t) else: - checkMeta(c, n, t) + checkMeta(c, n, t, hasError, nil) else: internalAssert c.config, false @@ -1648,13 +1780,16 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = assert s.typ != nil assignType(s.typ, t) s.typ.itemId = t.itemId # same id - checkConstructedType(c.config, s.info, s.typ) - if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil: - checkForMetaFields(c, s.typ.n) - - # fix bug #5170, bug #17162, bug #15526: ensure locally scoped types get a unique name: - if s.typ.kind in {tyEnum, tyRef, tyObject} and not isTopLevel(c): - incl(s.flags, sfGenSym) + var hasError = false + let baseType = s.typ.safeSkipTypes(abstractPtrs) + if baseType.kind in {tyObject, tyTuple} and not baseType.n.isNil and + (x.kind in {nkObjectTy, nkTupleTy} or + (x.kind in {nkRefTy, nkPtrTy} and x.len == 1 and + x[0].kind in {nkObjectTy, nkTupleTy}) + ): + checkForMetaFields(c, baseType.n, hasError) + if not hasError: + checkConstructedType(c.config, s.info, s.typ) #instAllTypeBoundOp(c, n.info) @@ -2600,20 +2735,23 @@ proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult: PNode) = proc evalInclude(c: PContext, n: PNode): PNode = result = newNodeI(nkStmtList, n.info) result.add n + template checkAs(it: PNode) = + if it.kind == nkInfix and it.len == 3: + let op = it[0].getPIdent + if op != nil and op.id == ord(wAs): + localError(c.config, it.info, "Cannot use '" & it[0].renderTree & "' in 'include'.") for i in 0..<n.len: - var imp: PNode let it = n[i] - if it.kind == nkInfix and it.len == 3 and it[0].ident.s != "/": - localError(c.config, it.info, "Cannot use '" & it[0].ident.s & "' in 'include'.") - if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket: - let sep = it[0] - let dir = it[1] - imp = newNodeI(nkInfix, it.info) - imp.add sep - imp.add dir - imp.add sep # dummy entry, replaced in the loop - for x in it[2]: - imp[2] = x + checkAs(it) + if it.kind in {nkInfix, nkPrefix} and it[^1].kind == nkBracket: + let lastPos = it.len - 1 + var imp = copyNode(it) + newSons(imp, it.len) + for i in 0 ..< lastPos: imp[i] = it[i] + imp[lastPos] = imp[0] # dummy entry, replaced in the loop + for x in it[lastPos]: + checkAs(x) + imp[lastPos] = x incMod(c, n, imp, result) else: incMod(c, n, it, result) @@ -2724,7 +2862,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType = let verdict = semConstExpr(c, n[i]) if verdict == nil or verdict.kind != nkIntLit or verdict.intVal == 0: localError(c.config, result.info, "concept predicate failed") - of tyUnknown: continue + of tyFromExpr: continue else: discard if n[i].typ == c.enforceVoidContext: #or usesResult(n[i]): voidContext = true diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index f2083c85c..817cb6249 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -218,63 +218,98 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = if k == skParam and c.inTemplateHeader > 0: local.flags.incl sfTemplateParam -proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode = +proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bool): PNode = incl(s.flags, sfUsed) # bug #12885; ideally sem'checking is performed again afterwards marking # the symbol as used properly, but the nfSem mechanism currently prevents # that from happening, so we mark the module as used here already: - markOwnerModuleAsUsed(c, s) + markOwnerModuleAsUsed(c.c, s) # we do not call onUse here, as the identifier is not really # resolved here. We will fixup the used identifiers later. case s.kind of skUnknown: # Introduced in this pass! Leave it as an identifier. result = n - of OverloadableSyms-{skTemplate,skMacro}: - result = symChoice(c, n, s, scOpen, isField) - of skTemplate, skMacro: - result = symChoice(c, n, s, scOpen, isField) - if result.kind == nkSym: - # template/macro symbols might need to be semchecked again - # prepareOperand etc don't do this without setting the type to nil - result.typ = nil + of OverloadableSyms: + result = symChoice(c.c, n, s, scOpen, isField) + if not isField and result.kind in {nkSym, nkOpenSymChoice}: + if openSym in c.c.features: + if result.kind == nkSym: + result = newOpenSym(result) + else: + result.typ = nil + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil of skGenericParam: if isField and sfGenSym in s.flags: result = n - else: result = newSymNodeTypeDesc(s, c.idgen, n.info) + else: + result = newSymNodeTypeDesc(s, c.c.idgen, n.info) + if not isField and s.owner != c.owner: + if openSym in c.c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil of skParam: result = n of skType: if isField and sfGenSym in s.flags: result = n - else: result = newSymNodeTypeDesc(s, c.idgen, n.info) + else: + if isAmbiguous: + # ambiguous types should be symchoices since lookup behaves + # differently for them in regular expressions + result = symChoice(c.c, n, s, scOpen, isField) + else: result = newSymNodeTypeDesc(s, c.c.idgen, n.info) + if not isField and not (s.owner == c.owner and + s.typ != nil and s.typ.kind == tyGenericParam) and + result.kind in {nkSym, nkOpenSymChoice}: + if openSym in c.c.features: + if result.kind == nkSym: + result = newOpenSym(result) + else: + result.typ = nil + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil else: if isField and sfGenSym in s.flags: result = n - else: result = newSymNode(s, n.info) + else: + result = newSymNode(s, n.info) + if not isField: + if openSym in c.c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil # Issue #12832 when defined(nimsuggest): - suggestSym(c.graph, n.info, s, c.graph.usageSym, false) + suggestSym(c.c.graph, n.info, s, c.c.graph.usageSym, false) # field access (dot expr) will be handled by builtinFieldAccess if not isField: - styleCheckUse(c, n.info, s) + styleCheckUse(c.c, n.info, s) -proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode = +proc semRoutineInTemplName(c: var TemplCtx, n: PNode, explicitInject: bool): PNode = result = n if n.kind == nkIdent: let s = qualifiedLookUp(c.c, n, {}) if s != nil: - if s.owner == c.owner and s.kind == skParam: + if s.owner == c.owner and (s.kind == skParam or + (sfGenSym in s.flags and not explicitInject)): incl(s.flags, sfUsed) result = newSymNode(s, n.info) onUse(n.info, s) else: for i in 0..<n.safeLen: - result[i] = semRoutineInTemplName(c, n[i]) + result[i] = semRoutineInTemplName(c, n[i], explicitInject) proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode = result = n checkSonsLen(n, bodyPos + 1, c.c.config) if n.kind notin nkLambdaKinds: # routines default to 'inject': - if symBinding(n[pragmasPos]) == spGenSym: + let binding = symBinding(n[pragmasPos]) + if binding == spGenSym: let (ident, hasParam) = getIdentReplaceParams(c, n[namePos]) if not hasParam: var s = newGenSym(k, ident, c) @@ -286,7 +321,7 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode = else: n[namePos] = ident else: - n[namePos] = semRoutineInTemplName(c, n[namePos]) + n[namePos] = semRoutineInTemplName(c, n[namePos], binding == spInject) # open scope for parameters openScope(c) for i in patternPos..paramsPos-1: @@ -343,6 +378,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = case n.kind of nkIdent: if n.ident.id in c.toInject: return n + c.c.isAmbiguous = false let s = qualifiedLookUp(c.c, n, {}) if s != nil: if s.owner == c.owner and s.kind == skParam and sfTemplateParam in s.flags: @@ -360,9 +396,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = result = newSymNode(s, n.info) onUse(n.info, s) else: - if s.kind in {skType, skVar, skLet, skConst}: + if s.kind in {skVar, skLet, skConst}: discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule}) - result = semTemplSymbol(c.c, n, s, c.noGenSym > 0) + result = semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous) of nkBind: result = semTemplBody(c, n[0]) of nkBindStmt: @@ -556,6 +592,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = of nkDotExpr, nkAccQuoted: # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam', # so we use the generic code for nkDotExpr too + c.c.isAmbiguous = false let s = qualifiedLookUp(c.c, n, {}) if s != nil: # mirror the nkIdent case @@ -570,9 +607,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = elif contains(c.toMixin, s.name.id): return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0) else: - if s.kind in {skType, skVar, skLet, skConst}: + if s.kind in {skVar, skLet, skConst}: discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule}) - return semTemplSymbol(c.c, n, s, c.noGenSym > 0) + return semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous) if n.kind == nkDotExpr: result = n result[0] = semTemplBody(c, n[0]) @@ -656,6 +693,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = pushOwner(c, s) openScope(c) n[namePos] = newSymNode(s) + s.ast = n # for implicitPragmas to use pragmaCallable(c, s, n, templatePragmas) implicitPragmas(c, s, n.info, templatePragmas) @@ -706,6 +744,17 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = c: c, owner: s ) + # handle default params: + for i in 1..<s.typ.n.len: + let param = s.typ.n[i].sym + if param.ast != nil: + # param default values need to be treated like template body: + if sfDirty in s.flags: + param.ast = semTemplBodyDirty(ctx, param.ast) + else: + param.ast = semTemplBody(ctx, param.ast) + if param.ast.referencesAnotherParam(s): + param.ast.flags.incl nfDefaultRefsParam if sfDirty in s.flags: n[bodyPos] = semTemplBodyDirty(ctx, n[bodyPos]) else: @@ -715,11 +764,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = closeScope(c) popOwner(c) - # set the symbol AST after pragmas, at least. This stops pragma that have - # been pushed (implicit) to be explicitly added to the template definition - # and misapplied to the body. see #18113 - s.ast = n - if sfCustomPragma in s.flags: if n[bodyPos].kind != nkEmpty: localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index c88795517..113946fef 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -15,7 +15,7 @@ const errStringLiteralExpected = "string literal expected" errIntLiteralExpected = "integer literal expected" errWrongNumberOfVariables = "wrong number of variables" - errInvalidOrderInEnumX = "invalid order in enum '$1'" + errDuplicateAliasInEnumX = "duplicate value in enum '$1'" errOverflowInEnumX = "The enum '$1' exceeds its maximum value ($2)" errOrdinalTypeExpected = "ordinal type expected; given: $1" errSetTooBig = "set is too large; use `std/sets` for ordinal types with more than 2^16 elements" @@ -69,6 +69,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = e: PSym = nil base: PType = nil identToReplace: ptr PNode = nil + counterSet = initPackedSet[BiggestInt]() counter = 0 base = nil result = newOrPrevType(tyEnum, prev, c) @@ -85,6 +86,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = var hasNull = false for i in 1..<n.len: if n[i].kind == nkEmpty: continue + var useAutoCounter = false case n[i].kind of nkEnumFieldDef: if n[i][0].kind == nkPragmaExpr: @@ -112,6 +114,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = of tyString, tyCstring: strVal = v x = counter + useAutoCounter = true else: if isOrdinalType(v.typ, allowEnumWithHoles=true): x = toInt64(getOrdValue(v)) @@ -120,22 +123,30 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = localError(c.config, v.info, errOrdinalTypeExpected % typeToString(v.typ, preferDesc)) if i != 1: if x != counter: incl(result.flags, tfEnumHasHoles) - if x < counter: - localError(c.config, n[i].info, errInvalidOrderInEnumX % e.name.s) - x = counter e.ast = strVal # might be nil counter = x of nkSym: e = n[i].sym + useAutoCounter = true of nkIdent, nkAccQuoted: e = newSymS(skEnumField, n[i], c) identToReplace = addr n[i] + useAutoCounter = true of nkPragmaExpr: e = newSymS(skEnumField, n[i][0], c) pragma(c, e, n[i][1], enumFieldPragmas) identToReplace = addr n[i][0] + useAutoCounter = true else: illFormedAst(n[i], c.config) + + if useAutoCounter: + while counter in counterSet and counter != high(typeof(counter)): + inc counter + counterSet.incl counter + elif counterSet.containsOrIncl(counter): + localError(c.config, n[i].info, errDuplicateAliasInEnumX % e.name.s) + e.typ = result e.position = int(counter) let symNode = newSymNode(e) @@ -464,6 +475,13 @@ proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType = let t = semTypeNode(c, it, nil) addSonSkipIntLitChecked(c, result, t, it, c.idgen) +proc firstRange(config: ConfigRef, t: PType): PNode = + if t.skipModifier().kind in tyFloat..tyFloat64: + result = newFloatNode(nkFloatLit, firstFloat(t)) + else: + result = newIntNode(nkIntLit, firstOrd(config, t)) + result.typ = t + proc semTuple(c: PContext, n: PNode, prev: PType): PType = var typ: PType result = newOrPrevType(tyTuple, prev, c) @@ -480,8 +498,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType = elif a[^2].kind != nkEmpty: typ = semTypeNode(c, a[^2], nil) if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange: - a[^1] = newIntNode(nkIntLit, firstOrd(c.config, typ)) - a[^1].typ = typ + a[^1] = firstRange(c.config, typ) hasDefaultField = true else: localError(c.config, a.info, errTypeExpected) @@ -529,7 +546,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, result = newSymG(kind, n, c) proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, - allowed: TSymFlags): PSym = + allowed: TSymFlags, fromTopLevel = false): PSym = if n.kind == nkPragmaExpr: checkSonsLen(n, 2, c.config) result = semIdentVis(c, kind, n[0], allowed) @@ -544,11 +561,15 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, else: discard else: result = semIdentVis(c, kind, n, allowed) + let invalidPragmasForPush = if fromTopLevel and sfWasGenSym notin result.flags: + {} + else: + {wExportc, wExportCpp, wDynlib} case kind of skField: implicitPragmas(c, result, n.info, fieldPragmas) - of skVar: implicitPragmas(c, result, n.info, varPragmas) - of skLet: implicitPragmas(c, result, n.info, letPragmas) - of skConst: implicitPragmas(c, result, n.info, constPragmas) + of skVar: implicitPragmas(c, result, n.info, varPragmas-invalidPragmasForPush) + of skLet: implicitPragmas(c, result, n.info, letPragmas-invalidPragmasForPush) + of skConst: implicitPragmas(c, result, n.info, constPragmas-invalidPragmasForPush) else: discard proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) = @@ -565,9 +586,14 @@ proc semBranchRange(c: PContext, n, a, b: PNode, covered: var Int128): PNode = let bc = semConstExpr(c, b) if ac.kind in {nkStrLit..nkTripleStrLit} or bc.kind in {nkStrLit..nkTripleStrLit}: localError(c.config, b.info, "range of string is invalid") - let at = fitNode(c, n[0].typ, ac, ac.info).skipConvTakeType - let bt = fitNode(c, n[0].typ, bc, bc.info).skipConvTakeType - + var at = fitNode(c, n[0].typ, ac, ac.info).skipConvTakeType + var bt = fitNode(c, n[0].typ, bc, bc.info).skipConvTakeType + # the calls to fitNode may introduce calls to converters + # mirrored with semCaseBranch for single elements + if at.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}: + at = semConstExpr(c, at) + if bt.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}: + bt = semConstExpr(c, bt) result = newNodeI(nkRange, a.info) result.add(at) result.add(bt) @@ -598,6 +624,8 @@ proc semCaseBranch(c: PContext, n, branch: PNode, branchIndex: int, var b = branch[i] if b.kind == nkRange: branch[i] = b + # same check as in semBranchRange for exhaustiveness + covered = covered + getOrdValue(b[1]) + 1 - getOrdValue(b[0]) elif isRange(b): branch[i] = semCaseBranchRange(c, n, b, covered) else: @@ -613,8 +641,8 @@ proc semCaseBranch(c: PContext, n, branch: PNode, branchIndex: int, checkMinSonsLen(n, 1, c.config) var tmp = fitNode(c, n[0].typ, r, r.info) # the call to fitNode may introduce a call to a converter - if tmp.kind == nkHiddenCallConv or - (tmp.kind == nkHiddenStdConv and n[0].typ.kind == tyCstring): + # mirrored with semBranchRange + if tmp.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}: tmp = semConstExpr(c, tmp) branch[i] = skipConv(tmp) inc(covered) @@ -768,6 +796,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, of nkRecWhen: var a = copyTree(n) var branch: PNode = nil # the branch to take + var cannotResolve = false # no branch should be taken for i in 0..<a.len: var it = a[i] if it == nil: illFormedAst(n, c.config) @@ -785,24 +814,30 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, let e = semExprWithType(c, it[0], {efDetermineType}) if e.typ.kind == tyFromExpr: it[0] = makeStaticExpr(c, e) + cannotResolve = true else: it[0] = forceBool(c, e) + let val = getConstExpr(c.module, it[0], c.idgen, c.graph) + if val == nil or val.kind != nkIntLit: + cannotResolve = true + elif not cannotResolve and val.intVal != 0 and branch == nil: + branch = it[1] of nkElse: checkSonsLen(it, 1, c.config) - if branch == nil: branch = it[0] + if branch == nil and not cannotResolve: branch = it[0] idx = 0 else: illFormedAst(n, c.config) - if c.inGenericContext > 0: + if c.inGenericContext > 0 and cannotResolve: # use a new check intset here for each branch: var newCheck: IntSet = check var newPos = pos var newf = newNodeI(nkRecList, n.info) semRecordNodeAux(c, it[idx], newCheck, newPos, newf, rectype, hasCaseFields) it[idx] = if newf.len == 1: newf[0] else: newf - if c.inGenericContext > 0: - father.add a - elif branch != nil: + if branch != nil: semRecordNodeAux(c, branch, check, pos, father, rectype, hasCaseFields) + elif cannotResolve: + father.add a elif father.kind in {nkElse, nkOfBranch}: father.add newNodeI(nkRecList, n.info) of nkRecCase: @@ -831,8 +866,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, else: typ = semTypeNode(c, n[^2], nil) if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange: - n[^1] = newIntNode(nkIntLit, firstOrd(c.config, typ)) - n[^1].typ = typ + n[^1] = firstRange(c.config, typ) hasDefaultField = true propagateToOwner(rectype, typ) var fieldOwner = if c.inGenericContext > 0: c.getCurrOwner @@ -849,8 +883,8 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, f.options = c.config.options if fieldOwner != nil and {sfImportc, sfExportc} * fieldOwner.flags != {} and - not hasCaseFields and f.loc.r == "": - f.loc.r = rope(f.name.s) + not hasCaseFields and f.loc.snippet == "": + f.loc.snippet = rope(f.name.s) f.flags.incl {sfImportc, sfExportc} * fieldOwner.flags inc(pos) if containsOrIncl(check, f.name.id): @@ -1151,11 +1185,11 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramType[i] = t result = paramType - of tyAlias, tyOwned, tySink: + of tyAlias, tyOwned: result = recurse(paramType.base) of tySequence, tySet, tyArray, tyOpenArray, - tyVar, tyLent, tyPtr, tyRef, tyProc: + tyVar, tyLent, tyPtr, tyRef, tyProc, tySink: # XXX: this is a bit strange, but proc(s: seq) # produces tySequence(tyGenericParam, tyNone). # This also seems to be true when creating aliases @@ -1262,7 +1296,7 @@ proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType = result = semTypeNode(c, n[0], nil) constraint = semNodeKindConstraints(n, c.config, 1) elif n.kind == nkCall and - n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice} and + n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice, nkOpenSym} and considerQuotedIdent(c, n[0]).s == "{}": result = semTypeNode(c, n[1], nil) constraint = semNodeKindConstraints(n, c.config, 2) @@ -1292,6 +1326,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, result = newProcType(c, n.info, prev) var check = initIntSet() var counter = 0 + template isCurrentlyGeneric: bool = + # genericParams might update as implicit generic params are added + genericParams != nil and genericParams.len > 0 for i in 1..<n.len: var a = n[i] @@ -1312,7 +1349,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, hasDefault = a[^1].kind != nkEmpty if hasType: + let isGeneric = isCurrentlyGeneric() + inc c.inGenericContext, ord(isGeneric) typ = semParamType(c, a[^2], constraint) + dec c.inGenericContext, ord(isGeneric) # TODO: Disallow typed/untyped in procs in the compiler/stdlib if kind in {skProc, skFunc} and (typ.kind == tyTyped or typ.kind == tyUntyped): if not isMagic(getCurrOwner(c)): @@ -1332,15 +1372,26 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, "either use ';' (semicolon) or explicitly write each default value") message(c.config, a.info, warnImplicitDefaultValue, msg) block determineType: - var defTyp = typ - if genericParams != nil and genericParams.len > 0: - defTyp = nil - def = semGenericStmt(c, def) - if hasUnresolvedArgs(c, def): + var canBeVoid = false + if kind == skTemplate: + if typ != nil and typ.kind == tyUntyped: + # don't do any typechecking or assign a type for + # `untyped` parameter default value + break determineType + elif hasUnresolvedArgs(c, def): + # template default value depends on other parameter + # don't do any typechecking def.typ = makeTypeFromExpr(c, def.copyTree) break determineType - - def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, defTyp) + elif typ != nil and typ.kind == tyTyped: + canBeVoid = true + let isGeneric = isCurrentlyGeneric() + inc c.inGenericContext, ord(isGeneric) + if canBeVoid: + def = semExpr(c, def, {efDetermineType, efAllowSymChoice}, typ) + else: + def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, typ) + dec c.inGenericContext, ord(isGeneric) if def.referencesAnotherParam(getCurrOwner(c)): def.flags.incl nfDefaultRefsParam @@ -1359,7 +1410,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c)) typ.flags.incl tfCheckedForDestructor - else: + elif def.typ != nil and def.typ.kind != tyFromExpr: # def.typ can be void # if def.typ != nil and def.typ.kind != tyNone: # example code that triggers it: # proc sort[T](cmp: proc(a, b: T): int = cmp) @@ -1412,11 +1463,12 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, onDef(a[j].info, arg) a[j] = newSymNode(arg) - var r: PType = - if n[0].kind != nkEmpty: - semTypeNode(c, n[0], nil) - else: - nil + var r: PType = nil + if n[0].kind != nkEmpty: + let isGeneric = isCurrentlyGeneric() + inc c.inGenericContext, ord(isGeneric) + r = semTypeNode(c, n[0], nil) + dec c.inGenericContext, ord(isGeneric) if r != nil and kind in {skMacro, skTemplate} and r.kind == tyTyped: # XXX: To implement the proposed change in the warning, just @@ -1469,7 +1521,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, result.flags.excl tfHasMeta result.n.typ = r - if genericParams != nil and genericParams.len > 0: + if isCurrentlyGeneric(): for n in genericParams: if {sfUsed, sfAnon} * n.sym.flags == {}: result.flags.incl tfUnresolved @@ -1626,7 +1678,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = recomputeFieldPositions(tx, tx.n, position) proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType = - if typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody} and prev != nil: + if prev != nil and (prev.kind == tyGenericBody or + typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody}): result = newTypeS(tyAlias, c) result.rawAddSon typeExpr result.sym = prev.sym @@ -1664,6 +1717,10 @@ proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType = # unnecessary new type creation let alias = maybeAliasType(c, result, prev) if alias != nil: result = alias + elif n.typ.kind == tyFromExpr and c.inGenericContext > 0: + # sometimes not possible to distinguish type from value in generic body, + # for example `T.Foo`, so both are handled under `tyFromExpr` + result = n.typ else: localError(c.config, n.info, "expected type, but got: " & n.renderTree) result = errorType(c) @@ -1749,12 +1806,15 @@ proc applyTypeSectionPragmas(c: PContext; pragmas, operand: PNode): PNode = discard "builtin pragma" else: trySuggestPragmas(c, key) - let ident = considerQuotedIdent(c, key) - if strTableGet(c.userPragmas, ident) != nil: + let ident = + if key.kind in nkIdentKinds: + considerQuotedIdent(c, key) + else: + nil + if ident != nil and strTableGet(c.userPragmas, ident) != nil: discard "User-defined pragma" else: - var amb = false - let sym = searchInScopes(c, ident, amb) + let sym = qualifiedLookUp(c, key, {}) # XXX: What to do here if amb is true? if sym != nil and sfCustomPragma in sym.flags: discard "Custom user pragma" @@ -1821,10 +1881,14 @@ proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType = proc semTypeOf(c: PContext; n: PNode; prev: PType): PType = openScope(c) + inc c.inTypeofContext + defer: dec c.inTypeofContext # compiles can raise an exception let t = semExprWithType(c, n, {efInTypeof}) closeScope(c) fixupTypeOf(c, prev, t) result = t.typ + if result.kind == tyFromExpr: + result.flags.incl tfNonConstExpr proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType = openScope(c) @@ -1835,10 +1899,14 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType = localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time") else: m = mode.intVal + inc c.inTypeofContext + defer: dec c.inTypeofContext # compiles can raise an exception let t = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {}) closeScope(c) fixupTypeOf(c, prev, t) result = t.typ + if result.kind == tyFromExpr: + result.flags.incl tfNonConstExpr proc semTypeIdent(c: PContext, n: PNode): PSym = if n.kind == nkSym: @@ -1926,11 +1994,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkTupleConstr: result = semAnonTuple(c, n, prev) of nkCallKinds: let x = n[0] - let ident = case x.kind - of nkIdent: x.ident - of nkSym: x.sym.name - of nkClosedSymChoice, nkOpenSymChoice: x[0].sym.name - else: nil + let ident = x.getPIdent if ident != nil and ident.s == "[]": let b = newNodeI(nkBracketExpr, n.info) for i in 1..<n.len: b.add(n[i]) @@ -2020,20 +2084,21 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = elif op.id == ord(wType): checkSonsLen(n, 2, c.config) result = semTypeOf(c, n[1], prev) - elif op.s == "typeof" and n[0].kind == nkSym and n[0].sym.magic == mTypeOf: + elif op.s == "typeof" and ( + (n[0].kind == nkSym and n[0].sym.magic == mTypeOf) or + (n[0].kind == nkOpenSym and n[0][0].sym.magic == mTypeOf)): result = semTypeOf2(c, n, prev) elif op.s == "owned" and optOwnedRefs notin c.config.globalOptions and n.len == 2: result = semTypeExpr(c, n[1], prev) else: - if c.inGenericContext > 0 and n.kind == nkCall: - let n = semGenericStmt(c, n) - result = makeTypeFromExpr(c, n.copyTree) - else: - result = semTypeExpr(c, n, prev) + result = semTypeExpr(c, n, prev) of nkWhenStmt: var whenResult = semWhen(c, n, false) if whenResult.kind == nkStmtList: whenResult.transitionSonsKind(nkStmtListType) - result = semTypeNode(c, whenResult, prev) + if whenResult.kind == nkWhenStmt: + result = whenResult.typ + else: + result = semTypeNode(c, whenResult, prev) of nkBracketExpr: checkMinSonsLen(n, 2, c.config) var head = n[0] @@ -2078,6 +2143,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mRef: result = semAnyRef(c, n, tyRef, prev) of mPtr: result = semAnyRef(c, n, tyPtr, prev) of mTuple: result = semTuple(c, n, prev) + of mBuiltinType: + case s.name.s + of "lent": result = semAnyRef(c, n, tyLent, prev) + of "sink": result = semAnyRef(c, n, tySink, prev) + of "owned": result = semAnyRef(c, n, tyOwned, prev) + else: result = semGeneric(c, n, s, prev) else: result = semGeneric(c, n, s, prev) of nkDotExpr: let typeExpr = semExpr(c, n) @@ -2107,7 +2178,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = if s.kind != skError: localError(c.config, n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) elif s.kind == skParam and s.typ.kind == tyTypeDesc: - internalAssert c.config, s.typ.base.kind != tyNone and prev == nil + internalAssert c.config, s.typ.base.kind != tyNone result = s.typ.base elif prev == nil: result = s.typ @@ -2131,7 +2202,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = if s.kind == skType: s.typ else: - internalAssert c.config, s.typ.base.kind != tyNone and prev == nil + internalAssert c.config, s.typ.base.kind != tyNone s.typ.base let alias = maybeAliasType(c, t, prev) if alias != nil: @@ -2192,6 +2263,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkType: result = n.typ of nkStmtListType: result = semStmtListType(c, n, prev) of nkBlockType: result = semBlockType(c, n, prev) + of nkOpenSym: result = semTypeNode(c, n[0], prev) else: result = semTypeExpr(c, n, prev) when false: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index f0ce8d76f..759e8e6ab 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -118,7 +118,12 @@ proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = result = replaceTypeVarsTAux(cl, t) checkMetaInvariants(cl, result) -proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = +proc prepareNode*(cl: var TReplTypeVars, n: PNode): PNode = + ## instantiates a given generic expression, not a type node + if n.kind == nkSym and n.sym.kind == skType and + n.sym.typ != nil and n.sym.typ.kind == tyGenericBody: + # generic body types are allowed as user expressions, see #24090 + return n let t = replaceTypeVarsT(cl, n.typ) if t != nil and t.kind == tyStatic and t.n != nil: return if tfUnresolved in t.flags: prepareNode(cl, t.n) @@ -131,11 +136,68 @@ proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = replaceTypeVarsS(cl, n.sym, result.typ) else: replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ)) - let isCall = result.kind in nkCallKinds - for i in 0..<n.safeLen: - # XXX HACK: ``f(a, b)``, avoid to instantiate `f` - if isCall and i == 0: result.add(n[i]) - else: result.add(prepareNode(cl, n[i])) + # we need to avoid trying to instantiate nodes that can have uninstantiated + # types, like generic proc symbols or raw generic type symbols + case n.kind + of nkSymChoices: + # don't try to instantiate symchoice symbols, they can be + # generic procs which the compiler will think are uninstantiated + # because their type will contain uninstantiated params + for i in 0..<n.len: + result.add(n[i]) + of nkCallKinds: + # don't try to instantiate call names since they may be generic proc syms + # also bracket expressions can turn into calls with symchoice [] and + # we need to not instantiate the Generic in Generic[int] + # exception exists for the call name being a dot expression since + # dot expressions need their LHS instantiated + assert n.len != 0 + # avoid instantiating generic proc symbols, refine condition if needed: + let ignoreFirst = n[0].kind notin {nkDotExpr, nkBracketExpr} + nkCallKinds + let name = n[0].getPIdent + let ignoreSecond = name != nil and name.s == "[]" and n.len > 1 and + # generic type instantiation: + ((n[1].typ != nil and n[1].typ.kind == tyTypeDesc) or + # generic proc instantiation: + (n[1].kind == nkSym and n[1].sym.isGenericRoutineStrict)) + if ignoreFirst: + result.add(n[0]) + else: + result.add(prepareNode(cl, n[0])) + if n.len > 1: + if ignoreSecond: + result.add(n[1]) + else: + result.add(prepareNode(cl, n[1])) + for i in 2..<n.len: + result.add(prepareNode(cl, n[i])) + of nkBracketExpr: + # don't instantiate Generic body type in expression like Generic[T] + # exception exists for the call name being a dot expression since + # dot expressions need their LHS instantiated + assert n.len != 0 + let ignoreFirst = n[0].kind != nkDotExpr and + # generic type instantiation: + ((n[0].typ != nil and n[0].typ.kind == tyTypeDesc) or + # generic proc instantiation: + (n[0].kind == nkSym and n[0].sym.isGenericRoutineStrict)) + if ignoreFirst: + result.add(n[0]) + else: + result.add(prepareNode(cl, n[0])) + for i in 1..<n.len: + result.add(prepareNode(cl, n[i])) + of nkDotExpr: + # don't try to instantiate RHS of dot expression, it can outright be + # undeclared, but definitely instantiate LHS + assert n.len >= 2 + result.add(prepareNode(cl, n[0])) + result.add(n[1]) + for i in 2..<n.len: + result.add(prepareNode(cl, n[i])) + else: + for i in 0..<n.safeLen: + result.add(prepareNode(cl, n[i])) proc isTypeParam(n: PNode): bool = # XXX: generic params should use skGenericParam instead of skType @@ -218,6 +280,9 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT if n == nil: return result = copyNode(n) if n.typ != nil: + if n.typ.kind == tyFromExpr: + # type of node should not be evaluated as a static value + n.typ.flags.incl tfNonConstExpr result.typ = replaceTypeVarsT(cl, n.typ) checkMetaInvariants(cl, result.typ) case n.kind @@ -230,7 +295,8 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT replaceTypeVarsS(cl, n.sym, result.typ) else: replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ)) - if result.sym.typ.kind == tyVoid: + # sym type can be nil if was gensym created by macro, see #24048 + if result.sym.typ != nil and result.sym.typ.kind == tyVoid: # don't add the 'void' field result = newNodeI(nkRecList, n.info) of nkRecWhen: @@ -569,6 +635,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result.kind = tyUserTypeClassInst of tyGenericBody: + if cl.allowMetaTypes: return localError( cl.c.config, cl.info, @@ -587,13 +654,18 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = assert t.n.typ != t var n = prepareNode(cl, t.n) if n.kind != nkEmpty: - n = cl.c.semConstExpr(cl.c, n) + if tfNonConstExpr in t.flags: + n = cl.c.semExprWithType(cl.c, n, flags = {efInTypeof}) + else: + n = cl.c.semConstExpr(cl.c, n) if n.typ.kind == tyTypeDesc: # XXX: sometimes, chained typedescs enter here. # It may be worth investigating why this is happening, # because it may cause other bugs elsewhere. result = n.typ.skipTypes({tyTypeDesc}) # result = n.typ.base + elif tfNonConstExpr in t.flags: + result = n.typ else: if n.typ.kind != tyStatic and n.kind != nkType: # XXX: In the future, semConstExpr should @@ -619,8 +691,31 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = elif t.elementType.kind != tyNone: result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.elementType)) - of tyUserTypeClass, tyStatic: + of tyUserTypeClass: result = t + + of tyStatic: + if cl.c.matchedConcept != nil: + # allow concepts to not instantiate statics for now + # they can't always infer them + return + if not containsGenericType(t) and (t.n == nil or t.n.kind in nkLiterals): + # no need to instantiate + return + bailout() + result = instCopyType(cl, t) + cl.localCache[t.itemId] = result + for i in FirstGenericParamAt..<result.kidsLen: + var r = result[i] + if r != nil: + r = replaceTypeVarsT(cl, r) + result[i] = r + propagateToOwner(result, r) + result.n = replaceTypeVarsN(cl, result.n) + if not cl.allowMetaTypes and result.n != nil and + result.base.kind != tyNone: + result.n = cl.c.semConstExpr(cl.c, result.n) + result.n.typ = result.base of tyGenericInst, tyUserTypeClassInst: bailout() @@ -641,7 +736,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = for i, resulti in result.ikids: if resulti != nil: - if resulti.kind == tyGenericBody: + if resulti.kind == tyGenericBody and not cl.allowMetaTypes: localError(cl.c.config, if t.sym != nil: t.sym.info else: cl.info, "cannot instantiate '" & typeToString(result[i], preferDesc) & @@ -706,6 +801,14 @@ proc replaceTypesInBody*(p: PContext, pt: TypeMapping, n: PNode; result = replaceTypeVarsN(cl, n, expectedType = expectedType) popInfoContext(p.config) +proc prepareTypesInBody*(p: PContext, pt: TypeMapping, n: PNode; + owner: PSym = nil): PNode = + var typeMap = initLayeredTypeMap(pt) + var cl = initTypeVars(p, typeMap, n.info, owner) + pushInfoContext(p.config, n.info) + result = prepareNode(cl, n) + popInfoContext(p.config) + when false: # deadcode proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode; diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 1b75f6be6..d8dfe1828 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -55,6 +55,8 @@ proc hashSym(c: var MD5Context, s: PSym) = c &= it.name.s c &= "." it = it.owner + c &= "#" + c &= s.disamb proc hashTypeSym(c: var MD5Context, s: PSym; conf: ConfigRef) = if sfAnon in s.flags or s.kind == skGenericParam: @@ -69,6 +71,8 @@ proc hashTypeSym(c: var MD5Context, s: PSym; conf: ConfigRef) = c &= it.name.s c &= "." it = it.owner + c &= "#" + c &= s.disamb proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]; conf: ConfigRef) = if n == nil: @@ -154,9 +158,9 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi # is actually safe without an infinite recursion check: if t.sym != nil: if {sfCompilerProc} * t.sym.flags != {}: - doAssert t.sym.loc.r != "" + doAssert t.sym.loc.snippet != "" # The user has set a specific name for this type - c &= t.sym.loc.r + c &= t.sym.loc.snippet elif CoOwnerSig in flags: c.hashTypeSym(t.sym, conf) else: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 30ce24500..6ea2c7bb5 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -23,7 +23,8 @@ when defined(nimPreviewSlimSystem): type MismatchKind* = enum kUnknown, kAlreadyGiven, kUnknownNamedParam, kTypeMismatch, kVarNeeded, - kMissingParam, kExtraArg, kPositionalAlreadyGiven + kMissingParam, kExtraArg, kPositionalAlreadyGiven, + kGenericParamTypeMismatch, kMissingGenericParam, kExtraGenericParam MismatchInfo* = object kind*: MismatchKind # reason for mismatch @@ -59,8 +60,8 @@ type magic*: TMagic # magic of operation baseTypeMatch: bool # needed for conversions from T to openarray[T] # for example - fauxMatch*: TTypeKind # the match was successful only due to the use - # of error or wildcard (unknown) types. + matchedErrorType*: bool # match is considered successful after matching + # error type to avoid cascading errors # this is used to prevent instantiations. genericConverter*: bool # true if a generic converter needs to # be instantiated @@ -80,7 +81,8 @@ type # or when the explain pragma is used. may be # triggered with an idetools command in the # future. - inheritancePenalty: int # to prefer closest father object type + # to prefer closest father object type + inheritancePenalty: int firstMismatch*: MismatchInfo # mismatch info for better error messages diagnosticsEnabled*: bool @@ -95,19 +97,18 @@ type const isNilConversion = isConvertible # maybe 'isIntConv' fits better? + maxInheritancePenalty = high(int) div 2 -proc markUsed*(c: PContext; info: TLineInfo, s: PSym) +proc markUsed*(c: PContext; info: TLineInfo, s: PSym; checkStyle = true) proc markOwnerModuleAsUsed*(c: PContext; s: PSym) -template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone - proc initCandidateAux(ctx: PContext, callee: PType): TCandidate {.inline.} = result = TCandidate(c: ctx, exactMatches: 0, subtypeMatches: 0, convMatches: 0, intConvMatches: 0, genericMatches: 0, state: csEmpty, firstMismatch: MismatchInfo(), callee: callee, call: nil, baseTypeMatch: false, - genericConverter: false, inheritancePenalty: 0 + genericConverter: false, inheritancePenalty: -1 ) proc initCandidate*(ctx: PContext, callee: PType): TCandidate = @@ -129,6 +130,110 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} = echo "binding ", key, " -> ", val idTablePut(c.bindings, key, val.skipIntLit(c.c.idgen)) +proc typeRel*(c: var TCandidate, f, aOrig: PType, + flags: TTypeRelFlags = {}): TTypeRelation + +proc matchGenericParam(m: var TCandidate, formal: PType, n: PNode) = + var arg = n.typ + if m.c.inGenericContext > 0: + # don't match yet-unresolved generic instantiations + while arg != nil and arg.kind == tyGenericParam: + arg = idTableGet(m.bindings, arg) + if arg == nil or arg.containsUnresolvedType: + m.state = csNoMatch + return + # fix up the type to get ready to match formal: + var formalBase = formal + while formalBase.kind == tyGenericParam and + formalBase.genericParamHasConstraints: + formalBase = formalBase.genericConstraint + if formalBase.kind == tyStatic and arg.kind != tyStatic: + # maybe call `paramTypesMatch` here, for now be conservative + if n.kind in nkSymChoices: n.flags.excl nfSem + let evaluated = m.c.semTryConstExpr(m.c, n, formalBase.skipTypes({tyStatic})) + if evaluated != nil: + arg = newTypeS(tyStatic, m.c, son = evaluated.typ) + arg.n = evaluated + elif formalBase.kind == tyTypeDesc: + if arg.kind != tyTypeDesc: + arg = makeTypeDesc(m.c, arg) + else: + arg = arg.skipTypes({tyTypeDesc}) + let tm = typeRel(m, formal, arg) + if tm in {isNone, isConvertible}: + m.state = csNoMatch + m.firstMismatch.kind = kGenericParamTypeMismatch + return + +proc matchGenericParams*(m: var TCandidate, binding: PNode, callee: PSym) = + ## matches explicit generic instantiation `binding` against generic params of + ## proc symbol `callee` + ## state is set to `csMatch` if all generic params match, `csEmpty` if + ## implicit generic parameters are missing (matches but cannot instantiate), + ## `csNoMatch` if a constraint fails or param count doesn't match + let c = m.c + let typeParams = callee.ast[genericParamsPos] + let paramCount = typeParams.len + let bindingCount = binding.len-1 + if bindingCount > paramCount: + m.state = csNoMatch + m.firstMismatch.kind = kExtraGenericParam + m.firstMismatch.arg = paramCount + 1 + return + for i in 1..bindingCount: + matchGenericParam(m, typeParams[i-1].typ, binding[i]) + if m.state == csNoMatch: + m.firstMismatch.arg = i + m.firstMismatch.formal = typeParams[i-1].sym + return + # not enough generic params given, check if remaining have defaults: + for i in bindingCount ..< paramCount: + let param = typeParams[i] + assert param.kind == nkSym + let paramSym = param.sym + if paramSym.ast != nil: + matchGenericParam(m, param.typ, paramSym.ast) + if m.state == csNoMatch: + m.firstMismatch.arg = i + 1 + m.firstMismatch.formal = paramSym + return + elif tfImplicitTypeParam in paramSym.typ.flags: + # not a mismatch, but can't create sym + m.state = csEmpty + return + else: + m.state = csNoMatch + m.firstMismatch.kind = kMissingGenericParam + m.firstMismatch.arg = i + 1 + m.firstMismatch.formal = paramSym + return + m.state = csMatch + +proc copyingEraseVoidParams(m: TCandidate, t: var PType) = + ## if `t` is a proc type with void parameters, copies it and erases them + assert t.kind == tyProc + let original = t + var copied = false + for i in 1 ..< original.len: + var f = original[i] + var isVoidParam = f.kind == tyVoid + if not isVoidParam: + let prev = idTableGet(m.bindings, f) + if prev != nil: f = prev + isVoidParam = f.kind == tyVoid + if isVoidParam: + if not copied: + # keep first i children + t = copyType(original, m.c.idgen, t.owner) + t.setSonsLen(i) + t.n = copyNode(original.n) + t.n.sons = original.n.sons + t.n.sons.setLen(i) + copied = true + elif copied: + t.add(f) + t.n.add(original.n[i]) + proc initCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1, diagnosticsEnabled = false): TCandidate = @@ -143,17 +248,20 @@ proc initCandidate*(ctx: PContext, callee: PSym, result.magic = result.calleeSym.magic result.bindings = initTypeMapping() if binding != nil and callee.kind in routineKinds: - var typeParams = callee.ast[genericParamsPos] - for i in 1..min(typeParams.len, binding.len-1): - var formalTypeParam = typeParams[i-1].typ - var bound = binding[i].typ - if bound != nil: - if formalTypeParam.kind == tyTypeDesc: - if bound.kind != tyTypeDesc: - bound = makeTypeDesc(ctx, bound) - else: - bound = bound.skipTypes({tyTypeDesc}) - put(result, formalTypeParam, bound) + matchGenericParams(result, binding, callee) + let genericMatch = result.state + if genericMatch != csNoMatch: + result.state = csEmpty + if genericMatch == csMatch: # csEmpty if not fully instantiated + # instantiate the type, emulates old compiler behavior + # wouldn't be needed if sigmatch could handle complex cases, + # examples are in texplicitgenerics + # might be buggy, see rest of generateInstance if problems occur + let typ = ctx.instantiateOnlyProcType(ctx, result.bindings, callee, binding.info) + result.callee = typ + else: + # createThread[void] requires this if the above branch is removed: + copyingEraseVoidParams(result, result.callee) proc newCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1): TCandidate = @@ -176,9 +284,6 @@ proc copyCandidate(dest: var TCandidate, src: TCandidate) = dest.baseTypeMatch = src.baseTypeMatch dest.bindings = src.bindings -proc typeRel*(c: var TCandidate, f, aOrig: PType, - flags: TTypeRelFlags = {}): TTypeRelation - proc checkGeneric(a, b: TCandidate): int = let c = a.c let aa = a.callee @@ -287,6 +392,15 @@ proc writeMatches*(c: TCandidate) = echo " conv matches: ", c.convMatches echo " inheritance: ", c.inheritancePenalty +proc cmpInheritancePenalty(a, b: int): int = + var eb = b + var ea = a + if b < 0: + eb = maxInheritancePenalty # defacto max penalty + if a < 0: + ea = maxInheritancePenalty + eb - ea + proc cmpCandidates*(a, b: TCandidate, isFormal=true): int = result = a.exactMatches - b.exactMatches if result != 0: return @@ -298,8 +412,7 @@ proc cmpCandidates*(a, b: TCandidate, isFormal=true): int = if result != 0: return result = a.convMatches - b.convMatches if result != 0: return - # the other way round because of other semantics: - result = b.inheritancePenalty - a.inheritancePenalty + result = cmpInheritancePenalty(a.inheritancePenalty, b.inheritancePenalty) if result != 0: return if isFormal: # check for generic subclass relation @@ -328,16 +441,27 @@ template describeArgImpl(c: PContext, n: PNode, i: int, startIdx = 1; prefer = p result.add renderTree(n[i][0]) result.add ": " if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}: - # XXX we really need to 'tryExpr' here! - arg = c.semOperand(c, n[i][1]) - n[i].typ = arg.typ - n[i][1] = arg + arg = c.semTryExpr(c, n[i][1]) + if arg == nil: + arg = n[i][1] + arg.typ = newTypeS(tyUntyped, c) + else: + if arg.typ == nil: + arg.typ = newTypeS(tyVoid, c) + n[i].typ = arg.typ + n[i][1] = arg else: if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse, nkOfBranch, nkElifBranch, nkExceptBranch}: - arg = c.semOperand(c, n[i]) - n[i] = arg + arg = c.semTryExpr(c, n[i]) + if arg == nil: + arg = n[i] + arg.typ = newTypeS(tyUntyped, c) + else: + if arg.typ == nil: + arg.typ = newTypeS(tyVoid, c) + n[i] = arg if arg.typ != nil and arg.typ.kind == tyError: return result.add argTypeToString(arg, prefer) @@ -361,6 +485,7 @@ proc concreteType(c: TCandidate, t: PType; f: PType = nil): PType = else: result = t of tyGenericParam, tyAnything, tyConcept: result = t + if c.isNoCall: return while true: result = idTableGet(c.bindings, t) if result == nil: @@ -388,9 +513,16 @@ proc handleRange(c: PContext, f, a: PType, min, max: TTypeKind): TTypeRelation = let k = ab.kind let nf = c.config.normalizeKind(f.kind) let na = c.config.normalizeKind(k) - if k == f.kind: result = isSubrange - elif k == tyInt and f.kind in {tyRange, tyInt..tyInt64, - tyUInt..tyUInt64} and + if k == f.kind: + # `a` is a range type matching its base type + # see very bottom for range types matching different types + if isIntLit(ab): + # range type can only give isFromIntLit for base type + result = isFromIntLit + else: + result = isSubrange + elif a.kind == tyInt and f.kind in {tyRange, tyInt..tyInt64, + tyUInt..tyUInt64} and isIntLit(ab) and getInt(ab.n) >= firstOrd(nil, f) and getInt(ab.n) <= lastOrd(nil, f): # passing 'nil' to firstOrd/lastOrd here as type checking rules should @@ -588,10 +720,9 @@ proc recordRel(c: var TCandidate, f, a: PType, flags: TTypeRelFlags): TTypeRelat let firstField = if f.kind == tyTuple: 0 else: 1 for _, ff, aa in tupleTypePairs(f, a): - let oldInheritancePenalty = c.inheritancePenalty var m = typeRel(c, ff, aa, flags) if m < isSubtype: return isNone - if m == isSubtype and c.inheritancePenalty > oldInheritancePenalty: + if m == isSubtype and aa.kind != tyNil and c.inheritancePenalty > -1: # we can't process individual element type conversions from a # type conversion for the whole tuple # subtype relations need type conversions when inheritance is used @@ -677,6 +808,8 @@ proc procParamTypeRel(c: var TCandidate; f, a: PType): TTypeRelation = proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = case a.kind of tyProc: + var f = f + copyingEraseVoidParams(c, f) if f.signatureLen != a.signatureLen: return result = isEqual # start with maximum; also correct for no # params at all @@ -786,7 +919,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = param.typ.flags.incl tfInferrableStatic else: param.ast = typ.n - of tyUnknown: + of tyFromExpr: param = paramSym skVar param.typ = typ.exactReplica #copyType(typ, c.idgen, typ.owner) @@ -865,6 +998,7 @@ proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType = proc tryResolvingStaticExpr(c: var TCandidate, n: PNode, allowUnresolved = false, + allowCalls = false, expectedType: PType = nil): PNode = # Consider this example: # type Value[N: static[int]] = object @@ -874,7 +1008,7 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode, # This proc is used to evaluate such static expressions. let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil, allowMetaTypes = allowUnresolved) - if instantiated.kind in nkCallKinds: + if not allowCalls and instantiated.kind in nkCallKinds: return nil result = c.c.semExpr(c.c, instantiated) @@ -946,7 +1080,8 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool = else: discard - elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil: + elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and + (lhs.typ.n == nil or idTableGet(c.bindings, lhs.typ) == nil): var inferred = newTypeS(tyStatic, c.c, lhs.typ.elementType) inferred.n = newIntNode(nkIntLit, rhs) put(c, lhs.typ, inferred) @@ -990,9 +1125,21 @@ proc inferStaticsInRange(c: var TCandidate, doInferStatic(lowerBound, getInt(upperBound) + 1 - lengthOrd(c.c.config, concrete)) template subtypeCheck() = - if result <= isSubrange and f.last.skipTypes(abstractInst).kind in { - tyRef, tyPtr, tyVar, tyLent, tyOwned}: + case result + of isIntConv: result = isNone + of isSubrange: + discard # XXX should be isNone with preview define, warnings + of isConvertible: + if f.last.skipTypes(abstractInst).kind != tyOpenArray: + # exclude var openarray which compiler supports + result = isNone + of isSubtype: + if f.last.skipTypes(abstractInst).kind in { + tyRef, tyPtr, tyVar, tyLent, tyOwned}: + # compiler can't handle subtype conversions with pointer indirection + result = isNone + else: discard proc isCovariantPtr(c: var TCandidate, f, a: PType): bool = # this proc is always called for a pair of matching types @@ -1144,6 +1291,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if prev == nil: body else: return typeRel(c, prev, a, flags) + if c.c.inGenericContext > 0 and not c.isNoCall and + (tfUnresolved in a.flags or a.kind in tyTypeClasses): + # cheap check for unresolved arg, not nested + return isNone + case a.kind of tyOr: # XXX: deal with the current dual meaning of tyGenericParam @@ -1196,10 +1348,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, return isGeneric of tyFromExpr: if c.c.inGenericContext > 0: - # generic type bodies can sometimes compile call expressions - # prevent expressions with unresolved types from - # being passed as parameters - return isNone + if not c.isNoCall: + # generic type bodies can sometimes compile call expressions + # prevent expressions with unresolved types from + # being passed as parameters + return isNone + else: + # Foo[templateCall(T)] shouldn't fail early if Foo has a constraint + # and we can't evaluate `templateCall(T)` yet + return isGeneric else: discard case f.kind @@ -1260,13 +1417,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, subtypeCheck() of tyArray: a = reduceToBase(a) - case a.kind - of tyArray: + if a.kind == tyArray: var fRange = f.indexType var aRange = a.indexType if fRange.kind in {tyGenericParam, tyAnything}: var prev = idTableGet(c.bindings, fRange) if prev == nil: + if typeRel(c, fRange, aRange) == isNone: + return isNone put(c, fRange, a.indexType) fRange = a else: @@ -1279,7 +1437,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isGeneric else: result = typeRel(c, ff, aa, flags) - if result < isGeneric: if nimEnableCovariance and trNoCovariance notin flags and @@ -1290,6 +1447,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, return isNone if fRange.rangeHasUnresolvedStatic: + if aRange.kind in {tyGenericParam} and aRange.reduceToBase() == aRange: + return return inferStaticsInRange(c, fRange, a) elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic: return inferStaticsInRange(c, aRange, f) @@ -1298,12 +1457,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, else: if lengthOrd(c.c.config, fRange) != lengthOrd(c.c.config, aRange): result = isNone - else: discard - of tyUncheckedArray: - if a.kind == tyUncheckedArray: - result = typeRel(c, elementType(f), elementType(a), flags) - if result < isGeneric: result = isNone - else: discard of tyOpenArray, tyVarargs: # varargs[untyped] is special too but handled earlier. So we only need to # handle varargs[typed]: @@ -1344,9 +1497,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, typeRel(c, base(f), base(a), flags) >= isGeneric: result = isConvertible else: discard - of tySequence: - case a.kind - of tySequence: + of tySequence, tyUncheckedArray: + if a.kind == f.kind: if (f[0].kind != tyGenericParam) and (a.elementType.kind == tyEmpty): result = isSubtype else: @@ -1361,8 +1513,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isSubtype else: result = isNone - of tyNil: result = isNone - else: discard + elif a.kind == tyNil: + result = isNone of tyOrdinal: if isOrdinalType(a): var x = if a.kind == tyOrdinal: a.elementType else: a @@ -1388,12 +1540,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, reduceToBase(a) if effectiveArgType.kind == tyObject: if sameObjectTypes(f, effectiveArgType): + c.inheritancePenalty = if tfFinal in f.flags: -1 else: 0 result = isEqual # elif tfHasMeta in f.flags: result = recordRel(c, f, a) elif trIsOutParam notin flags: - var depth = isObjectSubtype(c, effectiveArgType, f, nil) - if depth > 0: - inc(c.inheritancePenalty, depth) + c.inheritancePenalty = isObjectSubtype(c, effectiveArgType, f, nil) + if c.inheritancePenalty > 0: result = isSubtype of tyDistinct: a = a.skipTypes({tyOwned, tyGenericInst, tyRange}) @@ -1409,11 +1561,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, else: result = typeRel(c, f[0], a[0], flags) if result < isGeneric: - if result <= isConvertible: - result = isNone - elif tfIsConstructor notin a.flags: - # set constructors are a bit special... + if tfIsConstructor notin a.flags: + # set['a'..'z'] and set[char] have different representations result = isNone + else: + # but we can convert individual elements of the constructor + result = isConvertible of tyPtr, tyRef: a = reduceToBase(a) if a.kind == f.kind: @@ -1562,7 +1715,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if aAsObject.kind == tyObject and trIsOutParam notin flags: let baseType = aAsObject.base if baseType != nil: - c.inheritancePenalty += 1 + inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0) let ret = typeRel(c, f, baseType, flags) return if ret in {isEqual,isGeneric}: isSubtype else: ret @@ -1659,7 +1812,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, depth = -1 if depth >= 0: - c.inheritancePenalty += depth + inc c.inheritancePenalty, depth + int(c.inheritancePenalty < 0) # bug #4863: We still need to bind generic alias crap, so # we cannot return immediately: result = if depth == 0: isGeneric else: isSubtype @@ -1677,19 +1830,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, considerPreviousT: result = isNone let oldInheritancePenalty = c.inheritancePenalty - var maxInheritance = 0 + var minInheritance = maxInheritancePenalty for branch in f.kids: - c.inheritancePenalty = 0 + c.inheritancePenalty = -1 let x = typeRel(c, branch, aOrig, flags) - maxInheritance = max(maxInheritance, c.inheritancePenalty) - # 'or' implies maximum matching result: - if x > result: result = x + if x >= result: + if c.inheritancePenalty > -1: + minInheritance = min(minInheritance, c.inheritancePenalty) + result = x if result >= isIntConv: + if minInheritance < maxInheritancePenalty: + c.inheritancePenalty = oldInheritancePenalty + minInheritance if result > isGeneric: result = isGeneric bindingRet result else: result = isNone - c.inheritancePenalty = oldInheritancePenalty + maxInheritance of tyNot: considerPreviousT: if typeRel(c, f.elementType, aOrig, flags) != isNone: @@ -1799,18 +1954,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, else: # check if 'T' has a constraint as in 'proc p[T: Constraint](x: T)' if f.len > 0 and f[0].kind != tyNone: - let oldInheritancePenalty = c.inheritancePenalty result = typeRel(c, f[0], a, flags + {trDontBind, trBindGenericParam}) if doBindGP and result notin {isNone, isGeneric}: let concrete = concreteType(c, a, f) if concrete == nil: return isNone put(c, f, concrete) - # bug #6526 if result in {isEqual, isSubtype}: - # 'T: Class' is a *better* match than just 'T' - # but 'T: Subclass' is even better: - c.inheritancePenalty = oldInheritancePenalty - c.inheritancePenalty - - 100 * ord(result == isEqual) result = isGeneric elif a.kind == tyTypeDesc: # somewhat special typing rule, the following is illegal: @@ -1843,16 +1992,29 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, elif x.kind == tyGenericParam: result = isGeneric else: + # This is the bound type - can't benifit from these tallies + let + inheritancePenaltyOld = c.inheritancePenalty result = typeRel(c, x, a, flags) # check if it fits + c.inheritancePenalty = inheritancePenaltyOld if result > isGeneric: result = isGeneric of tyStatic: let prev = idTableGet(c.bindings, f) if prev == nil: if aOrig.kind == tyStatic: - if f.base.kind notin {tyNone, tyGenericParam}: + if c.c.inGenericContext > 0 and aOrig.n == nil and not c.isNoCall: + # don't match unresolved static value to static param to avoid + # faulty instantiations in calls in generic bodies + # but not for generic invocations as they only check constraints + result = isNone + elif f.base.kind notin {tyNone, tyGenericParam}: result = typeRel(c, f.base, a, flags) if result != isNone and f.n != nil: - if not exprStructuralEquivalent(f.n, aOrig.n): + var r = tryResolvingStaticExpr(c, f.n) + if r == nil: r = f.n + if not exprStructuralEquivalent(r, aOrig.n) and + not (aOrig.n != nil and aOrig.n.kind == nkIntLit and + inferStaticParam(c, r, aOrig.n.intVal)): result = isNone elif f.base.kind == tyGenericParam: # Handling things like `type A[T; Y: static T] = object` @@ -1904,8 +2066,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # proc foo(T: typedesc, x: T) # when `f` is an unresolved typedesc, `a` could be any # type, so we should not perform this check earlier - if c.c.inGenericContext > 0 and - a.skipTypes({tyTypeDesc}).kind == tyGenericParam: + if c.c.inGenericContext > 0 and a.containsUnresolvedType: # generic type bodies can sometimes compile call expressions # prevent unresolved generic parameters from being passed to procs as # typedesc parameters @@ -1934,28 +2095,36 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if aOrig != nil: put(c, f, aOrig) result = isGeneric - of tyProxy: + of tyError: result = isEqual of tyFromExpr: # fix the expression, so it contains the already instantiated types if f.n == nil or f.n.kind == nkEmpty: return isGeneric - let reevaluated = tryResolvingStaticExpr(c, f.n) - if reevaluated == nil: + if c.c.inGenericContext > 0: + # need to delay until instantiation + # also prevent infinite recursion below + return isNone + inc c.c.inGenericContext # to generate tyFromExpr again if unresolved + # use prepareNode for consistency with other tyFromExpr in semtypinst: + let instantiated = prepareTypesInBody(c.c, c.bindings, f.n) + let reevaluated = c.c.semExpr(c.c, instantiated).typ + dec c.c.inGenericContext + case reevaluated.kind + of tyFromExpr: + # not resolved result = isNone - return - case reevaluated.typ.kind of tyTypeDesc: - result = typeRel(c, a, reevaluated.typ.base, flags) + result = typeRel(c, reevaluated.base, a, flags) of tyStatic: - result = typeRel(c, a, reevaluated.typ.base, flags) - if result != isNone and reevaluated.typ.n != nil: - if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n): + result = typeRel(c, reevaluated.base, a, flags) + if result != isNone and reevaluated.n != nil: + if not exprStructuralEquivalent(aOrig.n, reevaluated.n): result = isNone else: # bug #14136: other types are just like 'tyStatic' here: - result = typeRel(c, a, reevaluated.typ, flags) - if result != isNone and reevaluated.typ.n != nil: - if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n): + result = typeRel(c, reevaluated, a, flags) + if result != isNone and reevaluated.n != nil: + if not exprStructuralEquivalent(aOrig.n, reevaluated.n): result = isNone of tyNone: if a.kind == tyNone: result = isEqual @@ -1994,7 +2163,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, c: PContext): PNode = result = newNodeI(kind, arg.info) if containsGenericType(f): - if not m.hasFauxMatch: + if not m.matchedErrorType: result.typ = getInstantiatedType(c, arg, m, f).skipTypes({tySink}) else: result.typ = errorType(c) @@ -2015,6 +2184,81 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, else: result.add arg +proc convertLiteral(kind: TNodeKind, c: PContext, m: TCandidate; n: PNode, newType: PType): PNode = + # based off changeType but generates implicit conversions instead + template addConsiderNil(s, node) = + let val = node + if val.isNil: return nil + s.add(val) + case n.kind + of nkCurly: + result = copyNode(n) + for i in 0..<n.len: + if n[i].kind == nkRange: + var x = copyNode(n[i]) + x.addConsiderNil convertLiteral(kind, c, m, n[i][0], elemType(newType)) + x.addConsiderNil convertLiteral(kind, c, m, n[i][1], elemType(newType)) + result.add x + else: + result.addConsiderNil convertLiteral(kind, c, m, n[i], elemType(newType)) + result.typ = newType + return + of nkBracket: + result = copyNode(n) + for i in 0..<n.len: + result.addConsiderNil convertLiteral(kind, c, m, n[i], elemType(newType)) + result.typ = newType + return + of nkPar, nkTupleConstr: + let tup = newType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct}) + if tup.kind == tyTuple: + result = copyNode(n) + if n.len > 0 and n[0].kind == nkExprColonExpr: + # named tuple? + for i in 0..<n.len: + var name = n[i][0] + if name.kind != nkSym: + #globalError(c.config, name.info, "invalid tuple constructor") + return nil + if tup.n != nil: + var f = getSymFromList(tup.n, name.sym.name) + if f == nil: + #globalError(c.config, name.info, "unknown identifier: " & name.sym.name.s) + return nil + result.addConsiderNil convertLiteral(kind, c, m, n[i][1], f.typ) + else: + result.addConsiderNil convertLiteral(kind, c, m, n[i][1], tup[i]) + else: + for i in 0..<n.len: + result.addConsiderNil convertLiteral(kind, c, m, n[i], tup[i]) + result.typ = newType + return + of nkCharLit..nkUInt64Lit: + if n.kind != nkUInt64Lit and not sameTypeOrNil(n.typ, newType) and isOrdinalType(newType): + let value = n.intVal + if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType): + return nil + result = copyNode(n) + result.typ = newType + return + of nkFloatLit..nkFloat64Lit: + if newType.skipTypes(abstractVarRange-{tyTypeDesc}).kind == tyFloat: + if not floatRangeCheck(n.floatVal, newType): + return nil + result = copyNode(n) + result.typ = newType + return + of nkSym: + if n.sym.kind == skEnumField and not sameTypeOrNil(n.sym.typ, newType) and isOrdinalType(newType): + let value = n.sym.position + if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType): + return nil + result = copyNode(n) + result.typ = newType + return + else: discard + return implicitConv(kind, newType, n, m, c) + proc isLValue(c: PContext; n: PNode, isOutParam = false): bool {.inline.} = let aa = isAssignable(nil, n) case aa @@ -2152,6 +2396,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, a.n == nil and tfGenericTypeParam notin a.flags: return newNodeIT(nkType, argOrig.info, makeTypeFromExpr(c, arg)) + elif a.kind == tyFromExpr and c.inGenericContext > 0: + # don't try to evaluate + discard elif arg.kind != nkEmpty: var evaluated = c.semTryConstExpr(c, arg) if evaluated != nil: @@ -2223,7 +2470,20 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, if f.skipTypes({tyRange}).kind in {tyInt, tyUInt}: inc(m.convMatches) inc(m.convMatches) - result = implicitConv(nkHiddenStdConv, f, arg, m, c) + if skipTypes(f, abstractVar-{tyTypeDesc}).kind == tySet: + if tfIsConstructor in a.flags and arg.kind == nkCurly: + # we marked the set as convertible only because the arg is a literal + # in which case we individually convert each element + let t = + if containsGenericType(f): + getInstantiatedType(c, arg, m, f).skipTypes({tySink}) + else: + f.skipTypes({tySink}) + result = convertLiteral(nkHiddenStdConv, c, m, arg, t) + else: + result = nil + else: + result = implicitConv(nkHiddenStdConv, f, arg, m, c) of isIntConv: # I'm too lazy to introduce another ``*matches`` field, so we conflate # ``isIntConv`` and ``isIntLit`` here: @@ -2256,8 +2516,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, inc(m.genericMatches) if arg.typ == nil: result = arg - elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or - m.inheritancePenalty > oldInheritancePenalty: + elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or cmpInheritancePenalty(oldInheritancePenalty, m.inheritancePenalty) > 0: result = implicitConv(nkHiddenSubConv, f, arg, m, c) elif arg.typ.isEmptyContainer: result = arg.copyTree @@ -2283,13 +2542,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, result = implicitConv(nkHiddenSubConv, f, arg, m, c) of isNone: # do not do this in ``typeRel`` as it then can't infer T in ``ref T``: - if a.kind in {tyProxy, tyUnknown}: - if a.kind == tyUnknown and c.inGenericContext > 0: - # don't bother with fauxMatch mechanism in generic type, - # reject match, typechecking will be delayed to instantiation - return nil + if a.kind == tyFromExpr: return nil + elif a.kind == tyError: inc(m.genericMatches) - m.fauxMatch = a.kind + m.matchedErrorType = true return arg elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList: # lift do blocks without params to lambdas @@ -2358,7 +2614,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, result = paramTypesMatchAux(m, f, a, arg, argOrig) else: # symbol kinds that don't participate in symchoice type disambiguation: - let matchSet = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage, skType} + let matchSet = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage} var best = -1 result = arg @@ -2403,6 +2659,12 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, if arg[i].sym.kind in matchSet: copyCandidate(z, m) z.callee = arg[i].typ + if arg[i].sym.kind == skType and z.callee.kind != tyTypeDesc: + # creating the symchoice with the type sym having typedesc type + # breaks a lot of stuff, so we make the typedesc type here + # mirrored from `newSymNodeTypeDesc` + z.callee = newType(tyTypeDesc, c.idgen, arg[i].sym.owner) + z.callee.addSonSkipIntLit(arg[i].sym.typ, c.idgen) if tfUnresolved in z.callee.flags: continue z.calleeSym = arg[i].sym z.calleeScope = cmpScopes(m.c, arg[i].sym) @@ -2453,7 +2715,7 @@ proc setSon(father: PNode, at: int, son: PNode) = # we are allowed to modify the calling node in the 'prepare*' procs: proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode = if formal.kind == tyUntyped and formal.len != 1: - # {tyTypeDesc, tyUntyped, tyTyped, tyProxy}: + # {tyTypeDesc, tyUntyped, tyTyped, tyError}: # a.typ == nil is valid result = a elif a.typ.isNil: @@ -2491,7 +2753,7 @@ proc arrayConstr(c: PContext, n: PNode): PType = result = newTypeS(tyArray, c) rawAddSon(result, makeRangeType(c, 0, 0, n.info)) addSonSkipIntLit(result, skipTypes(n.typ, - {tyGenericInst, tyVar, tyLent, tyOrdinal}), c.idgen) + {tyVar, tyLent, tyOrdinal}), c.idgen) proc arrayConstr(c: PContext, info: TLineInfo): PType = result = newTypeS(tyArray, c) @@ -2745,6 +3007,8 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = inc m.genericMatches inc m.exactMatches return + # initCandidate may have given csNoMatch if generic params didn't match: + if m.state == csNoMatch: return var marker = initIntSet() matchesAux(c, n, nOrig, m, marker) if m.state == csNoMatch: return @@ -2767,14 +3031,16 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = m.firstMismatch.formal = formal break else: + # mirrored with updateDefaultParams: if formal.ast.kind == nkEmpty: # The default param value is set to empty in `instantiateProcType` # when the type of the default expression doesn't match the type # of the instantiated proc param: - localError(c.config, m.call.info, - ("The default parameter '$1' has incompatible type " & - "with the explicitly requested proc instantiation") % - formal.name.s) + pushInfoContext(c.config, m.call.info, + if m.calleeSym != nil: m.calleeSym.detailedInfo else: "") + typeMismatch(c.config, formal.ast.info, formal.typ, formal.ast.typ, formal.ast) + popInfoContext(c.config) + formal.ast.typ = errorType(c) if nfDefaultRefsParam in formal.ast.flags: m.call.flags.incl nfDefaultRefsParam var defaultValue = copyTree(formal.ast) diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index d114f59da..1dd481ec0 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -385,6 +385,13 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = accum.maxAlign = 1 computeObjectOffsetsFoldFunction(conf, typ.n, true, accum) else: + if typ.baseClass == nil and lacksMTypeField(typ) and typ.n.len == 1 and + typ.n[0].kind == nkSym and + typ.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray: + # a dummy field is generated for an object with a single field + # with an UncheckedArray type + assert accum.offset == 0 + accum.offset = 1 computeObjectOffsetsFoldFunction(conf, typ.n, false, accum) let paddingAtEnd = int16(accum.finish()) if typ.sym != nil and diff --git a/compiler/spawn.nim b/compiler/spawn.nim index 972d49d3e..58d5a4928 100644 --- a/compiler/spawn.nim +++ b/compiler/spawn.nim @@ -109,6 +109,16 @@ stmtList: """ +proc castToVoidPointer(g: ModuleGraph, n: PNode, fvField: PNode): PNode = + if g.config.backend == backendCpp: + result = fvField + else: + let ptrType = getSysType(g, n.info, tyPointer) + result = newNodeI(nkCast, fvField.info) + result.add newNodeI(nkEmpty, fvField.info) + result.add fvField + result.typ = ptrType + proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; varSection, varInit, call, barrier, fv: PNode; idgen: IdGenerator; @@ -156,8 +166,9 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; if barrier == nil: # by now 'fv' is shared and thus might have beeen overwritten! we need # to use the thread-local view instead: + let castExpr = castToVoidPointer(g, f, threadLocalProm.newSymNode) body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.info, - threadLocalProm.newSymNode) + castExpr) else: body.add call if barrier != nil: @@ -413,7 +424,8 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp # create flowVar: result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1])) if barrier == nil: - result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info, fvField) + let castExpr = castToVoidPointer(g, n, fvField) + result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info, castExpr) elif spawnKind == srByVar: var field = newSym(skField, getIdent(g.cache, "fv"), idgen, owner, n.info, g.config.options) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 616ecd466..a5213086b 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -696,7 +696,7 @@ proc markOwnerModuleAsUsed(c: PContext; s: PSym) = else: inc i -proc markUsed(c: PContext; info: TLineInfo; s: PSym) = +proc markUsed(c: PContext; info: TLineInfo; s: PSym; checkStyle = true) = let conf = c.config incl(s.flags, sfUsed) if s.kind == skEnumField and s.owner != nil: @@ -713,7 +713,8 @@ proc markUsed(c: PContext; info: TLineInfo; s: PSym) = if sfError in s.flags: userError(conf, info, s) when defined(nimsuggest): suggestSym(c.graph, info, s, c.graph.usageSym, false) - styleCheckUse(c, info, s) + if checkStyle: + styleCheckUse(c, info, s) markOwnerModuleAsUsed(c, s) proc safeSemExpr*(c: PContext, n: PNode): PNode = diff --git a/compiler/transf.nim b/compiler/transf.nim index 070443b82..8dd24e090 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -56,6 +56,7 @@ type contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' deferDetected, tooEarly: bool isIntroducingNewLocalVars: bool # true if we are in `introducingNewLocalVars` (don't transform yields) + inAddr: bool flags: TransformFlags graph: ModuleGraph idgen: IdGenerator @@ -97,7 +98,7 @@ proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode = r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias, tySink}) incl(r.flags, sfFromGeneric) let owner = getCurrOwner(c) - if owner.isIterator and not c.tooEarly: + if owner.isIterator and not c.tooEarly and not isDefined(c.graph.config, "nimOptIters"): result = freshVarForClosureIter(c.graph, r, c.idgen, owner) else: result = newSymNode(r) @@ -176,7 +177,7 @@ proc transformSym(c: PTransf, n: PNode): PNode = proc freshVar(c: PTransf; v: PSym): PNode = let owner = getCurrOwner(c) - if owner.isIterator and not c.tooEarly: + if owner.isIterator and not c.tooEarly and not isDefined(c.graph.config, "nimOptIters"): result = freshVarForClosureIter(c.graph, v, c.idgen, owner) else: var newVar = copySym(v, c.idgen) @@ -415,9 +416,15 @@ proc transformYield(c: PTransf, n: PNode): PNode = result.add transform(c, v) for i in 0..<c.transCon.forStmt.len - 2: - let lhs = c.transCon.forStmt[i] - let rhs = transform(c, newTupleAccess(c.graph, tmp, i)) - result.add(asgnTo(lhs, rhs)) + if c.transCon.forStmt[i].kind == nkVarTuple: + for j in 0..<c.transCon.forStmt[i].len-1: + let lhs = c.transCon.forStmt[i][j] + let rhs = transform(c, newTupleAccess(c.graph, newTupleAccess(c.graph, tmp, i), j)) + result.add(asgnTo(lhs, rhs)) + else: + let lhs = c.transCon.forStmt[i] + let rhs = transform(c, newTupleAccess(c.graph, tmp, i)) + result.add(asgnTo(lhs, rhs)) else: for i in 0..<c.transCon.forStmt.len - 2: let lhs = c.transCon.forStmt[i] @@ -456,6 +463,14 @@ proc transformYield(c: PTransf, n: PNode): PNode = let rhs = transform(c, e) result.add(asgnTo(lhs, rhs)) + + # bug #23536; note that the info of forLoopBody should't change + for idx in 0 ..< result.len: + var changeNode = result[idx] + changeNode.info = c.transCon.forStmt.info + for i, child in changeNode: + child.info = changeNode.info + inc(c.transCon.yieldStmts) if c.transCon.yieldStmts <= 1: # common case @@ -466,12 +481,6 @@ proc transformYield(c: PTransf, n: PNode): PNode = result.add(introduceNewLocalVars(c, c.transCon.forLoopBody)) c.isIntroducingNewLocalVars = false - for idx in 0 ..< result.len: - var changeNode = result[idx] - changeNode.info = c.transCon.forStmt.info - for i, child in changeNode: - child.info = changeNode.info - proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode = result = transformSons(c, n) # inlining of 'var openarray' iterators; bug #19977 @@ -499,7 +508,15 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode = elif n.typ.skipTypes(abstractInst).kind in {tyVar}: result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen) else: - if n[0].kind in kinds: + if n[0].kind in kinds and + not (n[0][0].kind == nkSym and n[0][0].sym.kind == skForVar and + n[0][0].typ.skipTypes(abstractVar).kind == tyTuple + ) and not (n[0][0].kind == nkSym and n[0][0].sym.kind == skParam and + n.typ.kind == tyVar and + n.typ.skipTypes(abstractVar).kind == tyOpenArray and + n[0][0].typ.skipTypes(abstractVar).kind == tyString) + : # elimination is harmful to `for tuple unpack` because of newTupleAccess + # it is also harmful to openArrayLoc (var openArray) for strings # addr ( deref ( x )) --> x result = n[0][0] if n.typ.skipTypes(abstractVar).kind != tyOpenArray: @@ -511,6 +528,7 @@ proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode = # we cannot generate a proper thunk here for GC-safety reasons # (see internal documentation): + if jsNoLambdaLifting in c.graph.config.legacyFeatures and c.graph.config.backend == backendJs: return prc result = newNodeIT(nkClosure, prc.info, dest) var conv = newNodeIT(nkHiddenSubConv, prc.info, dest) conv.add(newNodeI(nkEmpty, prc.info)) @@ -634,9 +652,11 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto = case arg.kind of nkEmpty..nkNilLit: result = paDirectMapping - of nkDotExpr, nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr: + of nkDotExpr, nkDerefExpr, nkHiddenDeref: + result = putArgInto(arg[0], formal) + of nkAddr, nkHiddenAddr: result = putArgInto(arg[0], formal) - #if result == paViaIndirection: result = paFastAsgn + if result == paViaIndirection: result = paFastAsgn of nkCurly, nkBracket: for i in 0..<arg.len: if putArgInto(arg[i], formal) != paDirectMapping: @@ -1059,7 +1079,10 @@ proc transform(c: PTransf, n: PNode): PNode = of nkHiddenAddr: result = transformAddrDeref(c, n, {nkHiddenDeref}) of nkAddr: + let oldInAddr = c.inAddr + c.inAddr = true result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref}) + c.inAddr = oldInAddr of nkDerefExpr: result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr}) of nkHiddenDeref: @@ -1139,7 +1162,7 @@ proc transform(c: PTransf, n: PNode): PNode = let exprIsPointerCast = n.kind in {nkCast, nkConv, nkHiddenStdConv} and n.typ != nil and n.typ.kind == tyPointer - if not exprIsPointerCast: + if not exprIsPointerCast and not c.inAddr: var cnst = getConstExpr(c.module, result, c.idgen, c.graph) # we inline constants if they are not complex constants: if cnst != nil and not dontInlineConstant(n, cnst): diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim index d226b2e06..39193a42d 100644 --- a/compiler/typeallowed.nim +++ b/compiler/typeallowed.nim @@ -27,6 +27,7 @@ type taProcContextIsNotMacro taIsCastable taIsDefaultField + taVoid # only allow direct void fields of objects/tuples TTypeAllowedFlags* = set[TTypeAllowedFlag] @@ -60,6 +61,8 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, if typ == nil: return nil if containsOrIncl(marker, typ.id): return nil var t = skipTypes(typ, abstractInst-{tyTypeDesc, tySink}) + + let flags = if t.kind == tyVoid: flags else: flags-{taVoid} case t.kind of tyVar, tyLent: if kind in {skProc, skFunc, skConst} and (views notin c.features): @@ -115,7 +118,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, of tyStatic: if kind notin {skParam}: result = t of tyVoid: - if taField notin flags: result = t + if taVoid notin flags: result = t of tyTypeClasses: if tfGenericTypeParam in t.flags or taConcept in flags: #or taField notin flags: discard @@ -184,12 +187,12 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, t.baseClass != nil and taIsDefaultField notin flags: result = t else: - let flags = flags+{taField} + let flags = flags+{taField, taVoid} result = typeAllowedAux(marker, t.baseClass, kind, c, flags) if result.isNil and t.n != nil: result = typeAllowedNode(marker, t.n, kind, c, flags) of tyTuple: - let flags = flags+{taField} + let flags = flags+{taField, taVoid} for a in t.kids: result = typeAllowedAux(marker, a, kind, c, flags) if result != nil: break @@ -197,7 +200,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = typeAllowedNode(marker, t.n, kind, c, flags) of tyEmpty: if kind in {skVar, skLet}: result = t - of tyProxy: + of tyError: # for now same as error node; we say it's a valid type as it should # prevent cascading errors: result = nil diff --git a/compiler/types.nim b/compiler/types.nim index e5ce0aea1..a441b0ea2 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -115,7 +115,7 @@ proc isPureObject*(typ: PType): bool = proc isUnsigned*(t: PType): bool = t.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64} -proc getOrdValue*(n: PNode; onError = high(Int128)): Int128 = +proc getOrdValueAux*(n: PNode, err: var bool): Int128 = var k = n.kind if n.typ != nil and n.typ.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64}: k = nkUIntLit @@ -131,13 +131,22 @@ proc getOrdValue*(n: PNode; onError = high(Int128)): Int128 = toInt128(n.intVal) of nkNilLit: int128.Zero - of nkHiddenStdConv: getOrdValue(n[1], onError) + of nkHiddenStdConv: + getOrdValueAux(n[1], err) else: - # XXX: The idea behind the introduction of int128 was to finally - # have all calculations numerically far away from any - # overflows. This command just introduces such overflows and - # should therefore really be revisited. - onError + err = true + int128.Zero + +proc getOrdValue*(n: PNode): Int128 = + var err: bool = false + result = getOrdValueAux(n, err) + #assert err == false + +proc getOrdValue*(n: PNode, onError: Int128): Int128 = + var err = false + result = getOrdValueAux(n, err) + if err: + result = onError proc getFloatValue*(n: PNode): BiggestFloat = case n.kind @@ -223,7 +232,11 @@ proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter, if result: return if not containsOrIncl(marker, t.id): case t.kind - of tyGenericInst, tyGenericBody, tyAlias, tySink, tyInferred: + of tyGenericBody: + # treat as atomic, containsUnresolvedType wants always false, + # containsGenericType always gives true + discard + of tyGenericInst, tyAlias, tySink, tyInferred: result = iterOverTypeAux(marker, skipModifier(t), iter, closure) else: for a in t.kids: @@ -739,6 +752,16 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if tfThread in t.flags: addSep(prag) prag.add("gcsafe") + var effectsOfStr = "" + for i, a in t.paramTypes: + let j = paramTypeToNodeIndex(i) + if t.n != nil and j < t.n.len and t.n[j].kind == nkSym and t.n[j].sym.kind == skParam and sfEffectsDelayed in t.n[j].sym.flags: + addSep(effectsOfStr) + effectsOfStr.add(t.n[j].sym.name.s) + if effectsOfStr != "": + addSep(prag) + prag.add("effectsOf: ") + prag.add(effectsOfStr) if not hasImplicitRaises and prefer == preferInferredEffects and not isNil(t.owner) and not isNil(t.owner.typ) and not isNil(t.owner.typ.n) and (t.owner.typ.n.len > 0): let effects = t.owner.typ.n[0] if effects.kind == nkEffectList and effects.len == effectListLen: @@ -767,7 +790,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = proc firstOrd*(conf: ConfigRef; t: PType): Int128 = case t.kind - of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy: + of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyError: result = Zero of tySet, tyVar: result = firstOrd(conf, t.elementType) of tyArray: result = firstOrd(conf, t.indexType) @@ -900,7 +923,7 @@ proc lastOrd*(conf: ConfigRef; t: PType): Int128 = result = lastOrd(conf, skipModifier(t)) of tyUserTypeClasses: result = lastOrd(conf, last(t)) - of tyProxy: result = Zero + of tyError: result = Zero of tyOrdinal: if t.hasElementType: result = lastOrd(conf, skipModifier(t)) else: @@ -975,6 +998,8 @@ type AllowCommonBase PickyCAliases # be picky about the distinction between 'cint' and 'int32' IgnoreFlags # used for borrowed functions and methods; ignores the tfVarIsPtr flag + PickyBackendAliases # be picky about different aliases + IgnoreRangeShallow TTypeCmpFlags* = set[TTypeCmpFlag] @@ -1203,26 +1228,40 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = inc c.recCheck else: if containsOrIncl(c, a, b): return true + template maybeSkipRange(x: set[TTypeKind]): set[TTypeKind] = + if IgnoreRangeShallow in c.flags: + x + {tyRange} + else: + x + + template withoutShallowFlags(body) = + let oldFlags = c.flags + c.flags.excl IgnoreRangeShallow + body + c.flags = oldFlags if x == y: return true - var a = skipTypes(x, {tyAlias}) + let aliasSkipSet = maybeSkipRange({tyAlias}) + var a = skipTypes(x, aliasSkipSet) while a.kind == tyUserTypeClass and tfResolved in a.flags: - a = skipTypes(a.last, {tyAlias}) - var b = skipTypes(y, {tyAlias}) + a = skipTypes(a.last, aliasSkipSet) + var b = skipTypes(y, aliasSkipSet) while b.kind == tyUserTypeClass and tfResolved in b.flags: - b = skipTypes(b.last, {tyAlias}) + b = skipTypes(b.last, aliasSkipSet) assert(a != nil) assert(b != nil) - if a.kind != b.kind: - case c.cmp - of dcEq: return false - of dcEqIgnoreDistinct: - a = a.skipTypes({tyDistinct, tyGenericInst}) - b = b.skipTypes({tyDistinct, tyGenericInst}) - if a.kind != b.kind: return false - of dcEqOrDistinctOf: - a = a.skipTypes({tyDistinct, tyGenericInst}) - if a.kind != b.kind: return false + case c.cmp + of dcEq: + if a.kind != b.kind: return false + of dcEqIgnoreDistinct: + let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst}) + a = a.skipTypes(distinctSkipSet) + b = b.skipTypes(distinctSkipSet) + if a.kind != b.kind: return false + of dcEqOrDistinctOf: + let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst}) + a = a.skipTypes(distinctSkipSet) + if a.kind != b.kind: return false #[ The following code should not run in the case either side is an generic alias, @@ -1230,14 +1269,16 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = objects ie `type A[T] = SomeObject` ]# # this is required by tunique_type but makes no sense really: - if x.kind == tyGenericInst and IgnoreTupleFields notin c.flags and tyDistinct != y.kind: + if c.cmp == dcEq and x.kind == tyGenericInst and + IgnoreTupleFields notin c.flags and tyDistinct != y.kind: let lhs = x.skipGenericAlias rhs = y.skipGenericAlias if rhs.kind != tyGenericInst or lhs.base != rhs.base or rhs.kidsLen != lhs.kidsLen: return false - for ff, aa in underspecifiedPairs(rhs, lhs, 1, -1): - if not sameTypeAux(ff, aa, c): return false + withoutShallowFlags: + for ff, aa in underspecifiedPairs(rhs, lhs, 1, -1): + if not sameTypeAux(ff, aa, c): return false return true case a.kind @@ -1251,6 +1292,11 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = let symFlagsB = if b.sym != nil: b.sym.flags else: {} if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}: result = symFlagsA == symFlagsB + elif result and PickyBackendAliases in c.flags: + let symFlagsA = if a.sym != nil: a.sym.flags else: {} + let symFlagsB = if b.sym != nil: b.sym.flags else: {} + if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}: + result = a.id == b.id of tyStatic, tyFromExpr: result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b) @@ -1258,9 +1304,10 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = cycleCheck() result = sameTypeAux(a.skipModifier, b.skipModifier, c) of tyObject: - ifFastObjectTypeCheckFailed(a, b): - cycleCheck() - result = sameObjectStructures(a, b, c) and sameFlags(a, b) + withoutShallowFlags: + ifFastObjectTypeCheckFailed(a, b): + cycleCheck() + result = sameObjectStructures(a, b, c) and sameFlags(a, b) of tyDistinct: cycleCheck() if c.cmp == dcEq: @@ -1275,8 +1322,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = of tyError: result = b.kind == tyError of tyTuple: - cycleCheck() - result = sameTuple(a, b, c) and sameFlags(a, b) + withoutShallowFlags: + cycleCheck() + result = sameTuple(a, b, c) and sameFlags(a, b) of tyTypeDesc: if c.cmp == dcEqIgnoreDistinct: result = false elif ExactTypeDescValues in c.flags: @@ -1300,7 +1348,8 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = tyAnd, tyOr, tyNot, tyAnything, tyOwned: cycleCheck() if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n - result = sameChildrenAux(a, b, c) + withoutShallowFlags: + result = sameChildrenAux(a, b, c) if result and IgnoreFlags notin c.flags: if IgnoreTupleFields in c.flags: result = a.flags * {tfVarIsPtr, tfIsOutParam} == b.flags * {tfVarIsPtr, tfIsOutParam} @@ -1313,11 +1362,21 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = ((ExactConstraints notin c.flags) or sameConstraints(a.n, b.n)) of tyRange: cycleCheck() - result = sameTypeOrNilAux(a.elementType, b.elementType, c) and - sameValue(a.n[0], b.n[0]) and + result = sameTypeOrNilAux(a.elementType, b.elementType, c) + if result and IgnoreRangeShallow notin c.flags: + result = sameValue(a.n[0], b.n[0]) and sameValue(a.n[1], b.n[1]) - of tyGenericInst, tyAlias, tyInferred, tyIterable: + of tyAlias, tyInferred, tyIterable: + cycleCheck() + result = sameTypeAux(a.skipModifier, b.skipModifier, c) + of tyGenericInst: + # BUG #23445 + # The type system must distinguish between `T[int] = object #[empty]#` + # and `T[float] = object #[empty]#`! cycleCheck() + withoutShallowFlags: + for ff, aa in underspecifiedPairs(a, b, 1, -1): + if not sameTypeAux(ff, aa, c): return false result = sameTypeAux(a.skipModifier, b.skipModifier, c) of tyNone: result = false of tyConcept: @@ -1329,6 +1388,19 @@ proc sameBackendType*(x, y: PType): bool = c.cmp = dcEqIgnoreDistinct result = sameTypeAux(x, y, c) +proc sameBackendTypeIgnoreRange*(x, y: PType): bool = + var c = initSameTypeClosure() + c.flags.incl IgnoreTupleFields + c.flags.incl IgnoreRangeShallow + c.cmp = dcEqIgnoreDistinct + result = sameTypeAux(x, y, c) + +proc sameBackendTypePickyAliases*(x, y: PType): bool = + var c = initSameTypeClosure() + c.flags.incl {IgnoreTupleFields, PickyCAliases, PickyBackendAliases} + c.cmp = dcEqIgnoreDistinct + result = sameTypeAux(x, y, c) + proc compareTypes*(x, y: PType, cmp: TDistinctCompare = dcEq, flags: TTypeCmpFlags = {}): bool = @@ -1389,6 +1461,8 @@ proc commonSuperclass*(a, b: PType): PType = return t y = y.baseClass +proc lacksMTypeField*(typ: PType): bool {.inline.} = + (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags include sizealignoffsetimpl @@ -1425,6 +1499,23 @@ proc containsGenericTypeIter(t: PType, closure: RootRef): bool = proc containsGenericType*(t: PType): bool = result = iterOverType(t, containsGenericTypeIter, nil) +proc containsUnresolvedTypeIter(t: PType, closure: RootRef): bool = + if tfUnresolved in t.flags: return true + case t.kind + of tyStatic: + return t.n == nil + of tyTypeDesc: + if t.base.kind == tyNone: return true + if containsUnresolvedTypeIter(t.base, closure): return true + return false + of tyGenericInvocation, tyGenericParam, tyFromExpr, tyAnything: + return true + else: + return false + +proc containsUnresolvedType*(t: PType): bool = + result = iterOverType(t, containsUnresolvedTypeIter, nil) + proc baseOfDistinct*(t: PType; g: ModuleGraph; idgen: IdGenerator): PType = if t.kind == tyDistinct: result = t.elementType @@ -1708,6 +1799,13 @@ proc processPragmaAndCallConvMismatch(msg: var string, formal, actual: PType, co of efTagsIllegal: msg.add "\n.notTag catched an illegal effect" +proc typeNameAndDesc*(t: PType): string = + result = typeToString(t) + let desc = typeToString(t, preferDesc) + if result != desc: + result.add(" = ") + result.add(desc) + proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: PNode) = if formal.kind != tyError and actual.kind != tyError: let actualStr = typeToString(actual) @@ -1837,6 +1935,3 @@ proc isCharArrayPtr*(t: PType; allowPointerToChar: bool): bool = result = false else: result = false - -proc lacksMTypeField*(typ: PType): bool {.inline.} = - (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 6b2f677a7..1711fea46 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -106,6 +106,7 @@ type unanalysableMutation: bool inAsgnSource, inConstructor, inNoSideEffectSection: int inConditional, inLoop: int + inConvHasDestructor: int owner: PSym g: ModuleGraph @@ -427,16 +428,28 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) = # primitive literals including the empty are harmless: discard - of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast, nkConv: + of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast: destMightOwn(c, dest, n[1]) + of nkConv: + if hasDestructor(n.typ): + inc c.inConvHasDestructor + destMightOwn(c, dest, n[1]) + dec c.inConvHasDestructor + else: + destMightOwn(c, dest, n[1]) + of nkIfStmt, nkIfExpr: for i in 0..<n.len: + inc c.inConditional destMightOwn(c, dest, n[i].lastSon) + dec c.inConditional of nkCaseStmt: for i in 1..<n.len: + inc c.inConditional destMightOwn(c, dest, n[i].lastSon) + dec c.inConditional of nkStmtList, nkStmtListExpr: if n.len > 0: @@ -481,7 +494,7 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) = of nkCallKinds: if n.typ != nil: - if hasDestructor(n.typ): + if hasDestructor(n.typ) or c.inConvHasDestructor > 0: # calls do construct, what we construct must be destroyed, # so dest cannot be a cursor: dest.flags.incl ownsData @@ -489,8 +502,17 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) = # we know the result is derived from the first argument: var roots: seq[(PSym, int)] = @[] allRoots(n[1], roots, RootEscapes) - for r in roots: - connect(c, dest.sym, r[0], n[1].info) + if roots.len == 0 and c.inConditional > 0: + # when in a conditional expression, + # to ensure that the first argument isn't outlived + # by the lvalue, we need find the root, otherwise + # it is probably a local temporary + # (e.g. a return value from a call), + # we should prevent cursorfication + dest.flags.incl preventCursor + else: + for r in roots: + connect(c, dest.sym, r[0], n[1].info) else: let magic = if n[0].kind == nkSym: n[0].sym.magic else: mNone diff --git a/compiler/vm.nim b/compiler/vm.nim index a775cf584..161b025a6 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -507,7 +507,7 @@ proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) = setLen(node.sons, newLen) if oldLen < newLen: for i in oldLen..<newLen: - node[i] = getNullValue(typ.elementType, info, c.config) + node[i] = getNullValue(c, typ.elementType, info, c.config) const errNilAccess = "attempt to access a nil address" @@ -609,7 +609,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcYldVal: assert false of opcAsgnInt: decodeB(rkInt) - regs[ra].intVal = regs[rb].intVal + if regs[rb].kind == rkInt: + regs[ra].intVal = regs[rb].intVal + else: + stackTrace(c, tos, pc, "opcAsgnInt: got " & $regs[rb].kind) of opcAsgnFloat: decodeB(rkFloat) regs[ra].floatVal = regs[rb].floatVal @@ -639,6 +642,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].intVal = cast[int](regs[rb].node.intVal) of rkNodeAddr: regs[ra].intVal = cast[int](regs[rb].nodeAddr) + of rkRegisterAddr: + regs[ra].intVal = cast[int](regs[rb].regAddr) of rkInt: regs[ra].intVal = regs[rb].intVal else: @@ -674,16 +679,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = else: assert regs[rb].kind == rkNode let nb = regs[rb].node - case nb.kind - of nkCharLit..nkUInt64Lit: - ensureKind(rkInt) - regs[ra].intVal = nb.intVal - of nkFloatLit..nkFloat64Lit: - ensureKind(rkFloat) - regs[ra].floatVal = nb.floatVal + if nb == nil: + stackTrace(c, tos, pc, errNilAccess) else: - ensureKind(rkNode) - regs[ra].node = nb + case nb.kind + of nkCharLit..nkUInt64Lit: + ensureKind(rkInt) + regs[ra].intVal = nb.intVal + of nkFloatLit..nkFloat64Lit: + ensureKind(rkFloat) + regs[ra].floatVal = nb.floatVal + else: + ensureKind(rkNode) + regs[ra].node = nb of opcSlice: # A bodge, but this takes in `toOpenArray(rb, rc, rc)` and emits # nkTupleConstr(x, y, z) into the `regs[ra]`. These can later be used for calculating the slice we have taken. @@ -848,25 +856,30 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLdObj: # a = b.c decodeBC(rkNode) - let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[] - case src.kind - of nkEmpty..nkNilLit: - # for nkPtrLit, this could be supported in the future, use something like: - # derefPtrToReg(src.intVal + offsetof(src.typ, rc), typ_field, regs[ra], isAssign = false) - # where we compute the offset in bytes for field rc - stackTrace(c, tos, pc, errNilAccess & " " & $("kind", src.kind, "typ", typeToString(src.typ), "rc", rc)) - of nkObjConstr: - let n = src[rc + 1].skipColon - regs[ra].node = n - of nkTupleConstr: - let n = if src.typ != nil and tfTriggersCompileTime in src.typ.flags: - src[rc] - else: - src[rc].skipColon - regs[ra].node = n + if rb >= regs.len or regs[rb].kind == rkNone or + (regs[rb].kind == rkNode and regs[rb].node == nil) or + (regs[rb].kind == rkNodeAddr and regs[rb].nodeAddr[] == nil): + stackTrace(c, tos, pc, errNilAccess) else: - let n = src[rc] - regs[ra].node = n + let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[] + case src.kind + of nkEmpty..nkNilLit: + # for nkPtrLit, this could be supported in the future, use something like: + # derefPtrToReg(src.intVal + offsetof(src.typ, rc), typ_field, regs[ra], isAssign = false) + # where we compute the offset in bytes for field rc + stackTrace(c, tos, pc, errNilAccess & " " & $("kind", src.kind, "typ", typeToString(src.typ), "rc", rc)) + of nkObjConstr: + let n = src[rc + 1].skipColon + regs[ra].node = n + of nkTupleConstr: + let n = if src.typ != nil and tfTriggersCompileTime in src.typ.flags: + src[rc] + else: + src[rc].skipColon + regs[ra].node = n + else: + let n = src[rc] + regs[ra].node = n of opcLdObjAddr: # a = addr(b.c) decodeBC(rkNodeAddr) @@ -892,8 +905,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = stackTrace(c, tos, pc, errNilAccess) elif dest[shiftedRb].kind == nkExprColonExpr: writeField(dest[shiftedRb][1], regs[rc]) + dest[shiftedRb][1].flags.incl nfSkipFieldChecking else: writeField(dest[shiftedRb], regs[rc]) + dest[shiftedRb].flags.incl nfSkipFieldChecking of opcWrStrIdx: decodeBC(rkNode) let idx = regs[rb].intVal.int @@ -1361,7 +1376,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = else: internalError(c.config, c.debug[pc], "opcParseFloat: Incorrectly created openarray") else: - regs[ra].intVal = parseBiggestFloat(regs[ra].node.strVal, rcAddr.floatVal) + regs[ra].intVal = parseBiggestFloat(regs[rb].node.strVal, rcAddr.floatVal) of opcRangeChck: let rb = instr.regB @@ -1376,7 +1391,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let rb = instr.regB let rc = instr.regC let bb = regs[rb].node + if bb.kind == nkNilLit: + stackTrace(c, tos, pc, "attempt to call nil closure") let isClosure = bb.kind == nkTupleConstr + if isClosure and bb[0].kind == nkNilLit: + stackTrace(c, tos, pc, "attempt to call nil closure") let prc = if not isClosure: bb.sym else: bb[0].sym if prc.offset < -1: # it's a callback: @@ -1416,7 +1435,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos) newSeq(newFrame.slots, prc.offset+ord(isClosure)) if not isEmptyType(prc.typ.returnType): - putIntoReg(newFrame.slots[0], getNullValue(prc.typ.returnType, prc.info, c.config)) + putIntoReg(newFrame.slots[0], getNullValue(c, prc.typ.returnType, prc.info, c.config)) for i in 1..rc-1: newFrame.slots[i] = regs[rb+i] if isClosure: @@ -1549,7 +1568,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNew: ensureKind(rkNode) let typ = c.types[instr.regBx - wordExcess] - regs[ra].node = getNullValue(typ, c.debug[pc], c.config) + regs[ra].node = getNullValue(c, typ, c.debug[pc], c.config) regs[ra].node.flags.incl nfIsRef of opcNewSeq: let typ = c.types[instr.regBx - wordExcess] @@ -1561,7 +1580,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.typ = typ newSeq(regs[ra].node.sons, count) for i in 0..<count: - regs[ra].node[i] = getNullValue(typ.elementType, c.debug[pc], c.config) + regs[ra].node[i] = getNullValue(c, typ.elementType, c.debug[pc], c.config) of opcNewStr: decodeB(rkNode) regs[ra].node = newNodeI(nkStrLit, c.debug[pc]) @@ -1573,7 +1592,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLdNull: ensureKind(rkNode) let typ = c.types[instr.regBx - wordExcess] - regs[ra].node = getNullValue(typ, c.debug[pc], c.config) + regs[ra].node = getNullValue(c, typ, c.debug[pc], c.config) # opcLdNull really is the gist of the VM's problems: should it load # a fresh null to regs[ra].node or to regs[ra].node[]? This really # depends on whether regs[ra] represents the variable itself or whether @@ -2302,7 +2321,7 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode = # setup parameters: if not isEmptyType(sym.typ.returnType) or sym.kind == skMacro: - putIntoReg(tos.slots[0], getNullValue(sym.typ.returnType, sym.info, c.config)) + putIntoReg(tos.slots[0], getNullValue(c, sym.typ.returnType, sym.info, c.config)) # XXX We could perform some type checking here. for i in 0..<sym.typ.paramsLen: putIntoReg(tos.slots[i+1], args[i]) diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index e293ab7b2..294aaaa79 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -237,7 +237,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; of tySequence: result = mapTypeToBracket("seq", mSeq, t, info) of tyProc: if inst: - result = newNodeX(nkProcTy) + result = newNodeX(if tfIterator in t.flags: nkIteratorTy else: nkProcTy) var fp = newNodeX(nkFormalParams) if t.returnType == nil: fp.add newNodeI(nkEmpty, info) @@ -246,8 +246,15 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; for i in FirstParamAt..<t.kidsLen: fp.add newIdentDefs(t.n[i], t[i]) result.add fp - result.add if t.n[0].len > 0: t.n[0][pragmasEffects].copyTree - else: newNodeI(nkEmpty, info) + var prag = + if t.n[0].len > 0: + t.n[0][pragmasEffects].copyTree + else: + newNodeI(nkEmpty, info) + if t.callConv != ccClosure or tfExplicitCallConv in t.flags: + if prag.kind == nkEmpty: prag = newNodeI(nkPragma, info) + prag.add newIdentNode(getIdent(cache, $t.callConv), info) + result.add prag else: result = mapTypeToBracket("proc", mNone, t, info) of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info) @@ -282,7 +289,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; of tyUInt32: result = atomicType("uint32", mUInt32) of tyUInt64: result = atomicType("uint64", mUInt64) of tyVarargs: result = mapTypeToBracket("varargs", mVarargs, t, info) - of tyProxy: result = atomicType("error", mNone) + of tyError: result = atomicType("error", mNone) of tyBuiltInTypeClass: result = mapTypeToBracket("builtinTypeClass", mNone, t, info) of tyUserTypeClass, tyUserTypeClassInst: diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 366fc7b29..0c7a49984 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -53,6 +53,7 @@ type gfNode # Affects how variables are loaded - always loads as rkNode gfNodeAddr # Affects how variables are loaded - always loads as rkNodeAddr gfIsParam # do not deepcopy parameters, they are immutable + gfIsSinkParam # deepcopy sink parameters TGenFlags = set[TGenFlag] proc debugInfo(c: PCtx; info: TLineInfo): string = @@ -245,7 +246,7 @@ proc getTemp(cc: PCtx; tt: PType): TRegister = proc freeTemp(c: PCtx; r: TRegister) = let c = c.prc - if c.regInfo[r].kind in {slotSomeTemp..slotTempComplex}: + if r < c.regInfo.len and c.regInfo[r].kind in {slotSomeTemp..slotTempComplex}: # this seems to cause https://github.com/nim-lang/Nim/issues/10647 c.regInfo[r].inUse = false @@ -357,12 +358,13 @@ proc genBlock(c: PCtx; n: PNode; dest: var TDest) = #if c.prc.regInfo[i].kind in {slotFixedVar, slotFixedLet}: if i != dest: when not defined(release): - if c.prc.regInfo[i].inUse and c.prc.regInfo[i].kind in {slotTempUnknown, - slotTempInt, - slotTempFloat, - slotTempStr, - slotTempComplex}: - raiseAssert "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind + if c.config.cmd != cmdCheck: + if c.prc.regInfo[i].inUse and c.prc.regInfo[i].kind in {slotTempUnknown, + slotTempInt, + slotTempFloat, + slotTempStr, + slotTempComplex}: + raiseAssert "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind c.prc.regInfo[i] = (inUse: false, kind: slotEmpty) c.clearDest(n, dest) @@ -619,10 +621,17 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) = let fntyp = skipTypes(n[0].typ, abstractInst) for i in 0..<n.len: var r: TRegister = x+i - c.gen(n[i], r, {gfIsParam}) if i >= fntyp.signatureLen: + c.gen(n[i], r, {gfIsParam}) internalAssert c.config, tfVarargs in fntyp.flags c.gABx(n, opcSetType, r, c.genType(n[i].typ)) + else: + if fntyp[i] != nil and fntyp[i].kind == tySink and + fntyp[i].skipTypes({tySink}).kind in {tyObject, tyString, tySequence}: + c.gen(n[i], r, {gfIsSinkParam}) + else: + c.gen(n[i], r, {gfIsParam}) + if dest < 0: c.gABC(n, opcIndCall, 0, x, n.len) else: @@ -696,6 +705,9 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = let dest = c.genx(le, {gfNodeAddr}) c.gABC(le, opcWrDeref, dest, 0, value) c.freeTemp(dest) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + if sameBackendType(le.typ, le[1].typ): + genAsgnPatch(c, le[1], value) else: discard @@ -868,7 +880,7 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = genBinaryABC(c, n, dest, opc) c.genNarrow(n, dest) -proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = +proc genConv(c: PCtx; n, arg: PNode; dest: var TDest, flags: TGenFlags = {}; opc=opcConv) = let t2 = n.typ.skipTypes({tyDistinct}) let targ2 = arg.typ.skipTypes({tyDistinct}) @@ -882,7 +894,7 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = result = false if implicitConv(): - gen(c, arg, dest) + gen(c, arg, dest, flags) return let tmp = c.genx(arg) @@ -900,9 +912,15 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) = c.freeTemp(tmp) proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = + template isSigned(typ: PType): bool {.dirty.} = + typ.kind == tyEnum and firstOrd(c.config, typ) < 0 or + typ.kind in {tyInt..tyInt64} + template isUnsigned(typ: PType): bool {.dirty.} = + typ.kind == tyEnum and firstOrd(c.config, typ) >= 0 or + typ.kind in {tyUInt..tyUInt64, tyChar, tyBool} + const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar, tyEnum, tyBool} - var signedIntegers = {tyInt..tyInt64} - var unsignedIntegers = {tyUInt..tyUInt64, tyChar, tyEnum, tyBool} + let src = n[1].typ.skipTypes(abstractRange)#.kind let dst = n[0].typ.skipTypes(abstractRange)#.kind let srcSize = getSize(c.config, src) @@ -914,12 +932,12 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = if dest < 0: dest = c.getTemp(n[0].typ) c.gABC(n, opcAsgnInt, dest, tmp) if dstSize != sizeof(BiggestInt): # don't do anything on biggest int types - if dst.kind in signedIntegers: # we need to do sign extensions + if isSigned(dst): # we need to do sign extensions if dstSize <= srcSize: # Sign extension can be omitted when the size increases. c.gABC(n, opcSignExtend, dest, TRegister(dstSize*8)) - elif dst.kind in unsignedIntegers: - if src.kind in signedIntegers or dstSize < srcSize: + elif isUnsigned(dst): + if isSigned(src) or dstSize < srcSize: # Cast from signed to unsigned always needs narrowing. Cast # from unsigned to unsigned only needs narrowing when target # is smaller than source. @@ -947,7 +965,7 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = if dest < 0: dest = c.getTemp(n[0].typ) if src.kind == tyFloat32: c.gABC(n, opcCastFloatToInt32, dest, tmp) - if dst.kind in unsignedIntegers: + if isUnsigned(dst): # integers are sign extended by default. # since there is no opcCastFloatToUInt32, narrowing should do the trick. c.gABC(n, opcNarrowU, dest, TRegister(32)) @@ -1044,7 +1062,7 @@ proc whichAsgnOpc(n: PNode; requiresCopy = true): TOpcode = else: (if requiresCopy: opcAsgnComplex else: opcFastAsgnComplex) -proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = +proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMagic) = case m of mAnd: c.genAndOr(n, opcFJmp, dest) of mOr: c.genAndOr(n, opcTJmp, dest) @@ -1183,7 +1201,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8): c.gABC(n, opcNarrowU, dest, TRegister(size*8)) of mCharToStr, mBoolToStr, mCStrToStr, mStrToStr, mEnumToStr: - genConv(c, n, n[1], dest) + genConv(c, n, n[1], dest, flags) of mEqStr: genBinaryABC(c, n, dest, opcEqStr) of mEqCString: genBinaryABC(c, n, dest, opcEqCString) of mLeStr: genBinaryABC(c, n, dest, opcLeStr) @@ -1229,13 +1247,6 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.freeTemp(tmp1) c.genAsgnPatch(d2AsNode, d2) c.freeTemp(d2) - of mReset: - unused(c, n, dest) - var d = c.genx(n[1]) - # XXX use ldNullOpcode() here? - c.gABx(n, opcLdNull, d, c.genType(n[1].typ)) - c.gABC(n, opcNodeToReg, d, d) - c.genAsgnPatch(n[1], d) of mDefault, mZeroDefault: if dest < 0: dest = c.getTemp(n.typ) c.gABx(n, ldNullOpcode(n.typ), dest, c.genType(n.typ)) @@ -1530,7 +1541,11 @@ proc setSlot(c: PCtx; v: PSym) = if v.position == 0: v.position = getFreeRegister(c, if v.kind == skLet: slotFixedLet else: slotFixedVar, start = 1) -proc cannotEval(c: PCtx; n: PNode) {.noinline.} = +template cannotEval(c: PCtx; n: PNode) = + if c.config.cmd == cmdCheck: + localError(c.config, n.info, "cannot evaluate at compile time: " & + n.renderTree) + return globalError(c.config, n.info, "cannot evaluate at compile time: " & n.renderTree) @@ -1653,6 +1668,9 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = c.freeTemp(cc) else: gen(c, ri, dest) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + if sameBackendType(le.typ, le[1].typ): + genAsgn(c, le[1], ri, requiresCopy) else: let dest = c.genx(le, {gfNodeAddr}) genAsgn(c, dest, ri, requiresCopy) @@ -1691,10 +1709,10 @@ proc importcSym(c: PCtx; info: TLineInfo; s: PSym) = localError(c.config, info, "cannot 'importc' variable at compile time; " & s.name.s) -proc getNullValue*(typ: PType, info: TLineInfo; conf: ConfigRef): PNode +proc getNullValue*(c: PCtx; typ: PType, info: TLineInfo; conf: ConfigRef): PNode proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = - c.globals.add(getNullValue(s.typ, n.info, c.config)) + c.globals.add(getNullValue(c, s.typ, n.info, c.config)) s.position = c.globals.len # This is rather hard to support, due to the laziness of the VM code # generator. See tests/compile/tmacro2 for why this is necessary: @@ -1730,6 +1748,8 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = c.gABx(n, opcLdGlobalAddr, dest, s.position) elif isImportcVar: c.gABx(n, opcLdGlobalDerefFFI, dest, s.position) + elif gfIsSinkParam in flags: + genAsgn(c, dest, n, requiresCopy = true) elif fitsRegister(s.typ) and gfNode notin flags: var cc = c.getTemp(n.typ) c.gABx(n, opcLdGlobal, cc, s.position) @@ -1743,7 +1763,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = s.kind in {skParam, skResult}): if dest < 0: dest = s.position + ord(s.kind == skParam) - internalAssert(c.config, c.prc.regInfo[dest].kind < slotSomeTemp) + internalAssert(c.config, c.prc.regInfo.len > dest and c.prc.regInfo[dest].kind < slotSomeTemp) else: # we need to generate an assignment: let requiresCopy = c.prc.regInfo[dest].kind >= slotSomeTemp and @@ -1873,21 +1893,21 @@ proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = let opc = if gfNodeAddr in flags: opcLdArrAddr else: opcLdArr genArrAccessOpcode(c, n, dest, opc, flags) -proc getNullValueAux(t: PType; obj: PNode, result: PNode; conf: ConfigRef; currPosition: var int) = +proc getNullValueAux(c: PCtx; t: PType; obj: PNode, result: PNode; conf: ConfigRef; currPosition: var int) = if t != nil and t.baseClass != nil: let b = skipTypes(t.baseClass, skipPtrs) - getNullValueAux(b, b.n, result, conf, currPosition) + getNullValueAux(c, b, b.n, result, conf, currPosition) case obj.kind of nkRecList: - for i in 0..<obj.len: getNullValueAux(nil, obj[i], result, conf, currPosition) + for i in 0..<obj.len: getNullValueAux(c, nil, obj[i], result, conf, currPosition) of nkRecCase: - getNullValueAux(nil, obj[0], result, conf, currPosition) + getNullValueAux(c, nil, obj[0], result, conf, currPosition) for i in 1..<obj.len: - getNullValueAux(nil, lastSon(obj[i]), result, conf, currPosition) + getNullValueAux(c, nil, lastSon(obj[i]), result, conf, currPosition) of nkSym: let field = newNodeI(nkExprColonExpr, result.info) field.add(obj) - let value = getNullValue(obj.sym.typ, result.info, conf) + let value = getNullValue(c, obj.sym.typ, result.info, conf) value.flags.incl nfSkipFieldChecking field.add(value) result.add field @@ -1895,7 +1915,7 @@ proc getNullValueAux(t: PType; obj: PNode, result: PNode; conf: ConfigRef; currP inc currPosition else: globalError(conf, result.info, "cannot create null element for: " & $obj) -proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode = +proc getNullValue(c: PCtx; typ: PType, info: TLineInfo; conf: ConfigRef): PNode = var t = skipTypes(typ, abstractRange+{tyStatic, tyOwned}-{tyTypeDesc}) case t.kind of tyBool, tyEnum, tyChar, tyInt..tyInt64: @@ -1915,22 +1935,22 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode = result = newNodeIT(nkNilLit, info, t) else: result = newNodeIT(nkTupleConstr, info, t) - result.add(newNodeIT(nkNilLit, info, t)) - result.add(newNodeIT(nkNilLit, info, t)) + result.add(newNodeIT(nkNilLit, info, getSysType(c.graph, info, tyPointer))) + result.add(newNodeIT(nkNilLit, info, getSysType(c.graph, info, tyPointer))) of tyObject: result = newNodeIT(nkObjConstr, info, t) result.add(newNodeIT(nkEmpty, info, t)) # initialize inherited fields, and all in the correct order: var currPosition = 0 - getNullValueAux(t, t.n, result, conf, currPosition) + getNullValueAux(c, t, t.n, result, conf, currPosition) of tyArray: result = newNodeIT(nkBracket, info, t) for i in 0..<toInt(lengthOrd(conf, t)): - result.add getNullValue(elemType(t), info, conf) + result.add getNullValue(c, elemType(t), info, conf) of tyTuple: result = newNodeIT(nkTupleConstr, info, t) for a in t.kids: - result.add getNullValue(a, info, conf) + result.add getNullValue(c, a, info, conf) of tySet: result = newNodeIT(nkCurly, info, t) of tySequence, tyOpenArray: @@ -1958,7 +1978,7 @@ proc genVarSection(c: PCtx; n: PNode) = if s.position == 0: if importcCond(c, s): c.importcSym(a.info, s) else: - let sa = getNullValue(s.typ, a.info, c.config) + let sa = getNullValue(c, s.typ, a.info, c.config) #if s.ast.isNil: getNullValue(s.typ, a.info) #else: s.ast assert sa.kind != nkCall @@ -1980,7 +2000,7 @@ proc genVarSection(c: PCtx; n: PNode) = # the problem is that closure types are tuples in VM, but the types of its children # shouldn't have the same type as closure types. let tmp = c.genx(a[0], {gfNodeAddr}) - let sa = getNullValue(s.typ, a.info, c.config) + let sa = getNullValue(c, s.typ, a.info, c.config) let val = c.genx(sa) c.genAdditionalCopy(sa, opcWrDeref, tmp, 0, val) c.freeTemp(val) @@ -2165,7 +2185,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = if n[0].kind == nkSym: let s = n[0].sym if s.magic != mNone: - genMagic(c, n, dest, s.magic) + genMagic(c, n, dest, flags, s.magic) elif s.kind == skMethod: localError(c.config, n.info, "cannot call method " & s.name.s & " at compile time") @@ -2183,7 +2203,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = genLit(c, n, dest) of nkUIntLit..pred(nkNilLit): genLit(c, n, dest) of nkNilLit: - if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info, c.config), dest) + if not n.typ.isEmptyType: genLit(c, getNullValue(c, n.typ, n.info, c.config), dest) else: unused(c, n, dest) of nkAsgn, nkFastAsgn, nkSinkAsgn: unused(c, n, dest) @@ -2222,11 +2242,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = unused(c, n, dest) gen(c, n[0]) of nkHiddenStdConv, nkHiddenSubConv, nkConv: - genConv(c, n, n[1], dest) + genConv(c, n, n[1], dest, flags) of nkObjDownConv: - genConv(c, n, n[0], dest) + genConv(c, n, n[0], dest, flags) of nkObjUpConv: - genConv(c, n, n[0], dest) + genConv(c, n, n[0], dest, flags) of nkVarSection, nkLetSection: unused(c, n, dest) genVarSection(c, n) @@ -2235,18 +2255,21 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = #discard genProc(c, s) genLit(c, newSymNode(n[namePos].sym), dest) of nkChckRangeF, nkChckRange64, nkChckRange: - let - tmp0 = c.genx(n[0]) - tmp1 = c.genx(n[1]) - tmp2 = c.genx(n[2]) - c.gABC(n, opcRangeChck, tmp0, tmp1, tmp2) - c.freeTemp(tmp1) - c.freeTemp(tmp2) - if dest >= 0: - gABC(c, n, whichAsgnOpc(n), dest, tmp0) - c.freeTemp(tmp0) + if skipTypes(n.typ, abstractVar).kind in {tyUInt..tyUInt64}: + genConv(c, n, n[0], dest, flags) else: - dest = tmp0 + let + tmp0 = c.genx(n[0]) + tmp1 = c.genx(n[1]) + tmp2 = c.genx(n[2]) + c.gABC(n, opcRangeChck, tmp0, tmp1, tmp2) + c.freeTemp(tmp1) + c.freeTemp(tmp2) + if dest >= 0: + gABC(c, n, whichAsgnOpc(n), dest, tmp0) + c.freeTemp(tmp0) + else: + dest = tmp0 of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma, nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt, nkMixinStmt, nkBindStmt, declarativeDefs, nkMacroDef: @@ -2259,7 +2282,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of nkPar, nkClosure, nkTupleConstr: genTupleConstr(c, n, dest) of nkCast: if allowCast in c.features: - genConv(c, n, n[1], dest, opcCast) + genConv(c, n, n[1], dest, flags, opcCast) else: genCastIntFloat(c, n, dest) of nkTypeOfExpr: diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 23b41fd2e..45194e633 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -27,6 +27,7 @@ from std/envvars import getEnv, existsEnv, delEnv, putEnv, envPairs from std/os import getAppFilename from std/private/oscommon import dirExists, fileExists from std/private/osdirs import walkDir, createDir +from std/private/ospaths2 import getCurrentDir from std/times import cpuTime from std/hashes import hash @@ -341,8 +342,8 @@ proc registerAdditionalOps*(c: PCtx) = ## reproducible builds and users need to understand that this runs at CT. ## Note that `staticExec` can already do equal amount of damage so it's more ## of a semantic issue than a security issue. - registerCallback c, "stdlib.os.getCurrentDir", proc (a: VmArgs) {.nimcall.} = - setResult(a, os.getCurrentDir()) + registerCallback c, "stdlib.ospaths2.getCurrentDir", proc (a: VmArgs) {.nimcall.} = + setResult(a, getCurrentDir()) registerCallback c, "stdlib.osproc.execCmdEx", proc (a: VmArgs) {.nimcall.} = let options = getNode(a, 1).fromLit(set[osproc.ProcessOption]) a.setResult osproc.execCmdEx(getString(a, 0), options).toLit |