summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorJuan M Gómez <info@jmgomez.me>2023-05-30 20:47:26 +0100
committerGitHub <noreply@github.com>2023-05-30 21:47:26 +0200
commite43a51fcf3dff166838cdc3f2fc9690c5fa24846 (patch)
tree05a8f49b93236ea4478f7aca570ab9dd995a9552
parent20446b437bd6c35006fab78ed5e3bdd6f8056774 (diff)
downloadNim-e43a51fcf3dff166838cdc3f2fc9690c5fa24846.tar.gz
Implements: [C++] constructor pragma improvement (fix #21921) (#21916)
* implements: [C++] constructor pragma improvement (fix #21921)

t

* fix test so it doesnt use echo in globals

* Update compiler/ccgtypes.nim

* Update lib/std/private/dragonbox.nim

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
-rw-r--r--compiler/ccgstmts.nim18
-rw-r--r--compiler/ccgtypes.nim120
-rw-r--r--compiler/cgen.nim16
-rw-r--r--compiler/modulegraphs.nim2
-rw-r--r--compiler/pragmas.nim6
-rw-r--r--compiler/semstmts.nim59
-rw-r--r--lib/std/private/dragonbox.nim4
-rw-r--r--lib/std/private/schubfach.nim4
-rw-r--r--tests/cpp/tconstructor.nim35
9 files changed, 180 insertions, 84 deletions
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 1ed546256..36ef4f3ea 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -35,7 +35,9 @@ proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} =
   if n.kind == nkEmpty:
     result = false
   elif n.kind in nkCallKinds and n[0] != nil and n[0].typ != nil and n[0].typ.skipTypes(abstractInst).kind == tyProc:
-    if isInvalidReturnType(conf, n[0].typ, true):
+    if n[0].kind == nkSym and sfConstructor in n[0].sym.flags:
+      result = true
+    elif isInvalidReturnType(conf, n[0].typ, true):
       # var v = f()
       # is transformed into: var v;  f(addr v)
       # where 'f' **does not** initialize the result!
@@ -288,7 +290,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 genCppVarForConstructor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope) =
+proc genCppVarForCtor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope) =
   var params = newRopeAppender()
   var argsCounter = 0
   let typ = skipTypes(value[0].typ, abstractInst)
@@ -307,7 +309,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
     genGotoVar(p, value)
     return
   let imm = isAssignedImmediately(p.config, value)
-  let isCppConstructorCall = p.module.compileToCpp and imm and
+  let isCppCtorCall = p.module.compileToCpp and imm and
     value.kind in nkCallKinds and value[0].kind == nkSym and
     v.typ.kind != tyPtr and sfConstructor in value[0].sym.flags
   var targetProc = p
@@ -321,8 +323,8 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
     if sfPure in v.flags:
       # v.owner.kind != skModule:
       targetProc = p.module.preInitProc
-    if isCppConstructorCall and not containsHiddenPointer(v.typ):
-      callGlobalVarCppConstructor(targetProc, v, vn, value)
+    if isCppCtorCall and not containsHiddenPointer(v.typ):
+      callGlobalVarCppCtor(targetProc, v, vn, value)
     else:
       assignGlobalVar(targetProc, vn, valueAsRope)
 
@@ -356,8 +358,8 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
       genLineDir(p, vn)
       var decl = localVarDecl(p, vn)
       var tmp: TLoc
-      if isCppConstructorCall:
-        genCppVarForConstructor(p, v, vn, value, decl)
+      if isCppCtorCall:
+        genCppVarForCtor(p, v, vn, value, decl)
         line(p, cpsStmts, decl)
       else:
         initLocExprSingleUse(p, value, tmp)
@@ -388,7 +390,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
     startBlock(targetProc)
   if value.kind != nkEmpty and valueAsRope.len == 0:
     genLineDir(targetProc, vn)
-    if not isCppConstructorCall:
+    if not isCppCtorCall:
       loadInto(targetProc, vn, value, v.loc)
   if forHcr:
     endBlock(targetProc)
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index d6835cc50..9bcfa41ef 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -11,8 +11,7 @@
 
 # ------------------------- Name Mangling --------------------------------
 
-import sighashes, modulegraphs
-import strscans
+import sighashes, modulegraphs, strscans
 import ../dist/checksums/src/checksums/md5
 
 type
@@ -488,30 +487,36 @@ proc multiFormat*(frmt: var string, chars : static openArray[char], args: openAr
         res.add(substr(frmt, start, i - 1))
     frmt = res
 
-proc genVirtualProcParams(m: BModule; t: PType, rettype, params: var string,
+proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, params: var string,
                    check: var IntSet, declareEnvironment=true;
                    weakDep=false;) =
-  if t[0] == nil or isInvalidReturnType(m.config, t):
+  let t = prc.typ
+  let isCtor = sfConstructor in prc.flags
+  if isCtor:
+    rettype = ""
+  elif t[0] == nil or isInvalidReturnType(m.config, t):
     rettype = "void"
   else:
     if rettype == "":
       rettype = getTypeDescAux(m, t[0], check, dkResult)
     else:
       rettype = runtimeFormat(rettype.replace("'0", "$1"), [getTypeDescAux(m, t[0], check, dkResult)])
-  var this = t.n[1].sym
-  fillParamName(m, this)
-  fillLoc(this.loc, locParam, t.n[1],
-          this.paramStorageLoc)
-  if this.typ.kind == tyPtr:
-    this.loc.r = "this"
-  else:
-    this.loc.r = "(*this)"
-  
-  var types = @[getTypeDescWeak(m, this.typ, check, dkParam)]
-  var names = @[this.loc.r]
+  var types, names, args: seq[string]
+  if not isCtor:    
+    var this = t.n[1].sym
+    fillParamName(m, this)
+    fillLoc(this.loc, locParam, t.n[1],
+            this.paramStorageLoc)
+    if this.typ.kind == tyPtr:
+      this.loc.r = "this"
+    else:
+      this.loc.r = "(*this)"
+    names.add this.loc.r
+    types.add getTypeDescWeak(m, this.typ, check, dkParam)
 
-  for i in 2..<t.n.len: 
-    if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genVirtualProcParams")
+  let firstParam = if isCtor: 1 else: 2
+  for i in firstParam..<t.n.len: 
+    if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genMemberProcParams")
     var param = t.n[i].sym
     var descKind = dkParam
     if optByRef in param.options:
@@ -534,9 +539,15 @@ proc genVirtualProcParams(m: BModule; t: PType, rettype, params: var string,
     name = param.loc.r
     types.add typ
     names.add name
+    args.add types[^1] & " " & names[^1]
+
   multiFormat(params, @['\'', '#'], [types, names])
+  multiFormat(superCall, @['\'', '#'], [types, names])
   if params == "()":
-    params = "(void)"
+    if types.len == 0:
+      params = "(void)"
+    else:
+      params = "(" & args.join(", ") & ")"
   if tfVarargs in t.flags:
     if params != "(":
       params[^1] = ','
@@ -687,17 +698,25 @@ proc genRecordFieldsAux(m: BModule; n: PNode,
           result.addf("\t$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias])
   else: internalError(m.config, n.info, "genRecordFieldsAux()")
 
-proc genVirtualProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl:bool = false)
+proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl:bool = false)
 
 proc getRecordFields(m: BModule; typ: PType, check: var IntSet): Rope =
   result = newRopeAppender()
   genRecordFieldsAux(m, typ.n, typ, check, result)
-  if typ.itemId in m.g.graph.virtualProcsPerType:
-    let procs = m.g.graph.virtualProcsPerType[typ.itemId]
+  if typ.itemId in m.g.graph.memberProcsPerType:
+    let procs = m.g.graph.memberProcsPerType[typ.itemId]
+    var isDefaultCtorGen, isCtorGen: bool
     for prc in procs:
       var header: Rope
-      genVirtualProcHeader(m, prc, header, false, true)
-      result.add "\t" & header & ";\n"
+      if sfConstructor in prc.flags:
+        isCtorGen = true
+        if prc.typ.n.len == 1:
+          isDefaultCtorGen = true
+      genMemberProcHeader(m, prc, header, false, true)
+      result.addf "$1;$n", [header]
+    if isCtorGen and not isDefaultCtorGen:
+      var ch: IntSet
+      result.addf "$1() = default;$n", [getTypeDescAux(m, typ, ch, dkOther)]
     
 proc fillObjectFields*(m: BModule; typ: PType) =
   # sometimes generic objects are not consistently merged. We patch over
@@ -825,8 +844,6 @@ proc getOpenArrayDesc(m: BModule; t: PType, check: var IntSet; kind: TypeDescKin
 
 proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDescKind): Rope =
   # returns only the type's name
-  if kind == dkRefParam:
-    echo "llega con typedesc aux:", $origTyp.kind
   var t = origTyp.skipTypes(irrelevantForBackend-{tyOwned})
   if containsOrIncl(check, t.id):
     if not (isImportedCppType(origTyp) or isImportedCppType(t)):
@@ -1106,46 +1123,63 @@ proc isReloadable(m: BModule; prc: PSym): bool =
 proc isNonReloadable(m: BModule; prc: PSym): bool =
   return m.hcrOn and sfNonReloadable in prc.flags
 
-proc parseVFunctionDecl(val: string; name, params, retType: var string; isFnConst, isOverride: var bool) =
+proc parseVFunctionDecl(val: string; name, params, retType, superCall: var string; isFnConst, isOverride: var bool; isCtor: bool) =
   var afterParams: string
   if scanf(val, "$*($*)$s$*", name, params, afterParams):
     isFnConst = afterParams.find("const") > -1
     isOverride = afterParams.find("override") > -1
-    discard scanf(afterParams, "->$s$* ", retType)
+    if isCtor:
+      discard scanf(afterParams, ":$s$*", superCall)
+    else:
+      discard scanf(afterParams, "->$s$* ", retType)
+
   params = "(" & params & ")"
 
-proc genVirtualProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl : bool = false) =
-  assert sfVirtual in prc.flags
-  # using static is needed for inline procs
+proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl : bool = false) =
+  assert {sfVirtual, sfConstructor} * prc.flags != {}
+  let isCtor = sfConstructor in prc.flags
+  let isVirtual = not isCtor
   var check = initIntSet()
   fillBackendName(m, prc)
   fillLoc(prc.loc, locProc, prc.ast[namePos], OnUnknown)
