mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-21 20:10:44 +00:00
Fixes 3 small issues with concepts (#24481)
issue 1 - statics in the type:
This probably only handles simple cases. It's probably too accepting
only comparing the base, but that should only affect candidate selection
I think.
issue 2 - `tyArray` of length 3:
This is just a work around since I couldn't get the fix right in
previous PR
issue 3 - shadowing:
The part in `concepts.nim` that iterates candidates does not consider
imported idents if at least once module level ident matches. It does not
have to match in any other way then name.
EDIT: 2 more
issue 4 - composite typeclasses:
when declared in both the concept and the `proc` can cause problems
issue 5 - recursion:
simple recursion and scenarios where more than one concepts recurse
together (only tested two)
(cherry picked from commit e479151473)
This commit is contained in:
committed by
narimiran
parent
7d425e712e
commit
f5453e453e
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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}
|
||||
|
||||
8
tests/concepts/conceptsv2_helper.nim
Normal file
8
tests/concepts/conceptsv2_helper.nim
Normal file
@@ -0,0 +1,8 @@
|
||||
type
|
||||
ShadowConcept* = concept
|
||||
proc iGetShadowed(s: Self)
|
||||
DummyFitsObj* = object
|
||||
|
||||
proc iGetShadowed*(s: DummyFitsObj)=
|
||||
discard
|
||||
|
||||
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user