Skip to content

Commit

Permalink
Make compiler report structured
Browse files Browse the repository at this point in the history
Full rework of the compiler message handling pipeline. Remove old-style message generation that was
based purely on strings that were formatted in-place, and instead implement structured logging where
main compiler code only instantiates objects with required information.

Explanation of changes for this commit will be split into several sections, matching number of edit
iterations I had to make in order for this to work properly.

* File reorganization

In addition to the architectural changes this PR requires some type definition movements.

- PNode, PSym and PType definitions (and all associated types and enums) were moved to the
  ast_types.nim file which is then reexported in the ast.nim
- Enum defininitions for the nilability checking were moved to nilcheck_enums.nim and then
  reexported
- Enum definitions for the VM were moved to to vm_enums.nim

* New files

- Main definition of the report types is provided in the reports.nim file, together with minor
  helper procedures and definition of the ReportList type. This file is imported by options, msgs
  and other parts of the compiler.
- Implementation of the default error reporting is provided in the cli_reporter.nim - all
  disguisting hardcoded string hacks were moved to this single file. Now you can really witness the
  "error messages are good enough for me" thinking that prevailed in the compiler UX since it's
  inception.

* Changed files

Most of the changes follow very simple pattern - old-style hardcoded hacks are replaced with
structural report that is constructed in-place and then converted to string /when necessary/. I'm
pretty sure this change already puts me close to getting CS PHD - it was *unimaginably hard* to
figure out that hardcoding text formatting in place is not the best architecture. Damn, I can
probably apply to the nobel prize right now after I figure that out.

** Changes in message reporting pipeline

Old error messages where reportined via random combinations of the following:

- Direct calls to `msgs.liMessage` proc - it was mostly used in the helper templates like
  `lintReport`
- `message`
- `rawMessage`
- `fatal`
- `globalError` - template for reporting global errors. Misused to the point where name completely
  lost it's meaning and documentation does not make any sense whatsoever. [fn:global-err]
- `localError` - template for reporting necessary error location, wrapper around `liMessage`
- `warningDeprecated` - used two times in the whole compiler in order to print out error message
  about deprecated command switches.

Full pipeline of the error processing was:

- Message was generated in-place, without any colored formatting (was added in `liMessage`)
  - There were several ways message could be generated - all of them were used interchangeably,
    without any clear system.
    1. Some file had a collection of constant strings, such as `errCannotInferStaticParam = "cannot
       infer the value of the static param '$1'"` that were used in the `localReport` message
       formatting immediately. Some constants were used pretty often, some were used only once.
    2. Warning and hint messages were categorized to some extent, so and the enum was used to
       provide message formatting via `array[TMsgKind, string]` where `string` was a `std/strutils`
       formatting string.
    3. In a lot of cases error message was generated using hardcoded (and repeatedly copy-pasted)
       string
- It was passed down to the `liMessage` (removed now) procedure, that dispatched based on the global
  configuration state (whether `ignoreMsgBecauseOfIdeTools` holds for example)
- Then message, together with necessary bits of formatting (such as `Hint` prefix with coloring) was
  passed down to the `styledMsgWriteln(args: varargs[typed])` template, whcih did the final dispatch
  into
- One of the two different /macros/ for writing text out - `callStyledWriteLineStderr` and
  `callIgnoringStyle`.
  - Dispatch could happen into stdout/stderr or message writer hook if it was non-nil
- When message was written out it went into `writeLnHook` callback (misused for
  `{.explain.}` [fn:writeln-explain]) (hacked for `compiles()` [fn:writeln-compiles]) and was
  written out to the stdout/stderr.

It is now replaced with:

- `Report` object is instantiated at some point of a compilation process (it might be an immediate
  report via `localError` or delayed via `nkError` and written out later)
- `structuredReportHook` converts it to string internally. All decitions for formatting, coloring
  etc. happen inside of the structured report hook. Where to write message, which format and so on.
- `writeLnHook` /can/ be used by the `structuredReportHook` if needed - right now it is turned into
  simple convenience callback. In future this could even be replaced with simple helper proc, for
  now I left it as it is now because I'm not 100% sure that I can safely remove this.

** Changes in the warning and hint filtering

Report filtering is done in the particular *implementation* of the error hook -
`options.isEnabled()` provides a default predicate to check whether specific report can be written
out, but it must still be called manually. This allows to still see all the reports if needed. For
example, `cli_reporter.reportHook()` uses additional checks to force write some reports (such as
debug trace in `compiles()` context).

Most of the hint and warning were already categorized, altough some reports had to be split into
several (such as `--hint=Performance` that actually controlled reports for `CopiesToSink` and
`CannotMakeSink`, `--hint=Name` that controlled three reports).

None of the errors were categorized (reports from the `nkError` progress was incorporated, but in
I'm mostly describing changes wrt. to old reporting system) and all were generated in-place

** Minor related changes

- List of allowed reports is now stored in the `noteSets: array[ConfNoteSet, ReportKinds]` field of
  the `ConfigRef`, instead of *seven* separate feilds. Access is now done via getter/setter procs,
  which allows to track changes in the current configuration state.
- Renamed list of local options to `localOptions` - added accessors to track changes in the state.
- Move all default report filter configuration to `lineinfos.computeNotesVerbosity()` - previously
  it was scattered in two different places: `NotesVerbosity` for `--verbosity:N` was calculated in
  `lineinfos`, foreignPackageNotesDefault was constructed in `options`
- Changed formatting of the `internalAssert` and `internalError` messages
- Removed lots of string formatting procs from the various `sem*` modules - ideally they should
  *all* be moved to the `cli_reporter` and related.

-------

Additional notes

[fn:global-err] As I said earlier, `globalError` was misused - it is still not possible to fully get
rid of this sickening hack, since it is used for /control flow/ (you read this correct - it is an
error reporting template, and it is used for control flow. Wonderful design right?).

So, in short - `globalError` can raise `ERecoverableError` that can be caught in some other layer
(for example `sem.tryConstExpr` abuses that in order to bail out (pretty sure it was done as an
'optimization' of some sort, just like 'compiles') when expression fails to evaluate at
compile-time [fn:except])

[fn:except] nim-works#94 (comment)

[fn:writeln-explain] Of course you can't have a proper error reporting in the nim compiler, so this
hook was also misused to the point of complete nonsense. Most notable clusterfuck where you could
spot this little shit is implementation of `{.explain.}` pragma for concepts. It was implemented via
really 'smart' (aka welcome to hell) solution in

nim-works@74a8098 (sigmatch
~704) and then further "improved" in
nim-lang/Nim@fe48dd1 by slicing out parts
of the error message with `let msg = s.replace("Error:", errorPrefix)`

For now I had to reuse similar hack - I *do* override error reporting hook in order to collect all
the missing report information. In the future it would be unnecessary - when concept is matched it's
body will be evaluated to `nkError` with reports that are written out later.

[fn:writeln-compiles] when `compiles()` check is executed, all error output is temporarily disabled
(both stderr and stdout) via setting allowed output flags to `{}` (by default it is set to
  • Loading branch information
haxscramper committed Jan 16, 2022
1 parent fd14e3d commit 3fb02e1
Show file tree
Hide file tree
Showing 173 changed files with 13,855 additions and 6,010 deletions.
1,039 changes: 36 additions & 1,003 deletions compiler/ast.nim

Large diffs are not rendered by default.

1,173 changes: 1,173 additions & 0 deletions compiler/ast_types.nim

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions compiler/astalgo.nim
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,13 @@ proc value(this: var DebugPrinter; value: PNode) =
if this.renderSymType and value.typ != nil:
this.key "typ"
this.value value.typ

if value.kind == nkError:
this.key "file"
this.value $value[compilerInfoPos].strVal
this.key "line"
this.value $value[compilerInfoPos].info.line

if value.len > 0:
this.key "sons"
this.openBracket
Expand Down
8 changes: 5 additions & 3 deletions compiler/ccgcalls.nim
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ proc preventNrvo(p: BProc; le, ri: PNode): bool =
# annoying warnings, see #14514
if canRaise(ri[0]) and
locationEscapes(p, le, p.nestedTryStmts.len > 0):
message(p.config, le.info, warnObservableStores, $le)
localReport(p.config, le, reportSem rsemObservableStores)

proc hasNoInit(call: PNode): bool {.inline.} =
result = call[0].kind == nkSym and sfNoInit in call[0].sym.flags
Expand Down Expand Up @@ -500,7 +500,9 @@ proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
result = genArgNoParam(p, ri[i]) #, typ.n[i].sym)
else:
if tfVarargs notin typ.flags:
localError(p.config, ri.info, "wrong argument count")
localReport(p.config, ri.info, semReportCountMismatch(
rsemWrongNumberOfArguments, expected = 1, got = 0, node = ri))

result = nil
else:
result = genArgNoParam(p, ri[i])
Expand Down Expand Up @@ -624,7 +626,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
result.add genOtherArg(p, ri, k, typ)
result.add(~")")
else:
localError(p.config, ri.info, "call expression expected for C++ pattern")
localReport(p.config, ri, reportSem rsemExpectedCallForCxxPattern)
inc i
elif i+1 < pat.len and pat[i+1] == '.':
result.add genThisArg(p, ri, j, typ)
Expand Down
57 changes: 36 additions & 21 deletions compiler/ccgexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -951,16 +951,26 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
if not isConstExpr(y):
# semantic pass has already checked for const index expressions
if firstOrd(p.config, ty) == 0 and lastOrd(p.config, ty) >= 0:
if (firstOrd(p.config, b.t) < firstOrd(p.config, ty)) or (lastOrd(p.config, b.t) > lastOrd(p.config, ty)):
if (firstOrd(p.config, b.t) < firstOrd(p.config, ty)) or
(lastOrd(p.config, b.t) > lastOrd(p.config, ty)):

linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)){ #raiseIndexError2($1, $2); $3}$n",
[rdCharLoc(b), intLiteral(lastOrd(p.config, ty)), raiseInstr(p)])
else:
linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3){ #raiseIndexError3($1, $2, $3); $4}$n",
[rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)), raiseInstr(p)])
else:
let idx = getOrdValue(y)
if idx < firstOrd(p.config, ty) or idx > lastOrd(p.config, ty):
localError(p.config, x.info, formatErrorIndexBound(idx, firstOrd(p.config, ty), lastOrd(p.config, ty)))
if idx < firstOrd(p.config, ty) or lastOrd(p.config, ty) < idx:
localReport(
p.config, x.info, SemReport(
kind: rsemStaticOutOfBounds,
indexSpec: (
usedIdx: idx,
minIdx: firstOrd(p.config, ty),
maxIdx: lastOrd(p.config, ty)),
ast: y))

d.inheritLocation(a)
putIntoDest(p, d, n,
ropecg(p.module, "$1[($2)- $3]", [rdLoc(a), rdCharLoc(b), first]), a.storage)
Expand Down Expand Up @@ -1165,7 +1175,8 @@ proc genEcho(p: BProc, n: PNode) =
linefmt(p, cpsStmts, "fflush(stdout);$n", [])

proc gcUsage(conf: ConfigRef; n: PNode) =
if conf.selectedGC == gcNone: message(conf, n.info, warnGcMem, n.renderTree)
if conf.selectedGC == gcNone:
localReport(conf, n, reportSem rsemUseOfGc)

proc strLoc(p: BProc; d: TLoc): Rope =
if optSeqDestructors in p.config.globalOptions:
Expand Down Expand Up @@ -1330,9 +1341,7 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
# finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
# convention at least:
if op.typ == nil or op.typ.callConv != ccNimCall:
localError(p.module.config, a.lode.info,
"the destructor that is turned into a finalizer needs " &
"to have the 'nimcall' calling convention")
localReport(p.module.config, a.lode, reportSem rsemExpectedNimcallProc)
var f: TLoc
initLocExpr(p, newSymNode(op), f)
p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
Expand Down Expand Up @@ -1644,9 +1653,10 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
while t.kind == tyObject and t[0] != nil:
r.add(~".Sup")
t = skipTypes(t[0], skipPtrs)