-  var typ = prc.typ.n[1].sym.typ
-  var memberOp = "#."
+  var memberOp = "#." #only virtual
+  var typ: PType
+  if isCtor:
+    typ = prc.typ.sons[0]
+  else:
+    typ = prc.typ.sons[1]
   if typ.kind == tyPtr:
     typ = typ[0]
     memberOp = "#->"
   var typDesc = getTypeDescWeak(m, typ, check, dkParam)
   let asPtrStr = rope(if asPtr: "_PTR" else: "")
-  var name, params, rettype: string
+  var name, params, rettype, superCall: string
   var isFnConst, isOverride: bool
-  parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, isFnConst, isOverride)
-  genVirtualProcParams(m, prc.typ, rettype, params, check, true, false) 
+  parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, superCall, isFnConst, isOverride, isCtor)
+  genMemberProcParams(m, prc, superCall, rettype, params, check, true, false) 
   var fnConst, override: string
+  if isCtor:
+    name = typDesc
   if isFnConst:
     fnConst = " const"
   if isFwdDecl:
-    rettype = "virtual " & rettype
-    if isOverride: 
-      override = " override"
-  else: 
-    prc.loc.r = "$1 $2 (@)" % [memberOp, name]
+    if isVirtual:
+      rettype = "virtual " & rettype
+      if isOverride: 
+        override = " override"
+    superCall = ""
+  else:
+    if isVirtual:
+      prc.loc.r = "$1$2(@)" % [memberOp, name]
+    elif superCall != "":
+      superCall = " : " & superCall
+    
     name = "$1::$2" % [typDesc, name]
-  
+
   result.add "N_LIB_PRIVATE "
-  result.addf("$1$2($3, $4)$5$6$7",
+  result.addf("$1$2($3, $4)$5$6$7$8",
         [rope(CallingConvToStr[prc.typ.callConv]), asPtrStr, rettype, name,
-        params, fnConst, override])
+        params, fnConst, override, superCall])
   
 proc genProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false) =
   # using static is needed for inline procs
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index fe5c253dd..e79081dc6 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -640,9 +640,9 @@ proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) =
     else:
       decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r])
 
