summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorLemonBoy <LemonBoy@users.noreply.github.com>2018-10-09 19:58:23 +0200
committerAndreas Rumpf <rumpf_a@web.de>2018-10-09 19:58:23 +0200
commit33458894da13da7134604639c129273bfb0cb1c1 (patch)
tree76ed110300cb8cb104f4c51730650ce65a83ad79
parentee14ace5d3f3ede9d47d60bd415e46a3fb121fb2 (diff)
downloadNim-33458894da13da7134604639c129273bfb0cb1c1.tar.gz
Fix overload resolution for pragmas evaluation (#8902)
Fixes #6448
Fixes #4384
-rw-r--r--compiler/pragmas.nim4
-rw-r--r--compiler/semcall.nim13
-rw-r--r--compiler/semdata.nim5
-rw-r--r--compiler/semstmts.nim53
-rw-r--r--tests/macros/tmsginfo.nim2
-rw-r--r--tests/pragmas/foobar.nim3
-rw-r--r--tests/pragmas/t4384.nim3
-rw-r--r--tests/pragmas/t6448.nim16
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()