diff options
Diffstat (limited to 'compiler/concepts.nim')
-rw-r--r-- | compiler/concepts.nim | 115 |
1 files changed, 59 insertions, 56 deletions
diff --git a/compiler/concepts.nim b/compiler/concepts.nim index 54579f73f..d48bacdc5 100644 --- a/compiler/concepts.nim +++ b/compiler/concepts.nim @@ -11,10 +11,12 @@ ## for details. Note this is a first implementation and only the "Concept matching" ## section has been implemented. -import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, - types, intsets +import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types -from magicsys import addSonSkipIntLit +import std/intsets + +when defined(nimPreviewSlimSystem): + import std/assertions const logBindings = false @@ -23,30 +25,18 @@ const ## -------------------------------------- proc declareSelf(c: PContext; info: TLineInfo) = - ## adds the magical 'Self' symbols to the current scope. + ## Adds the magical 'Self' symbols to the current scope. let ow = getCurrOwner(c) - let s = newSym(skType, getIdent(c.cache, "Self"), nextSymId(c.idgen), ow, info) - s.typ = newType(tyTypeDesc, nextTypeId(c.idgen), ow) + let s = newSym(skType, getIdent(c.cache, "Self"), c.idgen, ow, info) + s.typ = newType(tyTypeDesc, c.idgen, ow) s.typ.flags.incl {tfUnresolved, tfPacked} - s.typ.add newType(tyEmpty, nextTypeId(c.idgen), ow) + s.typ.add newType(tyEmpty, c.idgen, ow) addDecl(c, s, info) -proc isSelf*(t: PType): bool {.inline.} = - ## is this the magical 'Self' type? - t.kind == tyTypeDesc and tfPacked in t.flags - -proc makeTypeDesc*(c: PContext, typ: PType): PType = - if typ.kind == tyTypeDesc and not isSelf(typ): - result = typ - else: - result = newTypeS(tyTypeDesc, c) - incl result.flags, tfCheckedForDestructor - result.addSonSkipIntLit(typ, c.idgen) - proc semConceptDecl(c: PContext; n: PNode): PNode = ## Recursive helper for semantic checking for the concept declaration. - ## Currently we only support lists of statements containing 'proc' - ## declarations and the like. + ## Currently we only support (possibly empty) lists of statements + ## containing 'proc' declarations and the like. case n.kind of nkStmtList, nkStmtListExpr: result = shallowCopy(n) @@ -59,8 +49,10 @@ proc semConceptDecl(c: PContext; n: PNode): PNode = for i in 0..<n.len-1: result[i] = n[i] result[^1] = semConceptDecl(c, n[^1]) + of nkCommentStmt: + result = n else: - localError(c.config, n.info, "unexpected construct in the new-styled concept " & renderTree(n)) + localError(c.config, n.info, "unexpected construct in the new-styled concept: " & renderTree(n)) result = n proc semConceptDeclaration*(c: PContext; n: PNode): PNode = @@ -97,14 +89,14 @@ proc existingBinding(m: MatchCon; key: PType): PType = proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = - ## the heart of the concept matching process. 'f' is the formal parameter of some + ## The heart of the concept matching process. 'f' is the formal parameter of some ## routine inside the concept that we're looking for. 'a' is the formal parameter ## of a routine that might match. const ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyGenericInst, tyAlias, tyInferred} case f.kind of tyAlias: - result = matchType(c, f.lastSon, a, m) + result = matchType(c, f.skipModifier, a, m) of tyTypeDesc: if isSelf(f): #let oldLen = m.inferred.len @@ -113,15 +105,19 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = #m.inferred.setLen oldLen #echo "A for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation) else: - if a.kind == tyTypeDesc and f.len == a.len: - for i in 0..<a.len: - if not matchType(c, f[i], a[i], m): return false - return true + if a.kind == tyTypeDesc and f.hasElementType == a.hasElementType: + if f.hasElementType: + result = matchType(c, f.elementType, a.elementType, m) + else: + result = true # both lack it + else: + result = false of tyGenericInvocation: - if a.kind == tyGenericInst and a[0].kind == tyGenericBody: - if sameType(f[0], a[0]) and f.len == a.len-1: - for i in 1 ..< f.len: + result = false + if a.kind == tyGenericInst and a.genericHead.kind == tyGenericBody: + if sameType(f.genericHead, a.genericHead) and f.kidsLen == a.kidsLen-1: + for i in FirstGenericParamAt ..< f.kidsLen: if not matchType(c, f[i], a[i], m): return false return true of tyGenericParam: @@ -131,17 +127,17 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = else: let old = existingBinding(m, f) if old == nil: - if f.len > 0 and f[0].kind != tyNone: + if f.hasElementType and f.elementType.kind != tyNone: # also check the generic's constraints: let oldLen = m.inferred.len - result = matchType(c, f[0], a, m) + result = matchType(c, f.elementType, a, m) m.inferred.setLen oldLen if result: when logBindings: echo "A adding ", f, " ", ak m.inferred.add((f, ak)) - elif m.magic == mArrGet and ak.kind in {tyArray, tyOpenArray, tySequence, tyVarargs, tyCString, tyString}: + elif m.magic == mArrGet and ak.kind in {tyArray, tyOpenArray, tySequence, tyVarargs, tyCstring, tyString}: when logBindings: echo "B adding ", f, " ", lastSon ak - m.inferred.add((f, lastSon ak)) + m.inferred.add((f, last ak)) result = true else: when logBindings: echo "C adding ", f, " ", ak @@ -152,25 +148,27 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = result = matchType(c, old, ak, m) if m.magic == mArrPut and ak.kind == tyGenericParam: result = true + else: + result = false #echo "B for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation) of tyVar, tySink, tyLent, tyOwned: # modifiers in the concept must be there in the actual implementation # too but not vice versa. if a.kind == f.kind: - result = matchType(c, f.sons[0], a.sons[0], m) + result = matchType(c, f.elementType, a.elementType, m) elif m.magic == mArrPut: - result = matchType(c, f.sons[0], a, m) + result = matchType(c, f.elementType, a, m) else: result = false of tyEnum, tyObject, tyDistinct: result = sameType(f, a) - of tyEmpty, tyString, tyCString, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid: + of tyEmpty, tyString, tyCstring, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid: result = a.skipTypes(ignorableForArgType).kind == f.kind of tyBool, tyChar, tyInt..tyUInt64: let ak = a.skipTypes(ignorableForArgType) result = ak.kind == f.kind or ak.kind == tyOrdinal or - (ak.kind == tyGenericParam and ak.len > 0 and ak[0].kind == tyOrdinal) + (ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal) of tyConcept: let oldLen = m.inferred.len let oldPotentialImplementation = m.potentialImplementation @@ -181,9 +179,11 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = m.inferred.setLen oldLen of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr, tyGenericInst: + # ^ XXX Rewrite this logic, it's more complex than it needs to be. + result = false let ak = a.skipTypes(ignorableForArgType - {f.kind}) - if ak.kind == f.kind and f.len == ak.len: - for i in 0..<ak.len: + if ak.kind == f.kind and f.kidsLen == ak.kidsLen: + for i in 0..<ak.kidsLen: if not matchType(c, f[i], ak[i], m): return false return true of tyOr: @@ -192,29 +192,30 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = # say the concept requires 'int|float|string' if the potentialImplementation # says 'int|string' that is good enough. var covered = 0 - for i in 0..<f.len: - for j in 0..<a.len: + for ff in f.kids: + for aa in a.kids: let oldLenB = m.inferred.len - let r = matchType(c, f[i], a[j], m) + let r = matchType(c, ff, aa, m) if r: inc covered break m.inferred.setLen oldLenB - result = covered >= a.len + result = covered >= a.kidsLen if not result: m.inferred.setLen oldLen else: - for i in 0..<f.len: - result = matchType(c, f[i], a, m) + result = false + for ff in f.kids: + result = matchType(c, ff, a, m) if result: break # and remember the binding! m.inferred.setLen oldLen of tyNot: if a.kind == tyNot: - result = matchType(c, f[0], a[0], m) + result = matchType(c, f.elementType, a.elementType, m) else: let oldLen = m.inferred.len - result = not matchType(c, f[0], a, m) + result = not matchType(c, f.elementType, a, m) m.inferred.setLen oldLen of tyAnything: result = true @@ -253,7 +254,7 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool = m.inferred.setLen oldLen return false - if not matchReturnType(c, n[0].sym.typ.sons[0], candidate.typ.sons[0], m): + if not matchReturnType(c, n[0].sym.typ.returnType, candidate.typ.returnType, m): m.inferred.setLen oldLen return false @@ -270,7 +271,7 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool = proc matchSyms(c: PContext, n: PNode; kinds: set[TSymKind]; m: var MatchCon): bool = ## Walk the current scope, extract candidates which the same name as 'n[namePos]', ## 'n' is the nkProcDef or similar from the concept that we try to match. - let candidates = searchInScopesFilterBy(c, n[namePos].sym.name, kinds) + let candidates = searchInScopesAllCandidatesFilterBy(c, n[namePos].sym.name, kinds) for candidate in candidates: #echo "considering ", typeToString(candidate.typ), " ", candidate.magic m.magic = candidate.magic @@ -302,17 +303,19 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool = result = matchSyms(c, n, {skMethod}, m) of nkIteratorDef: result = matchSyms(c, n, {skIterator}, m) + of nkCommentStmt: + result = true else: # error was reported earlier. result = false -proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TIdTable; invocation: PType): bool = +proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TypeMapping; invocation: PType): bool = ## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but - ## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fullfill the + ## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fulfill the ## concept's requirements. If so, we return true and fill the 'bindings' with pairs of ## (typeVar, instance) pairs. ('typeVar' is usually simply written as a generic 'T'.) ## 'invocation' can be nil for atomic concepts. For non-atomic concepts, it contains the - ## 'C[S, T]' parent type that we look for. We need this because we need to store bindings + ## `C[S, T]` parent type that we look for. We need this because we need to store bindings ## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that ## we do not add any bindings at all on an unsuccessful match! var m = MatchCon(inferred: @[], potentialImplementation: arg) @@ -333,8 +336,8 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TIdTable; invo # we have a match, so bind 'arg' itself to 'concpt': bindings.idTablePut(concpt, arg) # invocation != nil means we have a non-atomic concept: - if invocation != nil and arg.kind == tyGenericInst and invocation.len == arg.len-1: + if invocation != nil and arg.kind == tyGenericInst and invocation.kidsLen == arg.kidsLen-1: # bind even more generic parameters assert invocation.kind == tyGenericInvocation - for i in 1 ..< invocation.len: + for i in FirstGenericParamAt ..< invocation.kidsLen: bindings.idTablePut(invocation[i], arg[i]) |