Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion compiler/ccgexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3317,8 +3317,9 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) =
cCast(ptrType(destType),
wrapPar(cAddr(wrapPar(val))))),
a.storage)
elif p.module.compileToCpp:
elif p.module.compileToCpp or isImportedType(src):
# C++ implicitly downcasts for us
# importc types don't have a Sup field, so we must cast
expr(p, arg, d)
else:
var a: TLoc = initLocExpr(p, arg)
Expand Down
2 changes: 1 addition & 1 deletion compiler/ccgtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ proc isOrHasImportedCppType(typ: PType): bool =
searchTypeFor(typ.skipTypes({tyRef}), isImportedCppType)

proc hasNoInit(t: PType): bool =
let t = skipTypes(t, {tyGenericInst})
let t = skipTypes(t, {tyGenericInst, tyAlias})
result = t.sym != nil and sfNoInit in t.sym.flags

proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDescKind): Rope
Expand Down
2 changes: 1 addition & 1 deletion compiler/semtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1014,7 +1014,7 @@ proc skipGenericInvocation(t: PType): PType {.inline.} =
proc tryAddInheritedFields(c: PContext, check: var IntSet, pos: var int,
obj: PType, n: PNode, isPartial = false, innerObj: PType = nil): bool =
if ((not isPartial) and (obj.kind notin {tyObject, tyGenericParam} or tfFinal in obj.flags)) or
(innerObj != nil and obj.sym.id == innerObj.sym.id):
(innerObj != nil and obj.sym != nil and innerObj.sym != nil and obj.sym.id == innerObj.sym.id):
localError(c.config, n.info, "Cannot inherit from: '" & $obj & "'")
result = false
elif obj.kind == tyObject:
Expand Down
16 changes: 16 additions & 0 deletions compiler/semtypinst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,22 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags)
result.flags = result.flags + newbody.flags - tfInstClearedFlags

# Propagate importc/noinit from generic body to instantiated body (for computed type aliases)
if body.sym != nil and {sfImportc, sfNoInit} * body.sym.flags != {}:
let flagsToCopy = body.sym.flags * {sfImportc, sfNoInit}
if newbody.sym != nil:
newbody.sym.flags = newbody.sym.flags + flagsToCopy
if sfImportc in flagsToCopy:
newbody.sym.loc = body.sym.loc
elif newbody.sym.isNil and newbody.kind == tyObject:
# Computed type alias returned an anonymous object type - create a symbol for it
let s = newSym(skType, body.sym.name, cl.c.idgen, body.sym.owner, cl.info)
s.flags = s.flags + flagsToCopy
if sfImportc in flagsToCopy:
s.loc = body.sym.loc
s.typ = newbody
newbody.sym = s

setToPreviousLayer(cl.typeMap)

# This is actually wrong: tgeneric_closure fails with this line:
Expand Down
13 changes: 13 additions & 0 deletions compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1700,6 +1700,19 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
if hasCovariance:
continue

# Fallback for computed generic type aliases (bug #25340):
# Check if the aliased types (last elements) have inheritance relationship
var aLast = roota.last
var fLast = rootf.last
# Handle ref/ptr types - unwrap to get the underlying object
if aLast.kind == fLast.kind and aLast.kind in {tyRef, tyPtr}:
aLast = aLast.elementType
fLast = fLast.elementType
if aLast.kind == tyObject and fLast.kind == tyObject and trIsOutParam notin flags:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no subtype relation between G[T] and G[S] just because there is one between T and S. This is not sound!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand it aLast and fLast are not T and S, but the implementations of G[T] and G[S], so the behavior seems to be correct? Also I tried to come up with an example that demonstrates the unsound behavior, but could not. Again, I'm not 100% sure, but I don't see the problem here.

let depth = isObjectSubtype(c, aLast, fLast, nil)
if depth > 0:
inc c.inheritancePenalty, depth + ord(c.inheritancePenalty < 0)
return isSubtype
return isNone
if prev == nil: put(c, f, a)
else:
Expand Down
107 changes: 107 additions & 0 deletions tests/typerel/t25340.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import macros

block:
type
JSObj {.inheritable, pure.} = object
JSExternObjBase {.inheritable, pure, noinit, importc: "int", bycopy.} = object
HTMLElement = object of JSObj
Document = object of HTMLElement

macro parent(t: typedesc): untyped =
let n = t.getType()[1]
if $n == "JSObj":
return ident"JSExternObjBase"
else:
let imp = n.getTypeImpl()
imp.expectKind(nnkObjectTy)
let base = imp[1][0]
result = newTree(nnkObjectTy,
newEmptyNode(),
newTree(nnkOfInherit,
newTree(nnkBracketExpr, ident"JSExternObj", base)),
newEmptyNode())

type
JSExternObj[T] {.noinit, importc: "int", bycopy.} = parent(T)

var a: JSExternObj[JSObj]
var d: JSExternObj[Document]
a = d
proc p(a: JSExternObj[JSObj]) =
discard
p(d)

assert JSExternObj[Document] is JSExternObj[HTMLElement]
assert JSExternObj[HTMLElement] is JSExternObj[JSObj]
assert JSExternObj[Document] is JSExternObj[JSObj]

block:
type
JSObj {.inheritable.} = object
JSExternObjBase {.inheritable, pure.} = object
HTMLElement = object of JSObj
Document = object of HTMLElement

macro parent(t: typedesc): untyped =
let n = t.getType()[1]
if $n == "JSObj":
return ident"JSExternObjBase"
else:
let imp = n.getTypeImpl()
imp.expectKind(nnkObjectTy)
let base = imp[1][0]
result = newTree(nnkObjectTy,
newEmptyNode(),
newTree(nnkOfInherit,
newTree(nnkBracketExpr, ident"JSExternObj", base)),
newEmptyNode())

type
JSExternObj[T] = parent(T)

var a: JSExternObj[JSObj]
var d: JSExternObj[Document]
a = d
proc p(a: JSExternObj[JSObj]) =
discard
p(d)

assert JSExternObj[Document] is JSExternObj[HTMLElement]
assert JSExternObj[HTMLElement] is JSExternObj[JSObj]
assert JSExternObj[Document] is JSExternObj[JSObj]

block: # Refs
type
JSObj {.inheritable.} = object
JSExternObjBase {.inheritable, pure.} = ref object
HTMLElement = object of JSObj
Document = object of HTMLElement

macro parent(t: typedesc): untyped =
let n = t.getType()[1]
if $n == "JSObj":
return ident"JSExternObjBase"
else:
let imp = n.getTypeImpl()
imp.expectKind(nnkObjectTy)
let base = imp[1][0]
result = newTree(nnkRefTy,
newTree(nnkObjectTy,
newEmptyNode(),
newTree(nnkOfInherit,
newTree(nnkBracketExpr, ident"JSExternObj", base)),
newEmptyNode()))

type
JSExternObj[T] = parent(T)

var a: JSExternObj[JSObj]
var d: JSExternObj[Document]
a = d
proc p(a: JSExternObj[JSObj]) =
discard
p(d)

assert JSExternObj[Document] is JSExternObj[HTMLElement]
assert JSExternObj[HTMLElement] is JSExternObj[JSObj]
assert JSExternObj[Document] is JSExternObj[JSObj]