iterable[T] (#17196)

* fix failing test toSeq in manual which now works
* changelog
* reject proc fn(a: iterable)
* add iterable to spec
* remove MCS/UFCS limitation that now works
This commit is contained in:
Timothee Cour 2021-04-11 07:25:41 -05:00 • committed by GitHub
commit ceadf54d76
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 254 additions and 40 deletions

View file

@ -440,10 +440,12 @@ type
tyVoid
# now different from tyEmpty, hurray!
tyIterable
static:
# remind us when TTypeKind stops to fit in a single 64-bit word
assert TTypeKind.high.ord <= 63
# assert TTypeKind.high.ord <= 63
discard
const
tyPureObject* = tyTuple
@ -664,7 +666,7 @@ type
mDefault, mUnown, mIsolate, mAccessEnv, mReset,
mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
mOrdinal,
mOrdinal, mIterableType,
mInt, mInt8, mInt16, mInt32, mInt64,
mUInt, mUInt8, mUInt16, mUInt32, mUInt64,
mFloat, mFloat32, mFloat64, mFloat128,

View file

@ -132,3 +132,4 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasSpellSuggest")
defineSymbol("nimHasCustomLiterals")
defineSymbol("nimHasUnifiedTuple")
defineSymbol("nimHasIterable")

View file

@ -211,7 +211,7 @@ proc mapType(typ: PType): TJSTypeKind =
else: result = etyNone
of tyProc: result = etyProc
of tyCString: result = etyString
of tyConcept: doAssert false
of tyConcept, tyIterable: doAssert false
proc mapType(p: PProc; typ: PType): TJSTypeKind =
result = mapType(typ)

View file

@ -908,7 +908,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of tyOrdinal, tyRange, tyInferred,
tyGenericInst, tyAlias, tySink:
fillBody(c, lastSon(t), body, x, y)
of tyConcept: doAssert false
of tyConcept, tyIterable: doAssert false
proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
kind: TTypeAttachedOp; info: TLineInfo;

View file

@ -40,7 +40,7 @@ proc beautifyName(s: string, k: TSymKind): string =
"pointer", "float", "csize", "csize_t", "cdouble", "cchar", "cschar",
"cshort", "cu", "nil", "typedesc", "auto", "any",
"range", "openarray", "varargs", "set", "cfloat", "ref", "ptr",
"untyped", "typed", "static", "sink", "lent", "type", "owned"]:
"untyped", "typed", "static", "sink", "lent", "type", "owned", "iterable"]:
result.add s[i]
else:
result.add toUpperAscii(s[i])

View file

@ -62,7 +62,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
best, alt: var TCandidate,
errors: var CandidateErrors,
diagnosticsFlag: bool,
errorsEnabled: bool) =
errorsEnabled: bool, flags: TExprFlags) =
var o: TOverloadIter
var sym = initOverloadIter(o, c, headSymbol)
var scope = o.lastOverloadScope
@ -95,7 +95,11 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
matches(c, n, orig, z)
if z.state == csMatch:
# little hack so that iterators are preferred over everything else:
if sym.kind == skIterator: inc(z.exactMatches, 200)
if sym.kind == skIterator:
if not (efWantIterator notin flags and efWantIterable in flags):
inc(z.exactMatches, 200)
else:
dec(z.exactMatches, 200)
case best.state
of csEmpty, csNoMatch: best = z
of csMatch:
@ -356,7 +360,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
template pickBest(headSymbol) =
pickBestCandidate(c, headSymbol, n, orig, initialBinding,
filter, result, alt, errors, efExplain in flags,
errorsEnabled)
errorsEnabled, flags)
pickBest(f)
let overloadsState = result.state

View file

@ -54,7 +54,7 @@ type
inst*: PInstantiation
TExprFlag* = enum
efLValue, efWantIterator, efInTypeof,
efLValue, efWantIterator, efWantIterable, efInTypeof,
efNeedStatic,
# Use this in contexts where a static value is mandatory
efPreferStatic,

View file

@ -853,7 +853,7 @@ proc semStaticExpr(c: PContext, n: PNode): PNode =
proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
flags: TExprFlags): PNode =
if flags*{efInTypeof, efWantIterator} != {}:
if flags*{efInTypeof, efWantIterator, efWantIterable} != {}:
# consider: 'for x in pReturningArray()' --> we don't want the restriction
# to 'skIterator' anymore; skIterator is preferred in sigmatch already
# for typeof support.
@ -877,6 +877,11 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
# error correction, prevents endless for loop elimination in transf.
# See bug #2051:
result[0] = newSymNode(errorSym(c, n))
elif callee.kind == skIterator:
if efWantIterable in flags:
let typ = newTypeS(tyIterable, c)
rawAddSon(typ, result.typ)
result.typ = typ
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode
@ -1364,7 +1369,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
onUse(n[1].info, s)
return
n[0] = semExprWithType(c, n[0], flags+{efDetermineType})
n[0] = semExprWithType(c, n[0], flags+{efDetermineType, efWantIterable})
#restoreOldStyleType(n[0])
var i = considerQuotedIdent(c, n[1], n)
var ty = n[0].typ

View file

