Skip to content

Commit f5c758f

Browse files
committed
Make inline val transparent
Inline vals aim to be a replacement of final vals with inferred literal types. They inline by having a literal type as a return type. In inline def terms, this is equivalent to transparent inline def returning the constant which is also stable. ``` inline val x = 3 transparent def y: Int = 3 ``` Therefore, such a definition of an inline val is always a transparent inline. ``` /*transparent*/ inline val x = 3 ``` If we define inline vals as always being transparent inline vals, then the constant type becomes an implementation detail rather than a restriction. This would imply that the following inline val are allowed. ``` inline val a: Byte = 1 inline val b: Short = 2 inline val c: Int = 3 ``` Note that with literal types it is impossible to directly implement `a` and `b` as those need a type annotation to infer their type, but the type would be widened. New `inline val` definition: * An `inline val` is `transparent` * An `inline val` is `final` or `abstract` * The RHS of an `inline val` must be a pure constant value Advantages * Aligns better with inline defs * Can define constant `Byte` and `Short` The proposed implementation would just reuse the same literal constant mechanism but modify slightly type inference for inline vals. When we type an inline val we use the return type to type/infer the type of the RHS, but then if the return type is not a constant, the return unwidened type is replaced by the type of the RHS. ``` inline val x: Int = 3 // typed as inline val x: 3 = 3 ```
1 parent 6b2ef64 commit f5c758f

File tree

7 files changed

+40
-29
lines changed

7 files changed

+40
-29
lines changed

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

+15-9
Original file line numberDiff line numberDiff line change
@@ -882,18 +882,24 @@ trait Checking {
882882
}
883883
}
884884

885-
/** Check that `tree` can be right hand-side or argument to `inline` value or parameter. */
886-
def checkInlineConformant(tpt: Tree, tree: Tree, sym: Symbol)(using Context): Unit = {
885+
/** Check that `tree` can be right hand-side or argument to `inline` value.
886+
* Returns the type (tpt) of the val, possibly adapted.
887+
*/
888+
def checkInlineConformant(tpt: Tree, tree: Tree, sym: Symbol)(using Context): Tree = {
887889
if sym.is(Inline, butNot = DeferredOrTermParamOrAccessor) && !ctx.erasedTypes && !ctx.inInlineMethod then
888-
// final vals can be marked inline even if they're not pure, see Typer#patchFinalVals
889-
val purityLevel = if (sym.is(Final)) Idempotent else Pure
890-
tpt.tpe.widenTermRefExpr.dealias match
890+
tree.tpe.widenTermRefExpr.dealias match
891891
case tp: ConstantType =>
892+
// final vals can be marked inline even if they're not pure, see Typer#patchFinalVals
893+
val purityLevel = if (sym.is(Final)) Idempotent else Pure
892894
if !(exprPurity(tree) >= purityLevel) then
893-
ctx.error(em"inline value must be pure", tree.sourcePos)
895+
ctx.error("inline value must be pure", tree.sourcePos)
896+
if tree.tpe =:= tpt.tpe then tpt
897+
else TypeTree(tree.tpe).withSpan(tpt.span)
894898
case _ =>
895-
val pos = if tpt.span.isZeroExtent then tree.sourcePos else tpt.sourcePos
896-
ctx.error(em"inline value must have a literal constant type", pos)
899+
ctx.error("inline value must have a literal constant type", tree.sourcePos)
900+
tpt
901+
else
902+
tpt
897903
}
898904

899905
/** A hook to exclude selected symbols from double declaration check */
@@ -1222,7 +1228,7 @@ trait NoChecking extends ReChecking {
12221228
override def checkImplicitConversionDefOK(sym: Symbol)(using Context): Unit = ()
12231229
override def checkImplicitConversionUseOK(sym: Symbol, posd: Positioned)(using Context): Unit = ()
12241230
override def checkFeasibleParent(tp: Type, pos: SourcePosition, where: => String = "")(using Context): Type = tp
1225-
override def checkInlineConformant(tpt: Tree, tree: Tree, sym: Symbol)(using Context): Unit = ()
1231+
override def checkInlineConformant(tpt: Tree, tree: Tree, sym: Symbol)(using Context): Tree = tpt
12261232
override def checkNoAlphaConflict(stats: List[Tree])(using Context): Unit = ()
12271233
override def checkParentCall(call: Tree, caller: ClassSymbol)(using Context): Unit = ()
12281234
override def checkSimpleKinded(tpt: Tree)(using Context): Tree = tpt

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -1801,9 +1801,9 @@ class Typer extends Namer
18011801
case rhs @ Ident(nme.WILDCARD) => rhs withType tpt1.tpe
18021802
case rhs => typedExpr(rhs, tpt1.tpe.widenExpr)
18031803
}
1804-
val vdef1 = assignType(cpy.ValDef(vdef)(name, tpt1, rhs1), sym)
1804+
val tpt2 = checkInlineConformant(tpt1, rhs1, sym)
1805+
val vdef1 = assignType(cpy.ValDef(vdef)(name, tpt2, rhs1), sym)
18051806
checkSignatureRepeatedParam(sym)
1806-
checkInlineConformant(tpt1, rhs1, sym)
18071807
patchFinalVals(vdef1)
18081808
vdef1.setDefTree
18091809
}