-proc genCppVarForConstructor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope) 
+proc genCppVarForCtor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope) 
 
-proc callGlobalVarCppConstructor(p: BProc; v: PSym; vn, value: PNode) =
+proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode) =
   let s = vn.sym
   fillBackendName(p.module, s)
   fillLoc(s.loc, locGlobalVar, vn, OnHeap)
@@ -650,7 +650,7 @@ proc callGlobalVarCppConstructor(p: BProc; v: PSym; vn, value: PNode) =
   let td = getTypeDesc(p.module, vn.sym.typ, dkVar)
   genGlobalVarDecl(p, vn, td, "", decl)
   decl.add " " & $s.loc.r
-  genCppVarForConstructor(p, v, vn, value, decl)
+  genCppVarForCtor(p, v, vn, value, decl)
   p.module.s[cfsVars].add decl
 
 proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
@@ -1143,8 +1143,8 @@ proc isNoReturn(m: BModule; s: PSym): bool {.inline.} =
 proc genProcAux*(m: BModule, prc: PSym) =
   var p = newProc(prc, m)
   var header = newRopeAppender()
-  if m.config.backend == backendCpp and sfVirtual in prc.flags:
-    genVirtualProcHeader(m, prc, header)
+  if m.config.backend == backendCpp and {sfVirtual, sfConstructor} * prc.flags != {}:
+    genMemberProcHeader(m, prc, header)
   else:
     genProcHeader(m, prc, header)
   var returnStmt: Rope = ""
@@ -1162,7 +1162,7 @@ proc genProcAux*(m: BModule, prc: PSym) =
       internalError(m.config, prc.info, "proc has no result symbol")
     let resNode = prc.ast[resultPos]
     let res = resNode.sym # get result symbol
-    if not isInvalidReturnType(m.config, prc.typ):
+    if not isInvalidReturnType(m.config, prc.typ) and sfConstructor notin prc.flags:
       if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
       if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(procBody); val != nil):
         var decl = localVarDecl(p, resNode)
@@ -1175,6 +1175,8 @@ proc genProcAux*(m: BModule, prc: PSym) =
         assert(res.loc.r != "")
         initLocalVar(p, res, immediateAsgn=false)
       returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)])
+    elif sfConstructor in prc.flags:
+      fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap)      
     else:
       fillResult(p.config, resNode, prc.typ)
       assignParam(p, res, prc.typ[0])
@@ -1252,7 +1254,7 @@ proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
 
 proc genProcPrototype(m: BModule, sym: PSym) =
   useHeader(m, sym)
-  if lfNoDecl in sym.loc.flags or sfVirtual in sym.flags: return
+  if lfNoDecl in sym.loc.flags or {sfVirtual, sfConstructor} * sym.flags != {}: return
   if lfDynamicLib in sym.loc.flags:
     if sym.itemId.module != m.module.position and
         not containsOrIncl(m.declaredThings, sym.id):
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index de97ced99..08cdbfd0d 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -79,7 +79,7 @@ type
     procInstCache*: Table[ItemId, seq[LazyInstantiation]] # A symbol's ItemId.
     attachedOps*: array[TTypeAttachedOp, Table[ItemId, LazySym]] # Type ID, destructors, etc.
     methodsPerType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods
-    virtualProcsPerType*: Table[ItemId, seq[PSym]] # Type ID, attached virtual procs
+    memberProcsPerType*: Table[ItemId, seq[PSym]] # Type ID, attached member procs (only c++, virtual and ctor so far)
     enumToStringProcs*: Table[ItemId, LazySym]
     emittedTypeInfo*: Table[string, FileIndex]
 
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 11305db2a..158e68eef 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -971,8 +971,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         # only supported for backwards compat, doesn't do anything anymore
         noVal(c, it)
       of wConstructor:
-        noVal(c, it)
         incl(sym.flags, sfConstructor)
+        if sfImportc notin sym.flags:
+          sym.constraint = newEmptyStrNode(c, it, getOptionalStr(c, it, ""))
+          sym.constraint.strVal = sym.constraint.strVal
+          sym.flags.incl {sfExportc, sfMangleCpp}
+          sym.typ.callConv = ccNoConvention
       of wHeader:
         var lib = getLib(c, libHeader, getStrLitNode(c, it))
         addToLib(lib, sym)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 6e2fb9252..6cf9c6f7a 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -1654,6 +1654,16 @@ proc swapResult(n: PNode, sRes: PSym, dNode: PNode) =
         n[i] = dNode
     swapResult(n[i], sRes, dNode)
 
+
+proc addThis(c: PContext, n: PNode, t: PType, owner: TSymKind) =  
+  var s = newSym(skResult, getIdent(c.cache, "this"), c.idgen,
+              getCurrOwner(c), n.info)     
+  s.typ = t
+  incl(s.flags, sfUsed) 
+  c.p.resultSym = s
+  n.add newSymNode(c.p.resultSym)
+  addParamOrResult(c, c.p.resultSym, owner)
+
 proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) =
   template genResSym(s) =
     var s = newSym(skResult, getIdent(c.cache, "result"), c.idgen,
@@ -2189,24 +2199,33 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
   if sfBorrow in s.flags and c.config.cmd notin cmdDocLike:
     result[bodyPos] = c.graph.emptyNode
   
-  if sfVirtual in s.flags:
+  if {sfVirtual, sfConstructor} * s.flags != {} and sfImportc notin s.flags:
+    let isVirtual = sfVirtual in s.flags
+    let pragmaName = if isVirtual: "virtual" else: "constructor"
     if c.config.backend == backendCpp:
+      if s.typ.sons.len < 2 and isVirtual:
+        localError(c.config, n.info, "virtual must have at least one parameter") 
       for son in s.typ.sons:
         if son!=nil and son.isMetaType:
-          localError(c.config, n.info, "virtual unsupported for generic routine")
-
-      var typ = s.typ.sons[1]
-      if typ.kind == tyPtr:
+          localError(c.config, n.info, pragmaName & " unsupported for generic routine")
+      var typ: PType 
+      if sfConstructor in s.flags:
+        typ = s.typ.sons[0]
+        if typ == nil or typ.kind != tyObject:
+          localError(c.config, n.info, "constructor must return an object")
+      else:
+        typ = s.typ.sons[1]
+      if typ.kind == tyPtr and isVirtual:
         typ = typ[0]
       if typ.kind != tyObject:
-        localError(c.config, n.info, "virtual must be a non ref object type")
+        localError(c.config, n.info, "virtual must be either ptr to object or object type.")
       if typ.owner.id == s.owner.id and c.module.id == s.owner.id:
-        c.graph.virtualProcsPerType.mgetOrPut(typ.itemId, @[]).add s
+        c.graph.memberProcsPerType.mgetOrPut(typ.itemId, @[]).add s
       else:
         localError(c.config, n.info, 
-          "virtual procs must be defined in the same scope as the type they are virtual for and it must be a top level scope")
+          pragmaName & " procs must be defined in the same scope as the type they are virtual for and it must be a top level scope")
     else:
-      localError(c.config, n.info, "virtual procs are only supported in C++")
+      localError(c.config, n.info, pragmaName & " procs are only supported in C++")
 
   if n[bodyPos].kind != nkEmpty and sfError notin s.flags:
     # for DLL generation we allow sfImportc to have a body, for use in VM
@@ -2232,15 +2251,19 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         # Macros and Templates can have generic parameters, but they are only
         # used for overload resolution (there is no instantiation of the symbol)
         if s.kind notin {skMacro, skTemplate} and s.magic == mNone: paramsTypeCheck(c, s.typ)
-
-        maybeAddResult(c, s, n)
-        let resultType =
-          if s.kind == skMacro:
-            sysTypeFromName(c.graph, n.info, "NimNode")
-          elif not isInlineIterator(s.typ):
-            s.typ[0]
-          else:
-            nil
+        var resultType: PType
+        if sfConstructor in s.flags:
+          resultType = makePtrType(c, s.typ[0])
+          addThis(c, n, resultType, skProc)
+        else:
+          maybeAddResult(c, s, n)
+          resultType =
+            if s.kind == skMacro:
+              sysTypeFromName(c.graph, n.info, "NimNode")
+            elif not isInlineIterator(s.typ):
+              s.typ[0]
+            else:
+              nil
         # semantic checking also needed with importc in case used in VM
         s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], resultType))
         # unfortunately we cannot skip this step when in 'system.compiles'
diff --git a/lib/std/private/dragonbox.nim b/lib/std/private/dragonbox.nim
index 2ba22a751..e39ffd9a3 100644
--- a/lib/std/private/dragonbox.nim
+++ b/lib/std/private/dragonbox.nim
@@ -75,10 +75,10 @@ const
 const
   signMask*: BitsType = not (not BitsType(0) shr 1)
 
-proc constructDouble*(bits: BitsType): Double {.constructor.} =
+proc constructDouble*(bits: BitsType): Double  =
   result.bits = bits
 
-proc constructDouble*(value: ValueType): Double {.constructor.} =
+proc constructDouble*(value: ValueType): Double  =
   result.bits = cast[typeof(result.bits)](value)
 
 proc physicalSignificand*(this: Double): BitsType {.noSideEffect.} =
diff --git a/lib/std/private/schubfach.nim b/lib/std/private/schubfach.nim
index dad8363ba..194fb4bfa 100644
--- a/lib/std/private/schubfach.nim
+++ b/lib/std/private/schubfach.nim
@@ -39,10 +39,10 @@ const
   exponentMask: BitsType = maxIeeeExponent shl (significandSize - 1)
   signMask: BitsType = not (not BitsType(0) shr 1)
 
-proc constructSingle(bits: BitsType): Single {.constructor.} =
+proc constructSingle(bits: BitsType): Single  =
   result.bits = bits
 
-proc constructSingle(value: ValueType): Single {.constructor.} =
+proc constructSingle(value: ValueType): Single  =
   result.bits = cast[typeof(result.bits)](value)
 
 proc physicalSignificand(this: Single): BitsType {.noSideEffect.} =
diff --git a/tests/cpp/tconstructor.nim b/tests/cpp/tconstructor.nim
index 8489c71d3..d4d6a7ccf 100644
--- a/tests/cpp/tconstructor.nim
+++ b/tests/cpp/tconstructor.nim
@@ -1,6 +1,9 @@
 discard """
   targets: "cpp"
   cmd: "nim cpp $file"
+  output: '''
+1
+'''
 """
 
 {.emit:"""/*TYPESECTION*/
@@ -15,10 +18,38 @@ struct CppClass {
 };
 """.}
 
-type  CppClass* {.importcpp.} = object
+type  CppClass* {.importcpp, inheritable.} = object
   x: int32
   y: int32
 
 proc makeCppClass(x, y: int32): CppClass {.importcpp: "CppClass(@)", constructor.}
+#test globals are init with the constructor call
+var shouldCompile {.used.} = makeCppClass(1, 2)
 
-var shouldCompile = makeCppClass(1, 2)
+proc newCpp*[T](): ptr T {.importcpp:"new '*0()".}
+
+#creation
+type NimClassNoNarent* = object
+  x: int32
+
+proc makeNimClassNoParent(x:int32): NimClassNoNarent {. constructor.} =
+  this.x = x
+  discard
+
+let nimClassNoParent = makeNimClassNoParent(1)
+echo nimClassNoParent.x #acess to this just fine. Notice the field will appear last because we are dealing with constructor calls here
+
+var nimClassNoParentDef {.used.}: NimClassNoNarent  #test has a default constructor. 
+
+#inheritance 
+type NimClass* = object of CppClass
+
+proc makeNimClass(x:int32): NimClass {. constructor:"NimClass('1 #1) : CppClass(0, #1) ".} =
+  this.x = x
+
+#optinially define the default constructor so we get rid of the cpp warn and we can declare the obj (note: default constructor of 'tyObject_NimClass__apRyyO8cfRsZtsldq1rjKA' is implicitly deleted because base class 'CppClass' has no default constructor)
+proc makeCppClass(): NimClass {. constructor: "NimClass() : CppClass(0, 0) ".} = 
+  this.x = 1
+
+let nimClass = makeNimClass(1)
+var nimClassDef {.used.}: NimClass  #since we explictly defined the default constructor we can declare the obj
\ No newline at end of file