@ -356,6 +356,15 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
localError(c.config, n.info, errArrayExpectsTwoTypeParams)
result = newOrPrevType(tyError, prev, c)
proc semIterableType(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyIterable, prev, c)
if n.len == 2:
let base = semTypeNode(c, n[1], nil)
addSonSkipIntLit(result, base, c.idgen)
else:
localError(c.config, n.info, errXExpectsOneTypeParam % "iterable")
result = newOrPrevType(tyError, prev, c)
proc semOrdinal(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyOrdinal, prev, c)
if n.len == 2:
@ -1844,6 +1853,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of mRange: result = semRange(c, n, prev)
of mSet: result = semSet(c, n, prev)
of mOrdinal: result = semOrdinal(c, n, prev)
of mIterableType: result = semIterableType(c, n, prev)
of mSeq:
result = semContainer(c, n, tySequence, "seq", prev)
if optSeqDestructors in c.config.globalOptions:
@ -2067,6 +2077,9 @@ proc processMagicType(c: PContext, m: PSym) =
of mOrdinal:
setMagicIntegral(c.config, m, tyOrdinal, szUncomputedSize)
rawAddSon(m.typ, newTypeS(tyNone, c))
of mIterableType:
setMagicIntegral(c.config, m, tyIterable, 0)
rawAddSon(m.typ, newTypeS(tyNone, c))
of mPNimrodNode:
incl m.typ.flags, tfTriggersCompileTime
incl m.typ.flags, tfCheckedForDestructor

View file

@ -1119,6 +1119,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
return if x >= isGeneric: isGeneric else: x
return isNone
of tyIterable:
if f.kind != tyIterable: return isNone
of tyNot:
case f.kind
of tyNot:
@ -1421,6 +1423,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
of tyAlias, tySink:
result = typeRel(c, lastSon(f), a, flags)
of tyIterable:
if a.kind == tyIterable:
if f.len == 1:
result = typeRel(c, lastSon(f), lastSon(a), flags)
else:
# f.len = 3, for some reason
result = isGeneric
else:
result = isNone
of tyGenericInst:
var prev = PType(idTableGet(c.bindings, f))
let origF = f
@ -2270,14 +2281,18 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
# a.typ == nil is valid
result = a
elif a.typ.isNil:
# XXX This is unsound! 'formal' can differ from overloaded routine to
# overloaded routine!
let flags = {efDetermineType, efAllowStmt}
#if formal.kind == tyIter: {efDetermineType, efWantIterator}
#else: {efDetermineType, efAllowStmt}
#elif formal.kind == tyTyped: {efDetermineType, efWantStmt}
#else: {efDetermineType}
result = c.semOperand(c, a, flags)
if formal.kind == tyIterable:
let flags = {efDetermineType, efAllowStmt, efWantIterator, efWantIterable}
result = c.semOperand(c, a, flags)
else:
# XXX This is unsound! 'formal' can differ from overloaded routine to
# overloaded routine!
let flags = {efDetermineType, efAllowStmt}
#if formal.kind == tyIterable: {efDetermineType, efWantIterator}
#else: {efDetermineType, efAllowStmt}
#elif formal.kind == tyTyped: {efDetermineType, efWantStmt}
#else: {efDetermineType}
result = c.semOperand(c, a, flags)
else:
result = a
considerGenSyms(c, result)

View file

@ -97,6 +97,9 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
result = nil
of tyUntyped, tyTyped:
if kind notin {skParam, skResult} or taNoUntyped in flags: result = t
of tyIterable:
if kind notin {skParam} or taNoUntyped in flags: result = t
# tyIterable is only for templates and macros.
of tyStatic:
if kind notin {skParam}: result = t
of tyVoid:

View file

@ -460,8 +460,8 @@ const
"lent ", "varargs[$1]", "UncheckedArray[$1]", "Error Type",
"BuiltInTypeClass", "UserTypeClass",
"UserTypeClassInst", "CompositeTypeClass", "inferred",
"and", "or", "not", "any", "static", "TypeFromExpr", "out ",
"void"]
"and", "or", "not", "any", "static", "TypeFromExpr", "concept", # xxx bugfix
"void", "iterable"]
const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo,
preferGenericArg, preferResolved, preferMixed}
@ -645,6 +645,11 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tyDistinct:
result = "distinct " & typeToString(t[0],
if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName)
of tyIterable:
# xxx factor this pattern
result = "iterable"
if t.len > 0:
result &= "[" & typeToString(t[0]) & ']'
of tyTuple:
# we iterate over t.sons here, because t.n may be nil
if t.n != nil:
@ -1191,7 +1196,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
result = sameTypeOrNilAux(a[0], b[0], c) and
sameValue(a.n[0], b.n[0]) and
sameValue(a.n[1], b.n[1])
of tyGenericInst, tyAlias, tyInferred:
of tyGenericInst, tyAlias, tyInferred, tyIterable:
cycleCheck()
result = sameTypeAux(a.lastSon, b.lastSon, c)
of tyNone: result = false

View file

@ -287,6 +287,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
of tyAnd: result = mapTypeToBracket("and", mAnd, t, info)
of tyOr: result = mapTypeToBracket("or", mOr, t, info)
of tyNot: result = mapTypeToBracket("not", mNot, t, info)
of tyIterable: result = mapTypeToBracket("iterable", mIterableType, t, info)
of tyAnything: result = atomicType("anything", mNone)
of tyInferred: assert false
of tyStatic, tyFromExpr: