diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 29d19875d4157..986b847fd2b69 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -160,9 +160,13 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms) if z.state == csMatch: - # little hack so that iterators are preferred over everything else: + # Iterator preference is heuristic in iterator-admitting contexts. + # The dedicated iterable path uses `iteratorPreference`, other + # context use exact-match bump if sym.kind == skIterator: - if not (efWantIterator notin flags and efWantIterable in flags): + if efPreferIteratorForIterable in flags: + inc(z.iteratorPreference) + elif not (efWantIterator notin flags and efWantIterable in flags): inc(z.exactMatches, 200) else: dec(z.exactMatches, 200) @@ -671,7 +675,7 @@ proc bracketNotFoundError(c: PContext; n: PNode; flags: TExprFlags) = # copied from semOverloadedCallAnalyzeEffects, might be overkill: const baseFilter = {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate} let filter = - if flags*{efInTypeof, efWantIterator, efWantIterable} != {}: + if flags*{efInTypeof, efWantIterator, efWantIterable, efPreferIteratorForIterable} != {}: baseFilter + {skIterator} else: baseFilter # this will add the errors: diff --git a/compiler/semdata.nim b/compiler/semdata.nim index a3aae559fda24..0fc000051c442 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -54,7 +54,18 @@ type inst*: PInstantiation TExprFlag* = enum - efLValue, efWantIterator, efWantIterable, efInTypeof, + efLValue, + # The expression is used as an assignable location. + efWantIterator, + # Admit iterator candidates and prefer them during overload resolution. + efWantIterable, + # Admit iterator candidates for expressions that may feed iterable-style + # chaining. + efPreferIteratorForIterable, + # Prefer iterator candidates for `iterable[T]` matching and wrap a + # successful iterator call as `tyIterable`. + efInTypeof, + # The expression is being semchecked under `typeof`. efNeedStatic, # Use this in contexts where a static value is mandatory efPreferStatic, diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 2763adc074711..96a841580757e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -979,7 +979,7 @@ proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = - if flags*{efInTypeof, efWantIterator, efWantIterable} != {}: + if flags*{efInTypeof, efWantIterator, efWantIterable, efPreferIteratorForIterable} != {}: # consider: 'for x in pReturningArray()' --> we don't want the restriction # to 'skIterator' anymore; skIterator is preferred in sigmatch already # for typeof support. @@ -1006,7 +1006,8 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, # See bug #2051: result[0] = newSymNode(errorSym(c, n)) elif callee.kind == skIterator: - if efWantIterable in flags: + if result.typ.kind != tyIterable and + flags * {efWantIterable, efPreferIteratorForIterable} != {}: let typ = newTypeS(tyIterable, c) rawAddSon(typ, result.typ) result.typ = typ @@ -1525,7 +1526,7 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode = return # extra flags since LHS may become a call operand: - n[0] = semExprWithType(c, n[0], flags+{efDetermineType, efWantIterable, efAllowSymChoice}) + n[0] = semExprWithType(c, n[0], flags + {efDetermineType, efWantIterable, efAllowSymChoice}) #restoreOldStyleType(n[0]) var i = considerQuotedIdent(c, n[1], n) var ty = n[0].typ diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 7e07143837680..fdb7bc5cce9a8 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1096,7 +1096,12 @@ proc symForVar(c: PContext, n: PNode): PSym = proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode = result = n let iterBase = n[^2].typ - var iter = skipTypes(iterBase, {tyGenericInst, tyAlias, tySink, tyOwned}) + let iterType = + if iterBase.kind == tyIterable: + iterBase.skipModifier + else: + skipTypes(iterBase, {tyAlias, tySink, tyOwned}) + var iter = iterType var iterAfterVarLent = iter.skipTypes({tyGenericInst, tyAlias, tyLent, tyVar}) # n.len == 3 means that there is one for loop variable # and thus no tuple unpacking: @@ -1129,10 +1134,9 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode = else: var v = symForVar(c, n[0]) if getCurrOwner(c).kind == skModule: incl(v, sfGlobal) - # BUGFIX: don't use `iter` here as that would strip away - # the ``tyGenericInst``! See ``tests/compile/tgeneric.nim`` - # for an example: - v.typ = iterBase + # Use `iterType` here: it removes outer `tyIterable` / alias-like wrappers + # from the loop source, but still preserves `tyGenericInst` for the loop var. + v.typ = iterType n[0] = newSymNode(v) if sfGenSym notin v.flags and not isDiscardUnderscore(v): addDecl(c, v) elif v.owner == nil: setOwner(v, getCurrOwner(c)) @@ -1196,14 +1200,14 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode = c.p.breakInLoop = oldBreakInLoop dec(c.p.nestedLoopCounter) -proc implicitIterator(c: PContext, it: string, arg: PNode): PNode = +proc implicitIterator(c: PContext, it: string, arg: PNode, flags: TExprFlags): PNode = result = newNodeI(nkCall, arg.info) result.add(newIdentNode(getIdent(c.cache, it), arg.info)) if arg.typ != nil and arg.typ.kind in {tyVar, tyLent}: result.add newDeref(arg) else: result.add arg - result = semExprNoDeref(c, result, {efWantIterator}) + result = semExprNoDeref(c, result, flags + {efWantIterator}) proc isTrivalStmtExpr(n: PNode): bool = for i in 0..