diff options
Diffstat (limited to 'compiler')
49 files changed, 1114 insertions, 661 deletions
diff --git a/compiler/asciitables.nim b/compiler/asciitables.nim new file mode 100644 index 000000000..c25d54bde --- /dev/null +++ b/compiler/asciitables.nim @@ -0,0 +1,83 @@ +#[ +move to std/asciitables.nim once stable, or to a nimble paackage +once compiler can depend on nimble +]# + +type Cell* = object + text*: string + width*, row*, col*, ncols*, nrows*: int + +iterator parseTableCells*(s: string, delim = '\t'): Cell = + ## iterates over all cells in a `delim`-delimited `s`, after a 1st + ## pass that computes number of rows, columns, and width of each column. + var widths: seq[int] + var cell: Cell + template update() = + if widths.len<=cell.col: + widths.setLen cell.col+1 + widths[cell.col] = cell.width + else: + widths[cell.col] = max(widths[cell.col], cell.width) + cell.width = 0 + + for a in s: + case a + of '\n': + update() + cell.col = 0 + cell.row.inc + elif a == delim: + update() + cell.col.inc + else: + # todo: consider multi-width chars when porting to non-ascii implementation + cell.width.inc + if s.len > 0 and s[^1] != '\n': + update() + + cell.ncols = widths.len + cell.nrows = cell.row + 1 + cell.row = 0 + cell.col = 0 + cell.width = 0 + + template update2() = + cell.width = widths[cell.col] + yield cell + cell.text = "" + cell.width = 0 + cell.col.inc + + template finishRow() = + for col in cell.col..<cell.ncols: + cell.col = col + update2() + cell.col = 0 + + for a in s: + case a + of '\n': + finishRow() + cell.row.inc + elif a == delim: + update2() + else: + cell.width.inc + cell.text.add a + + if s.len > 0 and s[^1] != '\n': + finishRow() + +proc alignTable*(s: string, delim = '\t', fill = ' ', sep = " "): string = + ## formats a `delim`-delimited `s` representing a table; each cell is aligned + ## to a width that's computed for each column; consecutive columns are + ## delimted by `sep`, and alignment space is filled using `fill`. + ## More customized formatting can be done by calling `parseTableCells` directly. + for cell in parseTableCells(s, delim): + result.add cell.text + for i in cell.text.len..<cell.width: + result.add fill + if cell.col < cell.ncols-1: + result.add sep + if cell.col == cell.ncols-1 and cell.row < cell.nrows - 1: + result.add '\n' diff --git a/compiler/ast.nim b/compiler/ast.nim index 5f5f296cb..24891d6d3 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1087,6 +1087,13 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym, when debugIds: registerId(result) +proc astdef*(s: PSym): PNode = + # get only the definition (initializer) portion of the ast + if s.ast != nil and s.ast.kind == nkIdentDefs: + s.ast[2] + else: + s.ast + proc isMetaType*(t: PType): bool = return t.kind in tyMetaTypes or (t.kind == tyStatic and t.n == nil) or diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index d00371dd8..ed6255004 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1501,7 +1501,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]), a.storage) of tyOpenArray, tyVarargs: var b: TLoc - case a.t.kind + case skipTypes(a.t, abstractVarRange).kind of tyOpenArray, tyVarargs: putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage) of tyString, tySequence: diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim index 144a45816..2d68a198e 100644 --- a/compiler/ccgmerge.nim +++ b/compiler/ccgmerge.nim @@ -31,6 +31,7 @@ const cfsData: "NIM_merge_DATA", cfsProcs: "NIM_merge_PROCS", cfsInitProc: "NIM_merge_INIT_PROC", + cfsDatInitProc: "NIM_merge_DATINIT_PROC", cfsTypeInit1: "NIM_merge_TYPE_INIT1", cfsTypeInit2: "NIM_merge_TYPE_INIT2", cfsTypeInit3: "NIM_merge_TYPE_INIT3", diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 266f63647..23e16cb93 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -436,30 +436,25 @@ proc mangleRecFieldName(m: BModule; field: PSym): Rope = if result == nil: internalError(m.config, field.info, "mangleRecFieldName") proc genRecordFieldsAux(m: BModule, n: PNode, - accessExpr: Rope, rectype: PType, + rectype: PType, check: var IntSet): Rope = result = nil case n.kind of nkRecList: for i in countup(0, sonsLen(n) - 1): - add(result, genRecordFieldsAux(m, n.sons[i], accessExpr, rectype, check)) + add(result, genRecordFieldsAux(m, n.sons[i], rectype, check)) of nkRecCase: if n.sons[0].kind != nkSym: internalError(m.config, n.info, "genRecordFieldsAux") - add(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check)) + add(result, genRecordFieldsAux(m, n.sons[0], rectype, check)) # prefix mangled name with "_U" to avoid clashes with other field names, # since identifiers are not allowed to start with '_' - let uname = rope("_U" & mangle(n.sons[0].sym.name.s)) - let ae = if accessExpr != nil: "$1.$2" % [accessExpr, uname] - else: uname var unionBody: Rope = nil for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind of nkOfBranch, nkElse: let k = lastSon(n.sons[i]) if k.kind != nkSym: - let sname = "S" & rope(i) - let a = genRecordFieldsAux(m, k, "$1.$2" % [ae, sname], rectype, - check) + let a = genRecordFieldsAux(m, k, rectype, check) if a != nil: if tfPacked notin rectype.flags: add(unionBody, "struct {") @@ -469,22 +464,20 @@ proc genRecordFieldsAux(m: BModule, n: PNode, else: addf(unionBody, "#pragma pack(push, 1)$nstruct{", []) add(unionBody, a) - addf(unionBody, "} $1;$n", [sname]) + addf(unionBody, "};$n", []) if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props: addf(unionBody, "#pragma pack(pop)$n", []) else: - add(unionBody, genRecordFieldsAux(m, k, ae, rectype, check)) + add(unionBody, genRecordFieldsAux(m, k, rectype, check)) else: internalError(m.config, "genRecordFieldsAux(record case branch)") if unionBody != nil: - addf(result, "union{$n$1} $2;$n", [unionBody, uname]) + addf(result, "union{$n$1};$n", [unionBody]) of nkSym: let field = n.sym if field.typ.kind == tyVoid: return #assert(field.ast == nil) let sname = mangleRecFieldName(m, field) - let ae = if accessExpr != nil: "$1.$2" % [accessExpr, sname] - else: sname - fillLoc(field.loc, locField, n, ae, OnUnknown) + fillLoc(field.loc, locField, n, sname, OnUnknown) # 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: @@ -505,7 +498,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode, else: internalError(m.config, n.info, "genRecordFieldsAux()") proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope = - result = genRecordFieldsAux(m, typ.n, nil, typ, check) + result = genRecordFieldsAux(m, typ.n, typ, check) proc fillObjectFields*(m: BModule; typ: PType) = # sometimes generic objects are not consistently merged. We patch over @@ -544,12 +537,18 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope, 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() {if(this->raiseId) popCurrentExceptionEx(this->raiseId);}$n", [name]) + appcg(m, result, "~$1() {if(this->raiseId) #popCurrentExceptionEx(this->raiseId);}$n", [name]) # hack: forward declare popCurrentExceptionEx() on top of type description, # proper request to generate popCurrentExceptionEx not possible for 2 reasons: # generated function will be below declared Exception type and circular dependency # between Exception and popCurrentExceptionEx function - result = genProcHeader(m, magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx")) & ";" & result + + let popExSym = magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx") + if lfDynamicLib in popExSym.loc.flags and sfImportc in popExSym.flags: + # echo popExSym.flags, " ma flags ", popExSym.loc.flags + result = "extern " & getTypeDescAux(m, popExSym.typ, check) & " " & mangleName(m, popExSym) & ";\L" & result + else: + result = genProcHeader(m, popExSym) & ";\L" & result hasField = true else: appcg(m, result, " {$n $1 Sup;$n", @@ -971,9 +970,9 @@ proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope; proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope = # bugfix: we need to search the type that contains the discriminator: - var objtype = objtype + var objtype = objtype.skipTypes(abstractPtrs) while lookupInRecord(objtype.n, d.name) == nil: - objtype = objtype.sons[0] + objtype = objtype.sons[0].skipTypes(abstractPtrs) if objtype.sym == nil: internalError(m.config, d.info, "anonymous obj with discriminator") result = "NimDT_$1_$2" % [rope($hashType(objtype)), rope(d.name.s.mangle)] @@ -1042,6 +1041,8 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope; else: internalError(m.config, n.info, "genObjectFields(nkRecCase)") of nkSym: var field = n.sym + # Do not produce code for void types + if isEmptyType(field.typ): return if field.bitsize == 0: if field.loc.r == nil: fillObjectFields(m, typ) if field.loc.t == nil: diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 457a6e176..e4f16f4ed 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1087,30 +1087,24 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope = result = getCopyright(conf, cfile) addIntTypes(result, conf) -proc genFilenames(m: BModule): Rope = - discard cgsym(m, "dbgRegisterFilename") - result = nil - for i in 0..<m.config.m.fileInfos.len: - result.addf("dbgRegisterFilename($1);$N", - [m.config.m.fileInfos[i].projPath.string.makeCString]) - proc genMainProc(m: BModule) = + ## this function is called in cgenWriteModules after all modules are closed, + ## it means raising dependency on the symbols is too late as it will not propogate + ## into other modules, only simple rope manipulations are allowed + const # The use of a volatile function pointer to call Pre/NimMainInner # prevents inlining of the NimMainInner function and dependent # functions, which might otherwise merge their stack frames. PreMainBody = "void PreMainInner(void) {$N" & - "\tsystemInit000();$N" & - "$1" & "$2" & "$3" & "}$N$N" & "void PreMain(void) {$N" & "\tvoid (*volatile inner)(void);$N" & - "\tsystemDatInit000();$N" & "\tinner = PreMainInner;$N" & - "$4$5" & + "$1" & "\t(*inner)();$N" & "}$N$N" @@ -1217,21 +1211,17 @@ proc genMainProc(m: BModule) = else: nimMain = PosixNimMain otherMain = PosixCMain - if m.g.breakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint") if optEndb in m.config.options: - m.g.breakpoints.add(m.genFilenames) + for i in 0..<m.config.m.fileInfos.len: + m.g.breakpoints.addf("dbgRegisterFilename($1);$N", + [m.config.m.fileInfos[i].projPath.string.makeCString]) let initStackBottomCall = if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N") inc(m.labels) appcg(m, m.s[cfsProcs], PreMainBody, [ - m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit, - if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone: - ropecg(m, "\t#initThreadVarsEmulation();$N") - else: - "".rope, - initStackBottomCall]) + m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit]) appcg(m, m.s[cfsProcs], nimMain, [m.g.mainModInit, initStackBottomCall, rope(m.labels)]) @@ -1260,22 +1250,61 @@ proc getInitName(m: PSym): Rope = proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit000") -proc registerModuleToMain(g: BModuleList; m: PSym) = - var - init = m.getInitName - datInit = m.getDatInitName - addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init]) - addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit]) - if sfSystemModule notin m.flags: + +proc registerModuleToMain(g: BModuleList; m: BModule) = + if m.s[cfsDatInitProc].len > 0: + let datInit = m.module.getDatInitName + addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit]) addf(g.mainDatInit, "\t$1();$N", [datInit]) + + # Initialization of TLS and GC should be done in between + # systemDatInit and systemInit calls if any + if sfSystemModule in m.module.flags: + if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone: + add(g.mainDatInit, ropecg(m, "\t#initThreadVarsEmulation();$N")) + if m.config.target.targetOS != osStandalone and m.config.selectedGC != gcNone: + add(g.mainDatInit, ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")) + + if m.s[cfsInitProc].len > 0: + let init = m.module.getInitName + addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init]) let initCall = "\t$1();$N" % [init] - if sfMainModule in m.flags: + if sfMainModule in m.module.flags: add(g.mainModInit, initCall) + elif sfSystemModule in m.module.flags: + add(g.mainDatInit, initCall) # systemInit must called right after systemDatInit if any else: add(g.otherModsInit, initCall) +proc genDatInitCode(m: BModule) = + ## this function is called in cgenWriteModules after all modules are closed, + ## it means raising dependency on the symbols is too late as it will not propogate + ## into other modules, only simple rope manipulations are allowed + + var moduleDatInitRequired = false + + var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" % + [getDatInitName(m.module)] + + for i in cfsTypeInit1..cfsDynLibInit: + if m.s[i].len != 0: + moduleDatInitRequired = true + add(prc, genSectionStart(i, m.config)) + add(prc, m.s[i]) + add(prc, genSectionEnd(i, m.config)) + + addf(prc, "}$N$N", []) + + if moduleDatInitRequired: + add(m.s[cfsDatInitProc], prc) + proc genInitCode(m: BModule) = - var initname = getInitName(m.module) + ## this function is called in cgenWriteModules after all modules are closed, + ## it means raising dependency on the symbols is too late as it will not propogate + ## into other modules, only simple rope manipulations are allowed + + var moduleInitRequired = false + let initname = getInitName(m.module) var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" % [initname] if m.typeNodes > 0: appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n", @@ -1290,82 +1319,115 @@ proc genInitCode(m: BModule) = # Keep a bogus frame in case the code needs one add(prc, ~"\tTFrame FR_; FR_.len = 0;$N") + if m.preInitProc.s(cpsLocals).len > 0: + moduleInitRequired = true + add(prc, genSectionStart(cpsLocals, m.config)) + add(prc, m.preInitProc.s(cpsLocals)) + add(prc, genSectionEnd(cpsLocals, m.config)) + + if m.preInitProc.s(cpsInit).len > 0: + moduleInitRequired = true + add(prc, genSectionStart(cpsInit, m.config)) + add(prc, m.preInitProc.s(cpsInit)) + add(prc, genSectionEnd(cpsInit, m.config)) + + if m.preInitProc.s(cpsStmts).len > 0: + moduleInitRequired = true + add(prc, genSectionStart(cpsStmts, m.config)) + add(prc, m.preInitProc.s(cpsStmts)) + add(prc, genSectionEnd(cpsStmts, m.config)) + addf(prc, "}$N", []) + + if m.initProc.gcFrameId > 0: + moduleInitRequired = true + add(prc, initGCFrame(m.initProc)) + + if m.initProc.s(cpsLocals).len > 0: + moduleInitRequired = true add(prc, genSectionStart(cpsLocals, m.config)) - add(prc, m.preInitProc.s(cpsLocals)) + add(prc, m.initProc.s(cpsLocals)) add(prc, genSectionEnd(cpsLocals, m.config)) + if m.initProc.s(cpsInit).len > 0 or m.initProc.s(cpsStmts).len > 0: + moduleInitRequired = true + if optStackTrace in m.initProc.options and frameDeclared notin m.flags: + # BUT: the generated init code might depend on a current frame, so + # declare it nevertheless: + incl m.flags, frameDeclared + if preventStackTrace notin m.flags: + var procname = makeCString(m.module.name.s) + add(prc, initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info))) + else: + add(prc, ~"\tTFrame FR_; FR_.len = 0;$N") + add(prc, genSectionStart(cpsInit, m.config)) - add(prc, m.preInitProc.s(cpsInit)) + add(prc, m.initProc.s(cpsInit)) add(prc, genSectionEnd(cpsInit, m.config)) add(prc, genSectionStart(cpsStmts, m.config)) - add(prc, m.preInitProc.s(cpsStmts)) + add(prc, m.initProc.s(cpsStmts)) add(prc, genSectionEnd(cpsStmts, m.config)) - addf(prc, "}$N", []) - - add(prc, initGCFrame(m.initProc)) - add(prc, genSectionStart(cpsLocals, m.config)) - add(prc, m.initProc.s(cpsLocals)) - add(prc, genSectionEnd(cpsLocals, m.config)) + if optStackTrace in m.initProc.options and preventStackTrace notin m.flags: + add(prc, deinitFrame(m.initProc)) - if optStackTrace in m.initProc.options and frameDeclared notin m.flags: - # BUT: the generated init code might depend on a current frame, so - # declare it nevertheless: - incl m.flags, frameDeclared - if preventStackTrace notin m.flags: - var procname = makeCString(m.module.name.s) - add(prc, initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info))) - else: - add(prc, ~"\tTFrame FR_; FR_.len = 0;$N") + if m.initProc.gcFrameId > 0: + moduleInitRequired = true + add(prc, deinitGCFrame(m.initProc)) - add(prc, genSectionStart(cpsInit, m.config)) - add(prc, m.initProc.s(cpsInit)) - add(prc, genSectionEnd(cpsInit, m.config)) - - add(prc, genSectionStart(cpsStmts, m.config)) - add(prc, m.initProc.s(cpsStmts)) - add(prc, genSectionEnd(cpsStmts, m.config)) - - if optStackTrace in m.initProc.options and preventStackTrace notin m.flags: - add(prc, deinitFrame(m.initProc)) - add(prc, deinitGCFrame(m.initProc)) addf(prc, "}$N$N", []) - prc.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N", - [getDatInitName(m.module)]) - - for i in cfsTypeInit1..cfsDynLibInit: - add(prc, genSectionStart(i, m.config)) - add(prc, m.s[i]) - add(prc, genSectionEnd(i, m.config)) - - addf(prc, "}$N$N", []) # we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because # that would lead to a *nesting* of merge sections which the merger does # not support. So we add it to another special section: ``cfsInitProc`` - add(m.s[cfsInitProc], prc) for i, el in pairs(m.extensionLoaders): if el != nil: let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" % [(i.ord - '0'.ord).rope, el] - add(m.s[cfsInitProc], ex) + moduleInitRequired = true + add(prc, ex) + + if moduleInitRequired or sfMainModule in m.module.flags: + add(m.s[cfsInitProc], prc) + + genDatInitCode(m) + registerModuleToMain(m.g, m) proc genModule(m: BModule, cfile: Cfile): Rope = + var moduleIsEmpty = true + result = getFileHeader(m.config, cfile) result.add(genMergeInfo(m)) + if m.config.cppCustomNamespace.len > 0: + result.add openNamespaceNim(m.config.cppCustomNamespace) + generateThreadLocalStorage(m) generateHeaders(m) - for i in countup(cfsHeaders, cfsProcs): - add(result, genSectionStart(i, m.config)) - add(result, m.s[i]) - add(result, genSectionEnd(i, m.config)) - if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders: - result.add openNamespaceNim(m.config.cppCustomNamespace) - add(result, m.s[cfsInitProc]) - if m.config.cppCustomNamespace.len > 0: result.add closeNamespaceNim() + add(result, genSectionStart(cfsHeaders, m.config)) + add(result, m.s[cfsHeaders]) + add(result, genSectionEnd(cfsHeaders, m.config)) + + for i in countup(cfsForwardTypes, cfsProcs): + if m.s[i].len > 0: + moduleIsEmpty = false + add(result, genSectionStart(i, m.config)) + add(result, m.s[i]) + add(result, genSectionEnd(i, m.config)) + + if m.s[cfsInitProc].len > 0: + moduleIsEmpty = false + add(result, m.s[cfsInitProc]) + if m.s[cfsDatInitProc].len > 0: + moduleIsEmpty = false + add(result, m.s[cfsDatInitProc]) + + if m.config.cppCustomNamespace.len > 0: + result.add closeNamespaceNim() + + if moduleIsEmpty: + result = nil proc newPreInitProc(m: BModule): BProc = result = newProc(nil, m) @@ -1522,27 +1584,30 @@ proc writeModule(m: BModule, pending: bool) = finishTypeDescriptions(m) if sfMainModule in m.module.flags: # generate main file: + genMainProc(m) add(m.s[cfsProcHeaders], m.g.mainModProcs) generateThreadVarsSize(m) var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {}) var code = genModule(m, cf) - when hasTinyCBackend: - if conf.cmd == cmdRun: - tccgen.compileCCode($code) - return - - if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached} - addFileToCompile(m.config, cf) + if code != nil: + when hasTinyCBackend: + if conf.cmd == cmdRun: + tccgen.compileCCode($code) + return + + if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached} + addFileToCompile(m.config, cf) elif pending and mergeRequired(m) and sfMainModule notin m.module.flags: let cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {}) mergeFiles(cfile, m) genInitCode(m) finishTypeDescriptions(m) var code = genModule(m, cf) - if not writeRope(code, cfile): - rawMessage(m.config, errCannotOpenFile, cfile.string) - addFileToCompile(m.config, cf) + if code != nil: + if not writeRope(code, cfile): + rawMessage(m.config, errCannotOpenFile, cfile.string) + addFileToCompile(m.config, cf) else: # Consider: first compilation compiles ``system.nim`` and produces # ``system.c`` but then compilation fails due to an error. This means @@ -1560,13 +1625,16 @@ proc updateCachedModule(m: BModule) = mergeFiles(cfile, m) genInitCode(m) finishTypeDescriptions(m) - var code = genModule(m, cf) - if not writeRope(code, cfile): - rawMessage(m.config, errCannotOpenFile, cfile.string) + if code != nil: + if not writeRope(code, cfile): + rawMessage(m.config, errCannotOpenFile, cfile.string) + addFileToCompile(m.config, cf) else: + if sfMainModule notin m.module.flags: + genMainProc(m) cf.flags = {CfileFlag.Cached} - addFileToCompile(m.config, cf) + addFileToCompile(m.config, cf) proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = result = n @@ -1579,15 +1647,25 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = if n != nil: m.initProc.options = initProcOptions(m) genStmts(m.initProc, n) - # cached modules need to registered too: - registerModuleToMain(m.g, m.module) if sfMainModule in m.module.flags: + # raise dependencies on behalf of genMainProc + if m.config.target.targetOS != osStandalone and m.config.selectedGC != gcNone: + discard cgsym(m, "initStackBottomWith") + if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone: + discard cgsym(m, "initThreadVarsEmulation") + + if m.g.breakpoints != nil: + discard cgsym(m, "dbgRegisterBreakpoint") + if optEndb in m.config.options: + discard cgsym(m, "dbgRegisterFilename") + if m.g.forwardedProcs.len == 0: incl m.flags, objHasKidsValid let disp = generateMethodDispatchers(graph) for x in disp: genProcAux(m, x.sym) - genMainProc(m) + + m.g.modules_closed.add m proc genForwardedProcs(g: BModuleList) = # Forward declared proc:s lack bodies when first encountered, so they're given diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 28e36364e..50b484e31 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -32,6 +32,7 @@ type cfsData, # section for C constant data cfsProcs, # section for C procs that are not inline cfsInitProc, # section for the C init proc + cfsDatInitProc, # section for the C datInit proc cfsTypeInit1, # section 1 for declarations of type information cfsTypeInit2, # section 2 for init of type information cfsTypeInit3, # section 3 for init of type information @@ -112,6 +113,7 @@ type mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope mapping*: Rope # the generated mapping file (if requested) modules*: seq[BModule] # list of all compiled modules + modules_closed*: seq[BModule] # list of the same compiled modules, but in the order they were closed forwardedProcs*: seq[PSym] # proc:s that did not yet have a body generatedHeader*: BModule breakPointId*: int @@ -191,8 +193,6 @@ proc newModuleList*(g: ModuleGraph): BModuleList = graph: g, nimtvDeclared: initIntSet()) iterator cgenModules*(g: BModuleList): BModule = - for m in g.modules: - # ultimately, we are iterating over the file ids here. - # some "files" won't have an associated cgen module (like stdin) - # and we must skip over them. - if m != nil: yield m + for m in g.modules_closed: + # iterate modules in the order they were closed + yield m diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim index 9fbf4a0b0..e1824316a 100644 --- a/compiler/cmdlinehelper.nim +++ b/compiler/cmdlinehelper.nim @@ -48,18 +48,10 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi if self.suggestMode: conf.command = "nimsuggest" - # These defines/options should not be enabled while processing nimscript - # bug #4446, #9420, #8991, #9589, #9153 - undefSymbol(conf.symbols, "profiler") - undefSymbol(conf.symbols, "memProfiler") - undefSymbol(conf.symbols, "nodejs") - - # bug #9120 - conf.globalOptions.excl(optTaintMode) - - proc runNimScriptIfExists(path: AbsoluteFile)= - if fileExists(path): - runNimScript(cache, path, freshDefines = false, conf) + template runNimScriptIfExists(path: AbsoluteFile) = + let p = path # eval once + if fileExists(p): + runNimScript(cache, p, freshDefines = false, conf) # Caution: make sure this stays in sync with `loadConfigs` if optSkipSystemConfigFile notin conf.globalOptions: @@ -88,9 +80,6 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi # 'nimsuggest foo.nims' means to just auto-complete the NimScript file discard - # Reload configuration from .cfg file - loadConfigs(DefaultConfig, cache, conf) - # now process command line arguments again, because some options in the # command line can overwite the config file's settings extccomp.initVars(conf) diff --git a/compiler/commands.nim b/compiler/commands.nim index b090a09a5..30521f9ca 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -142,7 +142,7 @@ proc splitSwitch(conf: ConfigRef; switch: string, cmd, arg: var string, pass: TC proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass, info: TLineInfo) = case arg.normalize - of "on": conf.options = conf.options + op + of "","on": conf.options = conf.options + op of "off": conf.options = conf.options - op else: localError(conf, info, errOnOrOffExpectedButXFound % arg) @@ -158,7 +158,7 @@ proc processOnOffSwitchOrList(conf: ConfigRef; op: TOptions, arg: string, pass: proc processOnOffSwitchG(conf: ConfigRef; op: TGlobalOptions, arg: string, pass: TCmdLinePass, info: TLineInfo) = case arg.normalize - of "on": conf.globalOptions = conf.globalOptions + op + of "", "on": conf.globalOptions = conf.globalOptions + op of "off": conf.globalOptions = conf.globalOptions - op else: localError(conf, info, errOnOrOffExpectedButXFound % arg) @@ -224,7 +224,7 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo case arg.normalize of "boehm": result = conf.selectedGC == gcBoehm of "refc": result = conf.selectedGC == gcRefc - of "v2": result = conf.selectedGC == gcV2 + of "v2": result = false of "markandsweep": result = conf.selectedGC == gcMarkAndSweep of "generational": result = false of "destructors": result = conf.selectedGC == gcDestructors @@ -291,6 +291,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool of "patterns": result = contains(conf.options, optPatterns) of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace) of "nilseqs": result = contains(conf.options, optNilSeqs) + of "oldast": result = contains(conf.options, optOldAst) else: invalidCmdLineOption(conf, passCmd1, switch, info) proc processPath(conf: ConfigRef; path: string, info: TLineInfo, @@ -413,26 +414,19 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; if pass in {passCmd2, passPP}: addExternalFileToLink(conf, AbsoluteFile arg) of "debuginfo": - expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optCDebug) + processOnOffSwitchG(conf, {optCDebug}, arg, pass, info) of "embedsrc": - expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optEmbedOrigSrc) + processOnOffSwitchG(conf, {optEmbedOrigSrc}, arg, pass, info) of "compileonly", "c": - expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optCompileOnly) + processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info) of "nolinking": - expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optNoLinking) + processOnOffSwitchG(conf, {optNoLinking}, arg, pass, info) of "nomain": - expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optNoMain) + processOnOffSwitchG(conf, {optNoMain}, arg, pass, info) of "forcebuild", "f": - expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optForceFullMake) + processOnOffSwitchG(conf, {optForceFullMake}, arg, pass, info) of "project": - expectNoArg(conf, switch, arg, pass, info) - incl conf.globalOptions, optWholeProject + processOnOffSwitchG(conf, {optWholeProject}, arg, pass, info) of "gc": expectArg(conf, switch, arg, pass, info) case arg.normalize @@ -442,7 +436,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "refc": conf.selectedGC = gcRefc of "v2": - conf.selectedGC = gcV2 + message(conf, info, warnDeprecated, "--gc:v2 is deprecated; using default gc") of "markandsweep": conf.selectedGC = gcMarkAndSweep defineSymbol(conf.symbols, "gcmarkandsweep") @@ -498,7 +492,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; else: undefSymbol(conf.symbols, "hotcodereloading") of "oldnewlines": case arg.normalize - of "on": + of "","on": conf.oldNewlines = true defineSymbol(conf.symbols, "nimOldNewlines") of "off": @@ -508,6 +502,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; localError(conf, info, errOnOrOffExpectedButXFound % arg) of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info) of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, arg, pass, info) + of "oldast": processOnOffSwitch(conf, {optOldAst}, arg, pass, info) of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info) of "floatchecks": processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info) @@ -590,16 +585,16 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info) of "import": expectArg(conf, switch, arg, pass, info) - if pass in {passCmd2, passPP}: conf.implicitImports.add arg + if pass in {passCmd2, passPP}: + conf.implicitImports.add findModule(conf, arg, toFullPath(conf, info)).string of "include": expectArg(conf, switch, arg, pass, info) - if pass in {passCmd2, passPP}: conf.implicitIncludes.add arg + if pass in {passCmd2, passPP}: + conf.implicitIncludes.add findModule(conf, arg, toFullPath(conf, info)).string of "listcmd": - expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optListCmd) + processOnOffSwitchG(conf, {optListCmd}, arg, pass, info) of "genmapping": - expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optGenMapping) + processOnOffSwitchG(conf, {optGenMapping}, arg, pass, info) of "os": expectArg(conf, switch, arg, pass, info) if pass in {passCmd1, passPP}: @@ -615,8 +610,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; elif cpu != conf.target.hostCPU: setTarget(conf.target, conf.target.targetOS, cpu) of "run", "r": - expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optRun) + processOnOffSwitchG(conf, {optRun}, arg, pass, info) of "errormax": expectArg(conf, switch, arg, pass, info) # Note: `nim check` (etc) can overwrite this. @@ -664,21 +658,16 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "v2": conf.symbolFiles = v2Sf else: localError(conf, info, "invalid option for --incremental: " & arg) of "skipcfg": - expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optSkipSystemConfigFile) + processOnOffSwitchG(conf, {optSkipSystemConfigFile}, arg, pass, info) of "skipprojcfg": - expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optSkipProjConfigFile) + processOnOffSwitchG(conf, {optSkipProjConfigFile}, arg, pass, info) of "skipusercfg": - expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optSkipUserConfigFile) + processOnOffSwitchG(conf, {optSkipUserConfigFile}, arg, pass, info) of "skipparentcfg": - expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optSkipParentConfigFiles) + processOnOffSwitchG(conf, {optSkipParentConfigFiles}, arg, pass, info) of "genscript", "gendeps": - expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optGenScript) - incl(conf.globalOptions, optCompileOnly) + processOnOffSwitchG(conf, {optGenScript}, arg, pass, info) + processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info) of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info) of "lib": expectArg(conf, switch, arg, pass, info) @@ -712,16 +701,13 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; expectNoArg(conf, switch, arg, pass, info) conf.ideCmd = ideUse of "stdout": - expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optStdout) + processOnOffSwitchG(conf, {optStdout}, arg, pass, info) of "listfullpaths": - expectNoArg(conf, switch, arg, pass, info) - incl conf.globalOptions, optListFullPaths + processOnOffSwitchG(conf, {optListFullPaths}, arg, pass, info) of "dynliboverride": dynlibOverride(conf, switch, arg, pass, info) of "dynliboverrideall": - expectNoArg(conf, switch, arg, pass, info) - incl conf.globalOptions, optDynlibOverrideAll + processOnOffSwitchG(conf, {optDynlibOverrideAll}, arg, pass, info) of "cs": # only supported for compatibility. Does nothing. expectArg(conf, switch, arg, pass, info) diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index 40af11e70..03ce9a5cf 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -296,7 +296,8 @@ proc makePtrType(c: Con, baseType: PType): PType = template genOp(opr, opname, ri) = let op = opr if op == nil: - globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator not found for type " & typeToString(t)) + globalError(c.graph.config, dest.info, "internal error: '" & opname & + "' operator not found for type " & typeToString(t)) elif op.ast[genericParamsPos].kind != nkEmpty: globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator is generic") patchHead op @@ -365,7 +366,7 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode = result.add genWasMoved(n, c) result.add tempAsNode -proc sinkParamIsLastReadCheck(c: var Con, s: PNode) = +proc sinkParamIsLastReadCheck(c: var Con, s: PNode) = assert s.kind == nkSym and s.sym.kind == skParam if not isLastRead(s, c): localError(c.graph.config, c.otherRead.info, "sink parameter `" & $s.sym.name.s & @@ -427,7 +428,7 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode = result = copyNode(arg) for i in 0..<arg.len: var branch = copyNode(arg[i]) - if arg[i].kind in {nkElifBranch, nkElifExpr}: + if arg[i].kind in {nkElifBranch, nkElifExpr}: branch.add p(arg[i][0], c) branch.add pArgIfTyped(arg[i][1]) else: @@ -442,13 +443,13 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode = branch = arg[i] # of branch conditions are constants branch[^1] = pArgIfTyped(arg[i][^1]) elif arg[i].kind in {nkElifBranch, nkElifExpr}: - branch = copyNode(arg[i]) + branch = copyNode(arg[i]) branch.add p(arg[i][0], c) branch.add pArgIfTyped(arg[i][1]) else: - branch = copyNode(arg[i]) + branch = copyNode(arg[i]) branch.add pArgIfTyped(arg[i][0]) - result.add branch + result.add branch else: # an object that is not temporary but passed to a 'sink' parameter # results in a copy. @@ -476,7 +477,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = result.add ri2 of nkBracketExpr: if ri[0].kind == nkSym and isUnpackedTuple(ri[0].sym): - # unpacking of tuple: move out the elements + # unpacking of tuple: move out the elements result = genSink(c, dest.typ, dest, ri) else: result = genCopy(c, dest.typ, dest, ri) @@ -509,11 +510,11 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = branch = ri[i] # of branch conditions are constants branch[^1] = moveOrCopyIfTyped(ri[i][^1]) elif ri[i].kind in {nkElifBranch, nkElifExpr}: - branch = copyNode(ri[i]) + branch = copyNode(ri[i]) branch.add p(ri[i][0], c) branch.add moveOrCopyIfTyped(ri[i][1]) else: - branch = copyNode(ri[i]) + branch = copyNode(ri[i]) branch.add moveOrCopyIfTyped(ri[i][0]) result.add branch of nkBracket: @@ -550,7 +551,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = sinkParamIsLastReadCheck(c, ri) var snk = genSink(c, dest.typ, dest, ri) snk.add ri - result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved)) + result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved)) elif ri.sym.kind != skParam and isLastRead(ri, c): # Rule 3: `=sink`(x, z); wasMoved(z) var snk = genSink(c, dest.typ, dest, ri) @@ -647,7 +648,7 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = let params = owner.typ.n for i in 1 ..< params.len: let param = params[i].sym - if param.typ.kind == tySink and hasDestructor(param.typ): + if param.typ.kind == tySink and hasDestructor(param.typ): c.destroys.add genDestroy(c, param.typ.skipTypes({tyGenericInst, tyAlias, tySink}), params[i]) let body = p(n, c) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index cd32d95d5..df9584576 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -86,7 +86,7 @@ proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) = result.add("\n") inc i if i in jumpTargets: result.add("L" & $i & ": End\n") - + # consider calling `asciitables.alignTable` proc echoCfg*(c: ControlFlowGraph; start=0; last = -1) {.deprecated.} = ## echos the ControlFlowGraph for debugging purposes. diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 67f4108e1..33cd98f38 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -40,6 +40,7 @@ type # already. See bug #3655 destFile*: AbsoluteFile thisDir*: AbsoluteDir + examples: string PDoc* = ref TDocumentor ## Alias to type less. @@ -118,7 +119,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, result.conf = conf result.cache = cache initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex), - conf.configVars, filename.string, {roSupportRawDirective}, + conf.configVars, filename.string, {roSupportRawDirective, roSupportMarkdown}, docgenFindFile, compilerMsgHandler) if conf.configVars.hasKey("doc.googleAnalytics"): @@ -378,6 +379,14 @@ proc testExample(d: PDoc; ex: PNode) = "_examples" & $d.exampleCounter & ".nim")) #let nimcache = outp.changeFileExt"" & "_nimcache" renderModule(ex, d.filename, outp.string, conf = d.conf) + d.examples.add "import r\"" & outp.string & "\"\n" + +proc runAllExamples(d: PDoc) = + if d.examples.len == 0: return + let outputDir = d.conf.getNimcacheDir / RelativeDir"runnableExamples" + let outp = outputDir / RelativeFile(extractFilename(d.filename.changeFileExt"" & + "_examples.nim")) + writeFile(outp, d.examples) let backend = if isDefined(d.conf, "js"): "js" elif isDefined(d.conf, "cpp"): "cpp" elif isDefined(d.conf, "objc"): "objc" @@ -400,11 +409,9 @@ proc extractImports(n: PNode; result: PNode) = for i in 0..<n.safeLen: extractImports(n[i], result) proc prepareExamples(d: PDoc; n: PNode) = - var docComment = newTree(nkCommentStmt) let loc = d.conf.toFileLineCol(n.info) docComment.comment = "autogenerated by docgen from " & loc - var runnableExamples = newTree(nkStmtList, docComment, newTree(nkImportStmt, newStrNode(nkStrLit, d.filename))) @@ -935,6 +942,7 @@ proc generateIndex*(d: PDoc) = writeIndexFile(d[], dest.string) proc writeOutput*(d: PDoc, useWarning = false) = + runAllExamples(d) var content = genOutFile(d) if optStdout in d.conf.globalOptions: writeRope(stdout, content) @@ -947,6 +955,7 @@ proc writeOutput*(d: PDoc, useWarning = false) = outfile.string) proc writeOutputJson*(d: PDoc, useWarning = false) = + runAllExamples(d) var modDesc: string for desc in d.modDesc: modDesc &= desc @@ -982,7 +991,7 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef; d.isPureRst = true var rst = parseRst(readFile(filen.string), filen.string, 0, 1, d.hasToc, - {roSupportRawDirective}, conf) + {roSupportRawDirective, roSupportMarkdown}, conf) var modDesc = newStringOfCap(30_000) renderRstToOut(d[], rst, modDesc) d.modDesc = rope(modDesc) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 23f723e29..2c5af6433 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -63,8 +63,8 @@ compiler gcc: result = ( name: "gcc", objExt: "o", - optSpeed: " -O3 -ffast-math ", - optSize: " -Os -ffast-math ", + optSpeed: " -O3 ", + optSize: " -Os ", compilerExe: "gcc", cppCompiler: "g++", compileTmpl: "-c $options $include -o $objfile $file", @@ -88,8 +88,8 @@ compiler nintendoSwitchGCC: result = ( name: "switch_gcc", objExt: "o", - optSpeed: " -O3 -ffast-math ", - optSize: " -Os -ffast-math ", + optSpeed: " -O3 ", + optSize: " -Os ", compilerExe: "aarch64-none-elf-gcc", cppCompiler: "aarch64-none-elf-g++", compileTmpl: "-w -MMD -MP -MF $dfile -c $options $include -o $objfile $file", @@ -153,6 +153,13 @@ compiler vcc: structStmtFmt: "$3$n$1 $2", props: {hasCpp, hasAssume, hasDeclspec}) +compiler clangcl: + result = vcc() + result.name = "clang_cl" + result.compilerExe = "clang-cl" + result.cppCompiler = "clang-cl" + result.linkerExe = "clang-cl" + # Intel C/C++ Compiler compiler icl: result = vcc() @@ -353,7 +360,8 @@ const pcc(), ucc(), icl(), - icc()] + icc(), + clangcl()] hExt* = ".h" @@ -567,6 +575,8 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string = includeCmd = "" compilePattern = getCompilerExe(conf, c, cfile.cname) + includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)])) + var cf = if noAbsolutePaths(conf): AbsoluteFile extractFilename(cfile.cname.string) else: cfile.cname diff --git a/compiler/guards.nim b/compiler/guards.nim index a01c023e4..46e18d3bf 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -257,9 +257,9 @@ proc canon*(n: PNode; o: Operators): PNode = for i in 0 ..< n.len: result.sons[i] = canon(n.sons[i], o) elif n.kind == nkSym and n.sym.kind == skLet and - n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin + + n.sym.astdef.getMagic in (someEq + someAdd + someMul + someMin + someMax + someHigh + {mUnaryLt} + someSub + someLen + someDiv): - result = n.sym.ast.copyTree + result = n.sym.astdef.copyTree else: result = n case result.getMagic @@ -395,8 +395,8 @@ proc usefulFact(n: PNode; o: Operators): PNode = # if a: # ... # We make can easily replace 'a' by '2 < x' here: - if n.sym.ast != nil: - result = usefulFact(n.sym.ast, o) + if n.sym.astdef != nil: + result = usefulFact(n.sym.astdef, o) elif n.kind == nkStmtListExpr: result = usefulFact(n.lastSon, o) diff --git a/compiler/importer.nim b/compiler/importer.nim index 131b1ad8a..eef0c9bb9 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -92,6 +92,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = rawImportSymbol(c, e) e = nextIdentIter(it, fromMod.tab) else: rawImportSymbol(c, s) + suggestSym(c.config, n.info, s, c.graph.usageSym, false) proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) = var i: TTabIter @@ -161,9 +162,9 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym = localError(c.config, n.info, "A module cannot import itself") if sfDeprecated in result.flags: if result.constraint != nil: - message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s) + message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s & " is deprecated") else: - message(c.config, n.info, warnDeprecated, result.name.s) + message(c.config, n.info, warnDeprecated, result.name.s & " is deprecated") suggestSym(c.config, n.info, result, c.graph.usageSym, false) importStmtResult.add newSymNode(result, n.info) #newStrNode(toFullPath(c.config, f), n.info) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 4d22c4224..8625f2fe1 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -94,6 +94,7 @@ type options: TOptions module: BModule g: PGlobals + generatedParamCopies: IntSet beforeRetNeeded: bool unique: int # for temp identifier generation blocks: seq[TBlock] @@ -918,35 +919,15 @@ proc countJsParams(typ: PType): int = const nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkStringToCString, + nkObjConstr, nkTupleConstr, nkBracket, nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix, nkCommand, nkHiddenCallConv, nkCallStrLit} proc needsNoCopy(p: PProc; y: PNode): bool = - # if the node is a literal object constructor we have to recursively - # check the expressions passed into it - case y.kind - of nkObjConstr: - for arg in y.sons[1..^1]: - if not needsNoCopy(p, arg[1]): - return false - of nkTupleConstr: - for arg in y.sons: - var arg = arg - if arg.kind == nkExprColonExpr: - arg = arg[1] - if not needsNoCopy(p, arg): - return false - of nkBracket: - for arg in y.sons: - if not needsNoCopy(p, arg): - return false - of nodeKindsNeedNoCopy: - return true - else: - return (mapType(y.typ) != etyBaseIndex and - (skipTypes(y.typ, abstractInst).kind in - {tyRef, tyPtr, tyLent, tyVar, tyCString, tyProc} + IntegralTypes)) - return true + return y.kind in nodeKindsNeedNoCopy or + ((mapType(y.typ) != etyBaseIndex or (y.kind == nkSym and y.sym.kind == skParam)) and + (skipTypes(y.typ, abstractInst).kind in + {tyRef, tyPtr, tyLent, tyVar, tyCString, tyProc} + IntegralTypes)) proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = var a, b: TCompRes @@ -969,7 +950,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 (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: + if x.typ.kind == tyVar or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") @@ -1252,12 +1233,29 @@ proc genProcForSymIfNeeded(p: PProc, s: PSym) = if owner != nil: add(owner.locals, newp) else: attachProc(p, newp, 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.r, genTypeInfo(p, s.typ)] + add(owner.locals, owner.indentLine(copy)) + return + owner = owner.up + 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 == nil: internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) + if s.kind == skParam: + genCopyForParamIfNeeded(p, n) let k = mapType(p, s.typ) if k == etyBaseIndex: r.typ = etyBaseIndex @@ -1504,6 +1502,8 @@ proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output: for i in countup(1, sonsLen(rec) - 1): createRecordVarAux(p, lastSon(rec.sons[i]), excludedFieldIDs, output) of nkSym: + # Do not produce code for void types + if isEmptyType(rec.sym.typ): return if rec.sym.id notin excludedFieldIDs: if output.len > 0: output.add(", ") output.addf("$#: ", [mangleName(p.module, rec.sym)]) @@ -1886,12 +1886,13 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mLtStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)") of mIsNil: + # we want to accept undefined, so we == if mapType(n[1].typ) != etyBaseIndex: - unaryExpr(p, n, r, "", "($1 === null)") + unaryExpr(p, n, r, "", "($1 == null)") else: var x: TCompRes gen(p, n[1], x) - r.res = "($# === null && $# === 0)" % [x.address, x.res] + r.res = "($# == null && $# === 0)" % [x.address, x.res] of mEnumToStr: genRepr(p, n, r) of mNew, mNewFinalize: genNew(p, n) of mChr: gen(p, n.sons[1], r) @@ -1924,7 +1925,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2") else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)") of mSetLengthStr: - binaryExpr(p, n, r, "mnewString", "($1 === null ? $3 = mnewString($2) : $3.length = $2)") + binaryExpr(p, n, r, "mnewString", "($1 == null ? $3 = mnewString($2) : $3.length = $2)") of mSetLengthSeq: var x, y: TCompRes gen(p, n.sons[1], x) @@ -2005,6 +2006,10 @@ proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = if a.typ == etyBaseIndex: addf(r.res, "[$1, $2]", [a.address, a.res]) else: + if not needsNoCopy(p, n[i]): + let typ = n[i].typ.skipTypes(abstractInst) + useMagic(p, "nimCopy") + a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] add(r.res, a.res) add(r.res, "]") @@ -2017,9 +2022,13 @@ proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = var it = n.sons[i] if it.kind == nkExprColonExpr: it = it.sons[1] gen(p, it, a) + let typ = it.typ.skipTypes(abstractInst) if a.typ == etyBaseIndex: addf(r.res, "Field$#: [$#, $#]", [i.rope, a.address, a.res]) else: + if not needsNoCopy(p, it): + useMagic(p, "nimCopy") + a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] addf(r.res, "Field$#: $#", [i.rope, a.res]) r.res.add("}") @@ -2039,17 +2048,12 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = fieldIDs.incl(f.id) let typ = val.typ.skipTypes(abstractInst) - if (typ.kind in IntegralTypes+{tyCstring, tyRef, tyPtr} and - mapType(p, typ) != etyBaseIndex) or - a.typ == etyBaseIndex or - needsNoCopy(p, it.sons[1]): - discard - else: - useMagic(p, "nimCopy") - a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] if a.typ == etyBaseIndex: addf(initList, "$#: [$#, $#]", [f.loc.r, a.address, a.res]) else: + if not needsNoCopy(p, val): + useMagic(p, "nimCopy") + a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] addf(initList, "$#: $#", [f.loc.r, a.res]) let t = skipTypes(n.typ, abstractInst + skipPtrs) createObjInitList(p, t, fieldIDs, initList) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 635e6f08d..a4414d186 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -17,7 +17,7 @@ import hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream, - wordrecg, lineinfos, pathutils + wordrecg, lineinfos, pathutils, parseutils const MaxLineLength* = 80 # lines longer than this lead to a warning @@ -307,20 +307,6 @@ template tokenEndPrevious(tok, pos) = when defined(nimpretty): tok.offsetB = L.offsetBase + pos -{.push overflowChecks: off.} -# We need to parse the largest uint literal without overflow checks -proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int = - var i = start - if i < s.len and s[i] in {'0'..'9'}: - b = 0 - while i < s.len and s[i] in {'0'..'9'}: - b = b * 10 + (ord(s[i]) - ord('0')) - inc(i) - while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored - result = i - start -{.pop.} # overflowChecks - - template eatChar(L: var TLexer, t: var TToken, replacementChar: char) = add(t.literal, replacementChar) inc(L.bufpos) @@ -586,33 +572,43 @@ proc getNumber(L: var TLexer, result: var TToken) = of floatTypes: result.fNumber = parseFloat(result.literal) of tkUint64Lit: - xi = 0 - let len = unsafeParseUInt(result.literal, xi) - if len != result.literal.len or len == 0: - raise newException(ValueError, "invalid integer: " & $xi) - result.iNumber = xi + var iNumber: uint64 + var len: int + try: + len = parseBiggestUInt(result.literal, iNumber) + except ValueError: + raise newException(OverflowError, "number out of range: " & $result.literal) + if len != result.literal.len: + raise newException(ValueError, "invalid integer: " & $result.literal) + result.iNumber = cast[int64](iNumber) else: - result.iNumber = parseBiggestInt(result.literal) - - # Explicit bounds checks + var iNumber: int64 + var len: int + try: + len = parseBiggestInt(result.literal, iNumber) + except ValueError: + raise newException(OverflowError, "number out of range: " & $result.literal) + if len != result.literal.len: + raise newException(ValueError, "invalid integer: " & $result.literal) + result.iNumber = iNumber + + # Explicit bounds checks. Only T.high needs to be considered + # since result.iNumber can't be negative. let outOfRange = case result.tokType - of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high) - of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or - result.iNumber > BiggestInt(uint8.high)) - of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high) - of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or - result.iNumber > BiggestInt(uint16.high)) - of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high) - of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or - result.iNumber > BiggestInt(uint32.high)) + of tkInt8Lit: result.iNumber > int8.high + of tkUInt8Lit: result.iNumber > BiggestInt(uint8.high) + of tkInt16Lit: result.iNumber > int16.high + of tkUInt16Lit: result.iNumber > BiggestInt(uint16.high) + of tkInt32Lit: result.iNumber > int32.high + of tkUInt32Lit: result.iNumber > BiggestInt(uint32.high) else: false if outOfRange: lexMessageLitNum(L, "number out of range: '$1'", startpos) # Promote int literal to int64? Not always necessary, but more consistent if result.tokType == tkIntLit: - if (result.iNumber < low(int32)) or (result.iNumber > high(int32)): + if result.iNumber > high(int32): result.tokType = tkInt64Lit except ValueError: diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index b1ecf779e..165d75821 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -65,7 +65,7 @@ const warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", warnXIsNeverRead: "'$1' is never read", warnXmightNotBeenInit: "'$1' might not have been initialized", - warnDeprecated: "$1 is deprecated", + warnDeprecated: "$1", warnConfigDeprecated: "config file '$1' is deprecated", warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)", warnUnknownMagic: "unknown magic '$1' might crash the compiler", @@ -171,7 +171,7 @@ proc computeNotesVerbosity(): array[0..3, TNoteKinds] = warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd, hintSource, hintGlobalVar, hintGCStats} result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf, - hintProcessing, hintPattern, hintExecuting, hintLinking} + hintProcessing, hintPattern, hintExecuting, hintLinking, hintCC} const NotesVerbosity* = computeNotesVerbosity() diff --git a/compiler/lists.nim b/compiler/lists.nim deleted file mode 100644 index bfd052204..000000000 --- a/compiler/lists.nim +++ /dev/null @@ -1,28 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# This module is deprecated, don't use it. -# TODO Remove this - -import os - -static: - echo "WARNING: imported deprecated module compiler/lists.nim, use seq ore lists from the standard library" - -proc appendStr*(list: var seq[string]; data: string) {.deprecated.} = - # just use system.add - list.add(data) - -proc includeStr(list: var seq[string]; data: string): bool {.deprecated.} = - if list.contains(data): - result = true - else: - result = false - list.add data - diff --git a/compiler/lookups.nim b/compiler/lookups.nim index db03ac2e0..8bc263485 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -89,7 +89,7 @@ proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym = prettybase.replaceDeprecated(conf, n.info, s, result) else: message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " & - s.name.s) + s.name.s & " is deprecated") proc localSearchInScope*(c: PContext, s: PIdent): PSym = result = strTableGet(c.currentScope.symbols, s) @@ -150,7 +150,7 @@ type proc getSymRepr*(conf: ConfigRef; s: PSym): string = case s.kind - of skProc, skFunc, skMethod, skConverter, skIterator: + of routineKinds, skType: result = getProcHeader(conf, s) else: result = s.name.s @@ -174,7 +174,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = # maybe they can be made skGenericParam as well. if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and s.typ.kind != tyGenericParam: - message(c.config, s.info, hintXDeclaredButNotUsed, getSymRepr(c.config, s)) + message(c.config, s.info, hintXDeclaredButNotUsed, s.name.s) s = nextIter(it, scope.symbols) proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string; @@ -190,7 +190,7 @@ proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) = wrongRedefinition(c, info, sym.name.s, conflict.info) proc addDecl*(c: PContext, sym: PSym) = - let conflict = c.currentScope.addUniqueSym(sym) + let conflict = strTableInclReportConflict(c.currentScope.symbols, sym, true) if conflict != nil: wrongRedefinition(c, sym.info, sym.name.s, conflict.info) diff --git a/compiler/main.nim b/compiler/main.nim index 4e28ed483..49c2666ea 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -76,6 +76,8 @@ proc commandCompileToC(graph: ModuleGraph) = registerPass(graph, cgenPass) compileProject(graph) + if graph.config.errorCounter > 0: + return # issue #9933 cgenWriteModules(graph.backend, conf) if conf.cmd != cmdRun: let proj = changeFileExt(conf.projectFull, "") @@ -90,6 +92,7 @@ proc commandJsonScript(graph: ModuleGraph) = when not defined(leanCompiler): proc commandCompileToJS(graph: ModuleGraph) = + let conf = graph.config #incl(gGlobalOptions, optSafeCode) setTarget(graph.config.target, osJS, cpuJS) #initDefines() @@ -98,6 +101,8 @@ when not defined(leanCompiler): semanticPasses(graph) registerPass(graph, JSgenPass) compileProject(graph) + if optGenScript in graph.config.globalOptions: + writeDepsFile(graph, toGeneratedFile(conf, conf.projectFull, "")) proc interactivePasses(graph: ModuleGraph) = initDefines(graph.config.symbols) @@ -165,6 +170,7 @@ proc mainCommand*(graph: ModuleGraph) = of "c", "cc", "compile", "compiletoc": # compile means compileToC currently conf.cmd = cmdCompileToC + defineSymbol(graph.config.symbols, "c") commandCompileToC(graph) of "cpp", "compiletocpp": conf.cmd = cmdCompileToCpp @@ -281,6 +287,7 @@ proc mainCommand*(graph: ModuleGraph) = (key: "defined_symbols", val: definedSymbols), (key: "lib_paths", val: %libpaths), (key: "out", val: %conf.outFile.string), + (key: "nimcache", val: %getNimcacheDir(conf).string), (key: "hints", val: hints), (key: "warnings", val: warnings), ] diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim index f0718c4eb..9e27a2d7d 100644 --- a/compiler/modulepaths.nim +++ b/compiler/modulepaths.nim @@ -147,7 +147,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string = # hacky way to implement 'x / y /../ z': result = renderTree(n, {renderNoComments}).replace(" ") of nkDotExpr: - localError(conf, n.info, warnDeprecated, "using '.' instead of '/' in import paths") + localError(conf, n.info, warnDeprecated, "using '.' instead of '/' in import paths is deprecated") result = renderTree(n, {renderNoComments}).replace(".", "/") of nkImportAs: result = getModuleName(conf, n.sons[0]) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 7e6b67cbe..0dd5820b4 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -135,6 +135,10 @@ const WarningColor = fgYellow HintTitle = "Hint: " HintColor = fgGreen + # NOTE: currently line info line numbers start with 1, + # but column numbers start with 0, however most editors expect + # first column to be 1, so we need to +1 here + ColOffset* = 1 proc getInfoContextLen*(conf: ConfigRef): int = return conf.m.msgContext.len proc setInfoContextLen*(conf: ConfigRef; L: int) = setLen(conf.m.msgContext, L) @@ -155,7 +159,10 @@ template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string = if fileIdx.int32 < 0 or conf == nil: "???" else: - conf.m.fileInfos[fileIdx.int32].projPath.string + if optListFullPaths in conf.globalOptions: + conf.m.fileInfos[fileIdx.int32].fullPath.string + else: + conf.m.fileInfos[fileIdx.int32].projPath.string proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string = if fileIdx.int32 < 0 or conf == nil: result = "???" @@ -192,10 +199,10 @@ proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string = result = "???" return let absPath = conf.m.fileInfos[info.fileIndex.int32].fullPath.string - let relPath = conf.m.fileInfos[info.fileIndex.int32].projPath.string if optListFullPaths in conf.globalOptions: result = absPath else: + let relPath = conf.m.fileInfos[info.fileIndex.int32].projPath.string result = if absPath.len < relPath.len: absPath else: relPath proc toLinenumber*(info: TLineInfo): int {.inline.} = @@ -208,7 +215,9 @@ proc toFileLine*(conf: ConfigRef; info: TLineInfo): string {.inline.} = result = toFilename(conf, info) & ":" & $info.line proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} = - result = toFilename(conf, info) & "(" & $info.line & ", " & $info.col & ")" + # consider calling `helpers.lineInfoToString` instead + result = toFilename(conf, info) & "(" & $info.line & ", " & + $(info.col + ColOffset) & ")" proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info) @@ -359,7 +368,7 @@ proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) = styledMsgWriteln(styleBright, PosFormat % [toMsgFilename(conf, context.info), coordToStr(context.info.line.int), - coordToStr(context.info.col+1)], + coordToStr(context.info.col+ColOffset)], resetStyle, message) info = context.info @@ -446,7 +455,7 @@ proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): s of hintMin..hintMax: HintTitle else: ErrorTitle result = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int), - coordToStr(info.col+1)] & + coordToStr(info.col+ColOffset)] & title & getMessageStr(msg, arg) @@ -483,11 +492,8 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, color = HintColor if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)] inc(conf.hintCounter) - # NOTE: currently line info line numbers start with 1, - # but column numbers start with 0, however most editors expect - # first column to be 1, so we need to +1 here let x = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int), - coordToStr(info.col+1)] + coordToStr(info.col+ColOffset)] let s = getMessageStr(msg, arg) if not ignoreMsg: diff --git a/compiler/options.nim b/compiler/options.nim index 80d665d62..d39b0a268 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -40,7 +40,8 @@ type # please make sure we have under 32 options optMemTracker, optHotCodeReloading, optLaxStrings, - optNilSeqs + optNilSeqs, + optOldAst TOptions* = set[TOption] TGlobalOption* = enum # **keep binary compatible** @@ -77,7 +78,7 @@ type # please make sure we have under 32 options optShowAllMismatches # show all overloading resolution candidates optWholeProject # for 'doc2': output any dependency optMixedMode # true if some module triggered C++ codegen - optListFullPaths + optListFullPaths # use full paths in toMsgFilename, toFilename optNoNimblePath optDynlibOverrideAll @@ -132,7 +133,7 @@ type TSystemCC* = enum ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc, - ccTcc, ccPcc, ccUcc, ccIcl, ccIcc + ccTcc, ccPcc, ccUcc, ccIcl, ccIcc, ccClangCl CfileFlag* {.pure.} = enum Cached, ## no need to recompile this time @@ -482,16 +483,7 @@ proc setDefaultLibpath*(conf: ConfigRef) = conf.libpath = AbsoluteDir parentNimLibPath proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile = - # on Windows, 'expandFilename' calls getFullPathName which doesn't do - # case corrections, so we have to use this convoluted way of retrieving - # the true filename (see tests/modules and Nimble uses 'import Uri' instead - # of 'import uri'): - when defined(windows): - result = AbsoluteFile path.string.expandFilename - for x in walkFiles(result.string): - return AbsoluteFile x - else: - result = AbsoluteFile path.string.expandFilename + result = AbsoluteFile path.string.expandFilename proc shortenDir*(conf: ConfigRef; dir: string): string {. deprecated: "use 'relativeTo' instead".} = diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index bbaf7a069..db79e3eb9 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -115,20 +115,19 @@ proc compileConstraints(p: PNode, result: var TPatternCode; conf: ConfigRef) = else: patternError(p, conf) -proc semNodeKindConstraints*(p: PNode; conf: ConfigRef): PNode = +proc semNodeKindConstraints*(n: PNode; conf: ConfigRef; start: Natural): PNode = ## does semantic checking for a node kind pattern and compiles it into an ## efficient internal format. - assert p.kind == nkCurlyExpr - result = newNodeI(nkStrLit, p.info) + result = newNodeI(nkStrLit, n.info) result.strVal = newStringOfCap(10) result.strVal.add(chr(aqNone.ord)) - if p.len >= 2: - for i in 1..<p.len: - compileConstraints(p.sons[i], result.strVal, conf) + if n.len >= 2: + for i in start..<n.len: + compileConstraints(n[i], result.strVal, conf) if result.strVal.len > MaxStackSize-1: - internalError(conf, p.info, "parameter pattern too complex") + internalError(conf, n.info, "parameter pattern too complex") else: - patternError(p, conf) + patternError(n, conf) result.strVal.add(ppEof) type diff --git a/compiler/parser.nim b/compiler/parser.nim index 54b360a24..c9626c527 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -332,12 +332,15 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode = parMessage(p, errIdentifierExpected, p.tok) break of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi: + let lineinfo = parLineinfo(p) var accm = "" while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi}: accm.add(tokToStr(p.tok)) getTok(p) - result.add(newIdentNodeP(p.lex.cache.getIdent(accm), p)) + let node = newNodeI(nkIdent, lineinfo) + node.ident = p.lex.cache.getIdent(accm) + result.add(node) of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit: result.add(newIdentNodeP(p.lex.cache.getIdent(tokToStr(p.tok)), p)) getTok(p) @@ -1764,23 +1767,8 @@ proc parseSection(p: var TParser, kind: TNodeKind, else: parMessage(p, errIdentifierExpected, p.tok) -proc parseConstant(p: var TParser): PNode = - #| constant = identWithPragma (colon typeDesc)? '=' optInd expr indAndComment - result = newNodeP(nkConstDef, p) - addSon(result, identWithPragma(p)) - if p.tok.tokType == tkColon: - getTok(p) - optInd(p, result) - addSon(result, parseTypeDesc(p)) - else: - addSon(result, p.emptyNode) - eat(p, tkEquals) - optInd(p, result) - addSon(result, parseExpr(p)) - indAndComment(p, result) - proc parseEnum(p: var TParser): PNode = - #| enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+ + #| enum = 'enum' optInd (symbol optPragmas optInd ('=' optInd expr COMMENT?)? comma?)+ result = newNodeP(nkEnumTy, p) getTok(p) addSon(result, p.emptyNode) @@ -1790,25 +1778,35 @@ proc parseEnum(p: var TParser): PNode = while true: var a = parseSymbol(p) if a.kind == nkEmpty: return + + var symPragma = a + var pragma: PNode + if p.tok.tokType == tkCurlyDotLe: + pragma = optPragmas(p) + symPragma = newNodeP(nkPragmaExpr, p) + addSon(symPragma, a) + addSon(symPragma, pragma) + if p.tok.indent >= 0 and p.tok.indent <= p.currInd: - add(result, a) + add(result, symPragma) break + if p.tok.tokType == tkEquals and p.tok.indent < 0: getTok(p) - optInd(p, a) - var b = a - a = newNodeP(nkEnumFieldDef, p) - addSon(a, b) - addSon(a, parseExpr(p)) + optInd(p, symPragma) + var b = symPragma + symPragma = newNodeP(nkEnumFieldDef, p) + addSon(symPragma, b) + addSon(symPragma, parseExpr(p)) if p.tok.indent < 0 or p.tok.indent >= p.currInd: - rawSkipComment(p, a) + rawSkipComment(p, symPragma) if p.tok.tokType == tkComma and p.tok.indent < 0: getTok(p) - rawSkipComment(p, a) + rawSkipComment(p, symPragma) else: if p.tok.indent < 0 or p.tok.indent >= p.currInd: - rawSkipComment(p, a) - addSon(result, a) + rawSkipComment(p, symPragma) + addSon(result, symPragma) if p.tok.indent >= 0 and p.tok.indent <= p.currInd or p.tok.tokType == tkEof: break @@ -1920,6 +1918,8 @@ proc parseObject(p: var TParser): PNode = result = newNodeP(nkObjectTy, p) getTok(p) if p.tok.tokType == tkCurlyDotLe and p.validInd: + # Deprecated since v0.20.0 + parMessage(p, warnDeprecated, "type pragmas follow the type name; this form of writing pragmas is deprecated") addSon(result, parsePragma(p)) else: addSon(result, p.emptyNode) @@ -1992,13 +1992,42 @@ proc parseTypeClass(p: var TParser): PNode = proc parseTypeDef(p: var TParser): PNode = #| #| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux + #| indAndComment? / identVisDot genericParamList? pragma '=' optInd typeDefAux #| indAndComment? result = newNodeP(nkTypeDef, p) - addSon(result, identWithPragma(p, allowDot=true)) + var identifier = identVis(p, allowDot=true) + var identPragma = identifier + var pragma: PNode + var genericParam: PNode + var noPragmaYet = true + + if p.tok.tokType == tkCurlyDotLe: + pragma = optPragmas(p) + identPragma = newNodeP(nkPragmaExpr, p) + addSon(identPragma, identifier) + addSon(identPragma, pragma) + noPragmaYet = false + if p.tok.tokType == tkBracketLe and p.validInd: - addSon(result, parseGenericParamList(p)) + if not noPragmaYet: + # Deprecated since v0.20.0 + parMessage(p, warnDeprecated, "pragma before generic parameter list is deprecated") + genericParam = parseGenericParamList(p) else: - addSon(result, p.emptyNode) + genericParam = p.emptyNode + + if noPragmaYet: + pragma = optPragmas(p) + if pragma.kind != nkEmpty: + identPragma = newNodeP(nkPragmaExpr, p) + addSon(identPragma, identifier) + addSon(identPragma, pragma) + elif p.tok.tokType == tkCurlyDotLe: + parMessage(p, errGenerated, "pragma already present") + + addSon(result, identPragma) + addSon(result, genericParam) + if p.tok.tokType == tkEquals: result.info = parLineInfo(p) getTok(p) @@ -2035,6 +2064,23 @@ proc parseVariable(p: var TParser): PNode = result[^1] = postExprBlocks(p, result[^1]) indAndComment(p, result) +proc parseConstant(p: var TParser): PNode = + #| constant = (parseVarTuple / identWithPragma) (colon typeDesc)? '=' optInd expr indAndComment + if p.tok.tokType == tkParLe: result = parseVarTuple(p) + else: + result = newNodeP(nkConstDef, p) + addSon(result, identWithPragma(p)) + if p.tok.tokType == tkColon: + getTok(p) + optInd(p, result) + addSon(result, parseTypeDesc(p)) + else: + addSon(result, p.emptyNode) + eat(p, tkEquals) + optInd(p, result) + addSon(result, parseExpr(p)) + indAndComment(p, result) + proc parseBind(p: var TParser, k: TNodeKind): PNode = #| bindStmt = 'bind' optInd qualifiedIdent ^+ comma #| mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma diff --git a/compiler/passes.nim b/compiler/passes.nim index 9ccd2240a..50fc13e1e 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -102,9 +102,9 @@ proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNod for module in items(implicits): # implicit imports should not lead to a module importing itself if m.position != resolveMod(graph.config, module, relativeTo).int32: - var importStmt = newNodeI(nodeKind, gCmdLineInfo) + var importStmt = newNodeI(nodeKind, m.info) var str = newStrNode(nkStrLit, module) - str.info = gCmdLineInfo + str.info = m.info importStmt.addSon str if not processTopLevelStmt(graph, importStmt, a): break @@ -155,7 +155,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool { while true: openParsers(p, fileIdx, s, graph.cache, graph.config) - if sfSystemModule notin module.flags: + if module.owner == nil or module.owner.name.s != "stdlib" or module.name.s == "distros": # XXX what about caching? no processing then? what if I change the # modules to include between compilation runs? we'd need to track that # in ROD files. I think we should enable this feature only diff --git a/compiler/pathutils.nim b/compiler/pathutils.nim index 80c479898..7417845c0 100644 --- a/compiler/pathutils.nim +++ b/compiler/pathutils.nim @@ -17,11 +17,9 @@ type AbsoluteDir* = distinct string RelativeFile* = distinct string RelativeDir* = distinct string + AnyPath* = AbsoluteFile|AbsoluteDir|RelativeFile|RelativeDir -proc isEmpty*(x: AbsoluteFile): bool {.inline.} = x.string.len == 0 -proc isEmpty*(x: AbsoluteDir): bool {.inline.} = x.string.len == 0 -proc isEmpty*(x: RelativeFile): bool {.inline.} = x.string.len == 0 -proc isEmpty*(x: RelativeDir): bool {.inline.} = x.string.len == 0 +proc isEmpty*(x: AnyPath): bool {.inline.} = x.string.len == 0 proc copyFile*(source, dest: AbsoluteFile) = os.copyFile(source.string, dest.string) @@ -44,14 +42,13 @@ proc cmpPaths*(x, y: AbsoluteDir): int {.borrow.} proc createDir*(x: AbsoluteDir) {.borrow.} +proc `$`*(x: AnyPath): string = x.string + when true: proc eqImpl(x, y: string): bool {.inline.} = result = cmpPaths(x, y) == 0 - proc `==`*(x, y: AbsoluteFile): bool = eqImpl(x.string, y.string) - proc `==`*(x, y: AbsoluteDir): bool = eqImpl(x.string, y.string) - proc `==`*(x, y: RelativeFile): bool = eqImpl(x.string, y.string) - proc `==`*(x, y: RelativeDir): bool = eqImpl(x.string, y.string) + proc `==`*[T: AnyPath](x, y: T): bool = eqImpl(x.string, y.string) proc `/`*(base: AbsoluteDir; f: RelativeFile): AbsoluteFile = #assert isAbsolute(base.string) @@ -88,6 +85,12 @@ when true: when isMainModule: doAssert AbsoluteDir"/Users/me///" / RelativeFile"z.nim" == AbsoluteFile"/Users/me/z.nim" doAssert relativePath("/foo/bar.nim", "/foo/", '/') == "bar.nim" + doAssert $RelativeDir"foo/bar" == "foo/bar" + doAssert RelativeDir"foo/bar" == RelativeDir"foo/bar" + doAssert RelativeFile"foo/bar".changeFileExt(".txt") == RelativeFile"foo/bar.txt" + doAssert RelativeFile"foo/bar".addFileExt(".txt") == RelativeFile"foo/bar.txt" + doAssert not RelativeDir"foo/bar".isEmpty + doAssert RelativeDir"".isEmpty when isMainModule and defined(windows): let nasty = string(AbsoluteDir(r"C:\Users\rumpf\projects\nim\tests\nimble\nimbleDir\linkedPkgs\pkgB-#head\../../simplePkgs/pkgB-#head/") / RelativeFile"pkgA/module.nim") diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index f67e74f11..3967fa22d 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -73,6 +73,7 @@ const wThread, wRaises, wLocks, wTags, wGcSafe} forVarPragmas* = {wInject, wGensym} allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas + enumFieldPragmas* = {wDeprecated} proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode = let p = procAst[pragmasPos] @@ -241,7 +242,7 @@ proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) = # deprecated as of 0.18.1 message(c.config, n.info, warnDeprecated, "use {.experimental: \"codeReordering.\".} instead; " & - (if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}")) + (if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}") & " is deprecated") proc processCallConv(c: PContext, n: PNode) = if n.kind in nkPragmaCallKinds and n.len == 2 and n.sons[1].kind == nkIdent: @@ -446,14 +447,14 @@ proc processPop(c: PContext, n: PNode) = proc processDefine(c: PContext, n: PNode) = if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent): defineSymbol(c.config.symbols, n[1].ident.s) - message(c.config, n.info, warnDeprecated, "define") + message(c.config, n.info, warnDeprecated, "define is deprecated") else: invalidPragma(c, n) proc processUndef(c: PContext, n: PNode) = if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent): undefSymbol(c.config.symbols, n[1].ident.s) - message(c.config, n.info, warnDeprecated, "undef") + message(c.config, n.info, warnDeprecated, "undef is deprecated") else: invalidPragma(c, n) @@ -740,7 +741,7 @@ proc semCustomPragma(c: PContext, n: PNode): PNode = result.kind = n.kind # pragma(arg) -> pragma: arg proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, - validPragmas: TSpecialWords): bool = + validPragmas: TSpecialWords, comesFromPush: bool) : bool = var it = n.sons[i] var key = if it.kind in nkPragmaCallKinds and it.len > 1: it.sons[0] else: it if key.kind == nkBracketExpr: @@ -783,7 +784,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate) incl(sym.flags, sfAllUntyped) - message(c.config, n.info, warnDeprecated, "use 'untyped' parameters instead; immediate") + message(c.config, n.info, warnDeprecated, "use 'untyped' parameters instead; immediate is deprecated") else: invalidPragma(c, it) of wDirty: if sym.kind == skTemplate: incl(sym.flags, sfDirty) @@ -880,7 +881,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wExplain: sym.flags.incl sfExplain of wDeprecated: - if sym != nil and sym.kind in routineKinds + {skType}: + if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet}: if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it) incl(sym.flags, sfDeprecated) elif sym != nil and sym.kind != skModule: @@ -1090,10 +1091,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wThis: if it.kind in nkPragmaCallKinds and it.len == 2: c.selfName = considerQuotedIdent(c, it[1]) - message(c.config, n.info, warnDeprecated, "the '.this' pragma") + message(c.config, n.info, warnDeprecated, "the '.this' pragma is deprecated") elif it.kind == nkIdent or it.len == 1: c.selfName = getIdent(c.cache, "self") - message(c.config, n.info, warnDeprecated, "the '.this' pragma") + message(c.config, n.info, warnDeprecated, "the '.this' pragma is deprecated") else: localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments") of wNoRewrite: @@ -1111,15 +1112,25 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, else: sym.flags.incl sfUsed of wLiftLocals: discard else: invalidPragma(c, it) - elif sym == nil or (sym != nil and sym.kind in {skVar, skLet, skParam, - skField, skProc, skFunc, skConverter, skMethod, skType}): - n.sons[i] = semCustomPragma(c, it) - elif sym != nil: - illegalCustomPragma(c, it, sym) + elif comesFromPush and whichKeyword(ident) in {wTags, wRaises}: + discard "ignore the .push pragma; it doesn't apply" else: - invalidPragma(c, it) + if sym == nil or (sym != nil and sym.kind in {skVar, skLet, skParam, + skField, skProc, skFunc, skConverter, skMethod, skType}): + n.sons[i] = semCustomPragma(c, it) + elif sym != nil: + illegalCustomPragma(c, it, sym) + else: + invalidPragma(c, it) + +proc overwriteLineInfo(n: PNode; info: TLineInfo) = + n.info = info + for i in 0..<safeLen(n): + overwriteLineInfo(n[i], info) proc mergePragmas(n, pragmas: PNode) = + var pragmas = copyTree(pragmas) + overwriteLineInfo pragmas, n.info if n[pragmasPos].kind == nkEmpty: n[pragmasPos] = pragmas else: @@ -1134,7 +1145,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, pushInfoContext(c.config, n.info) var i = 0 while i < o.len: - if singlePragma(c, sym, o, i, validPragmas): + if singlePragma(c, sym, o, i, validPragmas, true): internalError(c.config, n.info, "implicitPragmas") inc i popInfoContext(c.config) @@ -1163,7 +1174,7 @@ proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = if n == nil: return var i = 0 while i < n.len: - if singlePragma(c, sym, n, i, validPragmas): break + if singlePragma(c, sym, n, i, validPragmas, false): break inc i proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index bfff86479..db60dafcc 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -42,15 +42,18 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; proc (a: VmArgs) = body - template cbos(name, body) {.dirty.} = + template cbexc(name, exc, body) {.dirty.} = result.registerCallback "stdlib.system." & astToStr(name), proc (a: VmArgs) = errorMsg = "" try: body - except OSError: + except exc: errorMsg = getCurrentExceptionMsg() + template cbos(name, body) {.dirty.} = + cbexc(name, OSError, body) + # Idea: Treat link to file as a file, but ignore link to directory to prevent # endless recursions out of the box. cbos listFiles: @@ -63,8 +66,10 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; os.removeFile getString(a, 0) cbos createDir: os.createDir getString(a, 0) - cbos getOsError: - setResult(a, errorMsg) + + result.registerCallback "stdlib.system.getError", + proc (a: VmArgs) = setResult(a, errorMsg) + cbos setCurrentDir: os.setCurrentDir getString(a, 0) cbos getCurrentDir: @@ -155,6 +160,12 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; setResult(a, os.getAppFilename()) cbconf cppDefine: options.cppDefine(conf, a.getString(0)) + cbexc stdinReadLine, EOFError: + setResult(a, "") + setResult(a, stdin.readLine()) + cbexc stdinReadAll, EOFError: + setResult(a, "") + setResult(a, stdin.readAll()) proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; freshDefines=true; conf: ConfigRef) = diff --git a/compiler/sem.nim b/compiler/sem.nim index 8332af346..3763c9b84 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -556,8 +556,6 @@ proc isEmptyTree(n: PNode): bool = else: result = false proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = - if n.kind == nkDefer: - localError(c.config, n.info, "defer statement not supported at top level") if c.topStmts == 0 and not isImportSystemStmt(c.graph, n): if sfSystemModule notin c.module.flags and not isEmptyTree(n): c.importTable.addSym c.graph.systemModule # import the "System" identifier diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index 5d676dc76..9f1ef313b 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -14,7 +14,7 @@ type TLiftCtx = object - c: PContext + graph: ModuleGraph info: TLineInfo # for construction kind: TTypeAttachedOp fn: PSym @@ -22,7 +22,7 @@ type recurse: bool proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) -proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; +proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym {.discardable.} proc at(a, i: PNode, elemType: PType): PNode = @@ -33,7 +33,7 @@ proc at(a, i: PNode, elemType: PType): PNode = proc liftBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) = for i in 0 ..< t.len: - let lit = lowerings.newIntLit(c.c.graph, x.info, i) + let lit = lowerings.newIntLit(c.graph, x.info, i) liftBodyAux(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i])) proc dotField(x: PNode, f: PSym): PNode = @@ -49,6 +49,14 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) = liftBodyAux(c, f.typ, body, x.dotField(f), y.dotField(f)) of nkNilLit: discard of nkRecCase: + if c.kind in {attachedSink, attachedAsgn, attachedDeepCopy}: + ## the value needs to be destroyed before we assign the selector + ## or the value is lost + let prevKind = c.kind + c.kind = attachedDestructor + liftBodyObj(c, n, body, x, y) + c.kind = prevKind + # copy the selector: liftBodyObj(c, n[0], body, x, y) # we need to generate a case statement: @@ -66,26 +74,25 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) = liftBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y) caseStmt.add(branch) body.add(caseStmt) - localError(c.c.config, c.info, "cannot lift assignment operator to 'case' object") of nkRecList: for t in items(n): liftBodyObj(c, t, body, x, y) else: - illFormedAstLocal(n, c.c.config) + illFormedAstLocal(n, c.graph.config) -proc genAddr(c: PContext; x: PNode): PNode = +proc genAddr(g: ModuleGraph; x: PNode): PNode = if x.kind == nkHiddenDeref: - checkSonsLen(x, 1, c.config) + checkSonsLen(x, 1, g.config) result = x.sons[0] else: - result = newNodeIT(nkHiddenAddr, x.info, makeVarType(c, x.typ)) + result = newNodeIT(nkHiddenAddr, x.info, makeVarType(x.typ.owner, x.typ)) addSon(result, x) -proc newAsgnCall(c: PContext; op: PSym; x, y: PNode): PNode = +proc newAsgnCall(g: ModuleGraph; op: PSym; x, y: PNode): PNode = #if sfError in op.flags: # localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s) result = newNodeI(nkCall, x.info) result.add newSymNode(op) - result.add genAddr(c, x) + result.add genAddr(g, x) result.add y proc newAsgnStmt(le, ri: PNode): PNode = @@ -98,10 +105,10 @@ proc newOpCall(op: PSym; x: PNode): PNode = result.add(newSymNode(op)) result.add x -proc destructorCall(c: PContext; op: PSym; x: PNode): PNode = +proc destructorCall(g: ModuleGraph; op: PSym; x: PNode): PNode = result = newNodeIT(nkCall, x.info, op.typ.sons[0]) result.add(newSymNode(op)) - result.add genAddr(c, x) + result.add genAddr(g, x) proc newDeepCopyCall(op: PSym; x, y: PNode): PNode = result = newAsgnStmt(x, newOpCall(op, y)) @@ -120,13 +127,13 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode; else: op = field if op == nil: - op = liftBody(c.c, t, c.kind, c.info) + op = liftBody(c.graph, t, c.kind, c.info) if sfError in op.flags: incl c.fn.flags, sfError else: - markUsed(c.c.config, c.info, op, c.c.graph.usageSym) + markUsed(c.graph.config, c.info, op, c.graph.usageSym) onUse(c.info, op) - body.add newAsgnCall(c.c, op, x, y) + body.add newAsgnCall(c.graph, op, x, y) result = true proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = @@ -134,9 +141,9 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = of attachedDestructor: let op = t.destructor if op != nil: - markUsed(c.c.config, c.info, op, c.c.graph.usageSym) + markUsed(c.graph.config, c.info, op, c.graph.usageSym) onUse(c.info, op) - body.add destructorCall(c.c, op, x) + body.add destructorCall(c.graph, op, x) result = true of attachedAsgn: result = considerAsgnOrSink(c, t, body, x, y, t.assignment) @@ -145,7 +152,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = of attachedDeepCopy: let op = t.deepCopy if op != nil: - markUsed(c.c.config, c.info, op, c.c.graph.usageSym) + markUsed(c.graph.config, c.info, op, c.graph.usageSym) onUse(c.info, op) body.add newDeepCopyCall(op, x, y) result = true @@ -162,13 +169,13 @@ proc addVar(father, v, value: PNode) = addSon(father, vpart) proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode = - var temp = newSym(skTemp, getIdent(c.c.cache, lowerings.genPrefix), c.fn, c.info) - temp.typ = getSysType(c.c.graph, body.info, tyInt) + var temp = newSym(skTemp, getIdent(c.graph.cache, lowerings.genPrefix), c.fn, c.info) + temp.typ = getSysType(c.graph, body.info, tyInt) incl(temp.flags, sfFromGeneric) var v = newNodeI(nkVarSection, c.info) result = newSymNode(temp) - v.addVar(result, lowerings.newIntLit(c.c.graph, body.info, first)) + v.addVar(result, lowerings.newIntLit(c.graph, body.info, first)) body.add v proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode = @@ -178,22 +185,22 @@ proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode = proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode = result = newNodeI(nkWhileStmt, c.info, 2) - let cmp = genBuiltin(c.c.graph, mLeI, "<=", i) - cmp.add genHigh(c.c.graph, dest) - cmp.typ = getSysType(c.c.graph, c.info, tyBool) + let cmp = genBuiltin(c.graph, mLeI, "<=", i) + cmp.add genHigh(c.graph, dest) + cmp.typ = getSysType(c.graph, c.info, tyBool) result.sons[0] = cmp result.sons[1] = newNodeI(nkStmtList, c.info) proc addIncStmt(c: var TLiftCtx; body, i: PNode) = - let incCall = genBuiltin(c.c.graph, mInc, "inc", i) - incCall.add lowerings.newIntLit(c.c.graph, c.info, 1) + let incCall = genBuiltin(c.graph, mInc, "inc", i) + incCall.add lowerings.newIntLit(c.graph, c.info, 1) body.add incCall -proc newSeqCall(c: PContext; x, y: PNode): PNode = +proc newSeqCall(g: ModuleGraph; x, y: PNode): PNode = # don't call genAddr(c, x) here: - result = genBuiltin(c.graph, mNewSeq, "newSeq", x) - let lenCall = genBuiltin(c.graph, mLengthSeq, "len", y) - lenCall.typ = getSysType(c.graph, x.info, tyInt) + result = genBuiltin(g, mNewSeq, "newSeq", x) + let lenCall = genBuiltin(g, mLengthSeq, "len", y) + lenCall.typ = getSysType(g, x.info, tyInt) result.add lenCall proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = @@ -204,7 +211,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = defaultOp(c, t, body, x, y) of tyArray: if tfHasAsgn in t.flags: - let i = declareCounter(c, body, firstOrd(c.c.config, t)) + let i = declareCounter(c, body, firstOrd(c.graph.config, t)) let whileLoop = genWhileLoop(c, i, x) let elemType = t.lastSon liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType), @@ -216,12 +223,12 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = of tySequence: # note that tfHasAsgn is propagated so we need the check on # 'selectedGC' here to determine if we have the new runtime. - if c.c.config.selectedGC == gcDestructors: + if c.graph.config.selectedGC == gcDestructors: discard considerOverloadedOp(c, t, body, x, y) elif tfHasAsgn in t.flags: if c.kind != attachedDestructor: - body.add newSeqCall(c.c, x, y) - let i = declareCounter(c, body, firstOrd(c.c.config, t)) + body.add newSeqCall(c.graph, x, y) + let i = declareCounter(c, body, firstOrd(c.graph.config, t)) let whileLoop = genWhileLoop(c, i, x) let elemType = t.lastSon liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType), @@ -235,11 +242,12 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = discard considerOverloadedOp(c, t, body, x, y) else: defaultOp(c, t, body, x, y) - of tyObject, tyDistinct: + of tyObject: + if not considerOverloadedOp(c, t, body, x, y): + liftBodyObj(c, t.n, body, x, y) + of tyDistinct: if not considerOverloadedOp(c, t, body, x, y): - if t.sons[0] != nil: - liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y) - if t.kind == tyObject: liftBodyObj(c, t.n, body, x, y) + liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y) of tyTuple: liftBodyTup(c, t, body, x, y) of tyProc: @@ -250,20 +258,20 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = # have to go through some indirection; we delegate this to the codegen: let call = newNodeI(nkCall, c.info, 2) call.typ = t - call.sons[0] = newSymNode(createMagic(c.c.graph, "deepCopy", mDeepCopy)) + call.sons[0] = newSymNode(createMagic(c.graph, "deepCopy", mDeepCopy)) call.sons[1] = y body.add newAsgnStmt(x, call) of tyVarargs, tyOpenArray: - localError(c.c.config, c.info, "cannot copy openArray") + localError(c.graph.config, c.info, "cannot copy openArray") of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything, tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt, tyTypeDesc, tyGenericInvocation, tyForward: - internalError(c.c.config, c.info, "assignment requested for type: " & typeToString(t)) + internalError(c.graph.config, c.info, "assignment requested for type: " & typeToString(t)) of tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink: liftBodyAux(c, lastSon(t), body, x, y) - of tyOptAsRef: internalError(c.c.config, "liftBodyAux") + of tyOptAsRef: internalError(c.graph.config, "liftBodyAux") proc newProcType(info: TLineInfo; owner: PSym): PType = result = newType(tyProc, owner) @@ -279,26 +287,54 @@ proc addParam(procType: PType; param: PSym) = addSon(procType.n, newSymNode(param)) rawAddSon(procType, param.typ) -proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; +proc liftBodyDistinctType(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym = + assert typ.kind == tyDistinct + let baseType = typ[0] + case kind + of attachedAsgn: + if baseType.assignment == nil: + discard liftBody(g, baseType, kind, info) + typ.assignment = baseType.assignment + result = typ.assignment + of attachedSink: + if baseType.sink == nil: + discard liftBody(g, baseType, kind, info) + typ.sink = baseType.sink + result = typ.sink + of attachedDeepCopy: + if baseType.deepCopy == nil: + discard liftBody(g, baseType, kind, info) + typ.deepCopy = baseType.deepCopy + result = typ.deepCopy + of attachedDestructor: + if baseType.destructor == nil: + discard liftBody(g, baseType, kind, info) + typ.destructor = baseType.destructor + result = typ.destructor + +proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym = + if typ.kind == tyDistinct: + return liftBodyDistinctType(g, typ, kind, info) + var a: TLiftCtx a.info = info - a.c = c + a.graph = g a.kind = kind let body = newNodeI(nkStmtList, info) let procname = case kind - of attachedAsgn: getIdent(c.cache, "=") - of attachedSink: getIdent(c.cache, "=sink") - of attachedDeepCopy: getIdent(c.cache, "=deepcopy") - of attachedDestructor: getIdent(c.cache, "=destroy") + of attachedAsgn: getIdent(g.cache, "=") + of attachedSink: getIdent(g.cache, "=sink") + of attachedDeepCopy: getIdent(g.cache, "=deepcopy") + of attachedDestructor: getIdent(g.cache, "=destroy") result = newSym(skProc, procname, typ.owner, info) a.fn = result a.asgnForType = typ - let dest = newSym(skParam, getIdent(c.cache, "dest"), result, info) - let src = newSym(skParam, getIdent(c.cache, "src"), result, info) - dest.typ = makeVarType(c, typ) + let dest = newSym(skParam, getIdent(g.cache, "dest"), result, info) + let src = newSym(skParam, getIdent(g.cache, "src"), result, info) + dest.typ = makeVarType(typ.owner, typ) src.typ = typ result.typ = newProcType(info, typ.owner) @@ -309,7 +345,7 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src)) # recursion is handled explicitly, do not register the type based operation # before 'liftBodyAux': - if c.config.selectedGC == gcDestructors and + if g.config.selectedGC == gcDestructors and typ.kind in {tySequence, tyString} and body.len == 0: discard "do not cache it yet" else: @@ -328,17 +364,17 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; incl result.flags, sfFromGeneric -proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym = +proc getAsgnOrLiftBody(g: ModuleGraph; typ: PType; info: TLineInfo): PSym = let t = typ.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink}) result = t.assignment if result.isNil: - result = liftBody(c, t, attachedAsgn, info) + result = liftBody(g, t, attachedAsgn, info) -proc overloadedAsgn(c: PContext; dest, src: PNode): PNode = - let a = getAsgnOrLiftBody(c, dest.typ, dest.info) - result = newAsgnCall(c, a, dest, src) +proc overloadedAsgn(g: ModuleGraph; dest, src: PNode): PNode = + let a = getAsgnOrLiftBody(g, dest.typ, dest.info) + result = newAsgnCall(g, a, dest, src) -proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) = +proc liftTypeBoundOps*(g: ModuleGraph; typ: PType; info: TLineInfo) = ## In the semantic pass this is called in strategic places ## to ensure we lift assignment, destructors and moves properly. ## The later 'destroyer' pass depends on it. @@ -350,11 +386,11 @@ proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) = let typ = typ.skipTypes({tyGenericInst, tyAlias}) # we generate the destructor first so that other operators can depend on it: if typ.destructor == nil: - liftBody(c, typ, attachedDestructor, info) + liftBody(g, typ, attachedDestructor, info) if typ.assignment == nil: - liftBody(c, typ, attachedAsgn, info) + liftBody(g, typ, attachedAsgn, info) if typ.sink == nil: - liftBody(c, typ, attachedSink, info) + liftBody(g, typ, attachedSink, info) -#proc patchResolvedTypeBoundOp*(c: PContext; n: PNode): PNode = +#proc patchResolvedTypeBoundOp*(g: ModuleGraph; n: PNode): PNode = # if n.kind == nkCall and diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 7e0ea5490..3723d3fc1 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -287,7 +287,18 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string = let ident = considerQuotedIdent(c, f, n).s if nfDotField in n.flags and nfExplicitCall notin n.flags: - result = errUndeclaredField % ident & result + let sym = n.sons[1].typ.sym + var typeHint = "" + if sym == nil: + # Perhaps we're in a `compiles(foo.bar)` expression, or + # in a concept, eg: + # ExplainedConcept {.explain.} = concept x + # x.foo is int + # We coudl use: `(c.config $ n.sons[1].info)` to get more context. + discard + else: + typeHint = " for type " & getProcHeader(c.config, sym) + result = errUndeclaredField % ident & typeHint & " " & result else: if result.len == 0: result = errUndeclaredRoutine % ident else: result = errBadRoutine % [ident, result] diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 735c6f6b1..a05bda32d 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -286,6 +286,13 @@ proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType = result = newTypeS(kind, c) addSonSkipIntLit(result, baseType) +proc makeVarType*(owner: PSym, baseType: PType; kind = tyVar): PType = + if baseType.kind == kind: + result = baseType + else: + result = newType(kind, owner) + addSonSkipIntLit(result, baseType) + proc makeTypeDesc*(c: PContext, typ: PType): PType = if typ.kind == tyTypeDesc: result = typ diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index ddec457a1..82f948492 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -108,6 +108,8 @@ const proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus = result = convOK + # We're interested in the inner type and not in the static tag + var src = src.skipTypes({tyStatic}) if sameType(castDest, src) and castDest.sym == src.sym: # don't annoy conversions that may be needed on another processor: if castDest.kind notin IntegralTypes+{tyRange}: @@ -230,7 +232,7 @@ proc semConv(c: PContext, n: PNode): PNode = # special case to make MyObject(x = 3) produce a nicer error message: if n[1].kind == nkExprEqExpr and targetType.skipTypes(abstractPtrs).kind == tyObject: - localError(c.config, n.info, "object contruction uses ':', not '='") + localError(c.config, n.info, "object construction uses ':', not '='") var op = semExprWithType(c, n.sons[1]) if targetType.isMetaType: let final = inferWithMetatype(c, targetType, op, true) @@ -403,8 +405,8 @@ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode = n[1] = makeTypeSymNode(c, lhsType, n[1].info) lhsType = n[1].typ else: - internalAssert c.config, lhsType.base.kind != tyNone - if c.inGenericContext > 0 and lhsType.base.containsGenericType: + if lhsType.base.kind == tyNone or + (c.inGenericContext > 0 and lhsType.base.containsGenericType): # BUGFIX: don't evaluate this too early: ``T is void`` return @@ -710,16 +712,20 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = let a = getConstExpr(c.module, n.sons[i], c.graph) if a == nil: return n call.add(a) + #echo "NOW evaluating at compile time: ", call.renderTree - if sfCompileTime in callee.flags: - result = evalStaticExpr(c.module, c.graph, call, c.p.owner) - if result.isNil: - localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call)) - else: result = fixupTypeAfterEval(c, result, n) + if c.inStaticContext == 0 or sfNoSideEffect in callee.flags: + if sfCompileTime in callee.flags: + result = evalStaticExpr(c.module, c.graph, call, c.p.owner) + if result.isNil: + localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call)) + else: result = fixupTypeAfterEval(c, result, n) + else: + result = evalConstExpr(c.module, c.graph, call) + if result.isNil: result = n + else: result = fixupTypeAfterEval(c, result, n) else: - result = evalConstExpr(c.module, c.graph, call) - if result.isNil: result = n - else: result = fixupTypeAfterEval(c, result, n) + result = n #if result != n: # echo "SUCCESS evaluated at compile time: ", call.renderTree @@ -798,7 +804,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = result = magicsAfterOverloadResolution(c, result, flags) if result.typ != nil and not (result.typ.kind == tySequence and result.typ.sons[0].kind == tyEmpty): - liftTypeBoundOps(c, result.typ, n.info) + liftTypeBoundOps(c.graph, result.typ, n.info) #result = patchResolvedTypeBoundOp(c, result) if c.matchedConcept == nil: result = evalAtCompileTime(c, result) @@ -913,7 +919,7 @@ proc semExprNoType(c: PContext, n: PNode): PNode = let isPush = hintExtendedContext in c.config.notes if isPush: pushInfoContext(c.config, n.info) result = semExpr(c, n, {efWantStmt}) - discardCheck(c, result, {}) + result = discardCheck(c, result, {}) if isPush: popInfoContext(c.config) proc isTypeExpr(n: PNode): bool = @@ -1039,7 +1045,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = of skConst: markUsed(c.config, n.info, s, c.graph.usageSym) onUse(n.info, s) - case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind + let typ = skipTypes(s.typ, abstractInst-{tyTypeDesc}) + case typ.kind of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, tyTuple, tySet, tyUInt..tyUInt64: if s.magic == mNone: result = inlineConst(c, n, s) @@ -1057,6 +1064,12 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = # deal with two different ``[]``. if s.ast.len == 0: result = inlineConst(c, n, s) else: result = newSymNode(s, n.info) + of tyStatic: + if typ.n != nil: + result = typ.n + result.typ = typ.base + else: + result = newSymNode(s, n.info) else: result = newSymNode(s, n.info) of skMacro: @@ -1581,7 +1594,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = typeMismatch(c.config, n.info, lhs.typ, rhsTyp) n.sons[1] = fitNode(c, le, rhs, goodLineInfo(n[1])) - liftTypeBoundOps(c, lhs.typ, lhs.info) + liftTypeBoundOps(c.graph, lhs.typ, lhs.info) #liftTypeBoundOps(c, n.sons[0].typ, n.sons[0].info) fixAbstractType(c, n) @@ -1628,7 +1641,7 @@ proc semProcBody(c: PContext, n: PNode): PNode = a.sons[1] = result result = semAsgn(c, a) else: - discardCheck(c, result, {}) + result = discardCheck(c, result, {}) if c.p.owner.kind notin {skMacro, skTemplate} and c.p.resultSym != nil and c.p.resultSym.typ.isMetaType: @@ -2324,7 +2337,6 @@ proc semExportExcept(c: PContext, n: PNode): PNode = proc semExport(c: PContext, n: PNode): PNode = result = newNodeI(nkExportStmt, n.info) - for i in 0..<n.len: let a = n.sons[i] var o: TOverloadIter @@ -2343,6 +2355,9 @@ proc semExport(c: PContext, n: PNode): PNode = it = nextIter(ti, s.tab) else: while s != nil: + if s.kind == skEnumField: + localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a) & + "; enum field cannot be exported individually") if s.kind in ExportableSymKinds+{skModule}: result.add(newSymNode(s, a.info)) strTableAdd(c.module.tab, s) @@ -2430,7 +2445,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result.kind = nkCall result = semExpr(c, result, flags) of nkBind: - message(c.config, n.info, warnDeprecated, "bind") + message(c.config, n.info, warnDeprecated, "bind is deprecated") result = semExpr(c, n.sons[0], flags) of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy: if c.matchedConcept != nil and n.len == 1: @@ -2620,6 +2635,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkStaticStmt: result = semStaticStmt(c, n) of nkDefer: + if c.currentScope == c.topLevelScope: + localError(c.config, n.info, "defer statement not supported at top level") n.sons[0] = semExpr(c, n.sons[0]) if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]): localError(c.config, n.info, "'defer' takes a 'void' expression") diff --git a/compiler/semfields.nim b/compiler/semfields.nim index 07321f477..d65d962cb 100644 --- a/compiler/semfields.nim +++ b/compiler/semfields.nim @@ -19,6 +19,9 @@ type c: PContext proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = + if c.field != nil and isEmptyType(c.field.typ): + result = newNode(nkEmpty) + return case n.kind of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n of nkIdent, nkSym: diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 9e7ed5cee..0bdd0b64c 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -132,7 +132,9 @@ proc ordinalValToString*(a: PNode; g: ModuleGraph): string = return field.name.s else: return field.ast.strVal - internalError(g.config, a.info, "no symbol for ordinal value: " & $x) + localError(g.config, a.info, + "Cannot convert int literal to $1. The value is invalid." % + [typeToString(t)]) else: result = $x diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 7e61854b8..05c8b181c 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -129,14 +129,15 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym) template typeWithSonsResult(kind, sons): PNode = newTypeWithSons(context, kind, sons).toNode(traitCall.info) - case trait.sym.name.s + let s = trait.sym.name.s + case s of "or", "|": return typeWithSonsResult(tyOr, @[operand, operand2]) of "and": return typeWithSonsResult(tyAnd, @[operand, operand2]) of "not": return typeWithSonsResult(tyNot, @[operand]) - of "name": + of "name", "$": result = newStrNode(nkStrLit, operand.typeToString(preferTypeName)) result.typ = newType(tyString, context) result.info = traitCall.info @@ -160,7 +161,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym) hasDestructor(t) result = newIntNodeT(ord(not complexObj), traitCall, c.graph) else: - localError(c.config, traitCall.info, "unknown trait") + localError(c.config, traitCall.info, "unknown trait: " & s) result = newNodeI(nkEmpty, traitCall.info) proc semTypeTraits(c: PContext, n: PNode): PNode = diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 5be57f614..288820d86 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -95,13 +95,6 @@ proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode = elif efInTypeof in flags: result.typ = n[1].typ -proc toCover(c: PContext, t: PType): BiggestInt = - let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc}) - if t2.kind == tyEnum and enumHasHoles(t2): - result = sonsLen(t2.n) - else: - result = lengthOrd(c.config, skipTypes(t, abstractVar-{tyTypeDesc})) - proc semProc(c: PContext, n: PNode): PNode proc semExprBranch(c: PContext, n: PNode; flags: TExprFlags = {}): PNode = @@ -137,13 +130,13 @@ proc fixNilType(c: PContext; n: PNode) = for it in n: fixNilType(c, it) n.typ = nil -proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) = +proc discardCheck(c: PContext, expr: PNode, flags: TExprFlags): PNode = + result = expr if c.matchedConcept != nil or efInTypeof in flags: return if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}: if implicitlyDiscardable(result): - var n = newNodeI(nkDiscardStmt, result.info, 1) - n[0] = result + result = newNode(nkDiscardStmt, result.info, @[result]) elif result.typ.kind != tyError and c.config.cmd != cmdInteractive: var n = result while n.kind in skipForDiscardable: n = n.lastSon @@ -175,7 +168,8 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags): PNode = else: illFormedAst(it, c.config) if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or (not hasElse and efInTypeof notin flags): - for it in n: discardCheck(c, it.lastSon, flags) + for it in n: + it.sons[^1] = discardCheck(c, it.sons[^1], flags) result.kind = nkIfStmt # propagate any enforced VoidContext: if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext @@ -273,12 +267,14 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode = dec c.p.inTryStmt if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}: - discardCheck(c, n.sons[0], flags) - for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags) + n.sons[0] = discardCheck(c, n.sons[0], flags) + for i in 1..n.len-1: + n.sons[i].sons[^1] = discardCheck(c, n.sons[i].sons[^1], flags) if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext else: - if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags) + if n.lastSon.kind == nkFinally: + n.sons[^1].sons[^1] = discardCheck(c, n.sons[^1].sons[^1], flags) n.sons[0] = fitNode(c, typ, n.sons[0], n.sons[0].info) for i in 1..last: var it = n.sons[i] @@ -322,14 +318,24 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = result = semIdentWithPragma(c, kind, n, {}) if result.owner.kind == skModule: incl(result.flags, sfGlobal) - suggestSym(c.config, n.info, result, c.graph.usageSym) + + proc getLineInfo(n: PNode): TLineInfo = + case n.kind + of nkPostfix: + getLineInfo(n.sons[1]) + of nkAccQuoted, nkPragmaExpr: + getLineInfo(n.sons[0]) + else: + n.info + let info = getLineInfo(n) + suggestSym(c.config, info, result, c.graph.usageSym) proc checkNilable(c: PContext; v: PSym) = if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and {tfNotNil, tfNeedsInit} * v.typ.flags != {}: - if v.ast.isNil: + if v.astdef.isNil: message(c.config, v.info, warnProveInit, v.name.s) - elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags: + elif tfNotNil in v.typ.flags and tfNotNil notin v.astdef.typ.flags: message(c.config, v.info, warnProveInit, v.name.s) include semasgn @@ -470,7 +476,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = # this can only happen for errornous var statements: if typ == nil: continue typeAllowedCheck(c.config, a.info, typ, symkind, if c.matchedConcept != nil: {taConcept} else: {}) - liftTypeBoundOps(c, typ, a.info) + liftTypeBoundOps(c.graph, typ, a.info) var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink}) if a.kind == nkVarTuple: if tup.kind != tyTuple: @@ -515,8 +521,6 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = message(c.config, a.info, warnShadowIdent, v.name.s) if a.kind != nkVarTuple: if def.kind != nkEmpty: - # this is needed for the evaluation pass and for the guard checking: - v.ast = def if sfThread in v.flags: localError(c.config, def.info, errThreadvarCannotInit) setVarType(c, v, typ) b = newNodeI(nkIdentDefs, a.info) @@ -528,6 +532,23 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = addSon(b, a.sons[length-2]) addSon(b, copyTree(def)) addToVarSection(c, result, n, b) + if optOldAst in c.config.options: + if def.kind != nkEmpty: + v.ast = def + else: + # this is needed for the evaluation pass, guard checking + # and custom pragmas: + var ast = newNodeI(nkIdentDefs, a.info) + if a[j].kind == nkPragmaExpr: + var p = newNodeI(nkPragmaExpr, a.info) + p.add newSymNode(v) + p.add a[j][1].copyTree + ast.add p + else: + ast.add newSymNode(v) + ast.add a.sons[length-2].copyTree + ast.add def + v.ast = ast else: if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j] # bug #7663, for 'nim check' this can be a non-tuple: @@ -545,21 +566,22 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = proc semConst(c: PContext, n: PNode): PNode = result = copyNode(n) + inc c.inStaticContext for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if c.config.cmd == cmdIdeTools: suggestStmt(c, a) if a.kind == nkCommentStmt: continue - if a.kind != nkConstDef: illFormedAst(a, c.config) - checkSonsLen(a, 3, c.config) - var v = semIdentDef(c, a.sons[0], skConst) - styleCheckDef(c.config, v) - onDef(a[0].info, v) + if a.kind notin {nkConstDef, nkVarTuple}: illFormedAst(a, c.config) + checkMinSonsLen(a, 3, c.config) + var length = sonsLen(a) + var typ: PType = nil - if a.sons[1].kind != nkEmpty: typ = semTypeNode(c, a.sons[1], nil) + if a.sons[length-2].kind != nkEmpty: + typ = semTypeNode(c, a.sons[length-2], nil) - var def = semConstExpr(c, a.sons[2]) + var def = semConstExpr(c, a.sons[length-1]) if def == nil: - localError(c.config, a.sons[2].info, errConstExprExpected) + localError(c.config, a.sons[length-1].info, errConstExprExpected) continue # check type compatibility between def.typ and typ: if typ != nil: @@ -567,21 +589,44 @@ proc semConst(c: PContext, n: PNode): PNode = else: typ = def.typ if typ == nil: - localError(c.config, a.sons[2].info, errConstExprExpected) + localError(c.config, a.sons[length-1].info, errConstExprExpected) continue if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit: localError(c.config, a.info, "invalid type for const: " & typeToString(typ)) continue - setVarType(c, v, typ) - v.ast = def # no need to copy - if sfGenSym notin v.flags: addInterfaceDecl(c, v) - elif v.owner == nil: v.owner = getCurrOwner(c) - var b = newNodeI(nkConstDef, a.info) - if importantComments(c.config): b.comment = a.comment - addSon(b, newSymNode(v)) - addSon(b, a.sons[1]) - addSon(b, copyTree(def)) - addSon(result, b) + + var b: PNode + if a.kind == nkVarTuple: + if typ.kind != tyTuple: + localError(c.config, a.info, errXExpected, "tuple") + elif length-2 != sonsLen(typ): + localError(c.config, a.info, errWrongNumberOfVariables) + b = newNodeI(nkVarTuple, a.info) + newSons(b, length) + b.sons[length-2] = a.sons[length-2] + b.sons[length-1] = def + + for j in countup(0, length-3): + var v = semIdentDef(c, a.sons[j], skConst) + if sfGenSym notin v.flags: addInterfaceDecl(c, v) + elif v.owner == nil: v.owner = getCurrOwner(c) + styleCheckDef(c.config, v) + onDef(a[j].info, v) + + if a.kind != nkVarTuple: + setVarType(c, v, typ) + v.ast = def # no need to copy + b = newNodeI(nkConstDef, a.info) + if importantComments(c.config): b.comment = a.comment + addSon(b, newSymNode(v)) + addSon(b, a.sons[1]) + addSon(b, copyTree(def)) + else: + setVarType(c, v, typ.sons[j]) + v.ast = def[j] + b.sons[j] = newSymNode(v) + addSon(result,b) + dec c.inStaticContext include semfields @@ -637,7 +682,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode = openScope(c) n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags) if efInTypeof notin flags: - discardCheck(c, n.sons[length-1], flags) + n.sons[^1] = discardCheck(c, n.sons[^1], flags) closeScope(c) dec(c.p.nestedLoopCounter) @@ -824,7 +869,8 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode = closeScope(c) if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or (not hasElse and efInTypeof notin flags): - for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags) + for i in 1..n.len-1: + n.sons[i].sons[^1] = discardCheck(c, n.sons[i].sons[^1], flags) # propagate any enforced VoidContext: if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext @@ -1074,6 +1120,8 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = #debug s.typ s.ast = a popOwner(c) + if sfExportc in s.flags and s.typ.kind == tyAlias: + localError(c.config, name.info, "{.exportc.} not allowed for type aliases") let aa = a.sons[2] if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and aa.sons[0].kind == nkObjectTy: @@ -1090,9 +1138,13 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = proc checkForMetaFields(c: PContext; n: PNode) = - template checkMeta(t) = + proc checkMeta(c: PContext; n: PNode; t: PType) = if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags: - localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString) + if t.kind == tyBuiltInTypeClass and t.len == 1 and t.sons[0].kind == tyProc: + localError(c.config, n.info, ("'$1' is not a concrete type; " & + "for a callback without parameters use 'proc()'") % t.typeToString) + else: + localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString) if n.isNil: return case n.kind @@ -1107,9 +1159,9 @@ proc checkForMetaFields(c: PContext; n: PNode) = tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink: let start = ord(t.kind in {tyGenericInvocation, tyGenericInst}) for i in start ..< t.len: - checkMeta(t.sons[i]) + checkMeta(c, n, t.sons[i]) else: - checkMeta(t) + checkMeta(c, n, t) else: internalAssert c.config, false @@ -1205,9 +1257,6 @@ proc semTypeSection(c: PContext, n: PNode): PNode = proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) = s.typ = semProcTypeNode(c, n, genericParams, nil, s.kind) - if s.kind notin {skMacro, skTemplate}: - if s.typ.sons[0] != nil and s.typ.sons[0].kind == tyStmt: - localError(c.config, n.info, "invalid return type: 'stmt'") proc addParams(c: PContext, n: PNode, kind: TSymKind) = for i in countup(1, sonsLen(n)-1): @@ -1423,8 +1472,15 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) = addResult(c, s.typ.sons[0], n.info, s.kind) addResultNode(c, n) +proc canonType(c: PContext, t: PType): PType = + if t.kind == tySequence: + result = c.graph.sysTypes[tySequence] + else: + result = t + proc semOverride(c: PContext, s: PSym, n: PNode) = - case s.name.s.normalize + let name = s.name.s.normalize + case name of "=destroy": let t = s.typ var noError = false @@ -1436,12 +1492,16 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = elif obj.kind == tyGenericInvocation: obj = obj.sons[0] else: break if obj.kind in {tyObject, tyDistinct, tySequence, tyString}: + obj = canonType(c, obj) if obj.destructor.isNil: obj.destructor = s else: localError(c.config, n.info, errGenerated, "cannot bind another '" & s.name.s & "' to: " & typeToString(obj)) noError = true + if obj.owner.getModule != s.getModule: + localError(c.config, n.info, errGenerated, + "type bound operation `=destroy` can be defined only in the same module with its type (" & obj.typeToString() & ")") if not noError and sfSystemModule notin s.owner.flags: localError(c.config, n.info, errGenerated, "signature for '" & s.name.s & "' must be proc[T: object](x: var T)") @@ -1465,6 +1525,11 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = else: localError(c.config, n.info, errGenerated, "cannot bind 'deepCopy' to: " & typeToString(t)) + + if t.owner.getModule != s.getModule: + localError(c.config, n.info, errGenerated, + "type bound operation `" & name & "` can be defined only in the same module with its type (" & t.typeToString() & ")") + else: localError(c.config, n.info, errGenerated, "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T") @@ -1487,12 +1552,18 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = objB = objB.sons[0] else: break if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB): + # attach these ops to the canonical tySequence + obj = canonType(c, obj) let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink) if opr[].isNil: opr[] = s else: localError(c.config, n.info, errGenerated, "cannot bind another '" & s.name.s & "' to: " & typeToString(obj)) + if obj.owner.getModule != s.getModule: + localError(c.config, n.info, errGenerated, + "type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") + return if sfSystemModule notin s.owner.flags: localError(c.config, n.info, errGenerated, @@ -1542,7 +1613,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) = foundObj = true x.methods.add((col,s)) if not foundObj: - message(c.config, n.info, warnDeprecated, "generic method not attachable to object type") + message(c.config, n.info, warnDeprecated, "generic method not attachable to object type is deprecated") else: # why check for the body? bug #2400 has none. Checking for sfForward makes # no sense either. @@ -1975,7 +2046,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = n.typ = n.sons[i].typ if not isEmptyType(n.typ): n.kind = nkStmtListExpr elif i != last or voidContext: - discardCheck(c, n.sons[i], flags) + n.sons[i] = discardCheck(c, n.sons[i], flags) else: n.typ = n.sons[i].typ if not isEmptyType(n.typ): n.kind = nkStmtListExpr diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 7159e17e7..c28902b1f 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -80,9 +80,14 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = if isPure: initStrTable(symbols) var hasNull = false for i in countup(1, sonsLen(n) - 1): + if n.sons[i].kind == nkEmpty: continue case n.sons[i].kind of nkEnumFieldDef: - e = newSymS(skEnumField, n.sons[i].sons[0], c) + if n.sons[i].sons[0].kind == nkPragmaExpr: + e = newSymS(skEnumField, n.sons[i].sons[0].sons[0], c) + pragma(c, e, n.sons[i].sons[0].sons[1], enumFieldPragmas) + else: + e = newSymS(skEnumField, n.sons[i].sons[0], c) var v = semConstExpr(c, n.sons[i].sons[1]) var strVal: PNode = nil case skipTypes(v.typ, abstractInst-{tyTypeDesc}).kind @@ -91,7 +96,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = strVal = v.sons[1] # second tuple part is the string value if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}: if not isOrdinalType(v.sons[0].typ, allowEnumWithHoles=true): - localError(c.config, v.sons[0].info, errOrdinalTypeExpected) + localError(c.config, v.sons[0].info, errOrdinalTypeExpected & "; given: " & typeToString(v.sons[0].typ, preferDesc)) x = getOrdValue(v.sons[0]) # first tuple part is the ordinal else: localError(c.config, strVal.info, errStringLiteralExpected) @@ -102,7 +107,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = x = counter else: if not isOrdinalType(v.typ, allowEnumWithHoles=true): - localError(c.config, v.info, errOrdinalTypeExpected) + localError(c.config, v.info, errOrdinalTypeExpected & "; given: " & typeToString(v.typ, preferDesc)) x = getOrdValue(v) if i != 1: if x != counter: incl(result.flags, tfEnumHasHoles) @@ -115,6 +120,9 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = e = n.sons[i].sym of nkIdent, nkAccQuoted: e = newSymS(skEnumField, n.sons[i], c) + of nkPragmaExpr: + e = newSymS(skEnumField, n.sons[i].sons[0], c) + pragma(c, e, n.sons[i].sons[1], enumFieldPragmas) else: illFormedAst(n[i], c.config) e.typ = result @@ -133,7 +141,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil): wrongRedefinition(c, e.info, e.name.s, conflict.info) inc(counter) - if not hasNull: incl(result.flags, tfNeedsInit) + if tfNotNil in e.typ.flags and not hasNull: incl(result.flags, tfNeedsInit) proc semSet(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tySet, prev, c) @@ -202,7 +210,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = tyError, tyObject}: message c.config, n[i].info, errGenerated, "region needs to be an object type" else: - message(c.config, n.info, warnDeprecated, "region for pointer types") + message(c.config, n.info, warnDeprecated, "region for pointer types is deprecated") addSonSkipIntLit(result, region) addSonSkipIntLit(result, t) if tfPartial in result.flags: @@ -591,6 +599,13 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, for i in lastIndex.succ..(sonsLen(branch) - 2): checkForOverlap(c, t, i, branchIndex) +proc toCover(c: PContext, t: PType): BiggestInt = + let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc}) + if t2.kind == tyEnum and enumHasHoles(t2): + result = sonsLen(t2.n) + else: + result = lengthOrd(c.config, skipTypes(t, abstractVar-{tyTypeDesc})) + proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, father: PNode, rectype: PType, hasCaseFields = false) proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, @@ -603,15 +618,16 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, return incl(a.sons[0].sym.flags, sfDiscriminant) var covered: BiggestInt = 0 + var chckCovered = false var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc}) - if not isOrdinalType(typ): - localError(c.config, n.info, "selector must be of an ordinal type") - elif firstOrd(c.config, typ) != 0: - localError(c.config, n.info, "low(" & $a.sons[0].sym.name.s & - ") must be 0 for discriminant") - elif lengthOrd(c.config, typ) > 0x00007FFF: - localError(c.config, n.info, "len($1) must be less than 32768" % a.sons[0].sym.name.s) - var chckCovered = true + case typ.kind + of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool, tyRange: + chckCovered = true + of tyFloat..tyFloat128, tyString, tyError: + discard + else: + if not isOrdinalType(typ): + localError(c.config, n.info, "selector must be of an ordinal type, float or string") for i in countup(1, sonsLen(n) - 1): var b = copyTree(n.sons[i]) addSon(a, b) @@ -620,12 +636,14 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, checkMinSonsLen(b, 2, c.config) semCaseBranch(c, a, b, i, covered) of nkElse: - chckCovered = false checkSonsLen(b, 1, c.config) + if chckCovered and covered == toCover(c, a.sons[0].typ): + localError(c.config, b.info, "invalid else, all cases are already covered") + chckCovered = false else: illFormedAst(n, c.config) delSon(b, sonsLen(b) - 1) semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype, hasCaseFields = true) - if chckCovered and covered != lengthOrd(c.config, a.sons[0].typ): + if chckCovered and covered != toCover(c, a.sons[0].typ): localError(c.config, a.info, "not all cases are covered") addSon(father, a) @@ -1010,7 +1028,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType = if n.kind == nkCurlyExpr: result = semTypeNode(c, n.sons[0], nil) - constraint = semNodeKindConstraints(n, c.config) + constraint = semNodeKindConstraints(n, c.config, 1) + elif n.kind == nkCall and + n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice} and + considerQuotedIdent(c, n[0]).s == "{}": + result = semTypeNode(c, n[1], nil) + constraint = semNodeKindConstraints(n, c.config, 2) else: result = semTypeNode(c, n, nil) @@ -1054,6 +1077,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if hasType: typ = semParamType(c, a.sons[length-2], constraint) + var owner = getCurrOwner(c).owner + # TODO: Disallow typed/untyped in procs in the compiler/stdlib + if (owner.kind != skModule or owner.owner.name.s != "stdlib") and + kind == skProc and (typ.kind == tyStmt or typ.kind == tyExpr): + localError(c.config, a.sons[length-2].info, "'" & typ.sym.name.s & "' is only allowed in templates and macros") if hasDefault: def = a[^1] @@ -1130,12 +1158,15 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, # turn explicit 'void' return type into 'nil' because the rest of the # compiler only checks for 'nil': if skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid: + if kind notin {skMacro, skTemplate} and r.kind in {tyStmt, tyExpr}: + localError(c.config, n.sons[0].info, "return type '" & typeToString(r) & + "' is only valid for macros and templates") # 'auto' as a return type does not imply a generic: - if r.kind == tyAnything: + elif r.kind == tyAnything: # 'p(): auto' and 'p(): expr' are equivalent, but the rest of the # compiler is hardly aware of 'auto': r = newTypeS(tyExpr, c) - elif r.kind != tyExpr: + else: if r.sym == nil or sfAnon notin r.sym.flags: let lifted = liftParamType(c, kind, genericParams, r, "result", n.sons[0].info) @@ -1287,7 +1318,19 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = if tx != result and tx.kind == tyObject and tx.sons[0] != nil: semObjectTypeForInheritedGenericInst(c, n, tx) -proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType +proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType = + if typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward} and prev != nil: + result = newTypeS(tyAlias, c) + result.rawAddSon typeExpr + result.sym = prev.sym + assignType(prev, result) + +proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) = + if prev != nil: + let result = newTypeS(tyAlias, c) + result.rawAddSon typExpr.typ + result.sym = prev.sym + assignType(prev, result) proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType = var n = semExprWithType(c, n, {efDetermineType}) @@ -1391,20 +1434,6 @@ proc semProcTypeWithScope(c: PContext, n: PNode, when useEffectSystem: setEffectsForProcType(c.graph, result, n.sons[1]) closeScope(c) -proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType = - if typeExpr.kind in {tyObject, tyEnum, tyDistinct} and prev != nil: - result = newTypeS(tyAlias, c) - result.rawAddSon typeExpr - result.sym = prev.sym - assignType(prev, result) - -proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) = - if prev != nil: - let result = newTypeS(tyAlias, c) - result.rawAddSon typExpr.typ - result.sym = prev.sym - assignType(prev, result) - proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym = if n.kind == nkType: result = symFromType(c, n.typ, n.info) @@ -1551,10 +1580,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = # XXX figure out why this has children already... result.sons.setLen 0 result.n = nil - if c.config.selectedGc == gcDestructors: - result.flags = {tfHasAsgn} - else: - result.flags = {} + result.flags = {tfHasAsgn} semContainerArg(c, n, "seq", result) else: result = semContainer(c, n, tySequence, "seq", prev) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index d66e8d121..e08559db6 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2071,6 +2071,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, # constructor in a call: if result == nil and f.kind == tyVarargs: if f.n != nil: + # Forward to the varargs converter result = localConvMatch(c, m, f, a, arg) else: r = typeRel(m, base(f), a) @@ -2083,10 +2084,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, # bug #4799, varargs accepting subtype relation object elif r == isSubtype: inc(m.subtypeMatches) - if f.kind == tyTypeDesc: + if base(f).kind == tyTypeDesc: result = arg else: - result = implicitConv(nkHiddenSubConv, f, arg, m, c) + result = implicitConv(nkHiddenSubConv, base(f), arg, m, c) m.baseTypeMatch = true else: result = userConvMatch(c, m, base(f), a, arg) @@ -2231,14 +2232,14 @@ proc matchesAux(c: PContext, n, nOrig: PNode, else: m.state = csNoMatch return - + if formal.typ.kind == tyVar: - let arg_converter = if arg.kind == nkHiddenDeref: arg[0] else: arg - if arg_converter.kind == nkHiddenCallConv: - if arg_converter.typ.kind != tyVar: + let argConverter = if arg.kind == nkHiddenDeref: arg[0] else: arg + if argConverter.kind == nkHiddenCallConv: + if argConverter.typ.kind != tyVar: m.state = csNoMatch m.mutabilityProblem = uint8(f-1) - return + return elif not n.isLValue: m.state = csNoMatch m.mutabilityProblem = uint8(f-1) @@ -2267,8 +2268,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, if n.sons[a].kind == nkHiddenStdConv: doAssert n.sons[a].sons[0].kind == nkEmpty and - n.sons[a].sons[1].kind == nkArgList and - n.sons[a].sons[1].len == 0 + n.sons[a].sons[1].kind in {nkBracket, nkArgList} # Steal the container and pass it along setSon(m.call, formal.position + 1, n.sons[a].sons[1]) else: diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index a34383d9f..fb256b895 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -18,7 +18,7 @@ const szIllegalRecursion* = -2 szUncomputedSize* = -1 -proc computeSizeAlign(conf: ConfigRef; typ: PType): void +proc computeSizeAlign(conf: ConfigRef; typ: PType) proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt = ## returns object alignment @@ -49,13 +49,14 @@ proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt = else: result = 1 -proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset: BiggestInt): tuple[offset, align: BiggestInt] = +proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, + initialOffset: BiggestInt): tuple[offset, align: BiggestInt] = ## ``offset`` is the offset within the object, after the node has been written, no padding bytes added ## ``align`` maximum alignment from all sub nodes assert n != nil if n.typ != nil and n.typ.size == szIllegalRecursion: result.offset = szIllegalRecursion - result.align = szIllegalRecursion + result.align = szIllegalRecursion return result.align = 1 @@ -71,66 +72,52 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset: of nkOfBranch, nkElse: # offset parameter cannot be known yet, it needs to know the alignment first let align = computeSubObjectAlign(conf, n.sons[i].lastSon) - if align == szIllegalRecursion: - result.offset = szIllegalRecursion + result.offset = szIllegalRecursion result.align = szIllegalRecursion return - if align == szUnknownSize or maxChildAlign == szUnknownSize: maxChildAlign = szUnknownSize else: maxChildAlign = max(maxChildAlign, align) else: internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)") - if maxChildAlign == szUnknownSize: result.align = szUnknownSize result.offset = szUnknownSize else: # the union neds to be aligned first, before the offsets can be assigned let kindUnionOffset = align(kindOffset, maxChildAlign) - var maxChildOffset: BiggestInt = 0 for i in 1 ..< sonsLen(n): let (offset, align) = computeObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset) maxChildOffset = max(maxChildOffset, offset) - result.align = max(kindAlign, maxChildAlign) result.offset = maxChildOffset - - of nkRecList: result.align = 1 # maximum of all member alignments var offset = initialOffset - for i, child in n.sons: let (new_offset, align) = computeObjectOffsetsFoldFunction(conf, child, offset) - if new_offset == szIllegalRecursion: result.offset = szIllegalRecursion result.align = szIllegalRecursion return - elif new_offset == szUnknownSize or offset == szUnknownSize: # if anything is unknown, the rest becomes unknown as well offset = szUnknownSize result.align = szUnknownSize - else: offset = new_offset result.align = max(result.align, align) - # final alignment if offset == szUnknownSize: result.offset = szUnknownSize else: result.offset = align(offset, result.align) - of nkSym: var size = szUnknownSize var align = szUnknownSize - if n.sym.bitsize == 0: # 0 represents bitsize not set computeSizeAlign(conf, n.sym.typ) size = n.sym.typ.size.int @@ -155,7 +142,6 @@ proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOf let kindOffset = computePackedObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset, debug) # the union neds to be aligned first, before the offsets can be assigned let kindUnionOffset = kindOffset - var maxChildOffset: BiggestInt = kindUnionOffset for i in 1 ..< sonsLen(n): let offset = computePackedObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset, debug) @@ -168,9 +154,17 @@ proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOf if result == szIllegalRecursion: break of nkSym: - computeSizeAlign(conf, n.sym.typ) - n.sym.offset = initialOffset.int - result = n.sym.offset + n.sym.typ.size + var size = szUnknownSize + if n.sym.bitsize == 0: + computeSizeAlign(conf, n.sym.typ) + size = n.sym.typ.size.int + + if initialOffset == szUnknownSize or size == szUnknownSize: + n.sym.offset = szUnknownSize + result = szUnknownSize + else: + n.sym.offset = int(initialOffset) + result = initialOffset + n.sym.typ.size else: result = szUnknownSize @@ -324,7 +318,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = var headerAlign: int16 if typ.sons[0] != nil: # compute header size - if conf.cmd == cmdCompileToCpp: # if the target is C++ the members of this type are written # into the padding byets at the end of the parent type. At the @@ -364,7 +357,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.size = szUnknownSize typ.align = szUnknownSize return - # header size is already in size from computeObjectOffsetsFoldFunction # maxAlign is probably not changed at all from headerAlign if tfPacked in typ.flags: @@ -373,7 +365,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = else: typ.align = int16(max(align, headerAlign)) typ.size = align(offset, typ.align) - of tyInferred: if typ.len > 1: computeSizeAlign(conf, typ.lastSon) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index dfa6e5ddb..f3f960136 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -456,20 +456,27 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; proc extractPragma(s: PSym): PNode = if s.kind in routineKinds: result = s.ast[pragmasPos] - elif s.kind in {skType}: + elif s.kind in {skType, skVar, skLet}: # s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma] result = s.ast[0][1] doAssert result == nil or result.kind == nkPragma proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) = - let pragmaNode = extractPragma(s) + var pragmaNode: PNode + if optOldAst in conf.options and s.kind in {skVar, skLet}: + pragmaNode = nil + else: + pragmaNode = if s.kind == skEnumField: extractPragma(s.owner) else: extractPragma(s) + let name = + if s.kind == skEnumField and sfDeprecated notin s.flags: "enum '" & s.owner.name.s & "' which contains field '" & s.name.s & "'" + else: s.name.s if pragmaNode != nil: for it in pragmaNode: if whichPragma(it) == wDeprecated and it.safeLen == 2 and it[1].kind in {nkStrLit..nkTripleStrLit}: - message(conf, info, warnDeprecated, it[1].strVal & "; " & s.name.s) + message(conf, info, warnDeprecated, it[1].strVal & "; " & name & " is deprecated") return - message(conf, info, warnDeprecated, s.name.s) + message(conf, info, warnDeprecated, name & " is deprecated") proc userError(conf: ConfigRef; info: TLineInfo; s: PSym) = let pragmaNode = extractPragma(s) @@ -486,6 +493,8 @@ proc markUsed(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) = incl(s.flags, sfUsed) if s.kind == skEnumField and s.owner != nil: incl(s.owner.flags, sfUsed) + if sfDeprecated in s.owner.flags: + warnAboutDeprecated(conf, info, s) if {sfDeprecated, sfError} * s.flags != {}: if sfDeprecated in s.flags: warnAboutDeprecated(conf, info, s) if sfError in s.flags: userError(conf, info, s) diff --git a/compiler/types.nim b/compiler/types.nim index b163ca4e9..797336ddf 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -107,20 +107,23 @@ proc isFloatLit*(t: PType): bool {.inline.} = result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName): string = - result = sym.owner.name.s & '.' & sym.name.s & '(' - var n = sym.typ.n - for i in countup(1, sonsLen(n) - 1): - let p = n.sons[i] - if p.kind == nkSym: - add(result, p.sym.name.s) - add(result, ": ") - add(result, typeToString(p.sym.typ, prefer)) - if i != sonsLen(n)-1: add(result, ", ") - else: - result.add renderTree(p) - add(result, ')') - if n.sons[0].typ != nil: - result.add(": " & typeToString(n.sons[0].typ, prefer)) + assert sym != nil + result = sym.owner.name.s & '.' & sym.name.s + if sym.kind in routineKinds: + result.add '(' + var n = sym.typ.n + for i in countup(1, sonsLen(n) - 1): + let p = n.sons[i] + if p.kind == nkSym: + add(result, p.sym.name.s) + add(result, ": ") + add(result, typeToString(p.sym.typ, prefer)) + if i != sonsLen(n)-1: add(result, ", ") + else: + result.add renderTree(p) + add(result, ')') + if n.sons[0].typ != nil: + result.add(": " & typeToString(n.sons[0].typ, prefer)) result.add "[declared in " result.add(conf$sym.info) result.add "]" @@ -429,7 +432,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = sfAnon notin t.sym.flags: if t.kind == tyInt and isIntLit(t): result = t.sym.name.s & " literal(" & $t.n.intVal & ")" - elif t.kind == tyAlias: + elif t.kind == tyAlias and t.sons[0].kind != tyAlias: result = typeToString(t.sons[0]) elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil: result = t.sym.name.s @@ -756,7 +759,7 @@ type TTypeCmpFlags* = set[TTypeCmpFlag] - TSameTypeClosure = object {.pure.} + TSameTypeClosure = object cmp: TDistinctCompare recCheck: int flags: TTypeCmpFlags diff --git a/compiler/unittest_light.nim b/compiler/unittest_light.nim new file mode 100644 index 000000000..d9842b399 --- /dev/null +++ b/compiler/unittest_light.nim @@ -0,0 +1,38 @@ +# note: consider merging tests/assert/testhelper.nim here. + +proc mismatch*[T](lhs: T, rhs: T): string = + ## Simplified version of `unittest.require` that satisfies a common use case, + ## while avoiding pulling too many dependencies. On failure, diagnostic + ## information is provided that in particular makes it easy to spot + ## whitespace mismatches and where the mismatch is. + proc replaceInvisible(s: string): string = + for a in s: + case a + of '\n': result.add "\\n\n" + else: result.add a + + proc quoted(s: string): string = result.addQuoted s + + result.add "\n" + result.add "lhs:{" & replaceInvisible( + $lhs) & "}\nrhs:{" & replaceInvisible($rhs) & "}\n" + when compiles(lhs.len): + if lhs.len != rhs.len: + result.add "lhs.len: " & $lhs.len & " rhs.len: " & $rhs.len & "\n" + when compiles(lhs[0]): + var i = 0 + while i < lhs.len and i < rhs.len: + if lhs[i] != rhs[i]: break + i.inc + result.add "first mismatch index: " & $i & "\n" + if i < lhs.len and i < rhs.len: + result.add "lhs[i]: {" & quoted($lhs[i]) & "}\nrhs[i]: {" & quoted( + $rhs[i]) & "}\n" + result.add "lhs[0..<i]:{" & replaceInvisible($lhs[ + 0..<i]) & "}" + +proc assertEquals*[T](lhs: T, rhs: T) = + when false: # can be useful for debugging to see all that's fed to this. + echo "----" & $lhs + if lhs!=rhs: + doAssert false, mismatch(lhs, rhs) diff --git a/compiler/vm.nim b/compiler/vm.nim index c8784c3e7..10d38fe77 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -10,10 +10,6 @@ ## This file implements the new evaluation engine for Nim code. ## An instruction is 1-3 int32s in memory, it is a register based VM. -const - debugEchoCode = false - traceCode = debugEchoCode - import ast except getstr import @@ -26,6 +22,9 @@ from evaltempl import evalTemplate from modulegraphs import ModuleGraph, PPassContext +const + traceCode = debugEchoCode + when hasFFI: import evalffi @@ -65,22 +64,27 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = return stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1) var info = c.debug[pc] - # we now use the same format as in system/except.nim - var s = substr(toFilename(c.config, info), 0) - # this 'substr' prevents a strange corruption. XXX This needs to be - # investigated eventually but first attempts to fix it broke everything - # see the araq-wip-fixed-writebarrier branch. + # we now use a format similar to the one in lib/system/excpt.nim + var s = "" + # todo: factor with quotedFilename + if optExcessiveStackTrace in c.config.globalOptions: + s = toFullPath(c.config, info) + else: + s = toFilename(c.config, info) var line = toLinenumber(info) + var col = toColumn(info) if line > 0: add(s, '(') add(s, $line) + add(s, ", ") + add(s, $(col + ColOffset)) add(s, ')') if x.prc != nil: for k in 1..max(1, 25-s.len): add(s, ' ') add(s, x.prc.name.s) msgWriteln(c.config, s) -proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, +proc stackTraceImpl(c: PCtx, tos: PStackFrame, pc: int, msg: string, lineInfo: TLineInfo) = msgWriteln(c.config, "stack trace: (most recent call last)") stackTraceAux(c, tos, pc) @@ -88,8 +92,14 @@ proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, if c.mode == emRepl: globalError(c.config, lineInfo, msg) else: localError(c.config, lineInfo, msg) -proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: string) = - stackTrace(c, tos, pc, msg, c.debug[pc]) +template stackTrace(c: PCtx, tos: PStackFrame, pc: int, + msg: string, lineInfo: TLineInfo) = + stackTraceImpl(c, tos, pc, msg, lineInfo) + return + +template stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: string) = + stackTraceImpl(c, tos, pc, msg, c.debug[pc]) + return proc bailOut(c: PCtx; tos: PStackFrame) = stackTrace(c, tos, c.exceptionInstr, "unhandled exception: " & @@ -950,13 +960,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeBC(rkInt) let a = regs[rb].node let b = regs[rc].node - if a.kind == nkSym and a.sym.kind in skProcKinds and + if a.kind == nkSym and a.sym.kind in skProcKinds and b.kind == nkSym and b.sym.kind in skProcKinds: regs[ra].intVal = if sfFromGeneric in a.sym.flags and a.sym.owner == b.sym: 1 else: 0 - else: - stackTrace(c, tos, pc, "node is not a proc symbol") + else: + stackTrace(c, tos, pc, "node is not a proc symbol") of opcEcho: let rb = instr.regB if rb == 1: @@ -1239,8 +1249,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let newLen = regs[rb].intVal.int if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess) else: c.setLenSeq(regs[ra].node, newLen, c.debug[pc]) - of opcReset: - internalError(c.config, c.debug[pc], "too implement") of opcNarrowS: decodeB(rkInt) let min = -(1.BiggestInt shl (rb-1)) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 493078f74..a43f8dbba 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -73,7 +73,7 @@ type opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq, opcIsNil, opcOf, opcIs, opcSubStr, opcParseFloat, opcConv, opcCast, - opcQuit, opcReset, + opcQuit, opcNarrowS, opcNarrowU, opcAddStrCh, diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 1f2a3e6d1..e7993dfb2 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -33,6 +33,11 @@ import import platform from os import splitFile +const + debugEchoCode* = defined(nimVMDebug) + +when debugEchoCode: + import asciitables when hasFFI: import evalffi @@ -43,9 +48,10 @@ type TGenFlags = set[TGenFlag] proc debugInfo(c: PCtx; info: TLineInfo): string = - result = toFilename(c.config, info).splitFile.name & ":" & $info.line + result = toFileLineCol(c.config, info) proc codeListing(c: PCtx, result: var string, start=0; last = -1) = + ## for debugging purposes # first iteration: compute all necessary labels: var jumpTargets = initIntSet() let last = if last < 0: c.code.len-1 else: min(last, c.code.len-1) @@ -54,7 +60,9 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) = if x.opcode in relativeJumps: jumpTargets.incl(i+x.regBx-wordExcess) - # for debugging purposes + template toStr(opc: TOpcode): string = ($opc).substr(3) + + result.add "code listing:\n" var i = start while i <= last: if i in jumpTargets: result.addf("L$1:\n", i) @@ -62,34 +70,39 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) = result.add($i) let opc = opcode(x) - if opc in {opcConv, opcCast}: + if opc in {opcIndCall, opcIndCallAsgn}: + result.addf("\t$#\tr$#, r$#, nargs:$#", opc.toStr, x.regA, + x.regB, x.regC) + elif opc in {opcConv, opcCast}: let y = c.code[i+1] let z = c.code[i+2] - result.addf("\t$#\tr$#, r$#, $#, $#", ($opc).substr(3), x.regA, x.regB, + result.addf("\t$#\tr$#, r$#, $#, $#", opc.toStr, x.regA, x.regB, c.types[y.regBx-wordExcess].typeToString, c.types[z.regBx-wordExcess].typeToString) inc i, 2 elif opc < firstABxInstr: - result.addf("\t$#\tr$#, r$#, r$#", ($opc).substr(3), x.regA, + result.addf("\t$#\tr$#, r$#, r$#", opc.toStr, x.regA, x.regB, x.regC) elif opc in relativeJumps: - result.addf("\t$#\tr$#, L$#", ($opc).substr(3), x.regA, + result.addf("\t$#\tr$#, L$#", opc.toStr, x.regA, i+x.regBx-wordExcess) elif opc in {opcLdConst, opcAsgnConst}: let idx = x.regBx-wordExcess - result.addf("\t$#\tr$#, $# ($#)", ($opc).substr(3), x.regA, + result.addf("\t$#\tr$#, $# ($#)", opc.toStr, x.regA, c.constants[idx].renderTree, $idx) elif opc in {opcMarshalLoad, opcMarshalStore}: let y = c.code[i+1] - result.addf("\t$#\tr$#, r$#, $#", ($opc).substr(3), x.regA, x.regB, + result.addf("\t$#\tr$#, r$#, $#", opc.toStr, x.regA, x.regB, c.types[y.regBx-wordExcess].typeToString) inc i else: - result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess) + result.addf("\t$#\tr$#, $#", opc.toStr, x.regA, x.regBx-wordExcess) result.add("\t#") result.add(debugInfo(c, c.debug[i])) result.add("\n") inc i + when debugEchoCode: + result = result.alignTable proc echoCode*(c: PCtx; start=0; last = -1) {.deprecated.} = var buf = "" @@ -1092,7 +1105,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mReset: unused(c, n, dest) var d = c.genx(n.sons[1]) - c.gABC(n, opcReset, d) + c.gABx(n, opcLdNull, d, c.genType(n.sons[1].typ)) + c.gABx(n, opcNodeToReg, d, d) + c.genAsgnPatch(n.sons[1], d) of mOf, mIs: if dest < 0: dest = c.getTemp(n.typ) var tmp = c.genx(n.sons[1]) @@ -1736,8 +1751,9 @@ proc genVarSection(c: PCtx; n: PNode) = #assert(a.sons[0].kind == nkSym) can happen for transformed vars if a.kind == nkVarTuple: for i in 0 .. a.len-3: - if not a[i].sym.isGlobal: setSlot(c, a[i].sym) - checkCanEval(c, a[i]) + if a[i].kind == nkSym: + if not a[i].sym.isGlobal: setSlot(c, a[i].sym) + checkCanEval(c, a[i]) c.gen(lowerTupleUnpacking(c.graph, a, c.getOwner)) elif a.sons[0].kind == nkSym: let s = a.sons[0].sym diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 75873bfe8..56c97dec6 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -13,7 +13,7 @@ from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc, floor, ceil, `mod` -from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir +from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir, getAppFilename template mathop(op) {.dirty.} = registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`) @@ -120,3 +120,6 @@ proc registerAdditionalOps*(c: PCtx) = setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1))) systemop gorgeEx macrosop getProjectPath + + registerCallback c, "stdlib.os.getCurrentCompilerExe", proc (a: VmArgs) {.nimcall.} = + setResult(a, getAppFilename()) |