Fix overload resolution for pragmas evaluation (#8902)

Fixes #6448
Fixes #4384
This commit is contained in:
LemonBoy 2018-10-09 19:58:23 +02:00 • committed by Andreas Rumpf
commit 33458894da
8 changed files with 66 additions and 33 deletions

View file

@ -723,13 +723,13 @@ proc semCustomPragma(c: PContext, n: PNode): PNode =
elif n.kind == nkExprColonExpr: elif n.kind == nkExprColonExpr:
# pragma: arg -> pragma(arg) # pragma: arg -> pragma(arg)
result = newTree(nkCall, n[0], n[1]) result = newTree(nkCall, n[0], n[1])
elif n.kind in nkPragmaCallKinds + {nkIdent}: elif n.kind in nkPragmaCallKinds:
result = n result = n
else: else:
invalidPragma(c, n) invalidPragma(c, n)
return 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: if r.isNil or sfCustomPragma notin r[0].sym.flags:
invalidPragma(c, n) invalidPragma(c, n)
else: else:

View file

@ -330,6 +330,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
pickBest(callOp) pickBest(callOp)
if overloadsState == csEmpty and result.state == csEmpty: if overloadsState == csEmpty and result.state == csEmpty:
if efNoUndeclared notin flags:
if nfDotField in n.flags and nfExplicitCall notin n.flags: if nfDotField in n.flags and nfExplicitCall notin n.flags:
localError(c.config, n.info, errUndeclaredField % considerQuotedIdent(c, f, n).s) localError(c.config, n.info, errUndeclaredField % considerQuotedIdent(c, f, n).s)
else: else:
@ -500,14 +501,14 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
# repeat the overload resolution, # repeat the overload resolution,
# this time enabling all the diagnostic output (this should fail again) # this time enabling all the diagnostic output (this should fail again)
discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain}) discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
else: elif efNoUndeclared notin flags:
notFoundError(c, n, errors) notFoundError(c, n, errors)
else: else:
if efExplain notin flags: if efExplain notin flags:
# repeat the overload resolution, # repeat the overload resolution,
# this time enabling all the diagnostic output (this should fail again) # this time enabling all the diagnostic output (this should fail again)
discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain}) discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
else: elif efNoUndeclared notin flags:
notFoundError(c, n, errors) notFoundError(c, n, errors)
proc explicitGenericInstError(c: PContext; n: PNode): PNode = proc explicitGenericInstError(c: PContext; n: PNode): PNode =

View file

@ -65,7 +65,10 @@ type
# to the user. # to the user.
efWantStmt, efAllowStmt, efDetermineType, efExplain, efWantStmt, efAllowStmt, efDetermineType, efExplain,
efAllowDestructor, efWantValue, efOperand, efNoSemCheck, 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] TExprFlags* = set[TExprFlag]

View file

@ -1218,13 +1218,6 @@ proc copyExcept(n: PNode, i: int): PNode =
for j in 0..<n.len: for j in 0..<n.len:
if j != i: result.add(n.sons[j]) 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; proc semProcAnnotation(c: PContext, prc: PNode;
validPragmas: TSpecialWords): PNode = validPragmas: TSpecialWords): PNode =
var n = prc.sons[pragmasPos] var n = prc.sons[pragmasPos]
@ -1232,39 +1225,53 @@ proc semProcAnnotation(c: PContext, prc: PNode;
for i in countup(0, n.len-1): for i in countup(0, n.len-1):
var it = n.sons[i] var it = n.sons[i]
var key = if it.kind in nkPragmaCallKinds and it.len >= 1: it.sons[0] else: it 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 whichPragma(it) != wInvalid:
if key.kind == nkIdent and key.ident.id == ord(wDelegator): # Not a custom pragma
if considerQuotedIdent(c, prc.sons[namePos]).s == "()": continue
prc.sons[namePos] = newIdentNode(c.cache.idDelegator, prc.info) elif strTableGet(c.userPragmas, considerQuotedIdent(c, key)) != nil:
prc.sons[pragmasPos] = copyExcept(n, i) # User-defined pragma
else:
localError(c.config, prc.info, "only a call operator can be a delegator")
continue 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 # we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and
# let the semantic checker deal with it: # let the semantic checker deal with it:
var x = newNodeI(nkCall, n.info) var x = newNodeI(nkCall, key.info)
x.add(newSymNode(m)) x.add(key)
prc.sons[pragmasPos] = copyExcept(n, i)
if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0:
prc.sons[pragmasPos] = c.graph.emptyNode
if it.kind in nkPragmaCallKinds and it.len > 1: if it.kind in nkPragmaCallKinds and it.len > 1:
# pass pragma arguments to the macro too: # pass pragma arguments to the macro too:
for i in 1..<it.len: for i in 1..<it.len:
x.add(it.sons[i]) 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) x.add(prc)
# recursion assures that this works for multiple macro annotations too: # 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. # since a proc annotation can set pragmas, we process these here again.
# This is required for SqueakNim-like export pragmas. # This is required for SqueakNim-like export pragmas.
if result.kind in procDefs and result[namePos].kind == nkSym and if result.kind in procDefs and result[namePos].kind == nkSym and
result[pragmasPos].kind != nkEmpty: result[pragmasPos].kind != nkEmpty:
pragma(c, result[namePos].sym, result[pragmasPos], validPragmas) pragma(c, result[namePos].sym, result[pragmasPos], validPragmas)
return return
proc setGenericParamsMisc(c: PContext; n: PNode): PNode = proc setGenericParamsMisc(c: PContext; n: PNode): PNode =

View file

@ -1,6 +1,6 @@
discard """ discard """
nimout: '''tmsginfo.nim(21, 1) Warning: foo1 [User] 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(15, 10) Warning: foo2 [User]
tmsginfo.nim(23, 1) Hint: foo3 [User] tmsginfo.nim(23, 1) Hint: foo3 [User]
tmsginfo.nim(19, 7) Hint: foo4 [User] tmsginfo.nim(19, 7) Hint: foo4 [User]

3
tests/pragmas/foobar.nim Normal file
View file

@ -0,0 +1,3 @@
import macros
macro async*(body: untyped): untyped =
return newStmtList()

3
tests/pragmas/t4384.nim Normal file
View file

@ -0,0 +1,3 @@
macro testMacro(body: untyped): untyped = discard
macro testMacro(s: string, body: untyped): untyped = discard
proc foo() {.testMacro: "foo".} = discard

16
tests/pragmas/t6448.nim Normal file
View file

@ -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()