Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix #12150 C++ free functions are now supported using new importcpp:"!$1" syntax #15653

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ provided by the operating system.
- The `cstring` doesn't support `[]=` operator in JS backend.

- nil dereference is not allowed at compile time. `cast[ptr int](nil)[]` is rejected at compile time.
- `importcpp` procs now support free functions via `!` prefix:
`proc freeFn(a: cint) {.importcpp: "!$1".}` maps to `void freeFn(int)`.
likewise with `importcpp: "!freeFn"`.

- `typetraits.distinctBase` now is identity instead of error for non distinct types.

Expand Down
6 changes: 4 additions & 2 deletions compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,9 @@ type
sfUsedInFinallyOrExcept # symbol is used inside an 'except' or 'finally'
sfSingleUsedTemp # For temporaries that we know will only be used once
sfNoalias # 'noalias' annotation, means C's 'restrict'
sfCompileToCpp
timotheecour marked this conversation as resolved.
Show resolved Hide resolved
# skModule: compile the module as C++ code
# skType, skProc: C++ symbol

TSymFlags* = set[TSymFlag]

Expand All @@ -321,7 +324,6 @@ const
sfReorder* = sfForward
# reordering pass is enabled

sfCompileToCpp* = sfInfixCall # compile the module as C++ code
sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
sfExperimental* = sfOverriden # module uses the .experimental switch
sfGoto* = sfOverriden # var is used for 'goto' code generation
Expand Down Expand Up @@ -1945,7 +1947,7 @@ proc canRaiseConservative*(fn: PNode): bool =

proc canRaise*(fn: PNode): bool =
if fn.kind == nkSym and (fn.sym.magic notin magicsThatCanRaise or
{sfImportc, sfInfixCall} * fn.sym.flags == {sfImportc} or
{sfImportc, sfCompileToCpp} * fn.sym.flags == {sfImportc} or
sfGeneratedOp in fn.sym.flags):
result = false
elif fn.kind == nkSym and fn.sym.magic == mEcho:
Expand Down
2 changes: 1 addition & 1 deletion compiler/ccgcalls.nim
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode, needsTmp = false): Rop
# means '*T'. See posix.nim for lots of examples that do that in the wild.
let callee = call[0]
if callee.kind == nkSym and
{sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and
{sfImportc, sfCompileToCpp, sfCompilerProc} * callee.sym.flags == {sfImportc} and
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
result = addrLoc(p.config, a)
else:
Expand Down
4 changes: 2 additions & 2 deletions compiler/ccgtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@ proc isImportedType(t: PType): bool =

proc isImportedCppType(t: PType): bool =
let x = t.skipTypes(irrelevantForBackend)
result = (t.sym != nil and sfInfixCall in t.sym.flags) or
(x.sym != nil and sfInfixCall in x.sym.flags)
result = (t.sym != nil and sfCompileToCpp in t.sym.flags) or
(x.sym != nil and sfCompileToCpp in x.sym.flags)

proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKind): Rope

Expand Down
2 changes: 1 addition & 1 deletion compiler/cgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1099,7 +1099,7 @@ proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
result = (sfCompileToCpp in m.module.flags and
sfCompileToCpp notin sym.getModule().flags and
m.config.backend != backendCpp) or (
sym.flags * {sfInfixCall, sfCompilerProc, sfMangleCpp} == {} and
sym.flags * {sfCompileToCpp, sfCompilerProc, sfMangleCpp} == {} and
sym.flags * {sfImportc, sfExportc} != {} and
sym.magic == mNone and
m.config.backend == backendCpp)
Expand Down
12 changes: 11 additions & 1 deletion compiler/pragmas.nim
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,19 @@ proc processImportCompilerProc(c: PContext; s: PSym, extname: string, info: TLin
incl(s.loc.flags, lfImportCompilerProc)

proc processImportCpp(c: PContext; s: PSym, extname: string, info: TLineInfo) =
var extname = extname
let isFreeFunction = extname.startsWith "!"
timotheecour marked this conversation as resolved.
Show resolved Hide resolved
#[
example: `proc fun2(a: cstring): cint {.importcpp:"!fun2".}`
see more tests in tests/cpp/t12150.nim
]#
if isFreeFunction:
extname = extname[1..^1]
else:
incl(s.flags, sfInfixCall)
setExternName(c, s, extname, info)
incl(s.flags, sfImportc)
incl(s.flags, sfInfixCall)
incl(s.flags, sfCompileToCpp)
excl(s.flags, sfForward)
if c.config.backend == backendC:
let m = s.getModule()
Expand Down
2 changes: 1 addition & 1 deletion compiler/sighashes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
else:
c.hashSym(t.sym)
of tyGenericInst:
if sfInfixCall in t.base.sym.flags:
if sfCompileToCpp in t.base.sym.flags:
# This is an imported C++ generic type.
# We cannot trust the `lastSon` to hold a properly populated and unique
# value for each instantiation, so we hash the generic parameters here:
Expand Down
2 changes: 1 addition & 1 deletion compiler/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ proc containsObject*(t: PType): bool =

proc isObjectWithTypeFieldPredicate(t: PType): bool =
result = t.kind == tyObject and t[0] == nil and
not (t.sym != nil and {sfPure, sfInfixCall} * t.sym.flags != {}) and
not (t.sym != nil and {sfPure, sfCompileToCpp} * t.sym.flags != {}) and
tfFinal notin t.flags

type
Expand Down
8 changes: 7 additions & 1 deletion doc/manual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7014,18 +7014,24 @@ language for maximum flexibility:
- A dot following the hash ``#.`` indicates that the call should use C++'s dot
or arrow notation.
- An at symbol ``@`` is replaced by the remaining arguments, separated by commas.
- An exclamation symbol ``!`` indicates a free (non-member) function.

For example:

.. code-block:: nim
// member function
proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "#.CppMethod(@)".}
var x: ptr CppObj
cppMethod(x[], 1, 2, 3)
// free function
proc freeFn(a: cint) {.importcpp: "!$1".} # or importcpp: "!freeFn"
freeFn(4)

Produces:

.. code-block:: C
x->CppMethod(1, 2, 3)
x->CppMethod(1, 2, 3);
freeFn(4);

As a special rule to keep backward compatibility with older versions of the
``importcpp`` pragma, if there is no special pattern
Expand Down
4 changes: 4 additions & 0 deletions tests/cpp/m12150.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
proc fun1(): cint {.exportcpp.} = 10
proc fun2(a: cstring): cint {.exportcpp.} = 11
proc fun2(): cint {.exportcpp.} = 12
proc fun3(): cint {.exportc.} = 13
18 changes: 18 additions & 0 deletions tests/cpp/t12150.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
discard """
targets: "cpp"
"""

proc fun1(): cint {.importcpp:"!$1".}
proc fun2(a: cstring): cint {.importcpp:"!fun2".}
proc fun2(): cint {.importcpp:"!$1".}
proc fun2Aux(): cint {.importcpp:"!fun2".}
proc fun3(): cint {.importc.}

doAssert fun1() == 10
doAssert fun2(nil) == 11
doAssert fun2() == 12
doAssert fun2Aux() == 12
doAssert fun3() == 13

import ./m12150