if isObjLackingTypeField(t):
globalError(p.config, x.info,
"no 'of' operator available for pure objects")
localReport(p.config, x, reportSem rsemDisallowedOfForPureObjects)

if nilCheck != nil:
r = ropecg(p.module, "(($1) && ($2))", [nilCheck, genOfHelper(p, dest, r, x.info)])
else:
Expand All @@ -1658,7 +1668,7 @@ proc genOf(p: BProc, n: PNode, d: var TLoc) =

proc genRepr(p: BProc, e: PNode, d: var TLoc) =
if optTinyRtti in p.config.globalOptions:
localError(p.config, e.info, "'repr' is not available for --newruntime")
localReport(p.config, e, reportSem rsemDisallowedReprForNewruntime)
var a: TLoc
initLocExpr(p, e[1], a)
var t = skipTypes(e[1].typ, abstractVarRange)
Expand Down Expand Up @@ -1701,7 +1711,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
ropecg(p.module, "#reprAny($1, $2)", [
rdLoc(a), genTypeInfoV1(p.module, t, e.info)]), a.storage)
of tyEmpty, tyVoid:
localError(p.config, e.info, "'repr' doesn't support 'void' type")
localReport(p.config, e, reportSem rsemUnexpectedVoidType)
else:
putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)",
[addrLoc(p.config, a), genTypeInfoV1(p.module, t, e.info)]),
Expand Down Expand Up @@ -2259,7 +2269,7 @@ proc genSlice(p: BProc; e: PNode; d: var TLoc) =
if d.k == locNone: getTemp(p, e.typ, d)
linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $3;$n", [rdLoc(d), x, y])
when false:
localError(p.config, e.info, "invalid context for 'toOpenArray'; " &
localReport(p.config, e.info, "invalid context for 'toOpenArray'; " &
"'toOpenArray' is only valid within a call expression")

proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) =
Expand Down Expand Up @@ -2423,12 +2433,15 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mEcho: genEcho(p, e[1].skipConv)
of mArrToSeq: genArrToSeq(p, e, d)
of mNLen..mNError, mSlurp..mQuoteAst:
localError(p.config, e.info, strutils.`%`(errXMustBeCompileTime, e[0].sym.name.s))
localReport(p.config, e.info, reportSym(
rsemConstExpressionExpected, e[0].sym))

of mSpawn:
when defined(leanCompiler):
p.config.quitOrRaise "compiler built without support for the 'spawn' statement"
else:
let n = spawn.wrapProcForSpawn(p.module.g.graph, p.module.idgen, p.module.module, e, e.typ, nil, nil)
let n = spawn.wrapProcForSpawn(
p.module.g.graph, p.module.idgen, p.module.module, e, e.typ, nil, nil)
expr(p, n, d)
of mParallel:
when defined(leanCompiler):
Expand All @@ -2438,8 +2451,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
expr(p, n, d)
of mDeepCopy:
if p.config.selectedGC in {gcArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions:
localError(p.config, e.info,
"for --gc:arc|orc 'deepcopy' support has to be enabled with --deepcopy:on")
localReport(p.config, e, reportSem rsemRequiresDeepCopyEnabled)

var a, b: TLoc
let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1]
Expand Down Expand Up @@ -2746,8 +2758,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
#if sym.kind == skIterator:
# echo renderTree(sym.getBody, {renderIds})
if sfCompileTime in sym.flags:
localError(p.config, n.info, "request to generate code for .compileTime proc: " &
sym.name.s)
localReport(p.config, n.info, reportSym(
rsemCannotCodegenCompiletimeProc, sym))

