diff options
author | LemonBoy <LemonBoy@users.noreply.github.com> | 2018-10-09 19:58:23 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2018-10-09 19:58:23 +0200 |
commit | 33458894da13da7134604639c129273bfb0cb1c1 (patch) | |
tree | 76ed110300cb8cb104f4c51730650ce65a83ad79 | |
parent | ee14ace5d3f3ede9d47d60bd415e46a3fb121fb2 (diff) | |
download | Nim-33458894da13da7134604639c129273bfb0cb1c1.tar.gz |
Fix overload resolution for pragmas evaluation (#8902)
Fixes #6448 Fixes #4384
-rw-r--r-- | compiler/pragmas.nim | 4 | ||||
-rw-r--r-- | compiler/semcall.nim | 13 | ||||
-rw-r--r-- | compiler/semdata.nim | 5 | ||||
-rw-r--r-- | compiler/semstmts.nim | 53 | ||||
-rw-r--r-- | tests/macros/tmsginfo.nim | 2 | ||||
-rw-r--r-- | tests/pragmas/foobar.nim | 3 | ||||
-rw-r--r-- | tests/pragmas/t4384.nim | 3 | ||||
-rw-r--r-- | tests/pragmas/t6448.nim | 16 |
8 files changed, 66 insertions, 33 deletions
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index c4308fc6f..eda52ab02 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -723,13 +723,13 @@ proc semCustomPragma(c: PContext, n: PNode): PNode = elif n.kind == nkExprColonExpr: # pragma: arg -> pragma(arg) result = newTree(nkCall, n[0], n[1]) - elif n.kind in nkPragmaCallKinds + {nkIdent}: + elif n.kind in nkPragmaCallKinds: result = n else: invalidPragma(c, n) return n - let r = c.semOverloadedCall(c, result, n, {skTemplate}, {}) + let r = c.semOverloadedCall(c, result, n, {skTemplate}, {efNoUndeclared}) if r.isNil or sfCustomPragma notin r[0].sym.flags: invalidPragma(c, n) else: diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 53f7045dd..71a9197b7 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -330,10 +330,11 @@ proc resolveOverloads(c: PContext, n, orig: PNode, pickBest(callOp) if overloadsState == csEmpty and result.state == csEmpty: - if nfDotField in n.flags and nfExplicitCall notin n.flags: - localError(c.config, n.info, errUndeclaredField % considerQuotedIdent(c, f, n).s) - else: - localError(c.config, n.info, errUndeclaredRoutine % considerQuotedIdent(c, f, n).s) + if efNoUndeclared notin flags: + if nfDotField in n.flags and nfExplicitCall notin n.flags: + localError(c.config, n.info, errUndeclaredField % considerQuotedIdent(c, f, n).s) + else: + localError(c.config, n.info, errUndeclaredRoutine % considerQuotedIdent(c, f, n).s) return elif result.state != csMatch: if nfExprCall in n.flags: @@ -500,14 +501,14 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, # repeat the overload resolution, # this time enabling all the diagnostic output (this should fail again) discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain}) - else: + elif efNoUndeclared notin flags: notFoundError(c, n, errors) else: if efExplain notin flags: # repeat the overload resolution, # this time enabling all the diagnostic output (this should fail again) discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain}) - else: + elif efNoUndeclared notin flags: notFoundError(c, n, errors) proc explicitGenericInstError(c: PContext; n: PNode): PNode = diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 6d6627690..bec4a59a4 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -65,7 +65,10 @@ type # to the user. efWantStmt, efAllowStmt, efDetermineType, efExplain, efAllowDestructor, efWantValue, efOperand, efNoSemCheck, - efNoEvaluateGeneric, efInCall, efFromHlo + efNoEvaluateGeneric, efInCall, efFromHlo, + efNoUndeclared + # Use this if undeclared identifiers should not raise an error during + # overload resolution. TExprFlags* = set[TExprFlag] diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 2817b5ef9..e02a38d8a 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1218,13 +1218,6 @@ proc copyExcept(n: PNode, i: int): PNode = for j in 0..<n.len: if j != i: result.add(n.sons[j]) -proc lookupMacro(c: PContext, n: PNode): PSym = - if n.kind == nkSym: - result = n.sym - if result.kind notin {skMacro, skTemplate}: result = nil - else: - result = searchInScopes(c, considerQuotedIdent(c, n), {skMacro, skTemplate}) - proc semProcAnnotation(c: PContext, prc: PNode; validPragmas: TSpecialWords): PNode = var n = prc.sons[pragmasPos] @@ -1232,39 +1225,53 @@ proc semProcAnnotation(c: PContext, prc: PNode; for i in countup(0, n.len-1): var it = n.sons[i] var key = if it.kind in nkPragmaCallKinds and it.len >= 1: it.sons[0] else: it - let m = lookupMacro(c, key) - if m == nil: - if key.kind == nkIdent and key.ident.id == ord(wDelegator): - if considerQuotedIdent(c, prc.sons[namePos]).s == "()": - prc.sons[namePos] = newIdentNode(c.cache.idDelegator, prc.info) - prc.sons[pragmasPos] = copyExcept(n, i) - else: - localError(c.config, prc.info, "only a call operator can be a delegator") + + if whichPragma(it) != wInvalid: + # Not a custom pragma + continue + elif strTableGet(c.userPragmas, considerQuotedIdent(c, key)) != nil: + # User-defined pragma continue - elif sfCustomPragma in m.flags: - continue # semantic check for custom pragma happens later in semProcAux # we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and # let the semantic checker deal with it: - var x = newNodeI(nkCall, n.info) - x.add(newSymNode(m)) - prc.sons[pragmasPos] = copyExcept(n, i) - if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0: - prc.sons[pragmasPos] = c.graph.emptyNode + var x = newNodeI(nkCall, key.info) + x.add(key) if it.kind in nkPragmaCallKinds and it.len > 1: # pass pragma arguments to the macro too: for i in 1..<it.len: x.add(it.sons[i]) + + # Drop the pragma from the list, this prevents getting caught in endless + # recursion when the nkCall is semanticized + prc.sons[pragmasPos] = copyExcept(n, i) + if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0: + prc.sons[pragmasPos] = c.graph.emptyNode + x.add(prc) # recursion assures that this works for multiple macro annotations too: - result = semExpr(c, x) + var r = semOverloadedCall(c, x, x, {skMacro}, {efNoUndeclared}) + if r == nil: + # Restore the old list of pragmas since we couldn't process this + prc.sons[pragmasPos] = n + # No matching macro was found but there's always the possibility this may + # be a .pragma. template instead + continue + + doAssert r.sons[0].kind == nkSym + # Expand the macro here + result = semMacroExpr(c, r, r, r.sons[0].sym, {}) + + doAssert result != nil + # since a proc annotation can set pragmas, we process these here again. # This is required for SqueakNim-like export pragmas. if result.kind in procDefs and result[namePos].kind == nkSym and result[pragmasPos].kind != nkEmpty: pragma(c, result[namePos].sym, result[pragmasPos], validPragmas) + return proc setGenericParamsMisc(c: PContext; n: PNode): PNode = diff --git a/tests/macros/tmsginfo.nim b/tests/macros/tmsginfo.nim index bf6c9d537..ebdce0155 100644 --- a/tests/macros/tmsginfo.nim +++ b/tests/macros/tmsginfo.nim @@ -1,6 +1,6 @@ discard """ nimout: '''tmsginfo.nim(21, 1) Warning: foo1 [User] -tmsginfo.nim(22, 11) template/generic instantiation from here +tmsginfo.nim(22, 13) template/generic instantiation from here tmsginfo.nim(15, 10) Warning: foo2 [User] tmsginfo.nim(23, 1) Hint: foo3 [User] tmsginfo.nim(19, 7) Hint: foo4 [User] diff --git a/tests/pragmas/foobar.nim b/tests/pragmas/foobar.nim new file mode 100644 index 000000000..46032e187 --- /dev/null +++ b/tests/pragmas/foobar.nim @@ -0,0 +1,3 @@ +import macros +macro async*(body: untyped): untyped = + return newStmtList() diff --git a/tests/pragmas/t4384.nim b/tests/pragmas/t4384.nim new file mode 100644 index 000000000..e6b193f79 --- /dev/null +++ b/tests/pragmas/t4384.nim @@ -0,0 +1,3 @@ +macro testMacro(body: untyped): untyped = discard +macro testMacro(s: string, body: untyped): untyped = discard +proc foo() {.testMacro: "foo".} = discard diff --git a/tests/pragmas/t6448.nim b/tests/pragmas/t6448.nim new file mode 100644 index 000000000..61e4a35d9 --- /dev/null +++ b/tests/pragmas/t6448.nim @@ -0,0 +1,16 @@ +discard """ + line: 9 + errormsg: '''ambiguous call; both foobar.async''' +""" + +import foobar +import asyncdispatch, macros + +proc bar() {.async.} = + echo 42 + +proc foo() {.async.} = + await bar() + +asyncCheck foo() +runForever() |