diff --git a/docs/_spec/06-expressions.md b/docs/_spec/06-expressions.md index fa21b4330728..ceb7dc4c36e5 100644 --- a/docs/_spec/06-expressions.md +++ b/docs/_spec/06-expressions.md @@ -205,8 +205,9 @@ An application `´f(e_1, ..., e_m)´` applies the method `´f´` to the argument For this expression to be well-typed, the method must be *applicable* to its arguments: If ´f´ has a method type `(´p_1´:´T_1, ..., p_n´:´T_n´)´U´`, each argument expression ´e_i´ is typed with the corresponding parameter type ´T_i´ as expected type. -Let ´S_i´ be the type of argument ´e_i´ ´(i = 1, ..., m)´. +Let ´S_i´ be the type of argument ´e_i´ ´(i = 1, ..., n)´. The method ´f´ must be _applicable_ to its arguments ´e_1, ..., e_n´ of types ´S_1, ..., S_n´. +If the last parameter type of ´f´ is [repeated](04-basic-declarations-and-definitions.html#repeated-parameters), [harmonization](#harmonization) is attempted on the suffix ´e_m, ..., e_n´ of the expression list that match the repeated parameter. We say that an argument expression ´e_i´ is a _named_ argument if it has the form `´x_i=e'_i´` and `´x_i´` is one of the parameter names `´p_1, ..., p_n´`. Once the types ´S_i´ have been determined, the method ´f´ of the above method type is said to be applicable if all of the following conditions hold: @@ -681,7 +682,8 @@ Expr1 ::= ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ E The _conditional expression_ `if (´e_1´) ´e_2´ else ´e_3´` chooses one of the values of ´e_2´ and ´e_3´, depending on the value of ´e_1´. The condition ´e_1´ is expected to conform to type `Boolean`. The then-part ´e_2´ and the else-part ´e_3´ are both expected to conform to the expected type of the conditional expression. -The type of the conditional expression is the [weak least upper bound](03-types.html#weak-conformance) of the types of ´e_2´ and ´e_3´. +If there is no expected type, [harmonization](#harmonization) is attempted on ´e_2´ and ´e_3´. +The type of the conditional expression is the [least upper bound](03-types.html#least-upper-bounds-and-greatest-lower-bounds) of the types of ´e_2´ and ´e_3´ after harmonization. A semicolon preceding the `else` symbol of a conditional expression is ignored. The conditional expression is evaluated by evaluating first ´e_1´. @@ -883,7 +885,7 @@ More generally, if the handler is a `PartialFunction`, it is applied only if it Let ´\mathit{pt}´ be the expected type of the try expression. The block ´b´ is expected to conform to ´\mathit{pt}´. The handler ´h´ is expected conform to type `scala.Function[scala.Throwable, ´\mathit{pt}\,´]`. -The type of the try expression is the [weak least upper bound](03-types.html#weak-conformance) of the type of ´b´ and the result type of ´h´. +The type of the try expression is the [least upper bound](03-types.html#least-upper-bounds-and-greatest-lower-bounds) of the type of ´b´ and the result type of ´h´. A try expression `try { ´b´ } finally ´e´` evaluates the block ´b´. If evaluation of ´b´ does not cause an exception to be thrown, the expression ´e´ is evaluated. @@ -1042,6 +1044,29 @@ When prefixing a class or object definition, modifiers `abstract`, `final`, and Evaluation of a statement sequence entails evaluation of the statements in the order they are written. +## Harmonization + +_Harmonization_ of a list of expressions tries to adapt `Int` literals to match the types of sibling trees. +For example, when writing + +```scala +scala.collection.mutable.ArrayBuffer(5.4, 6, 6.4) +``` + +the inferred element type would be `AnyVal` without harmonization. +Harmonization turns the integer literal `6` into the double literal `6.0` so that the element type becomes `Double`. + +Formally, given a list of expressions ´e_1, ..., e_n´ with types ´T_1, ..., T_n´, harmonization behaves as follows: + +1. If there is an expected type, return the original list. +2. Otherwise, if there exists ´T_i´ that is not a primitive numeric type (`Char`, `Byte`, `Short`, `Int`, `Long`, `Float`, `Double`), return the original list. +3. Otherwise, + 1. Partition the ´e_i´ into the integer literals ´f_j´ and the other expressions ´g_k´. + 2. If all the ´g_k´ have the same numeric type ´T´, possibly after widening, and if all the integer literals ´f_j´ can be converted without loss of precision to ´T´, return the list of ´e_i´ where every int literal is converted to ´T´. + 3. Otherwise, return the original list. + +Harmonization is used in [conditional expressions](#conditional-expressions) and [pattern matches](./08-pattern-matching.html), as well as in [local type inference](#local-type-inference). + ## Implicit Conversions Implicit conversions can be applied to expressions whose type does not match their expected type, to qualifiers in selections, and to unapplied methods. @@ -1063,14 +1088,10 @@ An expression ´e´ of polymorphic type which does not appear as the function part of a type application is converted to a type instance of ´T´ by determining with [local type inference](#local-type-inference) instance types `´T_1, ..., T_n´` for the type variables `´a_1, ..., a_n´` and implicitly embedding ´e´ in the [type application](#type-applications) `´e´[´T_1, ..., T_n´]`. -###### Numeric Widening -If ´e´ has a primitive number type which [weakly conforms](03-types.html#weak-conformance) 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 [in the standard library](12-the-scala-standard-library.html#numeric-value-types). - -Since conversions from `Int` to `Float` and from `Long` to `Float` or `Double` may incur a loss of precision, those implicit conversions are deprecated. -The conversion is permitted for literals if the original value can be recovered, that is, if conversion back to the original type produces the original value. +###### Numeric Literal Conversion +If the expected type is `Byte`, `Short`, `Long` or `Char`, and the expression ´e´ is an `Int` literal fitting in the range of that type, it is converted to the same literal in that type. -###### Numeric Literal Narrowing -If the expected type is `Byte`, `Short` or `Char`, and the expression ´e´ is an integer literal fitting in the range of that type, it is converted to the same literal in that type. +Likewise, if the expected type is `Float` or `Double`, and the expression ´e´ is a numeric literal (of any type) fitting in the range of that type, it is converted to the same literal in that type. ###### Value Discarding If ´e´ has some value type and the expected type is `Unit`, ´e´ is converted to the expected type by embedding it in the term `{ ´e´; () }`. diff --git a/docs/_spec/08-pattern-matching.md b/docs/_spec/08-pattern-matching.md index 1d50b814ee24..3c75d1136ac7 100644 --- a/docs/_spec/08-pattern-matching.md +++ b/docs/_spec/08-pattern-matching.md @@ -518,7 +518,8 @@ If no such bounds can be found, a compile time error results. If such bounds are found, the pattern matching clause starting with ´p´ is then typed under the assumption that each ´a_i´ has lower bound ´L_i'´ instead of ´L_i´ and has upper bound ´U_i'´ instead of ´U_i´. The expected type of every block ´b_i´ is the expected type of the whole pattern matching expression. -The type of the pattern matching expression is then the [weak least upper bound](03-types.html#weak-conformance) of the types of all blocks ´b_i´. +If there is no expected type, [harmonization](./03-types.html#harmonization) is attempted on the list of all blocks ´b_i´. +The type of the pattern matching expression is then the [least upper bound](03-types.html#least-upper-bounds-and-greatest-lower-bounds) of the types of all blocks ´b_i´ after harmonization. When applying a pattern matching expression to a selector value, patterns are tried in sequence until one is found which matches the [selector value](#patterns). Say this case is `case ´p_i \Rightarrow b_i´`. @@ -595,7 +596,7 @@ If the expected type is [SAM-convertible](06-expressions.html#sam-conversion) to ``` Here, each ´x_i´ is a fresh name. -As was shown [here](06-expressions.html#anonymous-functions), this anonymous function is in turn equivalent to the following instance creation expression, where ´T´ is the weak least upper bound of the types of all ´b_i´. +As was shown [here](06-expressions.html#anonymous-functions), this anonymous function is in turn equivalent to the following instance creation expression, where ´T´ is the least upper bound of the types of all ´b_i´. ```scala new scala.Function´k´[´S_1, ..., S_k´, ´T´] { @@ -619,7 +620,7 @@ new scala.PartialFunction[´S´, ´T´] { } ``` -Here, ´x´ is a fresh name and ´T´ is the weak least upper bound of the types of all ´b_i´. +Here, ´x´ is a fresh name and ´T´ is the least upper bound of the types of all ´b_i´. The final default case in the `isDefinedAt` method is omitted if one of the patterns ´p_1, ..., p_n´ is already a variable or wildcard pattern. ###### Example diff --git a/docs/_spec/TODOreference/dropped-features/weak-conformance.md b/docs/_spec/APPLIEDreference/dropped-features/weak-conformance.md similarity index 91% rename from docs/_spec/TODOreference/dropped-features/weak-conformance.md rename to docs/_spec/APPLIEDreference/dropped-features/weak-conformance.md index b1478326b2c9..03760642293d 100644 --- a/docs/_spec/TODOreference/dropped-features/weak-conformance.md +++ b/docs/_spec/APPLIEDreference/dropped-features/weak-conformance.md @@ -44,4 +44,5 @@ Therefore, Scala 3 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.md) +For more details, see Sections "Types > Weak Conformance" and "Expressions > Harmonization" in the specification. +TODO Link to the spec when it is published. diff --git a/docs/_spec/TODOreference/dropped-features/weak-conformance-spec.md b/docs/_spec/TODOreference/dropped-features/weak-conformance-spec.md deleted file mode 100644 index 07625dcfe885..000000000000 --- a/docs/_spec/TODOreference/dropped-features/weak-conformance-spec.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -layout: doc-page -title: "Dropped: Weak Conformance - More Details" -nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/weak-conformance-spec.html ---- - -To simplify the underlying type theory, Scala 3 drops the notion of -[*weak conformance*](https://www.scala-lang.org/files/archive/spec/2.13/03-types.html#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: - - - the expressions `Es` are partitioned into `Int` constants 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`, `Char`, `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` - - an `Int -> Byte` conversion of a constant - `c` if `c.toByte.toInt != c`, - - an `Int -> Short` conversion of a constant - `c` if `c.toShort.toInt != c`. - -## Examples - -```scala -inline val b = 33 -def f(): Int = b + 1 -Array(b, 33, 5.5) : Array[Double] // b is an inline val -Array(f(), 33, 5.5) : Array[AnyVal] // f() is not a constant -Array(5, 11L) : Array[Long] -Array(5, 11L, 5.5) : Array[AnyVal] // Long and Double found -Array(1.0f, 2) : Array[Float] -Array(1.0f, 1234567890): Array[AnyVal] // loss of precision -Array(b, 33, 'a') : Array[Char] -Array(5.toByte, 11) : Array[Byte] -```