docs/docs/reference/metaprogramming/inline.md

+9-12
Original file line numberDiff line numberDiff line change
@@ -235,18 +235,6 @@ inline val four = 4
235235
inline val four: 4 = 4
236236
```
237237

238-
It is also possible to have inline vals of types that do not have a syntax, such as `Short(4)`.
239-
240-
```scala
241-
trait InlineConstants {
242-
inline val myShort: Short
243-
}
244-
245-
object Constants extends InlineConstants {
246-
inline val myShort/*: Short(4)*/ = 4
247-
}
248-
```
249-
250238
## Transparent Inline Methods
251239

252240
Inline methods can additionally be declared `transparent`.
@@ -287,6 +275,15 @@ transparent inline def zero(): Int = 0
287275
val one: 1 = zero() + 1
288276
```
289277

278+
All `inline val`s effectively are transparent. This allows any `inline val` to be costant folded.
279+
280+
```scala
281+
inline val one: Int = 1
282+
283+
val two: 2 = one + one
284+
```
285+
286+
290287
## Inline Conditionals
291288

292289
If the condition of an if-then-else expressions is a constant expression then it simplifies to

tests/neg/i8841.check

-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
-- Error: tests/neg/i8841.scala:2:20 -----------------------------------------------------------------------------------
2-
2 | inline val log1 : Boolean = false // error
3-
| ^^^^^^^
4-
| inline value must have a literal constant type
51
-- Error: tests/neg/i8841.scala:3:20 -----------------------------------------------------------------------------------
62
3 | inline val log2 = true: Boolean // error
73
| ^^^^^^^^^^^^^

tests/neg/i8841.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
object Foo {
2-
inline val log1 : Boolean = false // error
2+
inline val log1 : Boolean = false
33
inline val log2 = true: Boolean // error
44
inline val log3: false = { println(); false } // error
55
}

tests/neg/inline-val.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11

22
inline val a = 1 : Int // error
3-
inline val b: Int = 1 // error
3+
inline val b: Int = 1
44
inline val c = b // error

tests/pos/inline-vals.scala

+12
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,15 @@ object Constants extends InlineConstants {
2121
inline val myInlinedChar = 'a'
2222
inline val myInlinedString = "abc"
2323
}
24+
25+
class Constants2 {
26+
inline val myInlinedBoolean: Boolean = true
27+
inline val myInlinedByte: Byte = 1
28+
inline val myInlinedShort: Short = 2
29+
inline val myInlinedInt: Int = 3
30+
inline val myInlinedLong: Long = 4
31+
inline val myInlinedFloat: Float = 5
32+
inline val myInlinedDouble: Double = 6
33+
inline val myInlinedChar: Char = 'a'
34+
inline val myInlinedString: String = "abc"
35+
}

0 commit comments

Comments
 (0)