summary refs log tree commit diff stats
diff options
context:
space:
mode:
authormetagn <metagngn@gmail.com>2024-10-03 20:35:21 +0300
committerGitHub <noreply@github.com>2024-10-03 19:35:21 +0200
commit89978b48baeed0c745d45d666a9786c8d9457581 (patch)
treeb5cf3cf90d1913bbf492b09afa3157898555e57b
parentd6a71a10671b66ee4f5be09f99234b3d834e7fce (diff)
downloadNim-89978b48baeed0c745d45d666a9786c8d9457581.tar.gz
use cbuilder for seq type generation (#24202)
`addSimpleStruct` is just so the compiler doesn't use so much extra
computation on analyzing the `typ` parameter for `addStruct`, which
doesn't change anything for `seq` types. We could probably still get
away with using `addStruct` instead, or making `addStruct` accept `nil`
as the `typ` argument but this would be even more computation.

There were a lot of hidden issues with `addStruct` being a template &
template argument substitution, so most of the behavior is moved into
`startStruct`/`finishStruct` procs.

This is turning out to be a lot of code for just a couple of changed
lines, we might have to split `cbuilder` into multiple modules.
-rw-r--r--compiler/cbuilder.nim109
-rw-r--r--compiler/ccgtypes.nim20
2 files changed, 89 insertions, 40 deletions
diff --git a/compiler/cbuilder.nim b/compiler/cbuilder.nim
index 6fc0bd881..bb1bdfe27 100644
--- a/compiler/cbuilder.nim
+++ b/compiler/cbuilder.nim
@@ -5,14 +5,19 @@ type
 template newBuilder(s: string): Builder =
   s
 
-proc addField(obj: var Builder; name, typ: Snippet) =
+proc addField(obj: var Builder; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
   obj.add('\t')
   obj.add(typ)
   obj.add(" ")
   obj.add(name)
+  if isFlexArray:
+    obj.add("[SEQ_DECL_SIZE]")
+  if initializer.len != 0:
+    obj.add(initializer)
   obj.add(";\n")
 
-proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool; initializer: Snippet) =
+proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
+  ## for fields based on an `skField` symbol
   obj.add('\t')
   if field.alignment > 0:
     obj.add("NIM_ALIGN(")
@@ -32,6 +37,13 @@ proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bo
     obj.add(initializer)
   obj.add(";\n")
 
+type
+  BaseClassKind = enum
+    bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
+  StructBuilderInfo = object
+    baseKind: BaseClassKind
+    preFieldsLen: int
+
 proc structOrUnion(t: PType): Snippet =
   let t = t.skipTypes({tyAlias, tySink})
   if tfUnion in t.flags: "union"
@@ -40,47 +52,80 @@ proc structOrUnion(t: PType): Snippet =
 proc ptrType(t: Snippet): Snippet =
   t & "*"
 
-template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: string; body: typed) =
-  if tfPacked in typ.flags:
+proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet): StructBuilderInfo =
+  result = StructBuilderInfo(baseKind: bcNone)
+  obj.add("struct ")
+  obj.add(name)
+  if baseType.len != 0:
+    if m.compileToCpp:
+      result.baseKind = bcCppInherit
+    else:
+      result.baseKind = bcSupField
+  if result.baseKind == bcCppInherit:
+    obj.add(" : public ")
+    obj.add(baseType)
+  obj.add(" ")
+  obj.add("{\n")
+  result.preFieldsLen = obj.len
+  if result.baseKind == bcSupField:
+    obj.addField(name = "Sup", typ = baseType)
+
+proc finishSimpleStruct(obj: var Builder; m: BModule; info: StructBuilderInfo) =
+  if info.baseKind == bcNone and info.preFieldsLen == obj.len:
+    # no fields were added, add dummy field
+    obj.addField(name = "dummy", typ = "char")
+  obj.add("};\n")
+
+template addSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet; body: typed) =
+  ## for independent structs, not directly based on a Nim type
+  let info = startSimpleStruct(obj, m, name, baseType)
+  body
+  finishSimpleStruct(obj, m, info)
+
+proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType: Snippet): StructBuilderInfo =
+  result = StructBuilderInfo(baseKind: bcNone)
+  if tfPacked in t.flags:
     if hasAttribute in CC[m.config.cCompiler].props:
-      obj.add(structOrUnion(typ))
+      obj.add(structOrUnion(t))
       obj.add(" __attribute__((__packed__))")
     else:
       obj.add("#pragma pack(push, 1)\n")
-      obj.add(structOrUnion(typ))
+      obj.add(structOrUnion(t))
   else:
-    obj.add(structOrUnion(typ))
+    obj.add(structOrUnion(t))
   obj.add(" ")
   obj.add(name)
-  type BaseClassKind = enum
-    bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
-  var baseKind = bcNone
-  if typ.kind == tyObject:
-    if typ.baseClass == nil:
-      if lacksMTypeField(typ):
-        baseKind = bcNone
+  if t.kind == tyObject:
+    if t.baseClass == nil:
+      if lacksMTypeField(t):
+        result.baseKind = bcNone
       elif optTinyRtti in m.config.globalOptions:
-        baseKind = bcNoneTinyRtti
+        result.baseKind = bcNoneTinyRtti
       else:
-        baseKind = bcNoneRtti
+        result.baseKind = bcNoneRtti
     elif m.compileToCpp:
-      baseKind = bcCppInherit
+      result.baseKind = bcCppInherit
+    else:
+      result.baseKind = bcSupField
+  elif baseType.len != 0:
+    if m.compileToCpp:
+      result.baseKind = bcCppInherit
     else:
-      baseKind = bcSupField
-  if baseKind == bcCppInherit:
+      result.baseKind = bcSupField
+  if result.baseKind == bcCppInherit:
     obj.add(" : public ")
     obj.add(baseType)
   obj.add(" ")
   obj.add("{\n")
-  let currLen = obj.len
-  case baseKind
+  result.preFieldsLen = obj.len
+  case result.baseKind
   of bcNone:
     # rest of the options add a field or don't need it due to inheritance,
     # we need to add the dummy field for uncheckedarray ahead of time
     # so that it remains trailing
-    if typ.itemId notin m.g.graph.memberProcsPerType and
-        typ.n != nil and typ.n.len == 1 and typ.n[0].kind == nkSym and
-        typ.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
+    if t.itemId notin m.g.graph.memberProcsPerType and
+        t.n != nil and t.n.len == 1 and t.n[0].kind == nkSym and
+        t.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
       # only consists of flexible array field, add *initial* dummy field
       obj.addField(name = "dummy", typ = "char")
   of bcCppInherit: discard
@@ -90,13 +135,21 @@ template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseT
     obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
   of bcSupField:
     obj.addField(name = "Sup", typ = baseType)
-  body
-  if baseKind == bcNone and currLen == obj.len and typ.itemId notin m.g.graph.memberProcsPerType:
+
+proc finishStruct(obj: var Builder; m: BModule; t: PType; info: StructBuilderInfo) =
+  if info.baseKind == bcNone and info.preFieldsLen == obj.len and
+      t.itemId notin m.g.graph.memberProcsPerType:
     # no fields were added, add dummy field
     obj.addField(name = "dummy", typ = "char")
   obj.add("};\n")
-  if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
-    result.add("#pragma pack(pop)\n")
+  if tfPacked in t.flags and hasAttribute notin CC[m.config.cCompiler].props:
+    obj.add("#pragma pack(pop)\n")
+
+template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: Snippet; body: typed) =
+  ## for structs built directly from a Nim type
+  let info = startStruct(obj, m, typ, name, baseType)
+  body
+  finishStruct(obj, m, typ, info)
 
 template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
   ## adds a field with a `struct { ... }` type, building it according to `body`
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 5b5218a3e..2c2556336 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -967,18 +967,14 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
       m.typeCache[sig] = result & seqStar(m)
       if not isImportedType(t):
         if skipTypes(t.elementType, typedescInst).kind != tyEmpty:
-          const
-            cppSeq = "struct $2 : #TGenericSeq {$n"
-            cSeq = "struct $2 {$n" &
-                  "  #TGenericSeq Sup;$n"
-          if m.compileToCpp:
-            appcg(m, m.s[cfsSeqTypes],
-                cppSeq & "  $1 data[SEQ_DECL_SIZE];$n" &
-                "};$n", [getTypeDescAux(m, t.elementType, check, kind), result])
-          else:
-            appcg(m, m.s[cfsSeqTypes],
-                cSeq & "  $1 data[SEQ_DECL_SIZE];$n" &
-                "};$n", [getTypeDescAux(m, t.elementType, check, kind), result])
+          var struct = newBuilder("")
+          let baseType = cgsymValue(m, "TGenericSeq")
+          struct.addSimpleStruct(m, name = result, baseType = baseType):
+            struct.addField(
+              name = "data",
+              typ = getTypeDescAux(m, t.elementType, check, kind),
+              isFlexArray = true)
+          m.s[cfsSeqTypes].add struct
         else:
           result = rope("TGenericSeq")
       result.add(seqStar(m))