mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-11 22:08:54 +00:00
[C++] Member pragma RFC (https://github.com/nim-lang/RFCs/issues/530) (#22272)
* [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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
53
tests/cpp/tmember.nim
Normal 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()
|
||||
Reference in New Issue
Block a user