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

Force experimental switch to be able to use call/dot operators #16924

Closed
wants to merge 9 commits into from
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
11 changes: 8 additions & 3 deletions compiler/semcall.nim
Original file line number Diff line number Diff line change
Expand Up @@ -377,19 +377,22 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
excl n.flags, nfExprCall
else: return

var insertedOp = false
if nfDotField in n.flags:
internalAssert c.config, f.kind == nkIdent and n.len >= 2

# leave the op head symbol empty,
# we are going to try multiple variants
n.sons[0..1] = [nil, n[1], f]
orig.sons[0..1] = [nil, orig[1], f]
insertedOp = true

template tryOp(x) =
let op = newIdentNode(getIdent(c.cache, x), n.info)
n[0] = op
orig[0] = op
pickBest(op)
if dotOperators in c.features:
pickBest(op)

if nfExplicitCall in n.flags:
tryOp ".()"
Expand All @@ -403,7 +406,9 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
let callOp = newIdentNode(getIdent(c.cache, ".="), n.info)
n.sons[0..1] = [callOp, n[1], calleeName]
orig.sons[0..1] = [callOp, orig[1], calleeName]
pickBest(callOp)
insertedOp = true
if dotOperators in c.features:
pickBest(callOp)

if overloadsState == csEmpty and result.state == csEmpty:
if efNoUndeclared notin flags: # for tests/pragmas/tcustom_pragma.nim
Expand All @@ -414,7 +419,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
localError(c.config, n.info, "expression '$1' cannot be called" %
renderTree(n, {renderNoComments}))
else:
if {nfDotField, nfDotSetter} * n.flags != {}:
if insertedOp:
# clean up the inserted ops
n.sons.delete(2)
n[0] = f
Expand Down
3 changes: 2 additions & 1 deletion compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,8 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
if n.len == 1: return semObjConstr(c, n, flags)
return semConv(c, n)
else:
result = overloadedCallOpr(c, n)
if callOperator in c.features:
result = overloadedCallOpr(c, n)
# Now that nkSym does not imply an iteration over the proc/iterator space,
# the old ``prc`` (which is likely an nkIdent) has to be restored:
if result == nil:
Expand Down
15 changes: 8 additions & 7 deletions compiler/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1975,13 +1975,14 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
pushOwner(c, s)

if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
if s.name.s[0] in {'.', '('}:
if s.name.s in [".", ".()", ".="] and {Feature.destructor, dotOperators} * c.features == {}:
localError(c.config, n.info, "the overloaded " & s.name.s &
" operator has to be enabled with {.experimental: \"dotOperators\".}")
elif s.name.s == "()" and callOperator notin c.features:
localError(c.config, n.info, "the overloaded " & s.name.s &
" operator has to be enabled with {.experimental: \"callOperator\".}")
when false:
if s.name.s[0] in {'.', '('}:
if s.name.s in [".", ".()", ".="] and {Feature.destructor, dotOperators} * c.features == {}:
localError(c.config, n.info, "the overloaded " & s.name.s &
" operator has to be enabled with {.experimental: \"dotOperators\".}")
elif s.name.s == "()" and callOperator notin c.features:
localError(c.config, n.info, "the overloaded " & s.name.s &
" operator has to be enabled with {.experimental: \"callOperator\".}")

if n[bodyPos].kind != nkEmpty and sfError notin s.flags:
# for DLL generation we allow sfImportc to have a body, for use in VM
Expand Down
2 changes: 1 addition & 1 deletion doc/manual_experimental.rst
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ The matched dot operators can be symbols of any callable kind (procs,
templates and macros), depending on the desired effect:

.. code-block:: nim
template `.` (js: PJsonNode, field: untyped): JSON = js[astToStr(field)]
template `.`(js: PJsonNode, field: untyped): JSON = js[astToStr(field)]

