# # # The Nim Compiler # (c) Copyright 2020 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## New styled concepts for Nim. See https://github.com/nim-lang/RFCs/issues/168 ## 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 from magicsys import addSonSkipIntLit const logBindings = false ## Code dealing with Concept declarations ## -------------------------------------- proc declareSelf(c: PContext; info: TLineInfo) = ## 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) s.typ.flags.incl {tfUnresolved, tfPacked} s.typ.add newType(tyEmpty, nextTypeId(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 (possibly empty) lists of statements ## containing 'proc' declarations and the like. case n.kind of nkStmtList, nkStmtListExpr: result = shallowCopy(n) for i in 0.. 0 and f[0].kind != tyNone: # also check the generic's constraints: let oldLen = m.inferred.len result = matchType(c, f[0], 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}: when logBindings: echo "B adding ", f, " ", lastSon ak m.inferred.add((f, lastSon ak)) result = true else: when logBindings: echo "C adding ", f, " ", ak m.inferred.add((f, ak)) #echo "binding ", typeToString(ak), " to ", typeToString(f) result = true elif not m.marker.containsOrIncl(old.id): result = matchType(c, old, ak, m) if m.magic == mArrPut and ak.kind == tyGenericParam: result = true #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) elif m.magic == mArrPut: result = matchType(c, f.sons[0], a, m) else: result = false of tyEnum, tyObject, tyDistinct: result = sameType(f, a) 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) of tyConcept: let oldLen = m.inferred.len let oldPotentialImplementation = m.potentialImplementation m.potentialImplementation = a result = conceptMatchNode(c, f.n.lastSon, m) m.potentialImplementation = oldPotentialImplementation if not result: m.inferred.setLen oldLen of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr, tyGenericInst: let ak = a.skipTypes(ignorableForArgType - {f.kind}) if ak.kind == f.kind and f.len == ak.len: for i in 0..= a.len if not result: m.inferred.setLen oldLen else: for i in 0..