diff options
Diffstat (limited to 'compiler/semcall.nim')
-rw-r--r-- | compiler/semcall.nim | 298 |
1 files changed, 212 insertions, 86 deletions
diff --git a/compiler/semcall.nim b/compiler/semcall.nim index f6beb1aeb..13f2273a9 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -58,10 +58,9 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode, ]# for paramSym in searchInScopesAllCandidatesFilterBy(c, symx.name, {skConst}): let paramTyp = paramSym.typ - if paramTyp.n.sym.kind in filter: + if paramTyp.n.kind == nkSym and paramTyp.n.sym.kind in filter: result.add((paramTyp.n.sym, o.lastOverloadScope)) - symx = nextOverloadIter(o, c, headSymbol) if result.len > 0: best = initCandidate(c, result[0].s, initialBinding, @@ -81,7 +80,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, # `matches` may find new symbols, so keep track of count var symCount = c.currentScope.symbols.counter - var o: TOverloadIter + var o: TOverloadIter = default(TOverloadIter) # https://github.com/nim-lang/Nim/issues/21272 # prevent mutation during iteration by storing them in a seq # luckily `initCandidateSymbols` does just that @@ -247,48 +246,151 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): candidates.add(getProcHeader(c.config, err.sym, prefer)) candidates.addDeclaredLocMaybe(c.config, err.sym) candidates.add("\n") - let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil + const genericParamMismatches = {kGenericParamTypeMismatch, kExtraGenericParam, kMissingGenericParam} + let isGenericMismatch = err.firstMismatch.kind in genericParamMismatches + var argList = n + if isGenericMismatch and n[0].kind == nkBracketExpr: + argList = n[0] + let nArg = + if err.firstMismatch.arg < argList.len: + argList[err.firstMismatch.arg] + else: + nil let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: "" - if n.len > 1 and verboseTypeMismatch in c.config.legacyFeatures: - candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg) - # candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging - case err.firstMismatch.kind - of kUnknownNamedParam: - if nArg == nil: - candidates.add("\n unknown named parameter") - else: - candidates.add("\n unknown named parameter: " & $nArg[0]) - of kAlreadyGiven: candidates.add("\n named param already provided: " & $nArg[0]) - of kPositionalAlreadyGiven: candidates.add("\n positional param was already given as named param") - of kExtraArg: candidates.add("\n extra argument given") - of kMissingParam: candidates.add("\n missing parameter: " & nameParam) - of kTypeMismatch, kVarNeeded: - doAssert nArg != nil - let wanted = err.firstMismatch.formal.typ - doAssert err.firstMismatch.formal != nil - candidates.add("\n required type for " & nameParam & ": ") - candidates.addTypeDeclVerboseMaybe(c.config, wanted) - candidates.add "\n but expression '" - if err.firstMismatch.kind == kVarNeeded: + if n.len > 1: + if verboseTypeMismatch notin c.config.legacyFeatures: + case err.firstMismatch.kind + of kUnknownNamedParam: + if nArg == nil: + candidates.add(" unknown named parameter") + else: + candidates.add(" unknown named parameter: " & $nArg[0]) + candidates.add "\n" + of kAlreadyGiven: + candidates.add(" named param already provided: " & $nArg[0]) + candidates.add "\n" + of kPositionalAlreadyGiven: + candidates.add(" positional param was already given as named param") + candidates.add "\n" + of kExtraArg: + candidates.add(" extra argument given") + candidates.add "\n" + of kMissingParam: + candidates.add(" missing parameter: " & nameParam) + candidates.add "\n" + of kExtraGenericParam: + candidates.add(" extra generic param given") + candidates.add "\n" + of kMissingGenericParam: + candidates.add(" missing generic parameter: " & nameParam) + candidates.add "\n" + of kVarNeeded: + doAssert nArg != nil + doAssert err.firstMismatch.formal != nil + candidates.add " expression '" candidates.add renderNotLValue(nArg) candidates.add "' is immutable, not 'var'" - else: - candidates.add renderTree(nArg) - candidates.add "' is of type: " + candidates.add "\n" + of kTypeMismatch: + doAssert nArg != nil + if nArg.kind in nkSymChoices: + candidates.add ambiguousIdentifierMsg(nArg, indent = 2) + let wanted = err.firstMismatch.formal.typ + doAssert err.firstMismatch.formal != nil + doAssert wanted != nil let got = nArg.typ - candidates.addTypeDeclVerboseMaybe(c.config, got) + if got != nil and got.kind == tyProc and wanted.kind == tyProc: + # These are proc mismatches so, + # add the extra explict detail of the mismatch + candidates.add " expression '" + candidates.add renderTree(nArg) + candidates.add "' is of type: " + candidates.addTypeDeclVerboseMaybe(c.config, got) + candidates.addPragmaAndCallConvMismatch(wanted, got, c.config) + effectProblem(wanted, got, candidates, c) + candidates.add "\n" + of kGenericParamTypeMismatch: + let pos = err.firstMismatch.arg + doAssert n[0].kind == nkBracketExpr and pos < n[0].len + let arg = n[0][pos] + doAssert arg != nil + var wanted = err.firstMismatch.formal.typ + if wanted.kind == tyGenericParam and wanted.genericParamHasConstraints: + wanted = wanted.genericConstraint + let got = arg.typ.skipTypes({tyTypeDesc}) + doAssert err.firstMismatch.formal != nil doAssert wanted != nil + doAssert got != nil + candidates.add " generic parameter mismatch, expected " + candidates.addTypeDeclVerboseMaybe(c.config, wanted) + candidates.add " but got '" + candidates.add renderTree(arg) + candidates.add "' of type: " + candidates.addTypeDeclVerboseMaybe(c.config, got) + if nArg.kind in nkSymChoices: + candidates.add "\n" + candidates.add ambiguousIdentifierMsg(nArg, indent = 2) + if got != nil and got.kind == tyProc and wanted.kind == tyProc: + # These are proc mismatches so, + # add the extra explict detail of the mismatch + candidates.addPragmaAndCallConvMismatch(wanted, got, c.config) if got != nil: - if got.kind == tyProc and wanted.kind == tyProc: - # These are proc mismatches so, - # add the extra explict detail of the mismatch - candidates.addPragmaAndCallConvMismatch(wanted, got, c.config) effectProblem(wanted, got, candidates, c) - - of kUnknown: discard "do not break 'nim check'" - candidates.add "\n" - if err.firstMismatch.arg == 1 and nArg.kind == nkTupleConstr and - n.kind == nkCommand: + candidates.add "\n" + of kUnknown: discard "do not break 'nim check'" + else: + candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg) + if err.firstMismatch.kind in genericParamMismatches: + candidates.add(" in generic parameters") + # candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging + case err.firstMismatch.kind + of kUnknownNamedParam: + if nArg == nil: + candidates.add("\n unknown named parameter") + else: + candidates.add("\n unknown named parameter: " & $nArg[0]) + of kAlreadyGiven: candidates.add("\n named param already provided: " & $nArg[0]) + of kPositionalAlreadyGiven: candidates.add("\n positional param was already given as named param") + of kExtraArg: candidates.add("\n extra argument given") + of kMissingParam: candidates.add("\n missing parameter: " & nameParam) + of kExtraGenericParam: + candidates.add("\n extra generic param given") + of kMissingGenericParam: + candidates.add("\n missing generic parameter: " & nameParam) + of kTypeMismatch, kGenericParamTypeMismatch, kVarNeeded: + doAssert nArg != nil + var wanted = err.firstMismatch.formal.typ + if isGenericMismatch and wanted.kind == tyGenericParam and + wanted.genericParamHasConstraints: + wanted = wanted.genericConstraint + doAssert err.firstMismatch.formal != nil + candidates.add("\n required type for " & nameParam & ": ") + candidates.addTypeDeclVerboseMaybe(c.config, wanted) + candidates.add "\n but expression '" + if err.firstMismatch.kind == kVarNeeded: + candidates.add renderNotLValue(nArg) + candidates.add "' is immutable, not 'var'" + else: + candidates.add renderTree(nArg) + candidates.add "' is of type: " + var got = nArg.typ + if isGenericMismatch: got = got.skipTypes({tyTypeDesc}) + candidates.addTypeDeclVerboseMaybe(c.config, got) + if nArg.kind in nkSymChoices: + candidates.add "\n" + candidates.add ambiguousIdentifierMsg(nArg, indent = 2) + doAssert wanted != nil + if got != nil: + if got.kind == tyProc and wanted.kind == tyProc: + # These are proc mismatches so, + # add the extra explict detail of the mismatch + candidates.addPragmaAndCallConvMismatch(wanted, got, c.config) + effectProblem(wanted, got, candidates, c) + + of kUnknown: discard "do not break 'nim check'" + candidates.add "\n" + if err.firstMismatch.arg == 1 and nArg != nil and + nArg.kind == nkTupleConstr and n.kind == nkCommand: maybeWrongSpace = true for diag in err.diagnostics: candidates.add(diag & "\n") @@ -365,23 +467,6 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = result.add("\n" & errExpectedPosition & "\n" & candidates) localError(c.config, n.info, result) -proc bracketNotFoundError(c: PContext; n: PNode) = - var errors: CandidateErrors = @[] - var o: TOverloadIter - let headSymbol = n[0] - var symx = initOverloadIter(o, c, headSymbol) - while symx != nil: - if symx.kind in routineKinds: - errors.add(CandidateError(sym: symx, - firstMismatch: MismatchInfo(), - diagnostics: @[], - enabled: false)) - symx = nextOverloadIter(o, c, headSymbol) - if errors.len == 0: - localError(c.config, n.info, "could not resolve: " & $n) - else: - notFoundError(c, n, errors) - proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string = result = "" if c.compilesContextId > 0: @@ -390,7 +475,7 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string = # also avoid slowdowns in evaluating `compiles(expr)`. discard else: - var o: TOverloadIter + var o: TOverloadIter = default(TOverloadIter) var sym = initOverloadIter(o, c, f) while sym != nil: result &= "\n found $1" % [getSymRepr(c.config, sym)] @@ -435,7 +520,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, filter, result, alt, errors, efExplain in flags, errorsEnabled, flags) - var dummyErrors: CandidateErrors + var dummyErrors: CandidateErrors = @[] template pickSpecialOp(headSymbol) = pickBestCandidate(c, headSymbol, n, orig, initialBinding, filter, result, alt, dummyErrors, efExplain in flags, @@ -474,7 +559,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode, if overloadsState == csEmpty and result.state == csEmpty: if efNoUndeclared notin flags: # for tests/pragmas/tcustom_pragma.nim result.state = csNoMatch - if efNoDiagnostics in flags: + if c.inGenericContext > 0 and nfExprCall in n.flags: + # untyped expression calls end up here, see #24099 return # xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident) localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f)) @@ -510,6 +596,39 @@ proc resolveOverloads(c: PContext, n, orig: PNode, getProcHeader(c.config, alt.calleeSym), args]) +proc bracketNotFoundError(c: PContext; n: PNode; flags: TExprFlags) = + var errors: CandidateErrors = @[] + let headSymbol = n[0] + block: + # we build a closed symchoice of all `[]` overloads for their errors, + # except add a custom error for the magics which always match + var choice = newNodeIT(nkClosedSymChoice, headSymbol.info, newTypeS(tyNone, c)) + var o: TOverloadIter = default(TOverloadIter) + var symx = initOverloadIter(o, c, headSymbol) + while symx != nil: + if symx.kind in routineKinds: + if symx.magic in {mArrGet, mArrPut}: + errors.add(CandidateError(sym: symx, + firstMismatch: MismatchInfo(), + diagnostics: @[], + enabled: false)) + else: + choice.add newSymNode(symx, headSymbol.info) + symx = nextOverloadIter(o, c, headSymbol) + n[0] = choice + # copied from semOverloadedCallAnalyzeEffects, might be overkill: + const baseFilter = {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate} + let filter = + if flags*{efInTypeof, efWantIterator, efWantIterable} != {}: + baseFilter + {skIterator} + else: baseFilter + # this will add the errors: + var r = resolveOverloads(c, n, n, filter, flags, errors, true) + if errors.len == 0: + localError(c.config, n.info, "could not resolve: " & $n) + else: + notFoundError(c, n, errors) + proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) = let a = if a.kind == nkHiddenDeref: a[0] else: a if a.kind == nkHiddenCallConv and a[0].kind == nkSym: @@ -551,7 +670,7 @@ proc inferWithMetatype(c: PContext, formal: PType, result = copyTree(arg) result.typ = formal -proc updateDefaultParams(call: PNode) = +proc updateDefaultParams(c: PContext, call: PNode) = # In generic procs, the default parameter may be unique for each # instantiation (see tlateboundgenericparams). # After a call is resolved, we need to re-assign any default value @@ -561,8 +680,18 @@ proc updateDefaultParams(call: PNode) = let calleeParams = call[0].sym.typ.n for i in 1..<call.len: if nfDefaultParam in call[i].flags: - let def = calleeParams[i].sym.ast + let formal = calleeParams[i].sym + let def = formal.ast if nfDefaultRefsParam in def.flags: call.flags.incl nfDefaultRefsParam + # mirrored with sigmatch: + if def.kind == nkEmpty: + # The default param value is set to empty in `instantiateProcType` + # when the type of the default expression doesn't match the type + # of the instantiated proc param: + pushInfoContext(c.config, call.info, call[0].sym.detailedInfo) + typeMismatch(c.config, def.info, formal.typ, def.typ, formal.ast) + popInfoContext(c.config) + def.typ = errorType(c) call[i] = def proc getCallLineInfo(n: PNode): TLineInfo = @@ -581,7 +710,7 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) = ## Helper proc to inherit bound generic parameters from expectedType into x. ## Does nothing if 'inferGenericTypes' isn't in c.features. if inferGenericTypes notin c.features: return - if expectedType == nil or x.callee[0] == nil: return # required for inference + if expectedType == nil or x.callee.returnType == nil: return # required for inference var flatUnbound: seq[PType] = @[] @@ -593,14 +722,14 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) = ## skips types and puts the skipped version on stack # It might make sense to skip here one by one. It's not part of the main # type reduction because the right side normally won't be skipped - const toSkip = { tyVar, tyLent, tyStatic, tyCompositeTypeClass, tySink } + const toSkip = {tyVar, tyLent, tyStatic, tyCompositeTypeClass, tySink} let x = a.skipTypes(toSkip) y = if a.kind notin toSkip: b else: b.skipTypes(toSkip) typeStack.add((x, y)) - stackPut(x.callee[0], expectedType) + stackPut(x.callee.returnType, expectedType) while typeStack.len() > 0: let (t, u) = typeStack.pop() @@ -608,17 +737,18 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) = continue case t.kind of ConcreteTypes, tyGenericInvocation, tyUncheckedArray: + # XXX This logic makes no sense for `tyUncheckedArray` # nested, add all the types to stack let startIdx = if u.kind in ConcreteTypes: 0 else: 1 - endIdx = min(u.len() - startIdx, t.len()) + endIdx = min(u.kidsLen() - startIdx, t.kidsLen()) for i in startIdx ..< endIdx: # early exit with current impl if t[i] == nil or u[i] == nil: return stackPut(t[i], u[i]) of tyGenericParam: - let prebound = x.bindings.idTableGet(t).PType + let prebound = x.bindings.idTableGet(t) if prebound != nil: continue # Skip param, already bound @@ -641,12 +771,12 @@ proc semResolvedCall(c: PContext, x: var TCandidate, markUsed(c, info, finalCallee) onUse(info, finalCallee) assert finalCallee.ast != nil - if x.hasFauxMatch: + if x.matchedErrorType: result = x.call result[0] = newSymNode(finalCallee, getCallLineInfo(result[0])) - if containsGenericType(result.typ) or x.fauxMatch == tyUnknown: - result.typ = newTypeS(x.fauxMatch, c) - if result.typ.kind == tyError: incl result.typ.flags, tfCheckedForDestructor + if containsGenericType(result.typ): + result.typ = newTypeS(tyError, c) + incl result.typ.flags, tfCheckedForDestructor return let gp = finalCallee.ast[genericParamsPos] if gp.isGenericParams: @@ -680,8 +810,9 @@ proc semResolvedCall(c: PContext, x: var TCandidate, result = x.call instGenericConvertersSons(c, result, x) result[0] = newSymNode(finalCallee, getCallLineInfo(result[0])) - result.typ = finalCallee.typ.returnType - updateDefaultParams(result) + if finalCallee.magic notin {mArrGet, mArrPut}: + result.typ = finalCallee.typ.returnType + updateDefaultParams(c, result) proc canDeref(n: PNode): bool {.inline.} = result = n.len >= 2 and (let t = n[1].typ; @@ -706,7 +837,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, candidates) result = semResolvedCall(c, r, n, flags, expectedType) else: - if efDetermineType in flags and c.inGenericContext > 0 and c.matchedConcept == nil: + if c.inGenericContext > 0 and c.matchedConcept == nil: result = semGenericStmt(c, n) result.typ = makeTypeFromExpr(c, result.copyTree) elif efExplain notin flags: @@ -724,21 +855,16 @@ proc explicitGenericInstError(c: PContext; n: PNode): PNode = result = n proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = + if s.kind in {skTemplate, skMacro}: + internalError c.config, n.info, "cannot get explicitly instantiated symbol of " & + (if s.kind == skTemplate: "template" else: "macro") # binding has to stay 'nil' for this to work! var m = newCandidate(c, s, nil) - - for i in 1..<n.len: - let formal = s.ast[genericParamsPos][i-1].typ - var arg = n[i].typ - # try transforming the argument into a static one before feeding it into - # typeRel - if formal.kind == tyStatic and arg.kind != tyStatic: - let evaluated = c.semTryConstExpr(c, n[i], n[i].typ) - if evaluated != nil: - arg = newTypeS(tyStatic, c, son = evaluated.typ) - arg.n = evaluated - let tm = typeRel(m, formal, arg) - if tm in {isNone, isConvertible}: return nil + matchGenericParams(m, n, s) + if m.state != csMatch: + # state is csMatch only if *all* generic params were matched, + # including implicit parameters + return nil var newInst = generateInstance(c, s, m.bindings, n.info) newInst.typ.flags.excl tfUnresolved let info = getCallLineInfo(n) @@ -749,7 +875,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = proc setGenericParams(c: PContext, n, expectedParams: PNode) = ## sems generic params in subscript expression for i in 1..<n.len: - let + let constraint = if expectedParams != nil and i <= expectedParams.len: expectedParams[i - 1].typ |