Skip to content

Commit

Permalink
consider instantiation errors in overload resolution as type mismatch
Browse files Browse the repository at this point in the history
  • Loading branch information
metagn committed Sep 12, 2024
1 parent 793cee4 commit c236086
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 26 deletions.
6 changes: 5 additions & 1 deletion compiler/semcall.nim
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
errors.add(CandidateError(
sym: sym,
firstMismatch: z.firstMismatch,
diagnostics: z.diagnostics))
diagnostics: z.diagnostics,
instError: z.instError))
else:
# this branch feels like a ticking timebomb
# one of two bad things could happen
Expand Down Expand Up @@ -388,6 +389,9 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):

of kUnknown: discard "do not break 'nim check'"
candidates.add "\n"
if err.instError != nil:
candidates.add(" instantiation error at " & (c.config $ err.instError.info) & ":\n")
candidates.add(" " & $err.instError & "\n")
if err.firstMismatch.arg == 1 and nArg != nil and
nArg.kind == nkTupleConstr and n.kind == nkCommand:
maybeWrongSpace = true
Expand Down
10 changes: 8 additions & 2 deletions compiler/semdata.nim
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ type
imported*: IntSet # of PIdent.id
of importExcept:
exceptSet*: IntSet # of PIdent.id

InstantiationError* = ref object
info*: TLineInfo
errorType*: PType
contextType*: PType

