* [C++] Member pragma RFC #530
rebase devel

* changes the test so `echo` is not used before Nim is init

* rebase devel

* fixes Error: use explicit initialization of X for clarity [Uninit]
This commit is contained in:
Juan M Gómez
2023-08-07 09:11:00 +01:00
committed by GitHub
parent fe9ae2c69a
commit b5b4b48c94
7 changed files with 85 additions and 23 deletions

View File

@@ -314,6 +314,7 @@ type
# an infinite loop, this flag is used as a sentinel to stop it.
sfVirtual # proc is a C++ virtual function
sfByCopy # param is marked as pass bycopy
sfMember # proc is a C++ member of a type
sfCodegenDecl # type, proc, global or proc param is marked as codegenDecl
TSymFlags* = set[TSymFlag]
@@ -347,6 +348,7 @@ const
sfBase* = sfDiscriminant
sfCustomPragma* = sfRegister # symbol is custom pragma template
sfTemplateRedefinition* = sfExportc # symbol is a redefinition of an earlier template
sfCppMember* = { sfVirtual, sfMember, sfConstructor } # proc is a C++ member, meaning it will be attached to the type definition
const
# getting ready for the future expr/stmt merge

View File

@@ -493,12 +493,12 @@ proc multiFormat*(frmt: var string, chars : static openArray[char], args: openAr
template cgDeclFrmt*(s: PSym): string =
s.constraint.strVal
proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, params: var string,
proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params: var string,
check: var IntSet, declareEnvironment=true;
weakDep=false;) =
let t = prc.typ
let isCtor = sfConstructor in prc.flags
if isCtor:
if isCtor or (name[0] == '~' and sfMember in prc.flags): #destructors cant have void
rettype = ""
elif t[0] == nil or isInvalidReturnType(m.config, t):
rettype = "void"
@@ -555,6 +555,7 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, params: var
multiFormat(params, @['\'', '#'], [types, names])
multiFormat(superCall, @['\'', '#'], [types, names])
multiFormat(name, @['\'', '#'], [types, names]) #so we can ~'1 on members
if params == "()":
if types.len == 0:
params = "(void)"
@@ -1148,11 +1149,14 @@ 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, superCall: var string; isFnConst, isOverride: var bool; isCtor: bool) =
proc parseVFunctionDecl(val: string; name, params, retType, superCall: var string; isFnConst, isOverride, isMemberVirtual: var bool; isCtor: bool) =
var afterParams: string = ""
if scanf(val, "$*($*)$s$*", name, params, afterParams):
isFnConst = afterParams.find("const") > -1
isOverride = afterParams.find("override") > -1
isMemberVirtual = name.find("virtual ") > -1
if isMemberVirtual:
name = name.replace("virtual ", "")
if isCtor:
discard scanf(afterParams, ":$s$*", superCall)
else:
@@ -1161,9 +1165,8 @@ proc parseVFunctionDecl(val: string; name, params, retType, superCall: var strin
params = "(" & params & ")"
proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl : bool = false) =
assert {sfVirtual, sfConstructor} * prc.flags != {}
assert sfCppMember * 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)
@@ -1179,9 +1182,10 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool =
var typDesc = getTypeDescWeak(m, typ, check, dkParam)
let asPtrStr = rope(if asPtr: "_PTR" else: "")
var name, params, rettype, superCall: string = ""
var isFnConst, isOverride: bool = false
parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, superCall, isFnConst, isOverride, isCtor)
genMemberProcParams(m, prc, superCall, rettype, params, check, true, false)
var isFnConst, isOverride, isMemberVirtual: bool = false
parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, superCall, isFnConst, isOverride, isMemberVirtual, isCtor)
genMemberProcParams(m, prc, superCall, rettype, name, params, check, true, false)
let isVirtual = sfVirtual in prc.flags or isMemberVirtual
var fnConst, override: string = ""
if isCtor:
name = typDesc
@@ -1194,7 +1198,7 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool =
override = " override"
superCall = ""
else:
if isVirtual:
if not isCtor:
prc.loc.r = "$1$2(@)" % [memberOp, name]
elif superCall != "":
superCall = " : " & superCall

View File

@@ -1149,7 +1149,7 @@ 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, sfConstructor} * prc.flags != {}:
if m.config.backend == backendCpp and sfCppMember * prc.flags != {}:
genMemberProcHeader(m, prc, header)
else:
genProcHeader(m, prc, header)
@@ -1260,7 +1260,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, sfConstructor} * sym.flags != {}: return
if lfNoDecl in sym.loc.flags or sfCppMember * sym.flags != {}: return
if lfDynamicLib in sym.loc.flags:
if sym.itemId.module != m.module.position and
not containsOrIncl(m.declaredThings, sym.id):

View File

