summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorJason Beetham <beefers331@gmail.com>2023-05-21 12:10:32 -0600
committerGitHub <noreply@github.com>2023-05-21 20:10:32 +0200
commit28a116a47701462a5f22e0fa496a91daff2c1816 (patch)
tree32f8450d028bb506958d8bcef2c2321c7a39e350
parent5606702e6d9aa583141c975f45534d0f55d9acc9 (diff)
downloadNim-28a116a47701462a5f22e0fa496a91daff2c1816.tar.gz
Fixed generic parameters failing to be used in inheritance (#21866)
-rw-r--r--compiler/semtypes.nim35
-rw-r--r--tests/types/tinheritgenericparameter.nim39
2 files changed, 63 insertions, 11 deletions
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index f4b284f7e..fc59b6f91 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -854,12 +854,18 @@ proc skipGenericInvocation(t: PType): PType {.inline.} =
   while result.kind in {tyGenericInst, tyGenericBody, tyRef, tyPtr, tyAlias, tySink, tyOwned}:
     result = lastSon(result)
 
-proc addInheritedFields(c: PContext, check: var IntSet, pos: var int,
-                        obj: PType) =
-  assert obj.kind == tyObject
-  if (obj.len > 0) and (obj[0] != nil):
-    addInheritedFields(c, check, pos, obj[0].skipGenericInvocation)
-  addInheritedFieldsAux(c, check, pos, obj.n)
+proc tryAddInheritedFields(c: PContext, check: var IntSet, pos: var int,
+                        obj: PType, n: PNode, isPartial = false): bool =
+  if (not isPartial) and (obj.kind notin {tyObject, tyGenericParam} or tfFinal in obj.flags):
+    localError(c.config, n.info, "Cannot inherit from: '" & $obj & "'")
+    result = false
+  elif obj.kind == tyObject:
+    result = true
+    if (obj.len > 0) and (obj[0] != nil):
+      result = result and tryAddInheritedFields(c, check, pos, obj[0].skipGenericInvocation, n)
+    addInheritedFieldsAux(c, check, pos, obj.n)
+  else:
+    result = true
 
 proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType =
   if n.len == 0:
@@ -886,7 +892,9 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
           if concreteBase.sym != nil and concreteBase.sym.magic == mException and
               sfSystemModule notin c.module.flags:
             message(c.config, n.info, warnInheritFromException, "")
-          addInheritedFields(c, check, pos, concreteBase)
+          if not tryAddInheritedFields(c, check, pos, concreteBase, n):
+            return newType(tyError, nextTypeId c.idgen, result.owner)
+
       else:
         if concreteBase.kind != tyError:
           localError(c.config, n[1].info, "inheritance only works with non-final objects; " &
@@ -904,7 +912,9 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
     result.n = newNodeI(nkRecList, n.info)
   else:
     # partial object so add things to the check
-    addInheritedFields(c, check, pos, result)
+    if not tryAddInheritedFields(c, check, pos, result, n, isPartial = true):
+      return newType(tyError, nextTypeId c.idgen, result.owner)
+
   semRecordNodeAux(c, n[2], check, pos, result.n, result)
   if n[0].kind != nkEmpty:
     # dummy symbol for `pragma`:
@@ -1435,19 +1445,21 @@ proc semGenericParamInInvocation(c: PContext, n: PNode): PType =
   result = semTypeNode(c, n, nil)
   n.typ = makeTypeDesc(c, result)
 
-proc semObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType) =
+proc trySemObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType): bool =
   var
     check = initIntSet()
     pos = 0
   let
     realBase = t[0]
     base = skipTypesOrNil(realBase, skipPtrs)
+  result = true
   if base.isNil:
     localError(c.config, n.info, errIllegalRecursionInTypeX % "object")
   else:
     let concreteBase = skipGenericInvocation(base)
     if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags:
-      addInheritedFields(c, check, pos, concreteBase)
+      if not tryAddInheritedFields(c, check, pos, concreteBase, n):
+        return false
     else:
       if concreteBase.kind != tyError:
         localError(c.config, n.info, errInheritanceOnlyWithNonFinalObjects)
@@ -1527,7 +1539,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
     return errorType(c)
   if tx != result and tx.kind == tyObject:
     if tx[0] != nil:
-      semObjectTypeForInheritedGenericInst(c, n, tx)
+      if not trySemObjectTypeForInheritedGenericInst(c, n, tx):
+        return newOrPrevType(tyError, prev, c)
     var position = 0
     recomputeFieldPositions(tx, tx.n, position)
 
diff --git a/tests/types/tinheritgenericparameter.nim b/tests/types/tinheritgenericparameter.nim
new file mode 100644
index 000000000..c88c50b7b
--- /dev/null
+++ b/tests/types/tinheritgenericparameter.nim
@@ -0,0 +1,39 @@
+discard """
+  cmd: "nim check --hints:off --warnings:off $file"
+  action: "reject"
+  nimout:'''
+tinheritgenericparameter.nim(36, 15) Error: Cannot inherit from: 'MyObject'
+tinheritgenericparameter.nim(36, 15) Error: Cannot inherit from: 'MyObject'
+tinheritgenericparameter.nim(36, 23) Error: object constructor needs an object type [proxy]
+tinheritgenericparameter.nim(36, 23) Error: expression '' has no type (or is ambiguous)
+tinheritgenericparameter.nim(37, 15) Error: Cannot inherit from: 'int'
+tinheritgenericparameter.nim(37, 15) Error: Cannot inherit from: 'int'
+tinheritgenericparameter.nim(37, 23) Error: object constructor needs an object type [proxy]
+tinheritgenericparameter.nim(37, 23) Error: expression '' has no type (or is ambiguous)
+'''
+"""
+
+type
+  MyObject = object
+  HorzLayout[Base, T] = ref object of Base
+    data: seq[T]
+  VertLayout[T, Base] = ref object of Base
+    data: seq[T]
+  UiElement = ref object of RootObj
+    a: int
+  MyType[T] = ref object of RootObj
+    data: seq[T]
+  OtherElement[T] = ref object of T
+  Child[T] = ref object of HorzLayout[UiElement, T]
+  Child2[T] = ref object of VertLayout[T, UiElement]
+  Child3[T] = ref object of HorzLayout[MyObject, T]
+  Child4[T] = ref object of HorzLayout[int, T]
+static:
+  var a = Child[int](a: 300, data: @[100, 200, 300])
+  assert a.a == 300
+  assert a.data == @[100, 200, 300]
+discard Child2[string]()
+discard Child3[string]()
+discard Child4[string]()
+discard OtherElement[MyType[int]]()
+