diff options
author | Juan M Gómez <info@jmgomez.me> | 2023-08-07 09:11:00 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-07 10:11:00 +0200 |
commit | b5b4b48c942b23991c8d11f41dc39b7e211e5b2e (patch) | |
tree | 625795a000486f2abab0beda5497a944ef641f87 | |
parent | fe9ae2c69adc39cd170b4bd31221fb66135fd571 (diff) | |
download | Nim-b5b4b48c942b23991c8d11f41dc39b7e211e5b2e.tar.gz |
[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]
-rw-r--r-- | compiler/ast.nim | 2 | ||||
-rw-r--r-- | compiler/ccgtypes.nim | 22 | ||||
-rw-r--r-- | compiler/cgen.nim | 4 | ||||
-rw-r--r-- | compiler/pragmas.nim | 10 | ||||
-rw-r--r-- | compiler/semstmts.nim | 15 | ||||
-rw-r--r-- | compiler/wordrecg.nim | 2 | ||||
-rw-r--r-- | tests/cpp/tmember.nim | 53 |
7 files changed, 85 insertions, 23 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index eccf5a985..aba877187 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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 diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 205031a91..6bac84e95 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -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 diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 2643e6edd..a3b74c408 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -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): diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index e0fdba566..56e25c0b4 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -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: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 448c26cf2..302ccc8b9 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -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: diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index f784f0a75..b2b0c8ae2 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -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", diff --git a/tests/cpp/tmember.nim b/tests/cpp/tmember.nim new file mode 100644 index 000000000..3f498c722 --- /dev/null +++ b/tests/cpp/tmember.nim @@ -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() |