Fix overload resolution for pragmas evaluation (#8902)
Fixes #6448 Fixes #4384
This commit is contained in:
parent
ee14ace5d3
commit
33458894da
8 changed files with 66 additions and 33 deletions
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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
3
tests/pragmas/foobar.nim
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import macros
|
||||||
|
macro async*(body: untyped): untyped =
|
||||||
|
return newStmtList()
|
||||||
3
tests/pragmas/t4384.nim
Normal file
3
tests/pragmas/t4384.nim
Normal 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
16
tests/pragmas/t6448.nim
Normal 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()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue