Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changing generic weight of tyGenericParam #22143

Merged
merged 58 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
3b045e1
Changing generic weight of `tyGenericParam`
Graveflo Jun 22, 2023
15ce607
[skip ci] (not working) adding intention to manual
Graveflo Jun 23, 2023
37f67e3
[skip ci] Logic update
Graveflo Jun 23, 2023
4e90040
Testing and removing empty `T`s weight
Graveflo Jun 23, 2023
dd6d847
[skip ci] Revert to open amalgamated tests for now
Graveflo Jun 24, 2023
c1042b2
Updating manual
Graveflo Jun 24, 2023
d48bf00
`tyBuiltInTypeClass` is generic in `typeRel
Graveflo Jun 25, 2023
3322f17
Merge branch 'devel' into generic-match-constraint
Graveflo Jun 25, 2023
60bb8aa
This passes the merged commits tests
Graveflo Jun 25, 2023
845cdd3
`isConvertible` works better for built-ins
Graveflo Jun 26, 2023
e7a07cb
Merge branch 'nim-lang:devel' into generic-match-constraint
Graveflo Jun 26, 2023
66e422b
Adding detail to matching rules
Graveflo Jun 27, 2023
635e160
Merge branch 'devel' into generic-match-constraint
Graveflo Jun 27, 2023
cee5223
Pruning in `typeRel` for `tyPtr, tyRef` may be nil
Graveflo Jun 27, 2023
6db4bfb
Better typeclass relations
Graveflo Jun 28, 2023
1a6058e
adding `tyCompositeTypeClass`
Graveflo Jun 28, 2023
f0499d8
Respecting bindings
Graveflo Jun 29, 2023
c7b3f11
Merge branch 'devel' into generic-match-constraint
Graveflo Jun 29, 2023
e6e9f3d
Fixing some misalignments
Graveflo Jun 29, 2023
6d378ba
forgot to pull
Graveflo Jun 29, 2023
60b8e71
Restruct tests. Fixed minor bug
Graveflo Jun 30, 2023
0ec01ac
[skip ci] more test mend
Graveflo Jul 1, 2023
45b39b1
fixes owned refs
Graveflo Jul 1, 2023
77921f4
Do not allow bad bindings
Graveflo Jul 2, 2023
ad10567
Merge branch 'devel' into generic-match-constraint
Graveflo Jul 2, 2023
2bb0c76
more test amends
Graveflo Jul 2, 2023
2f5288b
Disabling neo and datamancer in CI
Graveflo Jul 2, 2023
071df9a
Preserving [T]s importance
Graveflo Jul 2, 2023
6a65645
bare `typedesc` skips `tyNone`
Graveflo Jul 2, 2023
41ca8ac
`isObjectSubtype` has more friends
Graveflo Jul 2, 2023
585a277
Should not have messed with `depth`
Graveflo Jul 3, 2023
c812389
Merge branch 'devel' into generic-match-constraint
Graveflo Jul 3, 2023
bd8a399
metagn fix neo
Graveflo Jul 4, 2023
416e065
Merge branch 'devel' into generic-match-constraint
bung87 Aug 8, 2023
31b3852
Merge branch 'devel' into generic-match-constraint
Graveflo Sep 30, 2023
5871625
manual & merge adjustments
Graveflo Sep 30, 2023
ca22804
revert `tyPtr` and `tyRef`
Graveflo Sep 30, 2023
057d941
adjusting `sumGeneric`
Graveflo Sep 30, 2023
e1ed026
Adjust `tyPtr` and`tyRef` again
Graveflo Sep 30, 2023
3951052
Adding `tyNot` and `tyVoid` to `sumGeneric`
Graveflo Sep 30, 2023
f1bdfc4
`recordRel` propigates flags
Graveflo Sep 30, 2023
c95938a
first attempt workaround `tyArray`
Graveflo Oct 1, 2023
3445681
Merge branch 'devel' into generic-match-constraint
Graveflo Oct 26, 2023
216beda
Merge branch 'devel' into generic-match-constraint
Graveflo Nov 9, 2023
81d167c
style consistency and adjustment in `sumGeneric`
Graveflo Nov 9, 2023
82d2447
testing: `checkGeneric` demotes Inferred relations
Graveflo Nov 14, 2023
8ebbe7d
Merge branch 'devel' into generic-match-constraint
Graveflo Nov 21, 2023
78c53c0
Merge branch 'devel' into generic-match-constraint
Graveflo Dec 15, 2023
1000f3e
merge patch & manual adjust
Graveflo Dec 15, 2023
7c774c9
missed a `break` stmt in merge
Graveflo Dec 16, 2023
2ec34ab
Merge branch 'devel' into generic-match-constraint
Graveflo Dec 16, 2023
8c8e279
small docs adjustment
Graveflo Dec 16, 2023
737382f
Merge branch 'devel' into generic-match-constraint
Graveflo Dec 31, 2023
108766b
merge fix. Trim rambling comment
Graveflo Dec 31, 2023
5f5cff9
Merge branch 'devel' into generic-match-constraint
Graveflo Jan 2, 2024
8feccdf
correct my poor name choice of `getObjectType`
Graveflo Jan 3, 2024
f1ca3c8
Update doc/manual.md
Araq Jan 5, 2024
31b5bee
Update doc/manual.md
Araq Jan 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 36 additions & 27 deletions compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,7 @@ proc sumGeneric(t: PType): int =
inc result
of tyBool, tyChar, tyEnum, tyObject, tyPointer, tyVoid,
tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128,
tyUInt..tyUInt64, tyCompositeTypeClass, tyBuiltInTypeClass,
tyGenericParam:
tyUInt..tyUInt64, tyCompositeTypeClass, tyBuiltInTypeClass:
inc result
break
of tyGenericBody:
Expand All @@ -233,6 +232,12 @@ proc sumGeneric(t: PType): int =
t = t.elementType
if t.kind == tyEmpty: break
inc result
of tyGenericParam:
if t.len > 0:
t = t.skipModifier
else:
inc result
break
of tyUntyped, tyTyped: break
of tyGenericInvocation, tyTuple, tyAnd:
result += ord(t.kind == tyAnd)
Expand Down Expand Up @@ -451,37 +456,40 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
else: result = isIntConv
else: result = isNone

proc getObjectType(f: PType): PType =
proc reduceToBase(f: PType): PType =
#[
Returns a type that is f's effective typeclass. This is usually just one level deeper
in the hierarchy of generality for a type. `object`, `ref object`, `enum` and user defined
tyObjects are common return values.
Returns the lowest order (most general) type that that is compatible with the input.
E.g.
A[T] = ptr object ... A -> ptr object
A[N: static[int]] = array[N, int] ... A -> array
]#
case f.kind:
of tyGenericParam:
if f.len <= 0 or f.skipModifier == nil:
result = f
else:
result = reduceToBase(f.skipModifier)
of tyGenericInvocation:
result = getObjectType(f.baseClass)
result = reduceToBase(f.baseClass)
of tyCompositeTypeClass, tyAlias:
if not f.hasElementType or f.elementType == nil:
result = f
else:
result = getObjectType(f.elementType)
result = reduceToBase(f.elementType)
of tyGenericInst:
result = getObjectType(f.skipModifier)
result = reduceToBase(f.skipModifier)
of tyGenericBody:
result = getObjectType(f.typeBodyImpl)

result = reduceToBase(f.typeBodyImpl)
of tyUserTypeClass:
if f.isResolvedUserTypeClass:
result = f.base # ?? idk if this is right
else:
result = f.skipModifier
of tyStatic, tyOwned, tyVar, tyLent, tySink:
result = getObjectType(f.base)
result = reduceToBase(f.base)
of tyInferred:
# This is not true "After a candidate type is selected"
result = getObjectType(f.base)
of tyTyped, tyUntyped, tyFromExpr:
result = f
result = reduceToBase(f.base)
of tyRange:
result = f.elementType
else:
Expand Down Expand Up @@ -1244,7 +1252,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
result = typeRel(c, f.base, aOrig, flags + {trNoCovariance})
subtypeCheck()
of tyArray:
a = getObjectType(a)
a = reduceToBase(a)
case a.kind
of tyArray:
var fRange = f.indexType
Expand Down Expand Up @@ -1370,7 +1378,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
let effectiveArgType = if useTypeLoweringRuleInTypeClass:
a
else:
getObjectType(a)
reduceToBase(a)
if effectiveArgType.kind == tyObject:
if sameObjectTypes(f, effectiveArgType):
result = isEqual
Expand Down Expand Up @@ -1400,7 +1408,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
# set constructors are a bit special...
result = isNone
of tyPtr, tyRef:
a = getObjectType(a)
a = reduceToBase(a)
if a.kind == f.kind:
# ptr[R, T] can be passed to ptr[T], but not the other way round:
if a.len < f.len: return isNone
Expand Down Expand Up @@ -1691,7 +1699,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
considerPreviousT:
let target = f.genericHead
let targetKind = target.kind
var effectiveArgType = getObjectType(a)
var effectiveArgType = reduceToBase(a)
effectiveArgType = effectiveArgType.skipTypes({tyBuiltInTypeClass})
if targetKind == effectiveArgType.kind:
if effectiveArgType.isEmptyContainer:
Expand Down Expand Up @@ -1785,7 +1793,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
# check if 'T' has a constraint as in 'proc p[T: Constraint](x: T)'
if f.len > 0 and f[0].kind != tyNone:
let oldInheritancePenalty = c.inheritancePenalty
result = typeRel(c, f[0], a, flags + {trDontBind,trBindGenericParam})
result = typeRel(c, f[0], a, flags + {trDontBind, trBindGenericParam})
if doBindGP and result notin {isNone, isGeneric}:
let concrete = concreteType(c, a, f)
if concrete == nil: return isNone
Expand All @@ -1811,10 +1819,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
a.sym.transitionGenericParamToType()
a.flags.excl tfWildcard
elif doBind:
# The mechanics of `doBind` being a flag that also denotes sig cmp via
# negation is potentially problematic. `IsNone` is appropriate for
# preventing illegal bindings, but it is not necessarily appropriate
# before the bindings have been finalized.
# careful: `trDontDont` (set by `checkGeneric`) is not always respected in this call graph.
# typRel having two different modes (binding and non-binding) can make things harder to
# reason about and maintain. Refactoring typeRel to not be responsible for setting, or
# at least validating, bindings can have multiple benefits. This is debatable. I'm not 100% sure.
# A design that allows a proper complexity analysis of types like `tyOr` would be ideal.
concrete = concreteType(c, a, f)
if concrete == nil:
return isNone
Expand Down Expand Up @@ -2368,9 +2377,9 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
# roll back the side effects of the unification algorithm.
let c = m.c
var
x = newCandidate(c, m.callee)
y = newCandidate(c, m.callee)
z = newCandidate(c, m.callee)
x = newCandidate(c, m.callee) # potential "best"
y = newCandidate(c, m.callee) # potential competitor with x
z = newCandidate(c, m.callee) # buffer for copies of m
x.calleeSym = m.calleeSym
y.calleeSym = m.calleeSym
z.calleeSym = m.calleeSym
Expand Down
38 changes: 29 additions & 9 deletions doc/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -2635,7 +2635,9 @@ of the argument.
range.
3. Generic match: `f` is a generic type and `a` matches, for
instance `a` is `int` and `f` is a generic (constrained) parameter
type (like in `[T]` or `[T: int|char]`).
type (like in `[T]` or `[T: int|char]`). Constraints given an alias (as in `T`)
shall be used to define `f`, when `f` is composed of `T`, following simple variable
substitution.
4. Subrange or subtype match: `a` is a `range[T]` and `T`
matches `f` exactly. Or: `a` is a subtype of `f`.
5. Integral conversion match: `a` is convertible to `f` and `f` and `a`
Expand All @@ -2647,9 +2649,8 @@ of the argument.
There are two major methods of selecting the best matching candidate, namely
counting and disambiguation. Counting takes precedence to disambiguation. In counting,
each parameter is given a category and the number of parameters in each category is counted.
The categories are listed above and are in order of precedence. For example, if
a candidate with one exact match is compared to a candidate with multiple generic matches
and zero exact matches, the candidate with an exact match will win.
For example, if a candidate with one exact match is compared to a candidate with multiple
generic matches and zero exact matches, the candidate with an exact match will win.

In the following, `count(p, m)` counts the number of matches of the matching category `m`
for the routine `p`.
Expand All @@ -2669,10 +2670,12 @@ algorithm returns true:
return "ambiguous"
```

When counting is ambiguous, disambiguation begins. Parameters are iterated
by position and these parameter pairs are compared for their type relation. The general goal
of this comparison is to determine which parameter is more specific. The types considered are
not of the inputs from the callsite, but of the competing candidates' parameters.
When counting is ambiguous, disambiguation begins. Disamgiuation also has two stages, first a
Araq marked this conversation as resolved.
Show resolved Hide resolved
hierarchical type relation comparison, and if that is inconclusive, a complexity comparison.
Where counting relates the type of the operand to the formal parameter, disambiguation relates the
formal parameters with each other to find the most competitive choice.
Parameters are iterated by position and these parameter pairs are compared for their type
relation. The general goal of this comparison is to determine which parameter is least general.


Some examples:
Expand All @@ -2692,7 +2695,6 @@ Some examples:
```


If this algorithm returns "ambiguous" further disambiguation is performed:
If the argument `a` matches both the parameter type `f` of `p`
and `g` of `q` via a subtyping relation, the inheritance depth is taken
into account:
Expand Down Expand Up @@ -2734,6 +2736,23 @@ matches) is preferred:
gen(ri) # "ref T"
```

Type variables match
----------------------

When overload resolution is considering candidates, the type variable's definition
is not overlooked as it is used to define the formal parameter's type via variable substitution.

For example:
```nim
type A
proc p[T: A](param: T)
proc p[T: object](param: T)
```

These signatures are not ambiguous for an instance of `A` even though the formal parameters match ("T" == "T").
Instead `T` is treated as a variable in that (`T` ?= `T`) depending on the bound type of `T` at the time of
overload resoliution.
Araq marked this conversation as resolved.
Show resolved Hide resolved


Overloading based on 'var T'
--------------------------------------
Expand Down Expand Up @@ -5424,6 +5443,7 @@ Generics are Nim's means to parametrize procs, iterators or types with
`type parameters`:idx:. Depending on the context, the brackets are used either to
introduce type parameters or to instantiate a generic proc, iterator, or type.


The following example shows how a generic binary tree can be modeled:

```nim test = "nim c $1"
Expand Down
57 changes: 57 additions & 0 deletions tests/concepts/t976.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
discard """
output: '''
Printable
'''
joinable: false
"""

#[
The converter is a proper example of a confounding variable
Moved to an isolated file
]#

type
Obj1[T] = object
v: T
converter toObj1[T](t: T): Obj1[T] =
return Obj1[T](v: t)
block t976:
type
int1 = distinct int
int2 = distinct int
int1g = concept x
x is int1
int2g = concept x
x is int2

proc take[T: int1g](value: int1) =
when T is int2:
static: error("killed in take(int1)")

proc take[T: int2g](vale: int2) =
when T is int1:
static: error("killed in take(int2)")

var i1: int1 = 1.int1
var i2: int2 = 2.int2

take[int1](i1)
take[int2](i2)

template reject(e) =
static: assert(not compiles(e))

reject take[string](i2)
reject take[int1](i2)

# bug #6249
type
Obj2 = ref object
PrintAble = concept x
$x is string

proc `$`[T](nt: Obj1[T]): string =
when T is PrintAble: result = "Printable"
else: result = "Non Printable"

echo Obj2()
50 changes: 0 additions & 50 deletions tests/concepts/tconcepts_issues.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
discard """
output: '''
20.0 USD
Printable
true
true
true
Expand Down Expand Up @@ -78,55 +77,6 @@ block t3414:
let s2 = s1.find(10)



type
Obj1[T] = object
v: T
converter toObj1[T](t: T): Obj1[T] =
return Obj1[T](v: t)
block t976:
type
int1 = distinct int
int2 = distinct int
int1g = concept x
x is int1
int2g = concept x
x is int2

proc take[T: int1g](value: int1) =
when T is int2:
static: error("killed in take(int1)")

proc take[T: int2g](vale: int2) =
when T is int1:
static: error("killed in take(int2)")

var i1: int1 = 1.int1
var i2: int2 = 2.int2

take[int1](i1)
take[int2](i2)

template reject(e) =
static: assert(not compiles(e))

reject take[string](i2)
reject take[int1](i2)

# bug #6249
type
Obj2 = ref object
PrintAble = concept x
$x is string

proc `$`[T](nt: Obj1[T]): string =
when T is PrintAble: result = "Printable"
else: result = "Non Printable"

echo Obj2()



block t1128:
type
TFooContainer[T] = object
Expand Down
2 changes: 0 additions & 2 deletions tests/macros/tgetimpl.nim
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ static:
doAssert isSameOwner(poo, dummyproc) == false
wrappedScope()

#---------------------------------------------------------------

macro check_gen_proc(ex: typed): (bool, bool) =
let lenChoice = bindsym"len"
var is_equal = false
Expand Down
10 changes: 10 additions & 0 deletions tests/overload/issue22142/tfail_implicit_ambiguous.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
discard """
errormsg: "ambiguous call"
"""
type
A[T] = object
C = object

proc test[T: A](param: T): bool = false
proc test(param: A): bool = true
doAssert test(A[C]()) == true # previously would pass
12 changes: 12 additions & 0 deletions tests/overload/issue22142/tfail_nested_pointers.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
discard """
errormsg: "ambiguous call"
"""

type
A[T] = object
C = object
x:int
proc p[T: A[ptr]](x:ptr[T]):bool = false
proc p(x: ptr[A[ptr]]):bool = true
var a: A[ptr[C]]
doAssert p(a.addr) == true
Loading
Loading