PContext* = ref TContext
TContext* = object of TPassContext # a context represents the module
Expand Down Expand Up @@ -136,7 +141,8 @@ type
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
filter: TSymKinds, flags: TExprFlags, expectedType: PType = nil): PNode {.nimcall.}
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
semInferredLambda*: proc(c: PContext, pt: Table[ItemId, PType], n: PNode): PNode
semInferredLambda*: proc(c: PContext, pt: Table[ItemId, PType], n: PNode,
instError: var InstantiationError): PNode
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: Table[ItemId, PType],
info: TLineInfo): PSym
instantiateOnlyProcType*: proc (c: PContext, pt: TypeMapping,
Expand Down Expand Up @@ -487,7 +493,7 @@ proc makeNotType*(c: PContext, t1: PType): PType =
result.flags.incl tfHasMeta

proc nMinusOne(c: PContext; n: PNode): PNode =
result = newTreeI(nkCall, n.info, newSymNode(getSysMagic(c.graph, n.info, "pred", mPred)), n)
result = newTreeI(nkCall, n.info, newSymNode(getSysMagic(c.graph, n.info, "pred", mPred), n.info), n)

# Remember to fix the procs below this one when you make changes!
proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
Expand Down
5 changes: 3 additions & 2 deletions compiler/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1971,15 +1971,16 @@ proc semProcAnnotation(c: PContext, prc: PNode;

return result

proc semInferredLambda(c: PContext, pt: TypeMapping, n: PNode): PNode =
proc semInferredLambda(c: PContext, pt: TypeMapping, n: PNode, instError: var InstantiationError): PNode =
## used for resolving 'auto' in lambdas based on their callsite
var n = n
let original = n[namePos].sym
let s = original #copySym(original, false)
#incl(s.flags, sfFromGeneric)
#s.owner = original

n = replaceTypesInBody(c, pt, n, original)
n = replaceTypesInBody(c, pt, n, original, instError)
if instError != nil: return
result = n
s.ast = result
n[namePos].sym = s
Expand Down
45 changes: 32 additions & 13 deletions compiler/semtypinst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,30 @@ type
isReturnType*: bool
owner*: PSym # where this instantiation comes from
recursionLimit: int
instError*: InstantiationError
softError*: bool

proc `$`*(err: InstantiationError): string =
if err == nil or err.errorType == nil:
return "unknown instantiation error"
let contextExists = err.contextType != nil
if contextExists and err.contextType.kind == tyStatic:
return "cannot infer the value of the static param '" & err.contextType.sym.name.s & "'"
result = "cannot instantiate '" & typeToString(err.errorType, preferDesc) & "'"
if contextExists:
result.add(" inside of type definition: '")
result.add(err.contextType.owner.name.s)
result.add("'")
if err.errorType.kind == tyGenericBody:
result.add("; Maybe generic arguments are missing?")

proc doInstantiationError*(cl: var TReplTypeVars, t: PType, info: TLineInfo = cl.info, owner: PType = nil) =
let err = InstantiationError(info: info, errorType: t, contextType: owner)
if cl.softError:
if cl.instError == nil:
cl.instError = err
else:
localError(cl.c.config, info, $err)

proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym
Expand Down Expand Up @@ -389,7 +413,7 @@ proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType =
result = cl.typeMap.lookup(t)
if result == nil:
if cl.allowMetaTypes or tfRetType in t.flags: return
localError(cl.c.config, t.sym.info, "cannot instantiate: '" & typeToString(t) & "'")
doInstantiationError(cl, t, t.sym.info)
result = errorType(cl.c)
# In order to prevent endless recursions, we must remember
# this bad lookup and replace it with errorType everywhere.
Expand Down Expand Up @@ -636,12 +660,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =

of tyGenericBody:
if cl.allowMetaTypes: return
localError(
cl.c.config,
cl.info,
"cannot instantiate: '" &
typeToString(t, preferDesc) &
"'; Maybe generic arguments are missing?")
doInstantiationError(cl, t)
result = errorType(cl.c)
#result = replaceTypeVarsT(cl, lastSon(t))

Expand Down Expand Up @@ -737,11 +756,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
for i, resulti in result.ikids:
if resulti != nil:
if resulti.kind == tyGenericBody and not cl.allowMetaTypes:
localError(cl.c.config, if t.sym != nil: t.sym.info else: cl.info,
"cannot instantiate '" &
typeToString(result[i], preferDesc) &
"' inside of type definition: '" &
t.owner.name.s & "'; Maybe generic arguments are missing?")
let info = if t.sym != nil: t.sym.info else: cl.info
doInstantiationError(cl, result[i], info, owner = t)
var r = replaceTypeVarsT(cl, resulti)
if result.kind == tyObject:
# carefully coded to not skip the precious tyGenericInst:
Expand Down Expand Up @@ -792,14 +808,17 @@ proc initTypeVars*(p: PContext, typeMap: LayeredIdTable, info: TLineInfo;
info: info, c: p, owner: owner)

proc replaceTypesInBody*(p: PContext, pt: TypeMapping, n: PNode;
owner: PSym, allowMetaTypes = false,
owner: PSym, instError: var InstantiationError,
allowMetaTypes = false,
fromStaticExpr = false, expectedType: PType = nil): PNode =
var typeMap = initLayeredTypeMap(pt)
var cl = initTypeVars(p, typeMap, n.info, owner)
cl.allowMetaTypes = allowMetaTypes
cl.softError = true
pushInfoContext(p.config, n.info)
result = replaceTypeVarsN(cl, n, expectedType = expectedType)
popInfoContext(p.config)
instError = cl.instError

proc prepareTypesInBody*(p: PContext, pt: TypeMapping, n: PNode;
owner: PSym = nil): PNode =
Expand Down
21 changes: 13 additions & 8 deletions compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type
sym*: PSym
firstMismatch*: MismatchInfo
diagnostics*: seq[string]
instError*: InstantiationError
enabled*: bool

CandidateErrors* = seq[CandidateError]
Expand Down Expand Up @@ -84,6 +85,7 @@ type
# to prefer closest father object type
inheritancePenalty: int
firstMismatch*: MismatchInfo # mismatch info for better error messages
instError*: InstantiationError
diagnosticsEnabled*: bool

TTypeRelFlag* = enum
Expand Down Expand Up @@ -1006,9 +1008,10 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
# Here, N-1 will be initially nkStaticExpr that can be evaluated only after
# N is bound to a concrete value during the matching of the first param.
# This proc is used to evaluate such static expressions.
let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil,
var instError: InstantiationError = nil
let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil, instError,
allowMetaTypes = allowUnresolved)
if not allowCalls and instantiated.kind in nkCallKinds:
if instError != nil or (not allowCalls and instantiated.kind in nkCallKinds):
return nil
result = c.c.semExpr(c.c, instantiated)

Expand Down Expand Up @@ -1093,11 +1096,10 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =

return false

proc failureToInferStaticParam(conf: ConfigRef; n: PNode) =
proc failureToInferStaticParam(c: var TCandidate; n: PNode) =
let staticParam = n.findUnresolvedStatic
let name = if staticParam != nil: staticParam.sym.name.s
else: "unknown"
localError(conf, n.info, "cannot infer the value of the static param '" & name & "'")
let t = if staticParam != nil: staticParam.sym.typ else: nil
c.instError = InstantiationError(info: n.info, errorType: t, contextType: t)

proc inferStaticsInRange(c: var TCandidate,
inferred, concrete: PType): TTypeRelation =
Expand All @@ -1111,7 +1113,8 @@ proc inferStaticsInRange(c: var TCandidate,
if inferStaticParam(c, exp, toInt64(rhs)):
return isGeneric
else:
failureToInferStaticParam(c.c.config, exp)
failureToInferStaticParam(c, exp)
return isNone

result = isNone
if lowerBound.kind == nkIntLit:
Expand Down Expand Up @@ -2359,7 +2362,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
lastBindingCount = m.bindings.len
inc(instantiationCounter)
if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds:
result = c.semInferredLambda(c, m.bindings, arg)
result = c.semInferredLambda(c, m.bindings, arg, m.instError)
if m.instError != nil:
return nil
elif arg.kind != nkSym:
return nil
elif arg.sym.kind in {skMacro, skTemplate}:
Expand Down
28 changes: 28 additions & 0 deletions tests/errmsgs/toverloadinstantiation1.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
discard """
action: "reject"
matrix: "--hints:off -d:testsConciseTypeMismatch"
nimoutFull: true
nimout: '''
toverloadinstantiation1.nim(28, 6) Error: type mismatch
Expression: foo([1, 2, 3, 4])
[1] [1, 2, 3, 4]: array[0..3, int]
Expected one of (first mismatch at [position]):
[1] proc foo[I: static int](x: array[double(I), int])
instantiation error at toverloadinstantiation1.nim(27, 42):
cannot infer the value of the static param 'I'
'''
"""

proc double(x: int): int = x * 2

block: # this not erroring is checked by nimoutFull
proc foo[I: static int](x: array[I, int]) = discard
proc foo[I: static int](x: array[double(I), int]) = discard
foo([1, 2, 3, 4])
foo[2]([1, 2, 3, 4]) # this picks double overload

block:
proc foo[I: static int](x: array[double(I), int]) = discard
foo([1, 2, 3, 4])
29 changes: 29 additions & 0 deletions tests/errmsgs/toverloadinstantiation1_legacy.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
discard """
action: "reject"
matrix: "--hints:off"
nimoutFull: true
nimout: '''
toverloadinstantiation1_legacy.nim(29, 6) Error: type mismatch: got <array[0..3, int]>
but expected one of:
proc foo[I: static int](x: array[double(I), int])
first type mismatch at position: 1
required type for x: array[0..static(pred(double(I))), int]
but expression '[1, 2, 3, 4]' is of type: array[0..3, int]
instantiation error at toverloadinstantiation1_legacy.nim(28, 42):
cannot infer the value of the static param 'I'
expression: foo([1, 2, 3, 4])
'''
"""

proc double(x: int): int = x * 2

block: # this not erroring is checked by nimoutFull
proc foo[I: static int](x: array[I, int]) = discard
proc foo[I: static int](x: array[double(I), int]) = discard
foo([1, 2, 3, 4])
foo[2]([1, 2, 3, 4]) # this picks double overload

block:
proc foo[I: static int](x: array[double(I), int]) = discard
foo([1, 2, 3, 4])

0 comments on commit c236086

Please sign in to comment.