diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index d01ca28e4322f..54ea9769c6e2f 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -433,6 +433,18 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # One step is enough, because the recursive nature of # handleGenericInvocation will handle the alias-to-alias-to-alias case if newbody.isGenericAlias: newbody = newbody.skipGenericAlias + + let origSym = newbody.sym + if origSym != nil and sfFromGeneric notin origSym.flags: + # same as `replaceTypeVarsS` but directly set the type without recursion: + newbody.sym = copySym(origSym, cl.c.idgen) + incl(newbody.sym.flags, sfFromGeneric) + newbody.sym.owner = origSym.owner + newbody.sym.typ = newbody + # unfortunately calling `replaceTypeVarsN` causes recursion, so this AST + # is the original generic body AST + newbody.sym.ast = copyTree(origSym.ast) + rawAddSon(result, newbody) checkPartialConstructedType(cl.c.config, cl.info, newbody) if not cl.allowMetaTypes: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index bb99463b64dd4..a6cda65b15a24 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -505,6 +505,13 @@ proc skipToObject(t: PType; skipped: var SkippedPtr): PType = if r.kind == tyObject and ptrs <= 1: result = r else: result = nil +proc getObjectSym(t: PType): PSym = + if tfFromGeneric in t.flags and t.typeInst.kind == tyGenericInst: + var dummy: SkippedPtr + result = t.typeInst[0].skipToObject(dummy).sym + else: + result = t.sym + proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin: PType): bool = assert f.kind in {tyGenericInst, tyGenericInvocation, tyGenericBody} var askip = skippedNone @@ -512,11 +519,12 @@ proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin var t = a.skipToObject(askip) let r = f.skipToObject(fskip) if r == nil: return false + let rSym = getObjectSym(r) var depth = 0 var last = a # XXX sameObjectType can return false here. Need to investigate # why that is but sameObjectType does way too much work here anyway. - while t != nil and r.sym != t.sym and askip == fskip: + while t != nil and rSym != getObjectSym(t) and askip == fskip: t = t[0] if t == nil: break last = t @@ -1602,7 +1610,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # crossing path with metatypes/aliases, so we need to separate them # by checking sym.id let genericSubtype = isGenericSubtype(c, x, f, depth, f) - if not (genericSubtype and aobj.sym.id != fobj.sym.id) and aOrig.kind != tyGenericBody: + if not (genericSubtype and getObjectSym(aobj).id != getObjectSym(fobj).id) and aOrig.kind != tyGenericBody: depth = -1 if depth >= 0: diff --git a/compiler/suggest.nim b/compiler/suggest.nim index c7efad1af64e3..5554991bd7369 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -280,10 +280,8 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} = for module in c.friendModules: if fmoduleId == module.id: return true if f.kind == skField: - var symObj = f.owner - if symObj.typ.skipTypes({tyGenericBody, tyGenericInst, tyGenericInvocation, tyAlias}).kind in {tyRef, tyPtr}: - symObj = symObj.typ.toObjectFromRefPtrGeneric.sym - assert symObj != nil + var symObj = f.owner.typ.toObjectFromRefPtrGeneric.sym + assert symObj != nil for scope in allScopes(c.currentScope): for sym in scope.allowPrivateAccess: if symObj.id == sym.id: return true diff --git a/tests/generics/timpl_ast.nim b/tests/generics/timpl_ast.nim index 69a59e24d4aba..97fe128cd20d5 100644 --- a/tests/generics/timpl_ast.nim +++ b/tests/generics/timpl_ast.nim @@ -1,7 +1,3 @@ -discard """ - action: compile -""" - import std/macros import std/assertions @@ -47,3 +43,38 @@ block: # issues #9899, ##14708 macro parse(s: static string) = result = parseStmt(s) parse("type " & implRepr(Option)) + +block: # issue #22639 + type + Spectrum[N: static int] = object + data: array[N, float] + AngleInterpolator = object + data: seq[Spectrum[60]] + proc initInterpolator(num: int): AngleInterpolator = + result = AngleInterpolator() + for i in 0 ..< num: + result.data.add Spectrum[60]() + macro genCompatibleTuple(t: typed): untyped = + let typ = t.getType[1].getTypeImpl[2] + result = nnkTupleTy.newTree() + for i, ch in typ: # is `nnkObjectTy` + result.add nnkIdentDefs.newTree(ident(ch[0].strVal), # ch is `nnkIdentDefs` + ch[1], + newEmptyNode()) + proc fullSize[T: object | tuple](x: T): int = + var tmp: genCompatibleTuple(T) + result = 0 + for field, val in fieldPairs(x): + result += sizeof(val) + doAssert result == sizeof(tmp) + + let reflectivity = initInterpolator(1) + for el in reflectivity.data: + doAssert fullSize(el) == sizeof(el) + doAssert fullSize(reflectivity.data[0]) == sizeof(reflectivity.data[0]) + doAssert genCompatibleTuple(Spectrum[60]) is tuple[data: array[60, float]] + doAssert genCompatibleTuple(Spectrum[120]) is tuple[data: array[120, float]] + type Foo[T] = object + data: T + doAssert genCompatibleTuple(Foo[int]) is tuple[data: int] + doAssert genCompatibleTuple(Foo[float]) is tuple[data: float]