if useAliveDataFromDce in p.module.flags and sym.typ.callConv != ccInline:
fillProcLoc(p.module, n)
genProcPrototype(p.module, sym)
Expand Down Expand Up @@ -3000,7 +3013,8 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
if mapSetType(p.config, t) == ctArray: result = rope"{}"
else: result = rope"0"
else:
globalError(p.config, info, "cannot create null element for: " & $t.kind)
internalError(
p.config, info, "cannot create null element for: " & $t.kind)

proc caseObjDefaultBranch(obj: PNode; branch: Int128): int =
for i in 1 ..< obj.len:
Expand Down Expand Up @@ -3070,7 +3084,7 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
# not found, produce default value:
result.add getDefaultValue(p, field.typ, info)
else:
localError(p.config, info, "cannot create null element for: " & $obj)
internalError(p.config, info, "cannot create null element for: " & $obj)

proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode,
result: var Rope; count: var int;
Expand Down Expand Up @@ -3215,7 +3229,8 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType): Rope
result = genConstTuple(p, n, isConst, typ)
of tyOpenArray:
if n.kind != nkBracket:
internalError(p.config, n.info, "const openArray expression is not an array construction")
internalError(
p.config, n.info, "const openArray expression is not an array construction")

let data = genConstSimpleList(p, n, isConst)

Expand Down
6 changes: 4 additions & 2 deletions compiler/ccgliterals.nim
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
result = getTempName(m)
genStringLiteralDataOnlyV2(m, s, result, isConst)
else:
localError(m.config, info, "cannot determine how to produce code for string literal")
internalError(
m.config, info, "cannot determine how to produce code for string literal")

proc genNilStringLiteral(m: BModule; info: TLineInfo): Rope =
result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", [])
Expand All @@ -110,4 +111,5 @@ proc genStringLiteral(m: BModule; n: PNode): Rope =
of 0, 1: result = genStringLiteralV1(m, n)
of 2: result = genStringLiteralV2(m, n, isConst = true)
else:
localError(m.config, n.info, "cannot determine how to produce code for string literal")
internalError(
m.config, n.info, "cannot determine how to produce code for string literal")
2 changes: 1 addition & 1 deletion compiler/ccgreset.nim
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
let disc = n[0].sym
if disc.loc.r == nil: fillObjectFields(p.module, typ)
if disc.loc.t == nil:
internalError(p.config, n.info, "specializeResetN()")
internalError(p.config, n.info, "specializeResetN()")
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
for i in 1..<n.len:
let branch = n[i]
Expand Down
35 changes: 21 additions & 14 deletions compiler/ccgstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,8 @@ proc genBreakState(p: BProc, n: PNode, d: var TLoc) =

proc genGotoVar(p: BProc; value: PNode) =
if value.kind notin {nkCharLit..nkUInt64Lit}:
localError(p.config, value.info, "'goto' target must be a literal value")
localReport(p.config, value, reportSem rsemExpectedLiteralForGoto)

else:
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])

Expand Down Expand Up @@ -481,7 +482,7 @@ proc genGotoForCase(p: BProc; caseStmt: PNode) =
let it = caseStmt[i]
for j in 0..<it.len-1:
if it[j].kind == nkRange:
localError(p.config, it.info, "range notation not available for computed goto")
localReport(p.config, it, reportSem rsemDisallowedRangeForComputedGoto)
return
let val = getOrdValue(it[j])
lineF(p, cpsStmts, "NIMSTATE_$#:$n", [val.rope])
Expand Down Expand Up @@ -510,22 +511,28 @@ proc genComputedGoto(p: BProc; n: PNode) =
let it = n[i]
if it.kind == nkCaseStmt:
if lastSon(it).kind != nkOfBranch:
localError(p.config, it.info,
"case statement must be exhaustive for computed goto"); return
localReport(p.config, it, reportSem rsemExpectedExhaustiveCaseForComputedGoto)
return

casePos = i
if enumHasHoles(it[0].typ):
localError(p.config, it.info,
"case statement cannot work on enums with holes for computed goto"); return
localReport(p.config, it, reportSem rsemExpectedUnholyEnumForComputedGoto)
return

