summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorJuan M Gómez <info@jmgomez.me>2024-06-02 14:15:03 +0100
committerGitHub <noreply@github.com>2024-06-02 15:15:03 +0200
commitcb0ebecb2045143f71b7be40b853672a987fa4d1 (patch)
tree8a056febed7c21afa12af9e7d4282210cf1761e8
parent08f1eac8ac46baccd1701715feb8aca7b00e0be7 (diff)
downloadNim-cb0ebecb2045143f71b7be40b853672a987fa4d1.tar.gz
#Fixes #23657 C++ compilation fails with: 'T1_' was not declared in t… (#23666)
…his scope
-rw-r--r--compiler/ccgstmts.nim31
-rw-r--r--compiler/ccgtypes.nim9
-rw-r--r--compiler/cgen.nim31
-rw-r--r--compiler/lineinfos.nim2
-rw-r--r--tests/cpp/t23657.nim54
5 files changed, 103 insertions, 24 deletions
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 345639d94..a55512466 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -289,7 +289,7 @@ proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) =
     #echo "New code produced for ", v.name.s, " ", p.config $ value.info
     genBracedInit(p, value, isConst = false, v.typ, result)
 
-proc genCppParamsForCtor(p: BProc; call: PNode): string =
+proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): string =
   result = ""
   var argsCounter = 0
   let typ = skipTypes(call[0].typ, abstractInst)
@@ -298,12 +298,23 @@ proc genCppParamsForCtor(p: BProc; call: PNode): string =
     #if it's a type we can just generate here another initializer as we are in an initializer context
     if call[i].kind == nkCall and call[i][0].kind == nkSym and call[i][0].sym.kind == skType:
       if argsCounter > 0: result.add ","
-      result.add genCppInitializer(p.module, p, call[i][0].sym.typ)
+      result.add genCppInitializer(p.module, p, call[i][0].sym.typ, didGenTemp)
     else:
+      #We need to test for temp in globals, see: #23657
+      let param = 
+        if typ[i].kind in {tyVar} and call[i].kind == nkHiddenAddr:
+          call[i][0]
+        else:
+          call[i]
+      if param.kind != nkBracketExpr or param.typ.kind in 
+        {tyRef, tyPtr, tyUncheckedArray, tyArray, tyOpenArray, 
+          tyVarargs, tySequence, tyString, tyCstring, tyTuple}:
+        let tempLoc = initLocExprSingleUse(p, param)
+        didGenTemp = didGenTemp or tempLoc.k == locTemp
       genOtherArg(p, call, i, typ, result, argsCounter)
 
-proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope) =
-  let params = genCppParamsForCtor(p, call)
+proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope, didGenTemp: var bool) =
+  let params = genCppParamsForCtor(p, call, didGenTemp)
   if params.len == 0:
     decl = runtimeFormat("$#;\n", [decl])
   else:
@@ -330,7 +341,14 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
       # v.owner.kind != skModule:
       targetProc = p.module.preInitProc
     if isCppCtorCall and not containsHiddenPointer(v.typ):
-      callGlobalVarCppCtor(targetProc, v, vn, value)
+      var didGenTemp = false
+      callGlobalVarCppCtor(targetProc, v, vn, value, didGenTemp)
+      if didGenTemp:
+        message(p.config, vn.info, warnGlobalVarConstructorTemporary, vn.sym.name.s)
+        #We fail to call the constructor in the global scope so we do the call inside the main proc
+        assignGlobalVar(targetProc, vn, valueAsRope)
+        var loc = initLocExprSingleUse(targetProc, value)
+        genAssignment(targetProc, v.loc, loc, {})
     else:
       assignGlobalVar(targetProc, vn, valueAsRope)
 
@@ -365,7 +383,8 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
       var decl = localVarDecl(p, vn)
       var tmp: TLoc
       if isCppCtorCall:
-        genCppVarForCtor(p, value, decl)
+        var didGenTemp = false
+        genCppVarForCtor(p, value, decl, didGenTemp)
         line(p, cpsStmts, decl)
       else:
         tmp = initLocExprSingleUse(p, value)
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 927b56bba..75b520f3a 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -678,9 +678,9 @@ proc hasCppCtor(m: BModule; typ: PType): bool =
       if sfConstructor in prc.flags:
         return true
 
-proc genCppParamsForCtor(p: BProc; call: PNode): string
+proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): string
 
-proc genCppInitializer(m: BModule, prc: BProc; typ: PType): string =
+proc genCppInitializer(m: BModule, prc: BProc; typ: PType; didGenTemp: var bool): string =
   #To avoid creating a BProc per test when called inside a struct nil BProc is allowed
   result = "{}"
   if typ.itemId in m.g.graph.initializersPerType:
@@ -689,7 +689,7 @@ proc genCppInitializer(m: BModule, prc: BProc; typ: PType): string =
       var p = prc
       if p == nil:
         p = BProc(module: m)
-      result = "{" & genCppParamsForCtor(p, call) & "}"
+      result = "{" & genCppParamsForCtor(p, call, didGenTemp) & "}"
       if prc == nil:
         assert p.blocks.len == 0, "BProc belongs to a struct doesnt have blocks"
 
@@ -759,7 +759,8 @@ proc genRecordFieldsAux(m: BModule; n: PNode,
         # tyGenericInst for C++ template support
         let noInit = sfNoInit in field.flags or (field.typ.sym != nil and sfNoInit in field.typ.sym.flags)
         if not noInit and (fieldType.isOrHasImportedCppType() or hasCppCtor(m, field.owner.typ)):
-          var initializer = genCppInitializer(m, nil, fieldType)
+          var didGenTemp = false
+          var initializer = genCppInitializer(m, nil, fieldType, didGenTemp)
           result.addf("\t$1$3 $2$4;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias, initializer])
         else:
           result.addf("\t$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias])
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 287a6f5e4..c69e12a20 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -562,8 +562,9 @@ proc getTemp(p: BProc, t: PType, needsInit=false): TLoc =
   result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
                 storage: OnStack, flags: {})
   if p.module.compileToCpp and isOrHasImportedCppType(t):
+    var didGenTemp = false
     linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.r,
-      genCppInitializer(p.module, p, t)])
+      genCppInitializer(p.module, p, t, didGenTemp)])
   else:
     linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.r])
   constructLoc(p, result, not needsInit)
@@ -620,7 +621,8 @@ proc assignLocalVar(p: BProc, n: PNode) =
   let nl = if optLineDir in p.config.options: "" else: "\n"
   var decl = localVarDecl(p, n)
   if p.module.compileToCpp and isOrHasImportedCppType(n.typ):
-    decl.add genCppInitializer(p.module, p, n.typ)
+    var didGenTemp = false
+    decl.add genCppInitializer(p.module, p, n.typ, didGenTemp)
   decl.add ";" & nl
   line(p, cpsLocals, decl)
 
@@ -655,18 +657,7 @@ proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) =
     else:
       decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r])
 
-proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope)
-
-proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode) =
-  let s = vn.sym
-  fillBackendName(p.module, s)
-  fillLoc(s.loc, locGlobalVar, vn, OnHeap)
-  var decl: Rope = ""
-  let td = getTypeDesc(p.module, vn.sym.typ, dkVar)
-  genGlobalVarDecl(p, vn, td, "", decl)
-  decl.add " " & $s.loc.r
-  genCppVarForCtor(p, value, decl)
-  p.module.s[cfsVars].add decl
+proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope; didGenTemp: var bool)
 
 proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
   let s = n.sym
@@ -722,6 +713,18 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
     # fixes tests/run/tzeroarray:
     resetLoc(p, s.loc)
 
+proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode; didGenTemp: var bool) =
+  let s = vn.sym
+  fillBackendName(p.module, s)
+  fillLoc(s.loc, locGlobalVar, vn, OnHeap)
+  var decl: Rope = ""
+  let td = getTypeDesc(p.module, vn.sym.typ, dkVar)
+  genGlobalVarDecl(p, vn, td, "", decl)
+  decl.add " " & $s.loc.r
+  genCppVarForCtor(p, value, decl, didGenTemp)
+  if didGenTemp:  return # generated in the caller
+  p.module.s[cfsVars].add decl
+
 proc assignParam(p: BProc, s: PSym, retType: PType) =
   assert(s.loc.r != "")
   scopeMangledParam(p, s)
diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim
index dc0b6c360..b48252ace 100644
--- a/compiler/lineinfos.nim
+++ b/compiler/lineinfos.nim
@@ -95,6 +95,7 @@ type
     warnGenericsIgnoredInjection = "GenericsIgnoredInjection",
     warnStdPrefix = "StdPrefix"
     warnUser = "User",
+    warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary",
     # hints
     hintSuccess = "Success", hintSuccessX = "SuccessX",
     hintCC = "CC",
@@ -200,6 +201,7 @@ const
     warnGenericsIgnoredInjection: "$1",
     warnStdPrefix: "$1 needs the 'std' prefix",
     warnUser: "$1",
+    warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable",
     hintSuccess: "operation successful: $#",
     # keep in sync with `testament.isSuccess`
     hintSuccessX: "$build\n$loc lines; ${sec}s; $mem; proj: $project; out: $output",
diff --git a/tests/cpp/t23657.nim b/tests/cpp/t23657.nim
new file mode 100644
index 000000000..63deb7fb0
--- /dev/null
+++ b/tests/cpp/t23657.nim
@@ -0,0 +1,54 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp -r $file"
+  output: '''
+1.0
+1.0
+'''
+
+"""
+{.emit:"""/*TYPESECTION*/
+struct Point {
+  float x, y, z;
+  Point(float x, float y, float z): x(x), y(y), z(z) {}
+  Point() = default;
+};
+struct Direction {
+  float x, y, z;
+  Direction(float x, float y, float z): x(x), y(y), z(z) {}
+  Direction() = default;
+};
+struct Axis {
+  Point origin;
+  Direction direction;
+  Axis(Point origin, Direction direction): origin(origin), direction(direction) {}
+  Axis() = default;
+};
+
+""".}
+
+type
+  Point {.importcpp.} = object
+    x, y, z: float
+  
+  Direction {.importcpp.} = object
+    x, y, z: float
+
+  Axis {.importcpp.} = object
+    origin: Point
+    direction: Direction
+
+proc makeAxis(origin: Point, direction: Direction): Axis {. constructor, importcpp:"Axis(@)".}
+proc makePoint(x, y, z: float): Point {. constructor, importcpp:"Point(@)".}
+proc makeDirection(x, y, z: float): Direction {. constructor, importcpp:"Direction(@)".}
+
+var axis1 = makeAxis(Point(x: 1.0, y: 2.0, z: 3.0), Direction(x: 4.0, y: 5.0, z: 6.0)) #Triggers the error (T1)
+var axis2Ctor = makeAxis(makePoint(1.0, 2.0, 3.0), makeDirection(4.0, 5.0, 6.0)) #Do not triggers
+
+proc main() = #Do not triggers as Tx are inside the body
+  let test = makeAxis(Point(x: 1.0, y: 2.0, z: 3.0), Direction(x: 4.0, y: 5.0, z: 6.0))
+  echo test.origin.x
+
+main()
+
+echo $axis1.origin.x  #Make sures it's init
\ No newline at end of file