diff --git a/compiler/concepts.nim b/compiler/concepts.nim index 7e547b19ce3c..4329e4b4bfc1 100644 --- a/compiler/concepts.nim +++ b/compiler/concepts.nim @@ -263,6 +263,22 @@ proc conceptsMatch(c: PContext, fc, ac: PType; m: var MatchCon): MatchKind = return mkNoMatch return mkSubset +proc isObjectSubtype(f, a: PType): bool = + var t = a + result = false + while t != nil: + t = t.baseClass + if t == nil: + break + t = t.skipTypes({tyPtr,tyRef}) + if t == nil: + break + if t.kind != tyObject: + break + if sameObjectTypes(f, t): + result = true + break + proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool = ## 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 @@ -327,6 +343,8 @@ proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool = result = a.base.sym == f.sym else: result = sameType(f, a) + if not result and f.kind == tyObject and a.kind == tyObject: + result = isObjectSubtype(f, a) of tyEmpty, tyString, tyCstring, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid: result = a.skipTypes(ignorableForArgType).kind == f.kind of tyBool, tyChar, tyInt..tyUInt64: diff --git a/doc/manual.md b/doc/manual.md index 29006a753674..21abe9504c66 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -3008,6 +3008,12 @@ is more specific 2. if the concept is being compared with another concept the result is deferred to [Concept subset matching] 3. in any other case the concept is less specific then it's competitor +Currently, the concept evaluation mechanism evaluates to a successful match on the first acceptable candidate +for each defined binding. This has a couple of notable effects: + +- generic parameters are fulfilled by the first candidate match even if other candidates would also match and bind different parameters +- inheritable objects match as they do in normal overload resolution except the "depth" is not accounted for, because that would require calculating the minimum depth of any matching binding + Concept subset matching ------------------------- diff --git a/tests/concepts/tconceptsv2.nim b/tests/concepts/tconceptsv2.nim index 629ac1c876d7..d861c51c752c 100644 --- a/tests/concepts/tconceptsv2.nim +++ b/tests/concepts/tconceptsv2.nim @@ -585,3 +585,18 @@ block: discard assert (ref AObj[int]) is C + +block: + type + C = concept + proc x(a:Self, x: int) + StreamObj = object of RootObj + Stream = ref StreamObj + MemMapFileStreamObj = object of Stream + MemMapFileStream = ref MemMapFileStreamObj + + proc x(a: Stream, x: int) = discard + proc spring(x: C) = discard + + let test = MemMapFileStream() + spring(test)