From 08085a3df1a497f830fc6c18500b67f8cd5c31a4 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Fri, 15 Mar 2024 19:24:17 +0300 Subject: [PATCH 01/20] try all call kinds in generic type node handling --- compiler/semtypes.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 1219d6ed835b0..5935ef550eedd 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -2049,7 +2049,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = elif op.s == "owned" and optOwnedRefs notin c.config.globalOptions and n.len == 2: result = semTypeExpr(c, n[1], prev) else: - if c.inGenericContext > 0 and n.kind == nkCall: + if c.inGenericContext > 0 and n.kind in nkCallKinds: let n = semGenericStmt(c, n) result = makeTypeFromExpr(c, n.copyTree) else: From 863b2dad7f05a1ef33cf121e5e5b2e7171a167d9 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Fri, 15 Mar 2024 19:26:29 +0300 Subject: [PATCH 02/20] add #23406 test --- tests/generics/tcalltype.nim | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/generics/tcalltype.nim diff --git a/tests/generics/tcalltype.nim b/tests/generics/tcalltype.nim new file mode 100644 index 0000000000000..33758c111dbb6 --- /dev/null +++ b/tests/generics/tcalltype.nim @@ -0,0 +1,22 @@ +# issue #23406 + +template helper(_: untyped): untyped = + int + +type # Each of them should always be `int`. + GenA[T] = helper int + GenB[T] = helper(int) + GenC[T] = helper helper(int) + +block: + template helper(_: untyped): untyped = + float + + type + A = GenA[int] + B = GenB[int] + C = GenC[int] + + assert A is int # OK. + assert B is int # Fails; it is `float`! + assert C is int # OK. From 6eea0175135d10d5eaabd1e90dac720349da7a84 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Mon, 25 Mar 2024 18:22:53 +0300 Subject: [PATCH 03/20] try remove if branch --- compiler/semtypes.nim | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 5935ef550eedd..d344f990fce97 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -2049,11 +2049,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = elif op.s == "owned" and optOwnedRefs notin c.config.globalOptions and n.len == 2: result = semTypeExpr(c, n[1], prev) else: - if c.inGenericContext > 0 and n.kind in nkCallKinds: - let n = semGenericStmt(c, n) - result = makeTypeFromExpr(c, n.copyTree) - else: - result = semTypeExpr(c, n, prev) + result = semTypeExpr(c, n, prev) of nkWhenStmt: var whenResult = semWhen(c, n, false) if whenResult.kind == nkStmtList: whenResult.transitionSonsKind(nkStmtListType) From 7b450843b670485479fe17a00b085090a76d58d5 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Mon, 25 Mar 2024 19:40:26 +0300 Subject: [PATCH 04/20] try fix weave, t14509, adapt #23438 for union, tmacrogenerics --- compiler/semcall.nim | 2 +- compiler/semexprs.nim | 2 +- compiler/semtypes.nim | 2 ++ compiler/sigmatch.nim | 3 ++- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index a136cf4fe9d42..3981d256fd710 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -751,7 +751,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, candidates) result = semResolvedCall(c, r, n, flags, expectedType) else: - if efDetermineType in flags and c.inGenericContext > 0 and c.matchedConcept == nil: + if c.inGenericContext > 0 and c.matchedConcept == nil: result = semGenericStmt(c, n) result.typ = makeTypeFromExpr(c, result.copyTree) elif efExplain notin flags: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 0147245af4c6f..b5ae05e245e6e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1028,7 +1028,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, if result != nil: if result[0].kind != nkSym: - if not (efDetermineType in flags and c.inGenericContext > 0): + if not (c.inGenericContext > 0): # see generic context check in semOverloadedCall internalError(c.config, "semOverloadedCallAnalyseEffects") return let callee = result[0].sym diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index d344f990fce97..eff2cbeb843e0 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1684,6 +1684,8 @@ proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType = # unnecessary new type creation let alias = maybeAliasType(c, result, prev) if alias != nil: result = alias + elif n.typ.kind == tyFromExpr and c.inGenericContext > 0: + result = n.typ else: localError(c.config, n.info, "expected type, but got: " & n.renderTree) result = errorType(c) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 744e0ff8bca03..9b830ac08adb3 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1914,7 +1914,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # when `f` is an unresolved typedesc, `a` could be any # type, so we should not perform this check earlier if c.c.inGenericContext > 0 and - a.skipTypes({tyTypeDesc}).kind == tyGenericParam: + a.skipTypes({tyTypeDesc}).kind == tyGenericParam and + not (c.calleeSym != nil and c.calleeSym.kind in {skMacro, skTemplate}): # generic type bodies can sometimes compile call expressions # prevent unresolved generic parameters from being passed to procs as # typedesc parameters From 9b336ff0a5a9b5691f458a7c43af16611fa49a31 Mon Sep 17 00:00:00 2001 From: metagn Date: Sat, 6 Apr 2024 04:04:34 +0300 Subject: [PATCH 05/20] spaghetti --- compiler/sem.nim | 7 +++++++ compiler/semcall.nim | 6 +++++- compiler/semexprs.nim | 7 +++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index 44a4c1c1e68d9..55eba124e0ef9 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -501,6 +501,13 @@ const proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}; expectedType: PType = nil): PNode = + if c.inGenericContext > 0 and sfAllUntyped notin sym.flags: + # in generic type body, typed macros can only be instantiated + # when the generic type is instantiated + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, result.copyTree) + return + rememberExpansion(c, nOrig.info, sym) pushInfoContext(c.config, nOrig.info, sym.detailedInfo) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 3981d256fd710..d2642be1a3b33 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -701,9 +701,13 @@ proc semResolvedCall(c: PContext, x: var TCandidate, else: c.inheritBindings(x, expectedType) finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) - else: + elif c.inGenericContext == 0: # For macros and templates, the resolved generic params # are added as normal params. + # This is not done in a generic type body context, as typed macros + # cannot be instantiated yet and semMacroExpr/semTemplateExpr will + # reject them and delay their instantiation, when fully resolved types + # will be added instead. c.inheritBindings(x, expectedType) for s in instantiateGenericParamList(c, gp, x.bindings): case s.kind diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index b5ae05e245e6e..17900b9dbfa75 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -27,6 +27,13 @@ const proc semTemplateExpr(c: PContext, n: PNode, s: PSym, flags: TExprFlags = {}; expectedType: PType = nil): PNode = + if c.inGenericContext > 0 and sfAllUntyped notin s.flags: + # in generic type body, typed templates can only be instantiated + # when the generic type is instantiated + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, result.copyTree) + return + rememberExpansion(c, n.info, s) let info = getCallLineInfo(n) markUsed(c, info, s) From 85912bb4c06446a6f4d6b465a2cfa43037f03364 Mon Sep 17 00:00:00 2001 From: metagn Date: Fri, 16 Aug 2024 05:18:17 +0300 Subject: [PATCH 06/20] insane effort to fix deref issue, fix other test --- compiler/ast.nim | 2 ++ compiler/semexprs.nim | 13 +++++++++---- compiler/semtypinst.nim | 10 +++++++++- .../generics/tuninstantiatedgenericcalls.nim | 18 ++++++++++++++++++ tests/metatype/ttypedescnotnimnode.nim | 19 +++++++++++++++++++ tests/metatype/ttypeselectors.nim | 13 ------------- 6 files changed, 57 insertions(+), 18 deletions(-) create mode 100644 tests/metatype/ttypedescnotnimnode.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 648bc4392857a..4f20a0eb5d0ae 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -448,6 +448,8 @@ const tfGcSafe* = tfThread tfObjHasKids* = tfEnumHasHoles tfReturnsNew* = tfInheritable + tfNonConstExpr* = tfExplicitCallConv + ## tyFromExpr where the expression shouldn't be evaluated as a constant skError* = skUnknown var diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 17900b9dbfa75..a33477d2541c1 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1661,18 +1661,20 @@ proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode = result.add(newIdentNode(ident, n.info)) for s in n: result.add s -proc semDeref(c: PContext, n: PNode): PNode = +proc semDeref(c: PContext, n: PNode, flags: TExprFlags): PNode = checkSonsLen(n, 1, c.config) n[0] = semExprWithType(c, n[0]) let a = getConstExpr(c.module, n[0], c.idgen, c.graph) if a != nil: - if a.kind == nkNilLit: + if a.kind == nkNilLit and efInTypeof notin flags: localError(c.config, n.info, "nil dereference is not allowed") n[0] = a result = n var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink, tyOwned}) case t.kind of tyRef, tyPtr: n.typ = t.elementType + of tyMetaTypes, tyFromExpr: + n.typ = makeTypeFromExpr(c, n.copyTree) else: result = nil #GlobalError(n[0].info, errCircumNeedsPointer) @@ -1703,8 +1705,11 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = ## checking of assignments result = nil if n.len == 1: - let x = semDeref(c, n) + let x = semDeref(c, n, flags) if x == nil: return nil + if x.typ.kind == tyFromExpr: + # depends on generic type + return x result = newNodeIT(nkDerefExpr, x.info, x.typ) result.add(x[0]) return @@ -3399,7 +3404,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType result = semArrayConstr(c, n, flags, expectedType) of nkObjConstr: result = semObjConstr(c, n, flags, expectedType) of nkLambdaKinds: result = semProcAux(c, n, skProc, lambdaPragmas, flags) - of nkDerefExpr: result = semDeref(c, n) + of nkDerefExpr: result = semDeref(c, n, flags) of nkAddr: result = n checkSonsLen(n, 1, c.config) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index e87a4939a2f19..78b6b81958fb6 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -222,6 +222,9 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT if n == nil: return result = copyNode(n) if n.typ != nil: + if n.typ.kind == tyFromExpr: + # type of node should not be evaluated + n.typ.flags.incl tfNonConstExpr result.typ = replaceTypeVarsT(cl, n.typ) checkMetaInvariants(cl, result.typ) case n.kind @@ -591,13 +594,18 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = assert t.n.typ != t var n = prepareNode(cl, t.n) if n.kind != nkEmpty: - n = cl.c.semConstExpr(cl.c, n) + if tfNonConstExpr in t.flags: + n = cl.c.semExprWithType(cl.c, n, flags = {efInTypeof}) + else: + n = cl.c.semConstExpr(cl.c, n) if n.typ.kind == tyTypeDesc: # XXX: sometimes, chained typedescs enter here. # It may be worth investigating why this is happening, # because it may cause other bugs elsewhere. result = n.typ.skipTypes({tyTypeDesc}) # result = n.typ.base + elif tfNonConstExpr in t.flags: + result = n.typ else: if n.typ.kind != tyStatic and n.kind != nkType: # XXX: In the future, semConstExpr should diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim index 2163789e7bb25..7af6e202f238d 100644 --- a/tests/generics/tuninstantiatedgenericcalls.nim +++ b/tests/generics/tuninstantiatedgenericcalls.nim @@ -163,3 +163,21 @@ block: # issue #23339 outerField: Inner[O.aToB] var x: Outer[A] doAssert typeof(x.outerField.innerField) is B + +block: # deref syntax + type + Enqueueable = concept x + x is ptr + Foo[T: Enqueueable] = object + x: typeof(default(T)[]) + + proc p[T](f: Foo[T]) = + var bar: Foo[T] + discard + var foo: Foo[ptr int] + p(foo) + doAssert foo.x is int + foo.x = 123 + doAssert foo.x == 123 + inc foo.x + doAssert foo.x == 124 diff --git a/tests/metatype/ttypedescnotnimnode.nim b/tests/metatype/ttypedescnotnimnode.nim new file mode 100644 index 0000000000000..a36fe3b8e500e --- /dev/null +++ b/tests/metatype/ttypedescnotnimnode.nim @@ -0,0 +1,19 @@ +discard """ + errormsg: "type mismatch: got but expected 'typedesc'" + line: 14 +""" + +import macros + +# This is the same example as ttypeselectors but using a proc instead of a macro +# Instead of type mismatch for macro, proc just failed with internal error: getTypeDescAux(tyNone) +# https://github.com/nim-lang/Nim/issues/7231 + +proc getBase2*(bits: static[int]): typedesc = + if bits == 128: + result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint64")) + else: + result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint32")) + +type + MpUint2*[bits: static[int]] = getbase2(bits) diff --git a/tests/metatype/ttypeselectors.nim b/tests/metatype/ttypeselectors.nim index 5385a1be94919..d0843511d2181 100644 --- a/tests/metatype/ttypeselectors.nim +++ b/tests/metatype/ttypeselectors.nim @@ -98,16 +98,3 @@ var c: Bar[32] echo sizeof(a) echo sizeof(b) echo sizeof(c) - -# This is the same example but using a proc instead of a macro -# Instead of type mismatch for macro, proc just failed with internal error: getTypeDescAux(tyNone) -# https://github.com/nim-lang/Nim/issues/7231 - -proc getBase2*(bits: static[int]): typedesc = - if bits == 128: - result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint64")) - else: - result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint32")) - -type - MpUint2*[bits: static[int]] = getbase2(bits) From 53e3fa702b59ea45f9978aa66c460310af3d2cf9 Mon Sep 17 00:00:00 2001 From: metagn Date: Sat, 17 Aug 2024 18:15:41 +0300 Subject: [PATCH 07/20] allow early template macro instantiation? weird territory --- compiler/sem.nim | 2 +- compiler/semcall.nim | 2 +- compiler/semexprs.nim | 2 +- tests/generics/tuninstantiatedgenericcalls.nim | 12 ++++++++++++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index 55eba124e0ef9..6df8e345f4afe 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -501,7 +501,7 @@ const proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}; expectedType: PType = nil): PNode = - if c.inGenericContext > 0 and sfAllUntyped notin sym.flags: + if false and c.inGenericContext > 0 and sfAllUntyped notin sym.flags: # in generic type body, typed macros can only be instantiated # when the generic type is instantiated result = semGenericStmt(c, n) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index d2642be1a3b33..b0444fff484cd 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -701,7 +701,7 @@ proc semResolvedCall(c: PContext, x: var TCandidate, else: c.inheritBindings(x, expectedType) finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) - elif c.inGenericContext == 0: + elif true or c.inGenericContext == 0: # For macros and templates, the resolved generic params # are added as normal params. # This is not done in a generic type body context, as typed macros diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a33477d2541c1..a8de3bc30aea3 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -27,7 +27,7 @@ const proc semTemplateExpr(c: PContext, n: PNode, s: PSym, flags: TExprFlags = {}; expectedType: PType = nil): PNode = - if c.inGenericContext > 0 and sfAllUntyped notin s.flags: + if false and c.inGenericContext > 0 and sfAllUntyped notin s.flags: # in generic type body, typed templates can only be instantiated # when the generic type is instantiated result = semGenericStmt(c, n) diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim index 7af6e202f238d..54f9ce1d45190 100644 --- a/tests/generics/tuninstantiatedgenericcalls.nim +++ b/tests/generics/tuninstantiatedgenericcalls.nim @@ -181,3 +181,15 @@ block: # deref syntax doAssert foo.x == 123 inc foo.x doAssert foo.x == 124 + +block: + type Generic[T] = object + field: T + macro foo(x: typed): untyped = x + type Foo[T] = object + field: Generic[int].foo() + var x: Foo[int] + macro bar[T](x: typedesc[Generic[T]]): untyped = x + type Bar[T] = object + field: Generic[int].bar() + var y: Bar[int] From 97969cdb404aafaed137111684b163a7ea17e4e7 Mon Sep 17 00:00:00 2001 From: metagn Date: Sat, 17 Aug 2024 18:35:59 +0300 Subject: [PATCH 08/20] actual spaghetti --- compiler/semtypinst.nim | 21 ++++++++++++-- tests/generics/tmacrotype.nim | 28 +++++++++++++++++++ .../generics/tuninstantiatedgenericcalls.nim | 17 +++++++---- 3 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 tests/generics/tmacrotype.nim diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 78b6b81958fb6..cdc3b587932a8 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -118,6 +118,17 @@ proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = result = replaceTypeVarsTAux(cl, t) checkMetaInvariants(cl, result) +proc isLikeGenericInvocation(n: PNode): int = + result = -1 + if n.kind in nkCallKinds and + (let ident = n[0].getPIdent; ident != nil and ident.s == "[]"): + result = 1 + elif n.kind == nkBracketExpr: + result = 0 + if result >= 0 and (result > n.len or + n[result].typ == nil or n[result].typ.kind != tyTypeDesc): + result = -1 + proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = let t = replaceTypeVarsT(cl, n.typ) if t != nil and t.kind == tyStatic and t.n != nil: @@ -132,14 +143,20 @@ proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = else: replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ)) let isCall = result.kind in nkCallKinds + let ignoreUntil = isLikeGenericInvocation(n) + let isGenericInvocation = ignoreUntil >= 0 # don't try to instantiate symchoice symbols, they can be # generic procs which the compiler will think are uninstantiated # because their type will contain uninstantiated params let isSymChoice = result.kind in nkSymChoices for i in 0.. Date: Sat, 17 Aug 2024 20:01:02 +0300 Subject: [PATCH 09/20] sacrifice #23432 for now --- compiler/sem.nim | 2 +- compiler/semcall.nim | 2 +- compiler/semexprs.nim | 2 +- tests/generics/tmacrotype.nim | 10 ++++++++++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index 6df8e345f4afe..55eba124e0ef9 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -501,7 +501,7 @@ const proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}; expectedType: PType = nil): PNode = - if false and c.inGenericContext > 0 and sfAllUntyped notin sym.flags: + if c.inGenericContext > 0 and sfAllUntyped notin sym.flags: # in generic type body, typed macros can only be instantiated # when the generic type is instantiated result = semGenericStmt(c, n) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index b0444fff484cd..d2642be1a3b33 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -701,7 +701,7 @@ proc semResolvedCall(c: PContext, x: var TCandidate, else: c.inheritBindings(x, expectedType) finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) - elif true or c.inGenericContext == 0: + elif c.inGenericContext == 0: # For macros and templates, the resolved generic params # are added as normal params. # This is not done in a generic type body context, as typed macros diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a8de3bc30aea3..a33477d2541c1 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -27,7 +27,7 @@ const proc semTemplateExpr(c: PContext, n: PNode, s: PSym, flags: TExprFlags = {}; expectedType: PType = nil): PNode = - if false and c.inGenericContext > 0 and sfAllUntyped notin s.flags: + if c.inGenericContext > 0 and sfAllUntyped notin s.flags: # in generic type body, typed templates can only be instantiated # when the generic type is instantiated result = semGenericStmt(c, n) diff --git a/tests/generics/tmacrotype.nim b/tests/generics/tmacrotype.nim index ad42569c03a15..991a92bfdafc2 100644 --- a/tests/generics/tmacrotype.nim +++ b/tests/generics/tmacrotype.nim @@ -1,3 +1,13 @@ +discard """ + # XXX not actually fixed + disabled: true # cannot instantiate: 'T' + # the use of `typedesc` delays the macro unlike `typed` or `untyped` + # but this makes it impossible for overloading to infer the type + # some code depends on `typedesc` needing to be resolved, e.g. tmacrogenerics + # so we can't change it, but maybe we can add a version of `typedesc` that allows generic params + # maybe something like `typed{typedesc}` so it doesn't lift to a generic param +""" + import std/[sequtils, macros] block: # issue #23432 From 020e0c32255becc395494c691b48f3d0ff67672a Mon Sep 17 00:00:00 2001 From: metagn Date: Sat, 17 Aug 2024 20:05:49 +0300 Subject: [PATCH 10/20] cleanup attempt of last commit --- compiler/sem.nim | 2 +- compiler/semcall.nim | 2 +- compiler/semexprs.nim | 2 +- compiler/sigmatch.nim | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index 55eba124e0ef9..6df8e345f4afe 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -501,7 +501,7 @@ const proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}; expectedType: PType = nil): PNode = - if c.inGenericContext > 0 and sfAllUntyped notin sym.flags: + if false and c.inGenericContext > 0 and sfAllUntyped notin sym.flags: # in generic type body, typed macros can only be instantiated # when the generic type is instantiated result = semGenericStmt(c, n) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index d2642be1a3b33..b0444fff484cd 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -701,7 +701,7 @@ proc semResolvedCall(c: PContext, x: var TCandidate, else: c.inheritBindings(x, expectedType) finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) - elif c.inGenericContext == 0: + elif true or c.inGenericContext == 0: # For macros and templates, the resolved generic params # are added as normal params. # This is not done in a generic type body context, as typed macros diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a33477d2541c1..a8de3bc30aea3 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -27,7 +27,7 @@ const proc semTemplateExpr(c: PContext, n: PNode, s: PSym, flags: TExprFlags = {}; expectedType: PType = nil): PNode = - if c.inGenericContext > 0 and sfAllUntyped notin s.flags: + if false and c.inGenericContext > 0 and sfAllUntyped notin s.flags: # in generic type body, typed templates can only be instantiated # when the generic type is instantiated result = semGenericStmt(c, n) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 9b830ac08adb3..adfa9a38e08b2 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1914,8 +1914,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # when `f` is an unresolved typedesc, `a` could be any # type, so we should not perform this check earlier if c.c.inGenericContext > 0 and - a.skipTypes({tyTypeDesc}).kind == tyGenericParam and - not (c.calleeSym != nil and c.calleeSym.kind in {skMacro, skTemplate}): + a.skipTypes({tyTypeDesc}).kind == tyGenericParam#[ and + not (c.calleeSym != nil and c.calleeSym.kind in {skMacro, skTemplate})]#: # generic type bodies can sometimes compile call expressions # prevent unresolved generic parameters from being passed to procs as # typedesc parameters From 53620f5111771ec261dd52ac3c6583214cd88882 Mon Sep 17 00:00:00 2001 From: metagn Date: Mon, 19 Aug 2024 03:34:45 +0300 Subject: [PATCH 11/20] restrict to unresolved statics, maybe sigmatch should find anything unresolved --- compiler/sem.nim | 6 +++--- compiler/semcall.nim | 4 ++-- compiler/semexprs.nim | 6 +++--- .../generics/tuninstantiatedgenericcalls.nim | 21 +++++++++++++++++++ 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index 6df8e345f4afe..f16c961623551 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -501,9 +501,9 @@ const proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}; expectedType: PType = nil): PNode = - if false and c.inGenericContext > 0 and sfAllUntyped notin sym.flags: - # in generic type body, typed macros can only be instantiated - # when the generic type is instantiated + if c.inGenericContext > 0 and n.findUnresolvedStatic != nil: + # in generic type body, typed macros using unresolved statics + # can only be instantiated when the generic type is instantiated result = semGenericStmt(c, n) result.typ = makeTypeFromExpr(c, result.copyTree) return diff --git a/compiler/semcall.nim b/compiler/semcall.nim index b0444fff484cd..d8f3e362b9787 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -701,10 +701,10 @@ proc semResolvedCall(c: PContext, x: var TCandidate, else: c.inheritBindings(x, expectedType) finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) - elif true or c.inGenericContext == 0: + elif c.inGenericContext == 0 or n.findUnresolvedStatic == nil: # For macros and templates, the resolved generic params # are added as normal params. - # This is not done in a generic type body context, as typed macros + # This is not done with unresolved static arguments, as typed macros # cannot be instantiated yet and semMacroExpr/semTemplateExpr will # reject them and delay their instantiation, when fully resolved types # will be added instead. diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a8de3bc30aea3..8b2a50a495231 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -27,9 +27,9 @@ const proc semTemplateExpr(c: PContext, n: PNode, s: PSym, flags: TExprFlags = {}; expectedType: PType = nil): PNode = - if false and c.inGenericContext > 0 and sfAllUntyped notin s.flags: - # in generic type body, typed templates can only be instantiated - # when the generic type is instantiated + if c.inGenericContext > 0 and n.findUnresolvedStatic != nil: + # in generic type body, typed templates using unresolved statics + # can only be instantiated when the generic type is instantiated result = semGenericStmt(c, n) result.typ = makeTypeFromExpr(c, result.copyTree) return diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim index 6c594325387c8..ea6b379517cb9 100644 --- a/tests/generics/tuninstantiatedgenericcalls.nim +++ b/tests/generics/tuninstantiatedgenericcalls.nim @@ -200,3 +200,24 @@ block: var x2: Foo2[int] var y: Bar[int] var y2: Bar2[int] + +block: + macro pick(x: static int): untyped = + if x < 100: + result = bindSym"int" + else: + result = bindSym"float" + + type Foo[T: static int] = object + fixed1: pick(25) + fixed2: pick(125) + unknown: pick(T) + + var a: Foo[123] + doAssert a.fixed1 is int + doAssert a.fixed2 is float + doAssert a.unknown is float + var b: Foo[23] + doAssert b.fixed1 is int + doAssert b.fixed2 is float + doAssert b.unknown is int From 1f419c420b8882e007526f2d8788f34bc6c88b70 Mon Sep 17 00:00:00 2001 From: metagn Date: Mon, 19 Aug 2024 06:19:24 +0300 Subject: [PATCH 12/20] a bit ugly but works --- compiler/sem.nim | 7 ---- compiler/semcall.nim | 6 ++- compiler/semexprs.nim | 7 ---- compiler/sigmatch.nim | 10 +++-- tests/generics/tmacrotype.nim | 38 ------------------- .../generics/tuninstantiatedgenericcalls.nim | 18 +++++++++ tests/iter/titertypedesc.nim | 1 + 7 files changed, 31 insertions(+), 56 deletions(-) delete mode 100644 tests/generics/tmacrotype.nim diff --git a/compiler/sem.nim b/compiler/sem.nim index f16c961623551..44a4c1c1e68d9 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -501,13 +501,6 @@ const proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}; expectedType: PType = nil): PNode = - if c.inGenericContext > 0 and n.findUnresolvedStatic != nil: - # in generic type body, typed macros using unresolved statics - # can only be instantiated when the generic type is instantiated - result = semGenericStmt(c, n) - result.typ = makeTypeFromExpr(c, result.copyTree) - return - rememberExpansion(c, nOrig.info, sym) pushInfoContext(c.config, nOrig.info, sym.detailedInfo) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index d8f3e362b9787..72fac9f5f5884 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -701,7 +701,7 @@ proc semResolvedCall(c: PContext, x: var TCandidate, else: c.inheritBindings(x, expectedType) finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) - elif c.inGenericContext == 0 or n.findUnresolvedStatic == nil: + else: # For macros and templates, the resolved generic params # are added as normal params. # This is not done with unresolved static arguments, as typed macros @@ -747,6 +747,10 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, var errors: CandidateErrors = @[] # if efExplain in flags: @[] else: nil var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) if r.state == csMatch: + if c.inGenericContext > 0 and r.matchedUnresolvedStatic: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, result.copyTree) + return # this may be triggered, when the explain pragma is used if errors.len > 0: let (_, candidates) = presentFailedCandidates(c, n, errors) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 8b2a50a495231..2e90dffbf1aec 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -27,13 +27,6 @@ const proc semTemplateExpr(c: PContext, n: PNode, s: PSym, flags: TExprFlags = {}; expectedType: PType = nil): PNode = - if c.inGenericContext > 0 and n.findUnresolvedStatic != nil: - # in generic type body, typed templates using unresolved statics - # can only be instantiated when the generic type is instantiated - result = semGenericStmt(c, n) - result.typ = makeTypeFromExpr(c, result.copyTree) - return - rememberExpansion(c, n.info, s) let info = getCallLineInfo(n) markUsed(c, info, s) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index adfa9a38e08b2..d7738a26e63aa 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -84,6 +84,7 @@ type inheritancePenalty: int firstMismatch*: MismatchInfo # mismatch info for better error messages diagnosticsEnabled*: bool + matchedUnresolvedStatic*: bool TTypeRelFlag* = enum trDontBind @@ -1216,6 +1217,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, return isGeneric else: discard + if aOrig.kind == tyStatic and aOrig.n == nil: + c.matchedUnresolvedStatic = true + case f.kind of tyEnum: if a.kind == f.kind and sameEnumTypes(f, a): result = isEqual @@ -1858,6 +1862,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let prev = idTableGet(c.bindings, f) if prev == nil: if aOrig.kind == tyStatic: + if c.c.inGenericContext > 0 and aOrig.n == nil: + result = isNone if f.base.kind notin {tyNone, tyGenericParam}: result = typeRel(c, f.base, a, flags) if result != isNone and f.n != nil: @@ -1913,9 +1919,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # proc foo(T: typedesc, x: T) # when `f` is an unresolved typedesc, `a` could be any # type, so we should not perform this check earlier - if c.c.inGenericContext > 0 and - a.skipTypes({tyTypeDesc}).kind == tyGenericParam#[ and - not (c.calleeSym != nil and c.calleeSym.kind in {skMacro, skTemplate})]#: + if c.c.inGenericContext > 0 and a.containsGenericType: # generic type bodies can sometimes compile call expressions # prevent unresolved generic parameters from being passed to procs as # typedesc parameters diff --git a/tests/generics/tmacrotype.nim b/tests/generics/tmacrotype.nim deleted file mode 100644 index 991a92bfdafc2..0000000000000 --- a/tests/generics/tmacrotype.nim +++ /dev/null @@ -1,38 +0,0 @@ -discard """ - # XXX not actually fixed - disabled: true # cannot instantiate: 'T' - # the use of `typedesc` delays the macro unlike `typed` or `untyped` - # but this makes it impossible for overloading to infer the type - # some code depends on `typedesc` needing to be resolved, e.g. tmacrogenerics - # so we can't change it, but maybe we can add a version of `typedesc` that allows generic params - # maybe something like `typed{typedesc}` so it doesn't lift to a generic param -""" - -import std/[sequtils, macros] - -block: # issue #23432 - type - Future[T] = object - InternalRaisesFuture[T, E] = object - - macro Raising[T](F: typedesc[Future[T]], E: varargs[typedesc]): untyped = - ## Given a Future type instance, return a type storing `{.raises.}` - ## information - ## - ## Note; this type may change in the future - E.expectKind(nnkBracket) - - let raises = nnkTupleConstr.newTree(E.mapIt(it)) - nnkBracketExpr.newTree( - ident "InternalRaisesFuture", - nnkDotExpr.newTree(F, ident"T"), - raises - ) - - type X[E] = Future[void].Raising(E) - - proc f(x: X) = discard - - - var v: Future[void].Raising([ValueError]) - f(v) diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim index ea6b379517cb9..172a00bd44163 100644 --- a/tests/generics/tuninstantiatedgenericcalls.nim +++ b/tests/generics/tuninstantiatedgenericcalls.nim @@ -221,3 +221,21 @@ block: doAssert b.fixed1 is int doAssert b.fixed2 is float doAssert b.unknown is int + +import std/sequtils + +block: # version of #23432 with `typed`, don't delay instantiation + type + Future[T] = object + InternalRaisesFuture[T, E] = object + macro Raising[T](F: typedesc[Future[T]], E: varargs[typed]): untyped = + let raises = nnkTupleConstr.newTree(E.mapIt(it)) + nnkBracketExpr.newTree( + ident "InternalRaisesFuture", + nnkDotExpr.newTree(F, ident"T"), + raises + ) + type X[E] = Future[void].Raising(E) + proc f(x: X) = discard + var v: Future[void].Raising([ValueError]) + f(v) diff --git a/tests/iter/titertypedesc.nim b/tests/iter/titertypedesc.nim index a69f703c618ee..51d899049c6f2 100644 --- a/tests/iter/titertypedesc.nim +++ b/tests/iter/titertypedesc.nim @@ -1,4 +1,5 @@ discard """ + joinable: false # fails to #23977 output: '''0 (id: 0) @[] From 3e6030f0a0b8c83b57eb1f963c598f5cb09a5faa Mon Sep 17 00:00:00 2001 From: metagn Date: Mon, 19 Aug 2024 07:33:09 +0300 Subject: [PATCH 13/20] try fix CI --- compiler/semcall.nim | 4 +++- tests/generics/tcalltype.nim | 4 ++++ tests/iter/titertypedesc.nim | 1 - 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 72fac9f5f5884..634e00b595020 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -747,7 +747,9 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, var errors: CandidateErrors = @[] # if efExplain in flags: @[] else: nil var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) if r.state == csMatch: - if c.inGenericContext > 0 and r.matchedUnresolvedStatic: + if c.inGenericContext > 0 and r.matchedUnresolvedStatic and + r.calleeSym.kind in {skMacro, skTemplate}: + # macros and templates with unresolved statics should not instantiate result = semGenericStmt(c, n) result.typ = makeTypeFromExpr(c, result.copyTree) return diff --git a/tests/generics/tcalltype.nim b/tests/generics/tcalltype.nim index 33758c111dbb6..cba691f77c47b 100644 --- a/tests/generics/tcalltype.nim +++ b/tests/generics/tcalltype.nim @@ -1,3 +1,7 @@ +discard """ + joinable: false # breaks everything because of #23977 +""" + # issue #23406 template helper(_: untyped): untyped = diff --git a/tests/iter/titertypedesc.nim b/tests/iter/titertypedesc.nim index 51d899049c6f2..a69f703c618ee 100644 --- a/tests/iter/titertypedesc.nim +++ b/tests/iter/titertypedesc.nim @@ -1,5 +1,4 @@ discard """ - joinable: false # fails to #23977 output: '''0 (id: 0) @[] From 9c1395f3c8be84bbf5f9cd127a5b076b5a7db6a0 Mon Sep 17 00:00:00 2001 From: metagn Date: Mon, 19 Aug 2024 09:27:00 +0300 Subject: [PATCH 14/20] checking if this is the culprit --- compiler/semgnrc.nim | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index e2da56c5d7abd..3f68e2bc4e488 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -104,6 +104,14 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, if s.typ != nil and s.typ.kind == tyStatic: if s.typ.n != nil: result = s.typ.n + elif c.inGenericContext > 0: + result = newSymNodeTypeDesc(s, c.idgen, n.info) + if canOpenSym(result.sym): + if genericsOpenSym in c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil else: result = n else: @@ -128,6 +136,14 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, else: result.flags.incl nfDisabledOpenSym result.typ = nil + elif c.inGenericContext > 0: + result = newSymNodeTypeDesc(s, c.idgen, n.info) + if canOpenSym(result.sym): + if genericsOpenSym in c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil else: result = n onUse(n.info, s) From 13e1846f3980bcb61bb6751fe2f3b8ed9a25e5ff Mon Sep 17 00:00:00 2001 From: metagn Date: Mon, 19 Aug 2024 11:05:50 +0300 Subject: [PATCH 15/20] didn't have faith but only concept tests failed (and constantine) --- compiler/semgnrc.nim | 4 ++-- tests/generics/tuninstantiatedgenericcalls.nim | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 3f68e2bc4e488..677c3bbab8c49 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -104,7 +104,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, if s.typ != nil and s.typ.kind == tyStatic: if s.typ.n != nil: result = s.typ.n - elif c.inGenericContext > 0: + elif c.inGenericContext > 0 and c.matchedConcept == nil: result = newSymNodeTypeDesc(s, c.idgen, n.info) if canOpenSym(result.sym): if genericsOpenSym in c.features: @@ -136,7 +136,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, else: result.flags.incl nfDisabledOpenSym result.typ = nil - elif c.inGenericContext > 0: + elif c.inGenericContext > 0 and c.matchedConcept == nil: result = newSymNodeTypeDesc(s, c.idgen, n.info) if canOpenSym(result.sym): if genericsOpenSym in c.features: diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim index 172a00bd44163..4e10b5ab1e00a 100644 --- a/tests/generics/tuninstantiatedgenericcalls.nim +++ b/tests/generics/tuninstantiatedgenericcalls.nim @@ -201,7 +201,7 @@ block: var y: Bar[int] var y2: Bar2[int] -block: +when false: # doesn't work yet because `semGenericStmt` isn't good enough macro pick(x: static int): untyped = if x < 100: result = bindSym"int" From 5f18c7fb6c60c0c0ab4f7b79b6470b7663af510c Mon Sep 17 00:00:00 2001 From: metagn Date: Mon, 19 Aug 2024 11:44:17 +0300 Subject: [PATCH 16/20] actually fix concepts --- compiler/semgnrc.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 677c3bbab8c49..3da32c6394194 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -104,7 +104,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, if s.typ != nil and s.typ.kind == tyStatic: if s.typ.n != nil: result = s.typ.n - elif c.inGenericContext > 0 and c.matchedConcept == nil: + elif c.inGenericContext > 0 and withinConcept notin flags: result = newSymNodeTypeDesc(s, c.idgen, n.info) if canOpenSym(result.sym): if genericsOpenSym in c.features: @@ -136,7 +136,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, else: result.flags.incl nfDisabledOpenSym result.typ = nil - elif c.inGenericContext > 0 and c.matchedConcept == nil: + elif c.inGenericContext > 0 and withinConcept notin flags: result = newSymNodeTypeDesc(s, c.idgen, n.info) if canOpenSym(result.sym): if genericsOpenSym in c.features: From a031866c8d0f668ad8326d8b9e6f51bf33bb4fdf Mon Sep 17 00:00:00 2001 From: metagn Date: Mon, 19 Aug 2024 14:49:48 +0300 Subject: [PATCH 17/20] this is disgusting --- compiler/semexprs.nim | 27 +++++-- compiler/semtypinst.nim | 73 +++++++++++++------ .../generics/tuninstantiatedgenericcalls.nim | 2 +- 3 files changed, 72 insertions(+), 30 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 2e90dffbf1aec..6b70f7fd026a3 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1137,6 +1137,12 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType result.flags.incl nfExplicitCall for i in 1.. 0: + # don't make assumptions, entire expression needs to be tyFromExpr + n[0].typ = nil + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, result.copyTree) + return else: n[0] = n0 else: @@ -1480,17 +1486,26 @@ proc tryReadingGenericParam(c: PContext, n: PNode, i: PIdent, t: PType): PNode = of tyTypeParamsHolders: result = readTypeParameter(c, t, i, n.info) if result == c.graph.emptyNode: - result = n - n.typ = makeTypeFromExpr(c, n.copyTree) + if true or c.inGenericContext > 0: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, result.copyTree) + else: + result = nil of tyUserTypeClasses: if t.isResolvedUserTypeClass: result = readTypeParameter(c, t, i, n.info) else: - n.typ = makeTypeFromExpr(c, copyTree(n)) - result = n + if true or c.inGenericContext > 0: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, copyTree(result)) + else: + result = nil of tyGenericParam, tyAnything: - n.typ = makeTypeFromExpr(c, copyTree(n)) - result = n + if true or c.inGenericContext > 0: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, copyTree(result)) + else: + result = nil else: result = nil diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index cdc3b587932a8..4d74ebd636b7b 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -118,17 +118,6 @@ proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = result = replaceTypeVarsTAux(cl, t) checkMetaInvariants(cl, result) -proc isLikeGenericInvocation(n: PNode): int = - result = -1 - if n.kind in nkCallKinds and - (let ident = n[0].getPIdent; ident != nil and ident.s == "[]"): - result = 1 - elif n.kind == nkBracketExpr: - result = 0 - if result >= 0 and (result > n.len or - n[result].typ == nil or n[result].typ.kind != tyTypeDesc): - result = -1 - proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = let t = replaceTypeVarsT(cl, n.typ) if t != nil and t.kind == tyStatic and t.n != nil: @@ -142,20 +131,58 @@ proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = replaceTypeVarsS(cl, n.sym, result.typ) else: replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ)) - let isCall = result.kind in nkCallKinds - let ignoreUntil = isLikeGenericInvocation(n) - let isGenericInvocation = ignoreUntil >= 0 - # don't try to instantiate symchoice symbols, they can be - # generic procs which the compiler will think are uninstantiated - # because their type will contain uninstantiated params - let isSymChoice = result.kind in nkSymChoices - for i in 0.. 1 and + (n[1].typ != nil and n[1].typ.kind == tyTypeDesc) + if ignoreFirst: + result.add(n[0]) + else: + result.add(prepareNode(cl, n[0])) + if n.len > 1: + if ignoreSecond: + result.add(n[1]) + else: + result.add(prepareNode(cl, n[1])) + for i in 2..= 2 + result.add(prepareNode(cl, n[0])) + result.add(n[1]) + for i in 2.. Date: Mon, 19 Aug 2024 15:57:39 +0300 Subject: [PATCH 18/20] add tests for #23854 and #23855, prepare for cleanup --- compiler/semcall.nim | 4 --- compiler/semexprs.nim | 2 +- compiler/semgnrc.nim | 4 +++ tests/generics/t23854.nim | 65 +++++++++++++++++++++++++++++++++++++++ tests/generics/t23855.nim | 57 ++++++++++++++++++++++++++++++++++ 5 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 tests/generics/t23854.nim create mode 100644 tests/generics/t23855.nim diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 634e00b595020..dfb17c5c59b80 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -704,10 +704,6 @@ proc semResolvedCall(c: PContext, x: var TCandidate, else: # For macros and templates, the resolved generic params # are added as normal params. - # This is not done with unresolved static arguments, as typed macros - # cannot be instantiated yet and semMacroExpr/semTemplateExpr will - # reject them and delay their instantiation, when fully resolved types - # will be added instead. c.inheritBindings(x, expectedType) for s in instantiateGenericParamList(c, gp, x.bindings): case s.kind diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 6b70f7fd026a3..a22764817f0b4 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1500,7 +1500,7 @@ proc tryReadingGenericParam(c: PContext, n: PNode, i: PIdent, t: PType): PNode = result.typ = makeTypeFromExpr(c, copyTree(result)) else: result = nil - of tyGenericParam, tyAnything: + of tyFromExpr, tyGenericParam, tyAnything: if true or c.inGenericContext > 0: result = semGenericStmt(c, n) result.typ = makeTypeFromExpr(c, copyTree(result)) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 3da32c6394194..d294aa391e9f4 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -105,6 +105,8 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, if s.typ.n != nil: result = s.typ.n elif c.inGenericContext > 0 and withinConcept notin flags: + # fine to give a symbol node a generic type here since + # we are in a generic context and `prepareNode` will be called result = newSymNodeTypeDesc(s, c.idgen, n.info) if canOpenSym(result.sym): if genericsOpenSym in c.features: @@ -137,6 +139,8 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, result.flags.incl nfDisabledOpenSym result.typ = nil elif c.inGenericContext > 0 and withinConcept notin flags: + # fine to give a symbol node a generic type here since + # we are in a generic context and `prepareNode` will be called result = newSymNodeTypeDesc(s, c.idgen, n.info) if canOpenSym(result.sym): if genericsOpenSym in c.features: diff --git a/tests/generics/t23854.nim b/tests/generics/t23854.nim new file mode 100644 index 0000000000000..08a08927f644b --- /dev/null +++ b/tests/generics/t23854.nim @@ -0,0 +1,65 @@ +# issue #23854 + +import std/bitops + +const WordBitWidth = sizeof(pointer) * 8 + +func wordsRequired*(bits: int): int {.inline.} = + const divShiftor = fastLog2(uint32(WordBitWidth)) + result = (bits + WordBitWidth - 1) shr divShiftor + +type + Algebra* = enum + BLS12_381 + + BigInt*[bits: static int] = object + limbs*: array[wordsRequired(bits), uint] + + Fr*[Name: static Algebra] = object + residue_form*: BigInt[255] + + Fp*[Name: static Algebra] = object + residue_form*: BigInt[381] + + FF*[Name: static Algebra] = Fp[Name] or Fr[Name] + +type + EC_ShortW_Aff*[F] = object + ## Elliptic curve point for a curve in Short Weierstrass form + ## y² = x³ + a x + b + ## + ## over a field F + x*, y*: F + +type FieldKind* = enum + kBaseField + kScalarField + +template getBigInt*[Name: static Algebra](T: type FF[Name]): untyped = + ## Get the underlying BigInt type. + BigInt[123] + +func bits*[Name: static Algebra](T: type FF[Name]): static int = + T.getBigInt().bits + +template getScalarField*(EC: type EC_ShortW_Aff): untyped = + Fr[EC.F.Name] + +# ------------------------------------------------------------------------------ + +type + ECFFT_Descriptor*[EC] = object + ## Metadata for FFT on Elliptic Curve + order*: int + rootsOfUnity1*: ptr UncheckedArray[BigInt[EC.getScalarField().bits()]] # Error: in expression 'EC.getScalarField()': identifier expected, but found 'EC.getScalarField' + rootsOfUnity2*: ptr UncheckedArray[BigInt[getScalarField(EC).bits()]] # Compiler SIGSEGV: Illegal Storage Access + +func new*(T: type ECFFT_Descriptor): T = + discard + +# ------------------------------------------------------------------------------ + +proc main() = + let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new() + +main() diff --git a/tests/generics/t23855.nim b/tests/generics/t23855.nim new file mode 100644 index 0000000000000..66c6454a1ad2b --- /dev/null +++ b/tests/generics/t23855.nim @@ -0,0 +1,57 @@ +# issue #23855 + +import std/bitops + +const WordBitWidth = sizeof(pointer) * 8 + +func wordsRequired*(bits: int): int {.inline.} = + const divShiftor = fastLog2(uint32(WordBitWidth)) + result = (bits + WordBitWidth - 1) shr divShiftor + +type + Algebra* = enum + BLS12_381 + + BigInt*[bits: static int] = object + limbs*: array[wordsRequired(bits), uint] + + Fr*[Name: static Algebra] = object + residue_form*: BigInt[255] + + Fp*[Name: static Algebra] = object + residue_form*: BigInt[381] + + FF*[Name: static Algebra] = Fp[Name] or Fr[Name] + +template getBigInt*[Name: static Algebra](T: type FF[Name]): untyped = + ## Get the underlying BigInt type. + BigInt[123] + +type + EC_ShortW_Aff*[F] = object + ## Elliptic curve point for a curve in Short Weierstrass form + ## y² = x³ + a x + b + ## + ## over a field F + x*, y*: F + +func bits*[Name: static Algebra](T: type FF[Name]): static int = + T.getBigInt().bits + +# ------------------------------------------------------------------------------ + +type + ECFFT_Descriptor*[EC] = object + ## Metadata for FFT on Elliptic Curve + order*: int + rootsOfUnity*: ptr UncheckedArray[BigInt[Fr[EC.F.Name].bits()]] # Undeclared identifier `Name` + +func new*(T: type ECFFT_Descriptor): T = + discard + +# ------------------------------------------------------------------------------ + +proc main() = + let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new() + +main() From 604a8a05e3b3c074f755f127957f8ee481bb362d Mon Sep 17 00:00:00 2001 From: metagn Date: Mon, 19 Aug 2024 17:50:40 +0300 Subject: [PATCH 19/20] test mini cleanup --- compiler/ast.nim | 2 +- compiler/semcall.nim | 1 + compiler/semexprs.nim | 20 +++++--------------- compiler/semtypinst.nim | 2 +- compiler/sigmatch.nim | 2 -- tests/generics/t23854.nim | 5 +++++ tests/generics/t23855.nim | 4 ++++ 7 files changed, 17 insertions(+), 19 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 4f20a0eb5d0ae..d0d4f8db367f5 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -449,7 +449,7 @@ const tfObjHasKids* = tfEnumHasHoles tfReturnsNew* = tfInheritable tfNonConstExpr* = tfExplicitCallConv - ## tyFromExpr where the expression shouldn't be evaluated as a constant + ## tyFromExpr where the expression shouldn't be evaluated as a static value skError* = skUnknown var diff --git a/compiler/semcall.nim b/compiler/semcall.nim index dfb17c5c59b80..dda4e603c3773 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -746,6 +746,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, if c.inGenericContext > 0 and r.matchedUnresolvedStatic and r.calleeSym.kind in {skMacro, skTemplate}: # macros and templates with unresolved statics should not instantiate + # other routines are fine since the static will not be evaluated result = semGenericStmt(c, n) result.typ = makeTypeFromExpr(c, result.copyTree) return diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a22764817f0b4..772a1ec5e87ce 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1139,7 +1139,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType return semExpr(c, result, flags, expectedType) elif n0.typ.kind == tyFromExpr and c.inGenericContext > 0: # don't make assumptions, entire expression needs to be tyFromExpr - n[0].typ = nil result = semGenericStmt(c, n) result.typ = makeTypeFromExpr(c, result.copyTree) return @@ -1486,26 +1485,17 @@ proc tryReadingGenericParam(c: PContext, n: PNode, i: PIdent, t: PType): PNode = of tyTypeParamsHolders: result = readTypeParameter(c, t, i, n.info) if result == c.graph.emptyNode: - if true or c.inGenericContext > 0: - result = semGenericStmt(c, n) - result.typ = makeTypeFromExpr(c, result.copyTree) - else: - result = nil + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, result.copyTree) of tyUserTypeClasses: if t.isResolvedUserTypeClass: result = readTypeParameter(c, t, i, n.info) else: - if true or c.inGenericContext > 0: - result = semGenericStmt(c, n) - result.typ = makeTypeFromExpr(c, copyTree(result)) - else: - result = nil - of tyFromExpr, tyGenericParam, tyAnything: - if true or c.inGenericContext > 0: result = semGenericStmt(c, n) result.typ = makeTypeFromExpr(c, copyTree(result)) - else: - result = nil + of tyFromExpr, tyGenericParam, tyAnything: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, copyTree(result)) else: result = nil diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 4d74ebd636b7b..9343ba855da79 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -267,7 +267,7 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT result = copyNode(n) if n.typ != nil: if n.typ.kind == tyFromExpr: - # type of node should not be evaluated + # type of node should not be evaluated as a static value n.typ.flags.incl tfNonConstExpr result.typ = replaceTypeVarsT(cl, n.typ) checkMetaInvariants(cl, result.typ) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index d7738a26e63aa..a159753af9772 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1862,8 +1862,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let prev = idTableGet(c.bindings, f) if prev == nil: if aOrig.kind == tyStatic: - if c.c.inGenericContext > 0 and aOrig.n == nil: - result = isNone if f.base.kind notin {tyNone, tyGenericParam}: result = typeRel(c, f.base, a, flags) if result != isNone and f.n != nil: diff --git a/tests/generics/t23854.nim b/tests/generics/t23854.nim index 08a08927f644b..08f9d5e178f4d 100644 --- a/tests/generics/t23854.nim +++ b/tests/generics/t23854.nim @@ -59,7 +59,12 @@ func new*(T: type ECFFT_Descriptor): T = # ------------------------------------------------------------------------------ +template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = bits + proc main() = let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new() + when false: echo getBits(ctx.rootsOfUnity2) # doesn't work yet? + doAssert ctx.rootsOfUnity1[0].limbs.len == wordsRequired(123) + doAssert ctx.rootsOfUnity2[0].limbs.len == wordsRequired(123) main() diff --git a/tests/generics/t23855.nim b/tests/generics/t23855.nim index 66c6454a1ad2b..70f942ff80abb 100644 --- a/tests/generics/t23855.nim +++ b/tests/generics/t23855.nim @@ -51,7 +51,11 @@ func new*(T: type ECFFT_Descriptor): T = # ------------------------------------------------------------------------------ +template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = bits + proc main() = let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new() + when false: echo getBits(ctx.rootsOfUnity2) # doesn't work yet? + doAssert ctx.rootsOfUnity[0].limbs.len == wordsRequired(123) main() From f5893482fe06d85a02a107a30dcd0122961cb555 Mon Sep 17 00:00:00 2001 From: metagn Date: Mon, 19 Aug 2024 18:21:18 +0300 Subject: [PATCH 20/20] clean up #23854 #23855 tests --- tests/generics/t23854.nim | 14 +++++++------- tests/generics/t23855.nim | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/generics/t23854.nim b/tests/generics/t23854.nim index 08f9d5e178f4d..675f9d121a6d7 100644 --- a/tests/generics/t23854.nim +++ b/tests/generics/t23854.nim @@ -1,4 +1,4 @@ -# issue #23854 +# issue #23854, not entirely fixed import std/bitops @@ -23,6 +23,10 @@ type FF*[Name: static Algebra] = Fp[Name] or Fr[Name] +template getBigInt*[Name: static Algebra](T: type FF[Name]): untyped = + ## Get the underlying BigInt type. + typeof(default(T).residue_form) + type EC_ShortW_Aff*[F] = object ## Elliptic curve point for a curve in Short Weierstrass form @@ -35,10 +39,6 @@ type FieldKind* = enum kBaseField kScalarField -template getBigInt*[Name: static Algebra](T: type FF[Name]): untyped = - ## Get the underlying BigInt type. - BigInt[123] - func bits*[Name: static Algebra](T: type FF[Name]): static int = T.getBigInt().bits @@ -64,7 +64,7 @@ template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = b proc main() = let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new() when false: echo getBits(ctx.rootsOfUnity2) # doesn't work yet? - doAssert ctx.rootsOfUnity1[0].limbs.len == wordsRequired(123) - doAssert ctx.rootsOfUnity2[0].limbs.len == wordsRequired(123) + doAssert ctx.rootsOfUnity1[0].limbs.len == wordsRequired(255) + doAssert ctx.rootsOfUnity2[0].limbs.len == wordsRequired(255) main() diff --git a/tests/generics/t23855.nim b/tests/generics/t23855.nim index 70f942ff80abb..5ae927a94eb3f 100644 --- a/tests/generics/t23855.nim +++ b/tests/generics/t23855.nim @@ -1,4 +1,4 @@ -# issue #23855 +# issue #23855, not entirely fixed import std/bitops @@ -25,7 +25,7 @@ type template getBigInt*[Name: static Algebra](T: type FF[Name]): untyped = ## Get the underlying BigInt type. - BigInt[123] + typeof(default(T).residue_form) type EC_ShortW_Aff*[F] = object @@ -55,7 +55,7 @@ template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = b proc main() = let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new() - when false: echo getBits(ctx.rootsOfUnity2) # doesn't work yet? - doAssert ctx.rootsOfUnity[0].limbs.len == wordsRequired(123) + when false: echo getBits(ctx.rootsOfUnity) # doesn't work yet? + doAssert ctx.rootsOfUnity[0].limbs.len == wordsRequired(255) main()