@@ -34,7 +34,7 @@ const
wAsmNoStackFrame, wDiscardable, wNoInit, wCodegenDecl,
wGensym, wInject, wRaises, wEffectsOf, wTags, wForbids, wLocks, wDelegator, wGcSafe,
wConstructor, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy,
wRequires, wEnsures, wEnforceNoRaises, wSystemRaisesDefect, wVirtual, wQuirky}
wRequires, wEnsures, wEnforceNoRaises, wSystemRaisesDefect, wVirtual, wQuirky, wMember}
converterPragmas* = procPragmas
methodPragmas* = procPragmas+{wBase}-{wImportCpp}
templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty,
@@ -245,10 +245,10 @@ proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string =
if n.kind in nkPragmaCallKinds: result = expectStrLit(c, n)
else: result = defaultStr
proc processVirtual(c: PContext, n: PNode, s: PSym) =
proc processVirtual(c: PContext, n: PNode, s: PSym, flag: TSymFlag) =
s.constraint = newEmptyStrNode(c, n, getOptionalStr(c, n, "$1"))
s.constraint.strVal = s.constraint.strVal % s.name.s
s.flags.incl {sfVirtual, sfInfixCall, sfExportc, sfMangleCpp}
s.flags.incl {flag, sfInfixCall, sfExportc, sfMangleCpp}
s.typ.callConv = ccNoConvention
incl c.config.globalOptions, optMixedMode
@@ -1284,7 +1284,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
of wSystemRaisesDefect:
sym.flags.incl sfSystemRaisesDefect
of wVirtual:
processVirtual(c, it, sym)
processVirtual(c, it, sym, sfVirtual)
of wMember:
processVirtual(c, it, sym, sfMember)
else: invalidPragma(c, it)
elif comesFromPush and whichKeyword(ident) != wInvalid:

View File

@@ -2269,26 +2269,27 @@ 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, sfConstructor} * s.flags != {} and sfImportc notin s.flags:
if sfCppMember * s.flags != {} and sfImportc notin s.flags:
let isVirtual = sfVirtual in s.flags
let pragmaName = if isVirtual: "virtual" else: "constructor"
let isCtor = sfConstructor in s.flags
let pragmaName = if isVirtual: "virtual" elif isCtor: "constructor" else: "member"
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")
if s.typ.sons.len < 2 and not isCtor:
localError(c.config, n.info, pragmaName & " must have at least one parameter")
for son in s.typ.sons:
if son!=nil and son.isMetaType:
localError(c.config, n.info, pragmaName & " unsupported for generic routine")
var typ: PType
if sfConstructor in s.flags:
if isCtor:
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:
if typ.kind == tyPtr and not isCtor:
typ = typ[0]
if typ.kind != tyObject:
localError(c.config, n.info, "virtual must be either ptr to object or object type.")
localError(c.config, n.info, pragmaName & " 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.memberProcsPerType.mgetOrPut(typ.itemId, @[]).add s
else:

View File

@@ -103,7 +103,7 @@ type
wSwitch = "switch", wThis = "this", wThrow = "throw", wTrue = "true", wTypedef = "typedef",
wTypeid = "typeid", wTypeof = "typeof", wTypename = "typename",
wUnion = "union", wPacked = "packed", wUnsigned = "unsigned", wVirtual = "virtual",
wVoid = "void", wVolatile = "volatile", wWchar = "wchar_t",
wVoid = "void", wVolatile = "volatile", wWchar = "wchar_t", wMember = "member",
wAlignas = "alignas", wAlignof = "alignof", wConstexpr = "constexpr", wDecltype = "decltype",
wNullptr = "nullptr", wNoexcept = "noexcept",

53
tests/cpp/tmember.nim Normal file
View File

@@ -0,0 +1,53 @@
discard """
targets: "cpp"
cmd: "nim cpp $file"
output: '''
2
false
hello foo
hello boo
hello boo
destructing
destructing
'''
"""
proc print(s: cstring) {.importcpp:"printf(@)", header:"<stdio.h>".}
type
Doo {.exportc.} = object
test: int
proc memberProc(f: Doo) {.exportc, member.} =
echo $f.test
proc destructor(f: Doo) {.member: "~'1()", used.} =
print "destructing\n"
proc `==`(self, other: Doo): bool {.member:"operator==('2 const & #2) const -> '0"} =
self.test == other.test
let doo = Doo(test: 2)
doo.memberProc()
echo doo == Doo(test: 1)
#virtual
proc newCpp*[T](): ptr T {.importcpp:"new '*0()".}
type
Foo = object of RootObj
FooPtr = ptr Foo
Boo = object of Foo
BooPtr = ptr Boo
proc salute(self: FooPtr) {.member: "virtual $1()".} =
echo "hello foo"
proc salute(self: BooPtr) {.member: "virtual $1()".} =
echo "hello boo"
let foo = newCpp[Foo]()
let boo = newCpp[Boo]()
let booAsFoo = cast[FooPtr](newCpp[Boo]())
foo.salute()
boo.salute()
booAsFoo.salute()