Skip to content

New specification for dropped weak conformance. #5358

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

Merged
merged 1 commit into from
Nov 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
46 changes: 46 additions & 0 deletions docs/docs/reference/dropped/weak-conformance-spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
layout: doc-page
title: Dropped: Weak Conformance - More Details
---

To simplify the underlying type theory, Dotty drops the notion of weak
conformance altogether. Instead, it provides more flexibility when
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized that in the spec, the definition of numeric widening depends on the definition of weak conformance, from https://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html:

If e has a primitive number type which weakly conforms to the expected type, it is widened to the expected type using one of the numeric conversion methods toShort, toChar, toInt, toLong, toFloat, toDouble defined here.

So we'll need to rewrite that definition (relatedly, I'm also trying to restrict what numeric widening can do in scala/scala#7405)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it seems that we simply don't have any notion of numeric widening in Dotty, we just rely on the implicit conversions defined in Int.scala/Float.scala/etc

assigning a type to a constant expression. The new rule is:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good if this rule declared its name, which I think is "harmonize" (and also known as "new weak conformance").


- If a list of expressions `Es` appears as one of

- the elements of a vararg parameter, or
- the alternatives of an if-then-else or match expression, or
- the body and catch results of a try expression,


and all expressions have primitive numeric types, but they do not
all have the same type, then the following is attempted:

- the expressions `Es` are partitioned into `Int` literals on the
one hand, and all other expressions on the other hand
- if all the other expressions have the same numeric type `T`
(which can be one of `Byte`, `Short`, `Int`, `Long`, `Float`,
`Double`), possibly after widening, and if none of the `Int`
literals would incur a loss of precision when converted to `T`,
then they are thus converted (the other expressions are left
unchanged regardless)
- otherwise, the expressions `Es` are used unchanged

A loss of precision occurs for an `Int -> Float` conversion of a constant
`c` if `c.toFloat.toInt != c`. For an `Int -> Byte` conversion it occurs
if `c.toByte.toInt != c`. For an `Int -> Short` conversion, it occurs
if `c.toShort.toInt != c`.

__Examples:__

inline val b = 33
def f(): Int = b + 1
List(b, 33, 5.5) : List[Double] // b is an inline val
List(f(), 33, 5.5) : List[AnyVal] // f() is not a constant
List(5, 11L) : List[Long]
List(5, 11L, 5.5) : List[AnyVal] // Long and Double found
List(1.0f, 2) : List[Float]
List(1.0f, 1234567890): List[AnyVal] // loss of precision
List(b, 33, 'a') : List[AnyVal] // Char is not a numeric
List(5.toByte, 11) : List[Byte]
72 changes: 13 additions & 59 deletions docs/docs/reference/dropped/weak-conformance.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,62 +27,16 @@ Here, it is less clear why the type should be widened to
`List[Double]`, a `List[AnyVal]` seems to be an equally valid -- and
more principled -- choice.

To simplify the underlying type theory, Dotty drops the notion of weak
conformance altogether. Instead, it provides more flexibility when
assigning a type to a constant expression. The new rule is:

- If a list of expressions `Es` appears as one of

- the elements of a vararg parameter, or
- the alternatives of an if-then-else or match expression, or
- the body and catch results of a try expression,


and all expressions have primitive numeric types, but they do not
all have the same type, then the following is attempted: Every
constant expression `E` in `Es` is widened to the least primitive
numeric value type equal to or above the types of all expressions in `Es`,
if that can be done without a loss of precision. Here
_above_ and _least_ are interpreted according to the ordering given
below.


Double
/ \
Long Float
\ /
Int
/ \
Short Char
|
Byte

A loss of precision occurs for an `Int -> Float` conversion of a constant
`c` if `c.toFloat.toInt != c`. For a `Long -> Double` conversion it occurs
if `c.toDouble.toLong != c`.

If these widenings lead to all widened expressions having the same type,
we use the widened expressions instead of `Es`, otherwise we use `Es` unchanged.

__Examples:__

inline val b = 33
def f(): Int = b + 1
List(b, 33, 'a') : List[Int]
List(b, 33, 'a', f()) : List[Int]
List(1.0f, 'a', 0) : List[Float]
List(1.0f, 1L) : List[Double]
List(1.0f, 1L, f()) : List[AnyVal]
List(1.0f, 1234567890): List[AnyVal]

The expression on the second-to-last line has type `List[AnyVal]`,
since widenings only affect constants. Hence, `1.0f` and `1L` are
widened to `Double`, but `f()` still has type `Int`. The elements
don't agree on a type after widening, hence the elements are left
unchanged.

The expression on the last line has type `List[AnyVal]` because
`1234567890` cannot be converted to a `Float` without a loss of
precision.


Weak conformance applies to all "numeric" types (including `Char`), and
independently of whether the expressions are literals or not. However,
in hindsight, the only intended use case is for *integer literals* to
be adapted to the type of the other expressions. Other types of numerics
have an explicit type annotation embedded in their syntax (`f`, `d`,
`.`, `L` or `'` for `Char`s) which ensures that their author really
meant them to have that specific type).

Therefore, Dotty drops the general notion of weak conformance, and
instead keeps one rule: `Int` literals are adapted to other numeric
types if necessary.

[More details](weak-conformance-spec.html)