var js = parseJson("{ x: 1, y: 2}")
echo js.x # outputs 1
Expand Down
5 changes: 4 additions & 1 deletion lib/js/jsffi.nim
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ runnableExamples:
# Use jQuery to make the following code run, after the document is ready.
# This uses an experimental `.()` operator for `JsObject`, to emit
# JavaScript calls, when no corresponding proc exists for `JsObject`.
{.experimental: "dotOperators".}
proc main =
jq(document).ready(proc() =
console.log("Hello JavaScript!")
Expand Down Expand Up @@ -217,11 +218,11 @@ proc `==`*(x, y: JsRoot): bool {.importcpp: "(# === #)".}
## like in JavaScript, so if your JsObjects are in fact JavaScript Objects,
## and not strings or numbers, this is a *comparison of references*.

{.experimental.}
macro `.`*(obj: JsObject, field: untyped): JsObject =
## Experimental dot accessor (get) for type JsObject.
## Returns the value of a property of name `field` from a JsObject `x`.
runnableExamples:
{.experimental: "dotOperators".}
let obj = newJsObject()
obj.a = 20
assert obj.a.to(int) == 20
Expand Down Expand Up @@ -448,8 +449,10 @@ macro `{}`*(typ: typedesc, xs: varargs[untyped]): auto =
return `a`

result = quote do:
{.push experimental: "dotOperators".}
proc inner(): `typ` {.gensym.} =
`body`
{.pop.}
inner()

# Macro to build a lambda using JavaScript's `this`
Expand Down
6 changes: 2 additions & 4 deletions lib/std/wrapnils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
## Unstable API.

runnableExamples:
{.experimental: "dotOperators".}

type Foo = ref object
x1: string
x2: Foo
Expand Down Expand Up @@ -34,8 +36,6 @@ template unwrap(a: Wrapnil): untyped =
## See top-level example.
a.valueImpl

{.push experimental: "dotOperators".}

template `.`*(a: Wrapnil, b): untyped =
## See top-level example.
let a1 = a # to avoid double evaluations
Expand All @@ -53,8 +53,6 @@ template `.`*(a: Wrapnil, b): untyped =
# nil is "sticky"; this is needed, see tests
default(T)

{.pop.}

proc isValid(a: Wrapnil): bool =
## Returns true if `a` didn't contain intermediate `nil` values (note that
## `a.valueImpl` itself can be nil even in that case)
Expand Down
2 changes: 2 additions & 0 deletions tests/destructor/tatomicptrs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ deallocating
joinable: false
"""

{.experimental: "dotOperators".}

type
SharedPtr*[T] = object
x: ptr T
Expand Down
2 changes: 2 additions & 0 deletions tests/js/tjsffi.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Event { name: 'updates: test' }
'''
"""

{.experimental: "dotOperators".}

import jsffi, jsconsole

# Tests for JsObject
Expand Down
2 changes: 2 additions & 0 deletions tests/js/tjsffi_old.nim
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ true
# xxx instead of maintaining this near-duplicate test file, just have tests
# that check that importc, importcpp, importjs work and remove this file.

{.experimental: "dotOperators".}

import jsffi, jsconsole

# Tests for JsObject
Expand Down
3 changes: 3 additions & 0 deletions tests/specialops/musewithoutexperimental.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{.experimental: "callOperator".}

template `()`*(a, b: float): float = a + b
2 changes: 2 additions & 0 deletions tests/specialops/tdotops.nim
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ one param call to c with 10
'''
"""

{.experimental: "dotOperators".}
metagn marked this conversation as resolved.
Show resolved Hide resolved

type
T1 = object
x*: int
Expand Down
41 changes: 41 additions & 0 deletions tests/specialops/tusewithoutexperimental.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
block:
{.push experimental: "callOperator".}

template `()`(a, b: int): int = a + b
let x = 1
let y = 2
doAssert x.y == 3
doAssert compiles(x.y)
let z = 3.0
doAssert not compiles(x.z)

{.pop.}

doAssert not compiles(x.y)
doAssert not compiles(x.z)

block:
{.push experimental: "dotOperators".}

template `.`(a, b: string): string = a & b
let x = "x"
let y = "y"
doAssert x.y == "xy"
doAssert compiles(x.y)
let z = 'z'
doAssert not compiles(x.z)

{.pop.}

doAssert not compiles(x.y)
doAssert not compiles(x.z)

import musewithoutexperimental

let x = 1.0
let y = 2.0
doAssert not compiles(x.y)
metagn marked this conversation as resolved.
Show resolved Hide resolved

{.experimental: "callOperator".}

doAssert compiles(x.y)
2 changes: 2 additions & 0 deletions tests/stdlib/twrapnils.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import std/wrapnils

{.experimental: "dotOperators".}

const wrapnilExtendedExports = declared(wrapnil)
# for now, wrapnil, isValid, unwrap are not exported

Expand Down