From 469e1377cde19efe569b4d0d5a2258b5b015aee6 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 24 Jan 2026 06:07:41 +0100 Subject: [PATCH] IC: progress (#25453) --- compiler/ast.nim | 19 +- compiler/ast2nif.nim | 2 +- compiler/pipelines.nim | 2 +- compiler/renderer.nim | 380 ++++++++++++++++++++++++++++++++++++++- compiler/semtypinst.nim | 1 + compiler/types.nim | 353 +----------------------------------- tests/ic/tparseutils.nim | 10 ++ 7 files changed, 405 insertions(+), 362 deletions(-) create mode 100644 tests/ic/tparseutils.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 89f24c63ca..5b08ea5e60 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -501,6 +501,7 @@ const proc idGeneratorFromModule*(m: PSym): IdGenerator = assert m.kind == skModule result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0, disambTable: initCountTable[PIdent]()) + result.disambTable.inc m.name proc idGeneratorForPackage*(nextIdWillBe: int32): IdGenerator = result = IdGenerator(module: PackageModuleId, symId: nextIdWillBe - 1'i32, typeId: 0, disambTable: initCountTable[PIdent]()) @@ -549,22 +550,25 @@ proc addAllowNil*(father, son: PNode) {.inline.} = father.sons.add(son) proc add*(father, son: PType) = + ensureMutable father assert father.kind != tyProc or father.sonsImpl.len == 0 assert son != nil father.sonsImpl.add son proc addAllowNil*(father, son: PType) {.inline.} = + ensureMutable father assert father.kind != tyProc or father.sonsImpl.len == 0 father.sonsImpl.add son -template `[]`*(n: PType, i: int): PType = +proc `[]`*(n: PType, i: int): PType {.inline.} = if n.state == Partial: loadType(n) if n.kind == tyProc and i > 0: assert n.nImpl[i] != nil and n.nImpl[i].sym != nil n.nImpl[i].sym.typ else: n.sonsImpl[i] -template `[]=`*(n: PType, i: int; x: PType) = + +proc `[]=`*(n: PType, i: int; x: PType) {.inline.} = if n.state == Partial: loadType(n) if n.kind == tyProc and i > 0: assert n.nImpl[i] != nil and n.nImpl[i].sym != nil @@ -572,12 +576,13 @@ template `[]=`*(n: PType, i: int; x: PType) = else: n.sonsImpl[i] = x -template `[]`*(n: PType, i: BackwardsIndex): PType = +proc `[]`*(n: PType, i: BackwardsIndex): PType {.inline.} = if n.state == Partial: loadType(n) - n[n.len - i.int] -template `[]=`*(n: PType, i: BackwardsIndex; x: PType) = + n[n.sonsImpl.len - i.int] + +proc `[]=`*(n: PType, i: BackwardsIndex; x: PType) {.inline.} = if n.state == Partial: loadType(n) - n[n.len - i.int] = x + n[n.sonsImpl.len - i.int] = x proc getDeclPragma*(n: PNode): PNode = ## return the `nkPragma` node for declaration `n`, or `nil` if no pragma was found. @@ -930,6 +935,7 @@ proc `$`*(s: PSym): string = result = "" proc len*(n: PType): int {.inline.} = + if n.state == Partial: loadType(n) if n.kind == tyProc: result = if n.nImpl == nil: 0 else: n.nImpl.len else: @@ -1168,6 +1174,7 @@ proc skipTypesOrNil*(t: PType, kinds: TTypeKinds): PType = ## same as skipTypes but handles 'nil' result = t while result != nil and result.kind in kinds: + if result.state == Partial: loadType(result) if result.sonsImpl.len == 0: return nil result = last(result) diff --git a/compiler/ast2nif.nim b/compiler/ast2nif.nim index 97e6343da0..48803e25e6 100644 --- a/compiler/ast2nif.nim +++ b/compiler/ast2nif.nim @@ -321,7 +321,7 @@ proc collectGenericParams(w: var Writer; n: PNode) = proc writeSymDef(w: var Writer; dest: var TokenBuf; sym: PSym) = dest.addParLe sdefTag, trLineInfo(w, sym.infoImpl) dest.addSymDef pool.syms.getOrIncl(w.toNifSymName(sym)), NoLineInfo - if sfExported in sym.flagsImpl: + if {sfExported, sfFromGeneric} * sym.flagsImpl == {sfExported}: dest.addIdent "x" else: dest.addDotToken diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim index 843ed49ad0..3d094bd7b7 100644 --- a/compiler/pipelines.nim +++ b/compiler/pipelines.nim @@ -286,7 +286,7 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF let precomp = moduleFromNifFile(graph, fileIdx) if precomp.module == nil: let nifPath = toNifFilename(graph.config, fileIdx) - localError(graph.config, unknownLineInfo, + globalError(graph.config, unknownLineInfo, "nim m requires precompiled NIF for import: " & toFullPath(graph.config, fileIdx) & " (expected: " & nifPath & ")") return nil # Don't fall through to compile from source diff --git a/compiler/renderer.nim b/compiler/renderer.nim index e8cdfad6d2..5efe1f7744 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -14,7 +14,7 @@ {.used.} import - lexer, options, idents, ast, msgs, lineinfos, wordrecg + lexer, options, idents, ast, msgs, lineinfos, wordrecg, trees import std/[strutils] @@ -66,6 +66,359 @@ proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string # determines how long the subtree will likely be, the second # phase appends to a buffer that will be the output. +type + TPreferedDesc* = enum + preferName, # default + preferDesc, # probably should become what preferResolved is + preferExported, + preferModuleInfo, # fully qualified + preferGenericArg, + preferTypeName, + preferResolved, # fully resolved symbols + preferMixed, + # most useful, shows: symbol + resolved symbols if it differs, e.g.: + # tuple[a: MyInt{int}, b: float] + preferInlayHint, + preferInferredEffects, + +proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string +template `$`*(typ: PType): string = typeToString(typ) + +proc valueToString(a: PNode): string = + case a.kind + of nkCharLit, nkUIntLit..nkUInt64Lit: + result = $cast[uint64](a.intVal) + of nkIntLit..nkInt64Lit: + result = $a.intVal + of nkFloatLit..nkFloat128Lit: result = $a.floatVal + of nkStrLit..nkTripleStrLit: result = a.strVal + of nkStaticExpr: result = "static(" & a[0].renderTree & ")" + else: result = "" + +proc rangeToStr(n: PNode): string = + assert(n.kind == nkRange) + result = valueToString(n[0]) & ".." & valueToString(n[1]) + +const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo, + preferGenericArg, preferResolved, preferMixed, preferInlayHint, preferInferredEffects} + + +const + typeToStr: array[TTypeKind, string] = ["None", "bool", "char", "empty", + "Alias", "typeof(nil)", "untyped", "typed", "typeDesc", + # xxx typeDesc=>typedesc: typedesc is declared as such, and is 10x more common. + "GenericInvocation", "GenericBody", "GenericInst", "GenericParam", + "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple", + "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc", + "pointer", "OpenArray[$1]", "string", "cstring", "Forward", + "int", "int8", "int16", "int32", "int64", + "float", "float32", "float64", "float128", + "uint", "uint8", "uint16", "uint32", "uint64", + "owned", "sink", + "lent ", "varargs[$1]", "UncheckedArray[$1]", "Error Type", + "BuiltInTypeClass", "UserTypeClass", + "UserTypeClassInst", "CompositeTypeClass", "inferred", + "and", "or", "not", "any", "static", "TypeFromExpr", "concept", # xxx bugfix + "void", "iterable"] + +proc addTypeFlags(name: var string, typ: PType) {.inline.} = + if tfNotNil in typ.flags: name.add(" not nil") + +proc isIntLit*(t: PType): bool {.inline.} = + result = t.kind == tyInt and t.n != nil and t.n.kind == nkIntLit + +proc isFloatLit*(t: PType): bool {.inline.} = + result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit + +# TODO: It would be a good idea to kill the special state of a resolved +# concept by switching to tyAlias within the instantiated procs. +# Currently, tyAlias is always skipped with skipModifier, which means that +# we can store information about the matched concept in another position. +# Then builtInFieldAccess can be modified to properly read the derived +# consts and types stored within the concept. +template isResolvedUserTypeClass*(t: PType): bool = + tfResolved in t.flags + +proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = + let preferToplevel = prefer + proc getPrefer(prefer: TPreferedDesc): TPreferedDesc = + if preferToplevel in {preferResolved, preferMixed}: + preferToplevel # sticky option + else: + prefer + + proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = + result = "" + let prefer = getPrefer(prefer) + let t = typ + if t == nil: return + if prefer in preferToResolveSymbols and t.sym != nil and + sfAnon notin t.sym.flags and t.kind notin {tySequence, tyInferred}: + if t.kind == tyInt and isIntLit(t): + if prefer == preferInlayHint: + result = t.sym.name.s + else: + result = t.sym.name.s & " literal(" & $t.n.intVal & ")" + elif t.kind == tyAlias and t.elementType.kind != tyAlias: + result = typeToString(t.elementType) + elif prefer in {preferResolved, preferMixed}: + case t.kind + of IntegralTypes + {tyFloat..tyFloat128} + {tyString, tyCstring}: + result = typeToStr[t.kind] + of tyGenericBody: + result = typeToString(t.last) + of tyCompositeTypeClass: + # avoids showing `A[any]` in `proc fun(a: A)` with `A = object[T]` + result = typeToString(t.last.last) + else: + result = t.sym.name.s + if prefer == preferMixed and result != t.sym.name.s: + result = t.sym.name.s & "{" & result & "}" + elif prefer in {preferName, preferTypeName, preferInlayHint, preferInferredEffects} or t.sym.owner.isNil: + # note: should probably be: {preferName, preferTypeName, preferGenericArg} + result = t.sym.name.s + if t.kind == tyGenericParam and t.genericParamHasConstraints: + result.add ": " + result.add t.elementType.typeToString + else: + result = t.sym.owner.name.s & '.' & t.sym.name.s + result.addTypeFlags(t) + return + case t.kind + of tyInt: + if not isIntLit(t) or prefer == preferExported: + result = typeToStr[t.kind] + else: + case prefer: + of preferGenericArg: + result = $t.n.intVal + of preferInlayHint: + result = "int" + else: + result = "int literal(" & $t.n.intVal & ")" + of tyGenericInst: + result = typeToString(t.genericHead) & '[' + for needsComma, a in t.genericInstParams: + if needsComma: result.add(", ") + result.add(typeToString(a, preferGenericArg)) + result.add(']') + of tyGenericInvocation: + result = typeToString(t.genericHead) & '[' + for needsComma, a in t.genericInvocationParams: + if needsComma: result.add(", ") + result.add(typeToString(a, preferGenericArg)) + result.add(']') + of tyGenericBody: + result = typeToString(t.typeBodyImpl) & '[' + for i, a in t.genericBodyParams: + if i > 0: result.add(", ") + result.add(typeToString(a, preferTypeName)) + result.add(']') + of tyTypeDesc: + if t.elementType.kind == tyNone: result = "typedesc" + else: result = "typedesc[" & typeToString(t.elementType) & "]" + of tyStatic: + if prefer == preferGenericArg and t.n != nil: + result = t.n.renderTree + else: + result = "static[" & (if t.hasElementType: typeToString(t.skipModifier) else: "") & "]" + if t.n != nil: result.add "(" & renderTree(t.n) & ")" + of tyUserTypeClass: + if t.sym != nil and t.sym.owner != nil: + if t.isResolvedUserTypeClass: return typeToString(t.last) + return t.sym.owner.name.s + else: + result = "" + of tyBuiltInTypeClass: + result = + case t.base.kind + of tyVar: "var" + of tyRef: "ref" + of tyPtr: "ptr" + of tySequence: "seq" + of tyArray: "array" + of tySet: "set" + of tyRange: "range" + of tyDistinct: "distinct" + of tyProc: "proc" + of tyObject: "object" + of tyTuple: "tuple" + of tyOpenArray: "openArray" + else: typeToStr[t.base.kind] + of tyInferred: + let concrete = t.previouslyInferred + if concrete != nil: result = typeToString(concrete) + else: result = "inferred[" & typeToString(t.base) & "]" + of tyUserTypeClassInst: + let body = t.base + result = body.sym.name.s & "[" + for needsComma, a in t.userTypeClassInstParams: + if needsComma: result.add(", ") + result.add(typeToString(a)) + result.add "]" + of tyAnd: + for i, son in t.ikids: + if i > 0: result.add(" and ") + result.add(typeToString(son)) + of tyOr: + for i, son in t.ikids: + if i > 0: result.add(" or ") + result.add(typeToString(son)) + of tyNot: + result = "not " & typeToString(t.elementType) + of tyUntyped: + #internalAssert t.len == 0 + result = "untyped" + of tyFromExpr: + if t.n == nil: + result = "unknown" + else: + result = "typeof(" & renderTree(t.n) & ")" + of tyArray: + result = "array" + if t.hasElementType: + if t.indexType.kind == tyRange: + result &= "[" & rangeToStr(t.indexType.n) & ", " & + typeToString(t.elementType) & ']' + else: + result &= "[" & typeToString(t.indexType) & ", " & + typeToString(t.elementType) & ']' + of tyUncheckedArray: + result = "UncheckedArray" + if t.hasElementType: + result &= "[" & typeToString(t.elementType) & ']' + of tySequence: + if t.sym != nil and prefer != preferResolved: + result = t.sym.name.s + else: + result = "seq" + if t.hasElementType: + result &= "[" & typeToString(t.elementType) & ']' + of tyOrdinal: + result = "ordinal" + if t.hasElementType: + result &= "[" & typeToString(t.skipModifier) & ']' + of tySet: + result = "set" + if t.hasElementType: + result &= "[" & typeToString(t.elementType) & ']' + of tyOpenArray: + result = "openArray" + if t.hasElementType: + result &= "[" & typeToString(t.elementType) & ']' + of tyDistinct: + result = "distinct " & typeToString(t.elementType, + if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName) + of tyIterable: + # xxx factor this pattern + result = "iterable" + if t.hasElementType: + result &= "[" & typeToString(t.skipModifier) & ']' + of tyTuple: + # we iterate over t.sons here, because t.n may be nil + if t.n != nil: + result = "tuple[" + for i in 0.. 0: result.add ", " + result.add(typeToString(son)) + result.add(')') + of tyPtr, tyRef, tyVar, tyLent: + result = if isOutParam(t): "out " else: typeToStr[t.kind] + result.add typeToString(t.elementType) + of tyRange: + result = "range " + if t.n != nil and t.n.kind == nkRange: + result.add rangeToStr(t.n) + if prefer != preferExported: + result.add("(" & typeToString(t.elementType) & ")") + of tyProc: + result = if tfIterator in t.flags: "iterator " + elif t.owner != nil: + case t.owner.kind + of skTemplate: "template " + of skMacro: "macro " + of skConverter: "converter " + else: "proc " + else: + "proc " + if tfUnresolved in t.flags: result.add "[*missing parameters*]" + result.add "(" + for i, a in t.paramTypes: + if i > FirstParamAt: result.add(", ") + let j = paramTypeToNodeIndex(i) + if t.n != nil and j < t.n.len and t.n[j].kind == nkSym: + result.add(t.n[j].sym.name.s) + result.add(": ") + result.add(typeToString(a)) + result.add(')') + if t.returnType != nil: result.add(": " & typeToString(t.returnType)) + var prag = if t.callConv == ccNimCall and tfExplicitCallConv notin t.flags: "" else: $t.callConv + var hasImplicitRaises = false + if not isNil(t.owner) and not isNil(t.owner.ast) and (t.owner.ast.len - 1) >= pragmasPos: + let pragmasNode = t.owner.ast[pragmasPos] + let raisesSpec = effectSpec(pragmasNode, wRaises) + if not isNil(raisesSpec): + addSep(prag) + prag.add("raises: ") + prag.add(renderTree raisesSpec) + hasImplicitRaises = true + if tfNoSideEffect in t.flags: + addSep(prag) + prag.add("noSideEffect") + if tfThread in t.flags: + addSep(prag) + prag.add("gcsafe") + var effectsOfStr = "" + for i, a in t.paramTypes: + let j = paramTypeToNodeIndex(i) + if t.n != nil and j < t.n.len and t.n[j].kind == nkSym and t.n[j].sym.kind == skParam and sfEffectsDelayed in t.n[j].sym.flags: + addSep(effectsOfStr) + effectsOfStr.add(t.n[j].sym.name.s) + if effectsOfStr != "": + addSep(prag) + prag.add("effectsOf: ") + prag.add(effectsOfStr) + if not hasImplicitRaises and prefer == preferInferredEffects and not isNil(t.owner) and not isNil(t.owner.typ) and not isNil(t.owner.typ.n) and (t.owner.typ.n.len > 0): + let effects = t.n[0] + if effects.kind == nkEffectList and effects.len == effectListLen: + var inferredRaisesStr = "" + let effs = effects[exceptionEffects] + if not isNil(effs): + for eff in items(effs): + if not isNil(eff): + addSep(inferredRaisesStr) + inferredRaisesStr.add($eff.typ) + addSep(prag) + prag.add("raises: [") + prag.add(inferredRaisesStr) + prag.add("]") + if prag.len != 0: result.add("{." & prag & ".}") + of tyVarargs: + result = typeToStr[t.kind] % typeToString(t.elementType) + of tySink: + result = "sink " & typeToString(t.skipModifier) + of tyOwned: + result = "owned " & typeToString(t.elementType) + else: + result = typeToStr[t.kind] + result.addTypeFlags(t) + result = typeToString(typ, prefer) + + proc disamb(g: var TSrcGen; s: PSym): int = # we group by 's.name.s' to compute the stable name ID. result = 0 @@ -862,10 +1215,28 @@ proc genSymSuffix(result: var string, s: PSym) {.inline.} = result.add '_' result.addInt s.id +proc gsemmedParams(g: var TSrcGen, n: PNode) = + put(g, tkParLe, "(") + for i in 1.. 1: + putWithSpace(g, tkComma, ";") + let x {.cursor.} = n[i] + if x.kind == nkSym: + put g, tkSymbol, renderDefinitionName(x.sym) + putWithSpace(g, tkColon, ":") + put g, tkSymbol, typeToString(x.sym.typ) + else: + gsub(g, x) + put(g, tkParRi, ")") + if not isEmptyType(n[0].typ): + putWithSpace(g, tkColon, ":") + gsub(g, n[0]) + proc gproc(g: var TSrcGen, n: PNode) = var c: TContext = initContext() + var s: PSym = nil if n[namePos].kind == nkSym: - let s = n[namePos].sym + s = n[namePos].sym var ret = renderDefinitionName(s) ret.genSymSuffix(s) put(g, tkSymbol, ret) @@ -880,7 +1251,10 @@ proc gproc(g: var TSrcGen, n: PNode) = gsub(g, n[miscPos][1]) else: gsub(g, n[genericParamsPos]) - gsub(g, n[paramsPos]) + if n[paramsPos].len == 0 and s != nil and s.typ != nil and s.typ.n != nil: + gsemmedParams(g, s.typ.n) + else: + gsub(g, n[paramsPos]) if renderNoPragmas notin g.flags: gsub(g, n[pragmasPos]) if renderNoBody notin g.flags: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index ed9200f7f0..b290faec78 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -373,6 +373,7 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym = var g: G[string] ]# + # XXX FIXME This causes system.Natural to be duplicated during compilation of system.nim as cl.owner == nil! result = copySym(s, cl.c.idgen) incl(result.flagsImpl, sfFromGeneric) #idTablePut(cl.symMap, s, result) diff --git a/compiler/types.nim b/compiler/types.nim index 61d6ba201e..08c0c92dab 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -18,21 +18,9 @@ import std/[intsets, strutils] when defined(nimPreviewSlimSystem): import std/[assertions, formatfloat] -type - TPreferedDesc* = enum - preferName, # default - preferDesc, # probably should become what preferResolved is - preferExported, - preferModuleInfo, # fully qualified - preferGenericArg, - preferTypeName, - preferResolved, # fully resolved symbols - preferMixed, - # most useful, shows: symbol + resolved symbols if it differs, e.g.: - # tuple[a: MyInt{int}, b: float] - preferInlayHint, - preferInferredEffects, +export isResolvedUserTypeClass, TPreferedDesc, typeToString +type TTypeRelation* = enum # order is important! isNone, isConvertible, isIntConv, @@ -55,8 +43,6 @@ type pcmNotIterator pcmDifferentCallConv -proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string - proc addTypeDeclVerboseMaybe*(result: var string, conf: ConfigRef; typ: PType) = if optDeclaredLocs in conf.globalOptions: result.add typeToString(typ, preferMixed) @@ -64,8 +50,6 @@ proc addTypeDeclVerboseMaybe*(result: var string, conf: ConfigRef; typ: PType) = else: result.add typeToString(typ) -template `$`*(typ: PType): string = typeToString(typ) - # ------------------- type iterator: ---------------------------------------- type TTypeIter* = proc (t: PType, closure: RootRef): bool {.nimcall.} # true if iteration should stop @@ -157,12 +141,6 @@ proc getFloatValue*(n: PNode): BiggestFloat = of nkHiddenStdConv: getFloatValue(n[1]) else: NaN -proc isIntLit*(t: PType): bool {.inline.} = - result = t.kind == tyInt and t.n != nil and t.n.kind == nkIntLit - -proc isFloatLit*(t: PType): bool {.inline.} = - result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit - proc addTypeHeader*(result: var string, conf: ConfigRef; typ: PType; prefer: TPreferedDesc = preferMixed; getDeclarationPath = true) = result.add typeToString(typ, prefer) if getDeclarationPath: result.addDeclaredLoc(conf, typ.sym) @@ -460,337 +438,10 @@ proc canFormAcycle*(g: ModuleGraph, typ: PType): bool = let t = skipTypes(typ, abstractInst+{tyOwned}-{tyTypeDesc}) result = canFormAcycleAux(g, marker, t, t, false, false) -proc valueToString(a: PNode): string = - case a.kind - of nkCharLit, nkUIntLit..nkUInt64Lit: - result = $cast[uint64](a.intVal) - of nkIntLit..nkInt64Lit: - result = $a.intVal - of nkFloatLit..nkFloat128Lit: result = $a.floatVal - of nkStrLit..nkTripleStrLit: result = a.strVal - of nkStaticExpr: result = "static(" & a[0].renderTree & ")" - else: result = "" - -proc rangeToStr(n: PNode): string = - assert(n.kind == nkRange) - result = valueToString(n[0]) & ".." & valueToString(n[1]) - -const - typeToStr: array[TTypeKind, string] = ["None", "bool", "char", "empty", - "Alias", "typeof(nil)", "untyped", "typed", "typeDesc", - # xxx typeDesc=>typedesc: typedesc is declared as such, and is 10x more common. - "GenericInvocation", "GenericBody", "GenericInst", "GenericParam", - "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple", - "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc", - "pointer", "OpenArray[$1]", "string", "cstring", "Forward", - "int", "int8", "int16", "int32", "int64", - "float", "float32", "float64", "float128", - "uint", "uint8", "uint16", "uint32", "uint64", - "owned", "sink", - "lent ", "varargs[$1]", "UncheckedArray[$1]", "Error Type", - "BuiltInTypeClass", "UserTypeClass", - "UserTypeClassInst", "CompositeTypeClass", "inferred", - "and", "or", "not", "any", "static", "TypeFromExpr", "concept", # xxx bugfix - "void", "iterable"] - -const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo, - preferGenericArg, preferResolved, preferMixed, preferInlayHint, preferInferredEffects} - template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) = tc.add concrete tc.incl tfResolved -# TODO: It would be a good idea to kill the special state of a resolved -# concept by switching to tyAlias within the instantiated procs. -# Currently, tyAlias is always skipped with skipModifier, which means that -# we can store information about the matched concept in another position. -# Then builtInFieldAccess can be modified to properly read the derived -# consts and types stored within the concept. -template isResolvedUserTypeClass*(t: PType): bool = - tfResolved in t.flags - -proc addTypeFlags(name: var string, typ: PType) {.inline.} = - if tfNotNil in typ.flags: name.add(" not nil") - -proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = - let preferToplevel = prefer - proc getPrefer(prefer: TPreferedDesc): TPreferedDesc = - if preferToplevel in {preferResolved, preferMixed}: - preferToplevel # sticky option - else: - prefer - - proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = - result = "" - let prefer = getPrefer(prefer) - let t = typ - if t == nil: return - if prefer in preferToResolveSymbols and t.sym != nil and - sfAnon notin t.sym.flags and t.kind notin {tySequence, tyInferred}: - if t.kind == tyInt and isIntLit(t): - if prefer == preferInlayHint: - result = t.sym.name.s - else: - result = t.sym.name.s & " literal(" & $t.n.intVal & ")" - elif t.kind == tyAlias and t.elementType.kind != tyAlias: - result = typeToString(t.elementType) - elif prefer in {preferResolved, preferMixed}: - case t.kind - of IntegralTypes + {tyFloat..tyFloat128} + {tyString, tyCstring}: - result = typeToStr[t.kind] - of tyGenericBody: - result = typeToString(t.last) - of tyCompositeTypeClass: - # avoids showing `A[any]` in `proc fun(a: A)` with `A = object[T]` - result = typeToString(t.last.last) - else: - result = t.sym.name.s - if prefer == preferMixed and result != t.sym.name.s: - result = t.sym.name.s & "{" & result & "}" - elif prefer in {preferName, preferTypeName, preferInlayHint, preferInferredEffects} or t.sym.owner.isNil: - # note: should probably be: {preferName, preferTypeName, preferGenericArg} - result = t.sym.name.s - if t.kind == tyGenericParam and t.genericParamHasConstraints: - result.add ": " - result.add t.elementType.typeToString - else: - result = t.sym.owner.name.s & '.' & t.sym.name.s - result.addTypeFlags(t) - return - case t.kind - of tyInt: - if not isIntLit(t) or prefer == preferExported: - result = typeToStr[t.kind] - else: - case prefer: - of preferGenericArg: - result = $t.n.intVal - of preferInlayHint: - result = "int" - else: - result = "int literal(" & $t.n.intVal & ")" - of tyGenericInst: - result = typeToString(t.genericHead) & '[' - for needsComma, a in t.genericInstParams: - if needsComma: result.add(", ") - result.add(typeToString(a, preferGenericArg)) - result.add(']') - of tyGenericInvocation: - result = typeToString(t.genericHead) & '[' - for needsComma, a in t.genericInvocationParams: - if needsComma: result.add(", ") - result.add(typeToString(a, preferGenericArg)) - result.add(']') - of tyGenericBody: - result = typeToString(t.typeBodyImpl) & '[' - for i, a in t.genericBodyParams: - if i > 0: result.add(", ") - result.add(typeToString(a, preferTypeName)) - result.add(']') - of tyTypeDesc: - if t.elementType.kind == tyNone: result = "typedesc" - else: result = "typedesc[" & typeToString(t.elementType) & "]" - of tyStatic: - if prefer == preferGenericArg and t.n != nil: - result = t.n.renderTree - else: - result = "static[" & (if t.hasElementType: typeToString(t.skipModifier) else: "") & "]" - if t.n != nil: result.add "(" & renderTree(t.n) & ")" - of tyUserTypeClass: - if t.sym != nil and t.sym.owner != nil: - if t.isResolvedUserTypeClass: return typeToString(t.last) - return t.sym.owner.name.s - else: - result = "" - of tyBuiltInTypeClass: - result = - case t.base.kind - of tyVar: "var" - of tyRef: "ref" - of tyPtr: "ptr" - of tySequence: "seq" - of tyArray: "array" - of tySet: "set" - of tyRange: "range" - of tyDistinct: "distinct" - of tyProc: "proc" - of tyObject: "object" - of tyTuple: "tuple" - of tyOpenArray: "openArray" - else: typeToStr[t.base.kind] - of tyInferred: - let concrete = t.previouslyInferred - if concrete != nil: result = typeToString(concrete) - else: result = "inferred[" & typeToString(t.base) & "]" - of tyUserTypeClassInst: - let body = t.base - result = body.sym.name.s & "[" - for needsComma, a in t.userTypeClassInstParams: - if needsComma: result.add(", ") - result.add(typeToString(a)) - result.add "]" - of tyAnd: - for i, son in t.ikids: - if i > 0: result.add(" and ") - result.add(typeToString(son)) - of tyOr: - for i, son in t.ikids: - if i > 0: result.add(" or ") - result.add(typeToString(son)) - of tyNot: - result = "not " & typeToString(t.elementType) - of tyUntyped: - #internalAssert t.len == 0 - result = "untyped" - of tyFromExpr: - if t.n == nil: - result = "unknown" - else: - result = "typeof(" & renderTree(t.n) & ")" - of tyArray: - result = "array" - if t.hasElementType: - if t.indexType.kind == tyRange: - result &= "[" & rangeToStr(t.indexType.n) & ", " & - typeToString(t.elementType) & ']' - else: - result &= "[" & typeToString(t.indexType) & ", " & - typeToString(t.elementType) & ']' - of tyUncheckedArray: - result = "UncheckedArray" - if t.hasElementType: - result &= "[" & typeToString(t.elementType) & ']' - of tySequence: - if t.sym != nil and prefer != preferResolved: - result = t.sym.name.s - else: - result = "seq" - if t.hasElementType: - result &= "[" & typeToString(t.elementType) & ']' - of tyOrdinal: - result = "ordinal" - if t.hasElementType: - result &= "[" & typeToString(t.skipModifier) & ']' - of tySet: - result = "set" - if t.hasElementType: - result &= "[" & typeToString(t.elementType) & ']' - of tyOpenArray: - result = "openArray" - if t.hasElementType: - result &= "[" & typeToString(t.elementType) & ']' - of tyDistinct: - result = "distinct " & typeToString(t.elementType, - if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName) - of tyIterable: - # xxx factor this pattern - result = "iterable" - if t.hasElementType: - result &= "[" & typeToString(t.skipModifier) & ']' - of tyTuple: - # we iterate over t.sons here, because t.n may be nil - if t.n != nil: - result = "tuple[" - for i in 0.. 0: result.add ", " - result.add(typeToString(son)) - result.add(')') - of tyPtr, tyRef, tyVar, tyLent: - result = if isOutParam(t): "out " else: typeToStr[t.kind] - result.add typeToString(t.elementType) - of tyRange: - result = "range " - if t.n != nil and t.n.kind == nkRange: - result.add rangeToStr(t.n) - if prefer != preferExported: - result.add("(" & typeToString(t.elementType) & ")") - of tyProc: - result = if tfIterator in t.flags: "iterator " - elif t.owner != nil: - case t.owner.kind - of skTemplate: "template " - of skMacro: "macro " - of skConverter: "converter " - else: "proc " - else: - "proc " - if tfUnresolved in t.flags: result.add "[*missing parameters*]" - result.add "(" - for i, a in t.paramTypes: - if i > FirstParamAt: result.add(", ") - let j = paramTypeToNodeIndex(i) - if t.n != nil and j < t.n.len and t.n[j].kind == nkSym: - result.add(t.n[j].sym.name.s) - result.add(": ") - result.add(typeToString(a)) - result.add(')') - if t.returnType != nil: result.add(": " & typeToString(t.returnType)) - var prag = if t.callConv == ccNimCall and tfExplicitCallConv notin t.flags: "" else: $t.callConv - var hasImplicitRaises = false - if not isNil(t.owner) and not isNil(t.owner.ast) and (t.owner.ast.len - 1) >= pragmasPos: - let pragmasNode = t.owner.ast[pragmasPos] - let raisesSpec = effectSpec(pragmasNode, wRaises) - if not isNil(raisesSpec): - addSep(prag) - prag.add("raises: ") - prag.add($raisesSpec) - hasImplicitRaises = true - if tfNoSideEffect in t.flags: - addSep(prag) - prag.add("noSideEffect") - if tfThread in t.flags: - addSep(prag) - prag.add("gcsafe") - var effectsOfStr = "" - for i, a in t.paramTypes: - let j = paramTypeToNodeIndex(i) - if t.n != nil and j < t.n.len and t.n[j].kind == nkSym and t.n[j].sym.kind == skParam and sfEffectsDelayed in t.n[j].sym.flags: - addSep(effectsOfStr) - effectsOfStr.add(t.n[j].sym.name.s) - if effectsOfStr != "": - addSep(prag) - prag.add("effectsOf: ") - prag.add(effectsOfStr) - if not hasImplicitRaises and prefer == preferInferredEffects and not isNil(t.owner) and not isNil(t.owner.typ) and not isNil(t.owner.typ.n) and (t.owner.typ.n.len > 0): - let effects = t.n[0] - if effects.kind == nkEffectList and effects.len == effectListLen: - var inferredRaisesStr = "" - let effs = effects[exceptionEffects] - if not isNil(effs): - for eff in items(effs): - if not isNil(eff): - addSep(inferredRaisesStr) - inferredRaisesStr.add($eff.typ) - addSep(prag) - prag.add("raises: [") - prag.add(inferredRaisesStr) - prag.add("]") - if prag.len != 0: result.add("{." & prag & ".}") - of tyVarargs: - result = typeToStr[t.kind] % typeToString(t.elementType) - of tySink: - result = "sink " & typeToString(t.skipModifier) - of tyOwned: - result = "owned " & typeToString(t.elementType) - else: - result = typeToStr[t.kind] - result.addTypeFlags(t) - result = typeToString(typ, prefer) - proc firstOrd*(conf: ConfigRef; t: PType): Int128 = case t.kind of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyError: diff --git a/tests/ic/tparseutils.nim b/tests/ic/tparseutils.nim new file mode 100644 index 0000000000..bf977b94ee --- /dev/null +++ b/tests/ic/tparseutils.nim @@ -0,0 +1,10 @@ +discard """ + output: '''hello''' +""" + +import parseutils + +var w = "" +discard parseIdent("hello world", w) +echo w +