diff --git a/compiler/concepts.nim b/compiler/concepts.nim index 3f0d30538e..23f8c5d87c 100644 --- a/compiler/concepts.nim +++ b/compiler/concepts.nim @@ -78,6 +78,7 @@ type magic: TMagic ## mArrGet and mArrPut is wrong in system.nim and ## cannot be fixed that easily. ## Thus we special case it here. + concpt: PType proc existingBinding(m: MatchCon; key: PType): PType = ## checks if we bound the type variable 'key' already to some @@ -174,27 +175,43 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = result = ak.kind == f.kind or ak.kind == tyOrdinal or (ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.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 + if a.kind == tyConcept and f.n == a.n: + result = true + elif m.concpt.size == szIllegalRecursion: + result = false + else: + let oldLen = m.inferred.len + let oldPotentialImplementation = m.potentialImplementation + m.potentialImplementation = a + m.concpt.size = szIllegalRecursion + let oldConcept = m.concpt + m.concpt = f + result = conceptMatchNode(c, f.n.lastSon, m) + m.potentialImplementation = oldPotentialImplementation + m.concpt = oldConcept + m.concpt.size = szUnknownSize + if not result: + m.inferred.setLen oldLen of tyGenericBody: var ak = a if a.kind == tyGenericBody: ak = last(a) result = matchType(c, last(f), ak, m) of tyCompositeTypeClass: - result = matchType(c, last(f), a, m) + var ak = if a.kind == tyCompositeTypeClass: a.last else: a + result = matchType(c, last(f), ak, m) 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.kidsLen == ak.kidsLen: - result = matchKids(c, f, ak, m) + if f.kind == tyArray and f.kidsLen == 3: + # XXX: this is a work-around! + # system.nim creates these for the magic array typeclass + result = true + else: + result = false + let ak = a.skipTypes(ignorableForArgType - {f.kind}) + if ak.kind == f.kind and f.kidsLen == ak.kidsLen: + result = matchKids(c, f, ak, m) of tyOr: let oldLen = m.inferred.len if a.kind == tyOr: @@ -230,6 +247,16 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = result = true of tyOrdinal: result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam + of tyStatic: + result = false + var scomp = f.base + if scomp.kind == tyGenericParam: + if f.base.kidsLen > 0: + scomp = scomp.base + if a.kind == tyStatic: + result = matchType(c, scomp, a.base, m) + else: + result = matchType(c, scomp, a, m) else: result = false @@ -280,7 +307,8 @@ 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 = searchInScopesAllCandidatesFilterBy(c, n[namePos].sym.name, kinds) + var candidates = searchScopes(c, n[namePos].sym.name, kinds) + searchImportsAll(c, n[namePos].sym.name, kinds, candidates) for candidate in candidates: #echo "considering ", typeToString(candidate.typ), " ", candidate.magic m.magic = candidate.magic @@ -327,7 +355,7 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable ## `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) + var m = MatchCon(inferred: @[], potentialImplementation: arg, concpt: concpt) result = conceptMatchNode(c, concpt.n.lastSon, m) if result: for (a, b) in m.inferred: diff --git a/compiler/lookups.nim b/compiler/lookups.nim index d8fcf73e0f..745915cdf9 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -221,7 +221,14 @@ proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} = if i == limit: return inc i -proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] = +proc searchImportsAll*(c: PContext, s: PIdent, filter: TSymKinds, holding: var seq[PSym]) = + var marked = initIntSet() + for im in c.imports.mitems: + for s in symbols(im, marked, s, c.graph): + if s.kind in filter: + holding.add s + +proc searchScopes*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] = result = @[] for scope in allScopes(c.currentScope): var ti: TIdentIter = default(TIdentIter) @@ -231,14 +238,12 @@ proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKi result.add candidate candidate = nextIdentIter(ti, scope.symbols) +proc searchScopesAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] = + result = searchScopes(c,s,filter) if result.len == 0: - var marked = initIntSet() - for im in c.imports.mitems: - for s in symbols(im, marked, s, c.graph): - if s.kind in filter: - result.add s + searchImportsAll(c, s, filter, result) -proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] = +proc selectFromScopesElseAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] = result = @[] block outer: for scope in allScopes(c.currentScope): @@ -252,11 +257,7 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy candidate = nextIdentIter(ti, scope.symbols) if result.len == 0: - var marked = initIntSet() - for im in c.imports.mitems: - for s in symbols(im, marked, s, c.graph): - if s.kind in filter: - result.add s + searchImportsAll(c, s, filter, result) proc cmpScopes*(ctx: PContext, s: PSym): int = # Do not return a negative number @@ -644,7 +645,7 @@ const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage} proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind], includePureEnum = false): seq[PSym] = - result = searchInScopesFilterBy(c, ident, filter) + result = selectFromScopesElseAll(c, ident, filter) if skEnumField in filter and (result.len == 0 or includePureEnum): result.add allPureEnumFields(c, ident) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index d0ccb17b1f..92276b8487 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -56,7 +56,7 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode, proc name[T: static proc()]() = T() name[proc() = echo"hello"]() ]# - for paramSym in searchInScopesAllCandidatesFilterBy(c, symx.name, {skConst}): + for paramSym in searchScopesAll(c, symx.name, {skConst}): let paramTyp = paramSym.typ if paramTyp.n.kind == nkSym and paramTyp.n.sym.kind in filter: result.add((paramTyp.n.sym, o.lastOverloadScope)) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index cb51973857..135d26ba56 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -218,7 +218,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, let ident = considerQuotedIdent(c, n) # could be type conversion if like a.T and not a.T() let symKinds = if inCall: routineKinds else: routineKinds+{skType} - var candidates = searchInScopesFilterBy(c, ident, symKinds) + var candidates = selectFromScopesElseAll(c, ident, symKinds) if candidates.len > 0: let s = candidates[0] # XXX take into account the other candidates! isMacro = s.kind in {skTemplate, skMacro} diff --git a/tests/concepts/conceptsv2_helper.nim b/tests/concepts/conceptsv2_helper.nim new file mode 100644 index 0000000000..daab556cbe --- /dev/null +++ b/tests/concepts/conceptsv2_helper.nim @@ -0,0 +1,8 @@ +type + ShadowConcept* = concept + proc iGetShadowed(s: Self) + DummyFitsObj* = object + +proc iGetShadowed*(s: DummyFitsObj)= + discard + diff --git a/tests/concepts/tconceptsv2.nim b/tests/concepts/tconceptsv2.nim index 3d90204bec..f72f522ee6 100644 --- a/tests/concepts/tconceptsv2.nim +++ b/tests/concepts/tconceptsv2.nim @@ -3,8 +3,11 @@ action: "run" output: ''' B[system.int] A[system.string] +A[array[0..0, int]] +A[seq[int]] ''' """ +import conceptsv2_helper block: # issue #24451 type @@ -39,3 +42,89 @@ block: # typeclass var a = A[string]() a.accept() + +block: + type + SomethingLike[T] = concept + proc len(s: Self): int + proc `[]`(s: Self; index: int): T + + A[T] = object + x: T + + proc initA(x: SomethingLike): auto = + A[type x](x: x) + + var a: array[1, int] + var s: seq[int] + echo typeof(initA(a)) + echo typeof(initA(s)) + +block: + proc iGetShadowed(s: int)= + discard + proc spring(x: ShadowConcept)= + discard + let a = DummyFitsObj() + spring(a) + +block: + type + Buffer = concept + proc put(s: Self) + ArrayBuffer[T: static int] = object + proc put(x: ArrayBuffer)=discard + proc p(a: Buffer)=discard + var buffer = ArrayBuffer[5]() + p(buffer) + +block: # composite typeclass matching + type + A[T] = object + Buffer = concept + proc put(s: Self, i: A) + BufferImpl = object + WritableImpl = object + + proc launch(a: var Buffer)=discard + proc put(x: BufferImpl, i: A)=discard + + var a = BufferImpl() + launch(a) + +block: # simple recursion + type + Buffer = concept + proc put(s: var Self, i: auto) + proc second(s: Self) + Writable = concept + proc put(w: var Buffer, s: Self) + BufferImpl[T: static int] = object + WritableImpl = object + + proc launch(a: var Buffer, b: Writable)= discard + proc put(x: var BufferImpl, i: object)= discard + proc second(x: BufferImpl)= discard + proc put(x: var Buffer, y: WritableImpl)= discard + + var a = BufferImpl[5]() + launch(a, WritableImpl()) + +block: # more complex recursion + type + Buffer = concept + proc put(s: var Self, i: auto) + proc second(s: Self) + Writable = concept + proc put(w: var Buffer, s: Self) + BufferImpl[T: static int] = object + WritableImpl = object + + proc launch(a: var Buffer, b: Writable)= discard + proc put(x: var Buffer, i: object)= discard + proc put(x: var BufferImpl, i: object)= discard + proc second(x: BufferImpl)= discard + proc put(x: var Buffer, y: WritableImpl)= discard + + var a = BufferImpl[5]() + launch(a, WritableImpl())