diff --git a/changelog.md b/changelog.md index 6081c90b1eef4..bfd6a2db69934 100644 --- a/changelog.md +++ b/changelog.md @@ -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. diff --git a/compiler/ast.nim b/compiler/ast.nim index 2b1f76e2d3632..f21b124458549 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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 + # skModule: compile the module as C++ code + # skType, skProc: C++ symbol TSymFlags* = set[TSymFlag] @@ -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 @@ -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: diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 64b883087ad06..b53987647716a 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -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: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 9c751b1ca1b9b..15598359699bd 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -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 diff --git a/compiler/cgen.nim b/compiler/cgen.nim index f870828662183..fc0fe7fdafe50 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -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) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 8fbdd3579f380..b08404f27c21e 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -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 "!" + #[ + 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() diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 156bc66d79bdb..57136b90c024d 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -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: diff --git a/compiler/types.nim b/compiler/types.nim index a0d43ec095457..d181ceebed6fe 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -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 diff --git a/doc/manual.rst b/doc/manual.rst index 4884db0e1b266..e0917f8013a22 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -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 diff --git a/tests/cpp/m12150.nim b/tests/cpp/m12150.nim new file mode 100644 index 0000000000000..196c4a7499a8f --- /dev/null +++ b/tests/cpp/m12150.nim @@ -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 diff --git a/tests/cpp/t12150.nim b/tests/cpp/t12150.nim new file mode 100644 index 0000000000000..5f6ff8b8ca198 --- /dev/null +++ b/tests/cpp/t12150.nim @@ -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 +