let aSize = lengthOrd(p.config, it[0].typ)
if aSize > 10_000:
localError(p.config, it.info,
"case statement has too many cases for computed goto"); return
localReport(p.config, it, reportSem rsemTooManyEntriesForComputedGoto)
return

arraySize = toInt(aSize)
if firstOrd(p.config, it[0].typ) != 0:
localError(p.config, it.info,
"case statement has to start at 0 for computed goto"); return
localReport(p.config, it, reportSem rsemExpectedLow0ForComputedGoto)
return

if casePos < 0:
localError(p.config, n.info, "no case statement found for computed goto"); return
localReport(p.config, n, reportSem rsemExpectedCaseForComputedGoto)
return

var id = p.labels+1
inc p.labels, arraySize+1
let tmp = "TMP$1_" % [id.rope]
Expand All @@ -549,7 +556,7 @@ proc genComputedGoto(p: BProc; n: PNode) =
let it = caseStmt[i]
for j in 0..<it.len-1:
if it[j].kind == nkRange:
localError(p.config, it.info, "range notation not available for computed goto")
localReport(p.config, it, reportSem rsemDisallowedRangeForComputedGoto)
return

let val = getOrdValue(it[j])
Expand Down Expand Up @@ -1551,7 +1558,7 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
if optTinyRtti notin p.config.globalOptions:
let field = dotExpr[1].sym
genDiscriminantCheck(p, a, tmp, dotExpr[0].typ, field)
message(p.config, e.info, warnCaseTransition)
localReport(p.config, e, reportSem rsemCaseTransition)
genAssignment(p, a, tmp, {})

proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
Expand Down Expand Up @@ -1579,7 +1586,7 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
proc genStmts(p: BProc, t: PNode) =
var a: TLoc

let isPush = p.config.hasHint(hintExtendedContext)
let isPush = p.config.hasHint(rsemExtendedContext)
if isPush: pushInfoContext(p.config, t.info)
expr(p, t, a)
if isPush: popInfoContext(p.config)
Expand Down
16 changes: 9 additions & 7 deletions compiler/ccgtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1148,8 +1148,9 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) =
if typ.kind == tyObject:
if incompleteType(typ):
localError(m.config, info, "request for RTTI generation for incomplete object: " &
typeToString(typ))
localReport(m.config, info, reportTyp(
rsemRttiRequestForIncompleteObject, typ))

genTypeInfoAux(m, typ, origType, name, info)
else:
genTypeInfoAuxBase(m, typ, origType, name, rope("0"), info)
Expand Down Expand Up @@ -1293,8 +1294,8 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope =
# finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
# convention at least:
if theProc.typ == nil or theProc.typ.callConv != ccNimCall:
localError(m.config, info,
theProc.name.s & " needs to have the 'nimcall' calling convention")
localReport(m.config, info, reportSym(
rsemExpectedNimcallProc, theProc))

genProc(m, theProc)
result = theProc.loc.r
Expand All @@ -1315,8 +1316,9 @@ proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineIn
var typeName: Rope
if t.kind in {tyObject, tyDistinct}:
if incompleteType(t):
localError(m.config, info, "request for RTTI generation for incomplete object: " &
typeToString(t))
localReport(m.config, info, reportTyp(
rsemRttiRequestForIncompleteObject, t))

typeName = genTypeInfo2Name(m, t)
else:
typeName = rope("NIM_NIL")
Expand Down Expand Up @@ -1461,7 +1463,7 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
if t.n != nil: result = genTypeInfoV1(m, lastSon t, info)
else: internalError(m.config, "genTypeInfoV1(" & $t.kind & ')')
of tyUserTypeClasses:
internalAssert m.config, t.isResolvedUserTypeClass
internalAssert(m.config, t.isResolvedUserTypeClass, "")
return genTypeInfoV1(m, t.lastSon, info)
of tyProc:
if t.callConv != ccClosure:
Expand Down
Loading

0 comments on commit 3fb02e1

Please sign in to comment.