Skip to content

Commit 41c4428

Browse files
committed
Add reference section
Also, adapt the algorithm to the reference (i.e. convert only if the resulting types all agree).
1 parent 45b7f14 commit 41c4428

File tree

3 files changed

+88
-4
lines changed

3 files changed

+88
-4
lines changed

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
445445
val harmonizedArgs = harmonizeArgs(typedArgs)
446446
if (harmonizedArgs ne typedArgs) {
447447
ctx.typerState.constraint = origConstraint
448-
// reset constraint, we will test constraint anyway when we
448+
// reset constraint, we will re-establish constraint anyway when we
449449
// compare against the seqliteral. The reset is needed
450450
// otherwise pos/harmonize.scala would fail on line 40.
451451
typedArgs = harmonizedArgs
@@ -1448,20 +1448,23 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
14481448
defn.isValueSubType(cls.typeRef, sup) &&
14491449
!(cls == defn.LongClass && sup.isRef(defn.FloatClass))
14501450
// exclude Long <: Float from list of allowable widenings
1451+
// TODO: should we do this everywhere we ask for isValueSubType?
14511452
val lub = defn.ScalaNumericValueTypeList.find(lubTpe =>
14521453
clss.forall(cls => isCompatible(cls, lubTpe))).get
1453-
ts.mapConserve { t =>
1454+
val ts1 = ts.mapConserve { t =>
14541455
tpe(t).widenTermRefExpr match {
14551456
case ct: ConstantType => adapt(t, lub)
14561457
case _ => t
14571458
}
14581459
}
1460+
if (numericClasses(ts1, Set()).size == 1) ts1 else ts
14591461
}
14601462
else ts
14611463
}
14621464

14631465
/** If `trees` all have numeric value types, and they do not have all the same type,
1464-
* pick a common numeric supertype and convert all trees to this type.
1466+
* pick a common numeric supertype and convert all constant trees to this type.
1467+
* If the resulting trees all have the same type, return them instead of the original ones.
14651468
*/
14661469
def harmonize(trees: List[Tree])(implicit ctx: Context): List[Tree] = {
14671470
def adapt(tree: Tree, pt: Type): Tree = tree match {
@@ -1472,7 +1475,8 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
14721475
}
14731476

14741477
/** If all `types` are numeric value types, and they are not all the same type,
1475-
* pick a common numeric supertype and return it instead of every original type.
1478+
* pick a common numeric supertype and widen any constant types in `tpes` to it.
1479+
* If the resulting types are all the same, return them instead of the original ones.
14761480
*/
14771481
private def harmonizeTypes(tpes: List[Type])(implicit ctx: Context): List[Type] =
14781482
harmonizeWith(tpes)(identity, (tp, pt) => pt)
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
layout: doc-page
3+
title: Dropped: Weak Conformance
4+
---
5+
6+
In some situations, Scala used a {\em weak conformance} relation when
7+
testing type compatibility or computing the least upper bound of a set
8+
of types. The principal motivation behind weak conformance was to
9+
make as expression like this have type `List[Double]`:
10+
11+
List(1.0, math.sqrt(3.0), 0, -3.3) // : List[Double]
12+
13+
It's "obvious" that this should be a `List[Double]`. However, without
14+
some special provision, the least upper bound of the lists's element
15+
types `(Double, Double, Int, Double)` would be `AnyVal`, hence the list
16+
expression would be given type `List[AnyVal]`.
17+
18+
A less obvious example is the following one, which was also typed as a
19+
`List[Double]`, using the weak conformance relation.
20+
21+
val n: Int = 3
22+
val c: Char = 'X'
23+
val n: Double = math.sqrt(3.0)
24+
List(n, c, d) // used to be: List[Double], now: List[AnyVal]
25+
26+
Here, it is less clear why the type should be widened to
27+
`List[Double]`, a `List[AnyVal` seems to be an equally valid -- and
28+
more principled -- choice.
29+
30+
To simplify the underlying type theory, Dotty drops the notion of weak
31+
conformance altogether. Instead, it provides more flexibility when
32+
assigning a type to a constant expression. The rule is:
33+
34+
- If a list of expressions `Es` appears as one of
35+
36+
- the elements of a vararg parameter, or
37+
- the alternatives of an if-then-else or match expression, or
38+
- the body and catch results of a try expression,
39+
40+
and all expressions have primitive numeric types, but they do not
41+
all have the same type, then the following is attempted: Every
42+
constant expression `E` in `Es` is widened to the least primitive
43+
numeric value type above the types of all expressions in `Es`. Here
44+
_above_ and _least_ are interpreted according to the ordering given
45+
below.
46+
47+
48+
Double
49+
/ \
50+
Long Float
51+
\ /
52+
Int
53+
/ \
54+
Short Char
55+
|
56+
Byte
57+
58+
If these widenings lead to all expressions `Es` having the same type,
59+
we use the transformed list of expressions instead of `Es`, otherwise
60+
we use `Es` unchanged.
61+
62+
__Examples:__
63+
64+
inline val b: Byte = 3
65+
inline val s: Short = 33
66+
def f(): Int = b + s
67+
List(b, s, 'a') : List[Int]
68+
List(b, s, 'a', f()): List[Int]
69+
List(1.0f, 'a', 0) : List[Float]
70+
List(1.0f, 1L) : List[Double]
71+
List(1.0f, 1L, f()) : List[AnyVal]
72+
73+
The expression on the last line has type `List[AnyVal]`, since widenings
74+
only affect constants. Hence, `1.0f` and `1L` are widened to `Double`,
75+
but `f()` still has type `Int`. The elements don't agree on a type after
76+
widening, hence the expressions are left unchanged.
77+
78+

docs/sidebar.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ sidebar:
9595
url: docs/reference/dropped/xml.html
9696
- title: Auto-Application
9797
url: docs/reference/dropped/auto-apply.html
98+
- title: Weak Conformance
99+
url: docs/reference/dropped/weak-conformance.html
98100
- title: Contributing
99101
subsection:
100102
- title: Getting Started

0 commit comments

Comments
 (0)