aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-09-12 13:01:28 +0200
committerMartin Odersky <odersky@gmail.com>2016-10-02 16:12:28 +0200
commit845b689a4a652fa79a7d0621f5ebe15bbf9225c7 (patch)
treece308233797b8b032b25c8ed29b6663da8742340
parentb559aff35d6d365284fc1e05a3ca49a17551df29 (diff)
downloaddotty-845b689a4a652fa79a7d0621f5ebe15bbf9225c7.tar.gz
dotty-845b689a4a652fa79a7d0621f5ebe15bbf9225c7.tar.bz2
dotty-845b689a4a652fa79a7d0621f5ebe15bbf9225c7.zip
Add inline for vals
- allow inline as an alternative to final for vals (final is retained for backwards compatibility for now) - allow inline for parameters - check that rhs of inline value has a constant type - check that arguments to inline value parameters have constant type - check that inline members are not deferred - make inline members effectively final
-rw-r--r--docs/SyntaxSummary.txt5
-rw-r--r--src/dotty/annotation/internal/InlineParam.scala6
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala2
-rw-r--r--src/dotty/tools/dotc/core/Flags.scala6
-rw-r--r--src/dotty/tools/dotc/core/Types.scala15
-rw-r--r--src/dotty/tools/dotc/parsing/Parsers.scala15
-rw-r--r--src/dotty/tools/dotc/typer/Checking.scala9
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala7
-rw-r--r--tests/neg/inlinevals.scala24
10 files changed, 81 insertions, 10 deletions
diff --git a/docs/SyntaxSummary.txt b/docs/SyntaxSummary.txt
index 6c83c71ab..519180775 100644
--- a/docs/SyntaxSummary.txt
+++ b/docs/SyntaxSummary.txt
@@ -231,14 +231,15 @@ grammar.
ClsParamClauses ::= {ClsParamClause} [[nl] `(' `implicit' ClsParams `)']
ClsParamClause ::= [nl] `(' [ClsParams] ')'
ClsParams ::= ClsParam {`' ClsParam}
- ClsParam ::= {Annotation} [{Modifier} (`val' | `var')] Param ValDef(mods, id, tpe, expr) -- point of mods on val/var
+ ClsParam ::= {Annotation}
+ [{Modifier} (`val' | `var') | `inline'] Param ValDef(mods, id, tpe, expr) -- point of mods on val/var
Param ::= id `:' ParamType [`=' Expr]
| INT
DefParamClauses ::= {DefParamClause} [[nl] `(' `implicit' DefParams `)']
DefParamClause ::= [nl] `(' [DefParams] ')'
DefParams ::= DefParam {`,' DefParam}
- DefParam ::= {Annotation} Param ValDef(mods, id, tpe, expr) -- point of mods at id.
+ DefParam ::= {Annotation} [`inline'] Param ValDef(mods, id, tpe, expr) -- point of mods at id.
Bindings ::= `(' Binding {`,' Binding `)' bindings
Binding ::= (id | `_') [`:' Type] ValDef(_, id, tpe, EmptyTree)
diff --git a/src/dotty/annotation/internal/InlineParam.scala b/src/dotty/annotation/internal/InlineParam.scala
new file mode 100644
index 000000000..a144f9edb
--- /dev/null
+++ b/src/dotty/annotation/internal/InlineParam.scala
@@ -0,0 +1,6 @@
+package dotty.annotation.internal
+
+import scala.annotation.Annotation
+
+/** An annotation produced by Namer to indicate an inline parameter */
+final class InlineParam() extends Annotation
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index 8bf340337..12677edb6 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -468,6 +468,8 @@ class Definitions {
def ImplicitNotFoundAnnot(implicit ctx: Context) = ImplicitNotFoundAnnotType.symbol.asClass
lazy val InlineAnnotType = ctx.requiredClassRef("scala.inline")
def InlineAnnot(implicit ctx: Context) = InlineAnnotType.symbol.asClass
+ lazy val InlineParamAnnotType = ctx.requiredClassRef("dotty.annotation.internal.InlineParam")
+ def InlineParamAnnot(implicit ctx: Context) = InlineParamAnnotType.symbol.asClass
lazy val InvariantBetweenAnnotType = ctx.requiredClassRef("dotty.annotation.internal.InvariantBetween")
def InvariantBetweenAnnot(implicit ctx: Context) = InvariantBetweenAnnotType.symbol.asClass
lazy val MigrationAnnotType = ctx.requiredClassRef("scala.annotation.migration")
diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala
index cf3b23c91..1a1182c7b 100644
--- a/src/dotty/tools/dotc/core/Flags.scala
+++ b/src/dotty/tools/dotc/core/Flags.scala
@@ -556,6 +556,12 @@ object Flags {
/** A type parameter or type parameter accessor */
final val TypeParamOrAccessor = TypeParam | TypeParamAccessor
+ /** A deferred member or a parameter accessor (these don't have right hand sides) */
+ final val DeferredOrParamAccessor = Deferred | ParamAccessor
+
+ /** value that's final or inline */
+ final val FinalOrInline = Final | Inline
+
/** If symbol of a type alias has these flags, prefer the alias */
final val AliasPreferred = TypeParam | BaseTypeArg | ExpandedName
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 0af673561..0c9a12701 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -2419,7 +2419,12 @@ object Types {
apply(nme.syntheticParamNames(paramTypes.length), paramTypes)(resultTypeExp)
def apply(paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType =
apply(nme.syntheticParamNames(paramTypes.length), paramTypes, resultType)
+
+ /** Produce method type from parameter symbols, with special mappings for repeated
+ * and inline parameters.
+ */
def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context) = {
+ /** Replace @repeated annotations on Seq or Array types by <repeated> types */
def translateRepeated(tp: Type): Type = tp match {
case tp @ ExprType(tp1) => tp.derivedExprType(translateRepeated(tp1))
case AnnotatedType(tp, annot) if annot matches defn.RepeatedAnnot =>
@@ -2429,7 +2434,15 @@ object Types {
case tp =>
tp
}
- def paramInfo(param: Symbol): Type = translateRepeated(param.info)
+ /** Add @inlineParam to inline call-by-value parameters */
+ def translateInline(tp: Type): Type = tp match {
+ case tp @ ExprType(tp1) => tp
+ case _ => AnnotatedType(tp, Annotation(defn.InlineParamAnnot))
+ }
+ def paramInfo(param: Symbol): Type = {
+ val paramType = translateRepeated(param.info)
+ if (param.is(Inline)) translateInline(paramType) else paramType
+ }
def transformResult(mt: MethodType) =
resultType.subst(params, (0 until params.length).toList map (MethodParam(mt, _)))
apply(params map (_.name.asTermName), params map paramInfo)(transformResult _)
diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala
index 2fd79f999..0a25bf801 100644
--- a/src/dotty/tools/dotc/parsing/Parsers.scala
+++ b/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -1641,12 +1641,13 @@ object Parsers {
/** ClsParamClauses ::= {ClsParamClause} [[nl] `(' `implicit' ClsParams `)']
* ClsParamClause ::= [nl] `(' [ClsParams] ')'
* ClsParams ::= ClsParam {`' ClsParam}
- * ClsParam ::= {Annotation} [{Modifier} (`val' | `var')] id `:' ParamType [`=' Expr]
+ * ClsParam ::= {Annotation} [{Modifier} (`val' | `var') | `inline'] Param
* DefParamClauses ::= {DefParamClause} [[nl] `(' `implicit' DefParams `)']
* DefParamClause ::= [nl] `(' [DefParams] ')'
* DefParams ::= DefParam {`,' DefParam}
- * DefParam ::= {Annotation} id `:' ParamType [`=' Expr]
- */
+ * DefParam ::= {Annotation} [`inline'] Param
+ * Param ::= id `:' ParamType [`=' Expr]
+ */
def paramClauses(owner: Name, ofCaseClass: Boolean = false): List[List[ValDef]] = {
var implicitFlag = EmptyFlags
var firstClauseOfCaseClass = ofCaseClass
@@ -1665,12 +1666,16 @@ object Parsers {
in.nextToken()
addFlag(mods, Mutable)
} else {
- if (!(mods.flags &~ ParamAccessor).isEmpty) syntaxError("`val' or `var' expected")
+ if (!(mods.flags &~ (ParamAccessor | Inline)).isEmpty)
+ syntaxError("`val' or `var' expected")
if (firstClauseOfCaseClass) mods else mods | PrivateLocal
}
}
}
- else mods = atPos(start) { mods | Param }
+ else {
+ if (in.token == INLINE) mods = addModifier(mods)
+ mods = atPos(start) { mods | Param }
+ }
atPos(start, nameStart) {
val name = ident()
val tpt =
diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala
index 415cd5d6a..42eede5e9 100644
--- a/src/dotty/tools/dotc/typer/Checking.scala
+++ b/src/dotty/tools/dotc/typer/Checking.scala
@@ -344,6 +344,7 @@ object Checking {
fail(i"only classes can have declared but undefined members$varNote")
checkWithDeferred(Private)
checkWithDeferred(Final)
+ checkWithDeferred(Inline)
}
if (sym.isValueClass && sym.is(Trait) && !sym.isRefinementClass)
fail(i"$sym cannot extend AnyVal")
@@ -479,6 +480,13 @@ trait Checking {
tp
}
+ /** Check that `tree` is a pure expression of constant type */
+ def checkInlineConformant(tree: Tree, what: => String)(implicit ctx: Context): Unit =
+ tree.tpe.widenTermRefExpr match {
+ case tp: ConstantType if isPureExpr(tree) => // ok
+ case _ => ctx.error(em"$what must be a constant expression", tree.pos)
+ }
+
/** Check that class does not define same symbol twice */
def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = {
val seen = new mutable.HashMap[Name, List[Symbol]] {
@@ -543,6 +551,7 @@ trait NoChecking extends Checking {
override def checkClassType(tp: Type, pos: Position, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type = tp
override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = ()
override def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp
+ override def checkInlineConformant(tree: Tree, what: => String)(implicit ctx: Context) = ()
override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = ()
override def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = ()
override def checkSimpleKinded(tpt: Tree)(implicit ctx: Context): Tree = tpt
diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala
index a33061453..2e714ab6d 100644
--- a/src/dotty/tools/dotc/typer/Namer.scala
+++ b/src/dotty/tools/dotc/typer/Namer.scala
@@ -891,7 +891,7 @@ class Namer { typer: Typer =>
// println(s"final inherited for $sym: ${inherited.toString}") !!!
// println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}")
- def isInline = sym.is(Final, butNot = Method | Mutable)
+ def isInline = sym.is(FinalOrInline, butNot = Method | Mutable)
// Widen rhs type and approximate `|' but keep ConstantTypes if
// definition is inline (i.e. final in Scala2).
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index d99d85fba..58fb47f2c 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -1140,6 +1140,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case rhs => typedExpr(rhs, tpt1.tpe)
}
val vdef1 = assignType(cpy.ValDef(vdef)(name, tpt1, rhs1), sym)
+ if (sym.is(Inline, butNot = DeferredOrParamAccessor))
+ checkInlineConformant(rhs1, "right-hand side of inline value")
patchIfLazy(vdef1)
vdef1
}
@@ -1808,7 +1810,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
tree
}
- else if (tree.tpe <:< pt)
+ else if (tree.tpe <:< pt) {
+ if (pt.hasAnnotation(defn.InlineParamAnnot))
+ checkInlineConformant(tree, "argument to inline parameter")
if (Inliner.hasBodyToInline(tree.symbol) &&
!ctx.owner.ownersIterator.exists(_.isInlineMethod) &&
!ctx.settings.YnoInline.value &&
@@ -1822,6 +1826,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
tree.asInstance(pt)
else
tree
+ }
else if (wtp.isInstanceOf[MethodType]) missingArgs
else {
typr.println(i"adapt to subtype ${tree.tpe} !<:< $pt")
diff --git a/tests/neg/inlinevals.scala b/tests/neg/inlinevals.scala
new file mode 100644
index 000000000..184aa2168
--- /dev/null
+++ b/tests/neg/inlinevals.scala
@@ -0,0 +1,24 @@
+object Test {
+
+ def power(x: Double, inline n: Int): Double = ???
+
+ inline val N = 10
+ def X = 20
+
+ inline inline val twice = 30 // error: repeated modifier
+
+ class C(inline x: Int, private inline val y: Int) {
+ inline val foo: Int // error: abstract member may not be inline
+ inline def bar: Int // error: abstract member may not be inline
+ }
+
+ power(2.0, N) // ok, since it's a by-name parameter
+ power(2.0, X) // error: argument to inline parameter must be a constant expression
+
+ inline val M = X // error: rhs must be constant expression
+
+ def byname(inline f: => String): Int = ??? // ok
+
+ byname("hello" ++ " world")
+
+}