Skip to content

Add Constant and replace transparent parameters #4942

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

Closed
wants to merge 2 commits into from
Closed
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
2 changes: 1 addition & 1 deletion bench/tests/power-macro/PowerMacro.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import scala.quoted.Expr

object PowerMacro {

transparent def power(transparent n: Long, x: Double) = ~powerCode(n, '(x))
transparent def power(n: Long & Constant, x: Double) = ~powerCode(n, '(x))

def powerCode(n: Long, x: Expr[Double]): Expr[Double] =
if (n == 0) '(1.0)
Expand Down
9 changes: 7 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,12 @@ class Definitions {
List(AnyClass.typeRef), EmptyScope)
lazy val SingletonType: TypeRef = SingletonClass.typeRef

lazy val ConstantClass: ClassSymbol =
// TODO needs to be synthetic as is SingletonClass?
enterCompleteClassSymbol(
ScalaPackageClass, tpnme.Constant, PureInterfaceCreationFlags | Final,
List(SingletonType), EmptyScope)

lazy val SeqType: TypeRef =
if (isNewCollections) ctx.requiredClassRef("scala.collection.immutable.Seq")
else ctx.requiredClassRef("scala.collection.Seq")
Expand Down Expand Up @@ -734,8 +740,6 @@ class Definitions {
def ImplicitNotFoundAnnot(implicit ctx: Context) = ImplicitNotFoundAnnotType.symbol.asClass
lazy val ForceInlineAnnotType = ctx.requiredClassRef("scala.forceInline")
def ForceInlineAnnot(implicit ctx: Context) = ForceInlineAnnotType.symbol.asClass
lazy val TransparentParamAnnotType = ctx.requiredClassRef("scala.annotation.internal.TransparentParam")
def TransparentParamAnnot(implicit ctx: Context) = TransparentParamAnnotType.symbol.asClass
lazy val InvariantBetweenAnnotType = ctx.requiredClassRef("scala.annotation.internal.InvariantBetween")
def InvariantBetweenAnnot(implicit ctx: Context) = InvariantBetweenAnnotType.symbol.asClass
lazy val MigrationAnnotType = ctx.requiredClassRef("scala.annotation.migration")
Expand Down Expand Up @@ -1209,6 +1213,7 @@ class Definitions {
NullClass,
NothingClass,
SingletonClass,
ConstantClass,
EqualsPatternClass)

lazy val syntheticCoreClasses = syntheticScalaClasses ++ List(
Expand Down
3 changes: 0 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -563,9 +563,6 @@ object Flags {
/** A transparent implicit method */
final val TransparentImplicitMethod = allOf(Transparent, Implicit, Method)

/** A transparent parameter */
final val TransparentParam = allOf(Transparent, Param)

/** An enum case */
final val EnumCase = allOf(Enum, Case)

Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
if (base.exists && base.ne(tp1))
return isSubType(base, tp2, if (tp1.isRef(cls2)) approx else approx.addLow)
if (cls2 == defn.SingletonClass && tp1.isStable) return true
if (cls2 == defn.ConstantClass && tp1.isConstant) return true
}
else if (cls2.is(JavaDefined)) {
// If `cls2` is parameterized, we are seeing a raw type, so we need to compare only the symbol
Expand Down
19 changes: 10 additions & 9 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ object Types {
case _ => false
}

/** Does this type denote a constant type */
final def isConstant(implicit ctx: Context): Boolean = stripTypeVar match {
case _: ConstantType => true
case tp: ExprType => tp.resultType.isConstant
case tp: AnnotatedType => tp.parent.isConstant
case _ => false
}

/** Is this type a (possibly refined or applied or aliased) type reference
* to the given type symbol?
* @sym The symbol to compare to. It must be a class symbol or abstract type.
Expand Down Expand Up @@ -3008,15 +3016,8 @@ object Types {
* - add @inlineParam to transparent call-by-value parameters
*/
def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context) = {
def translateTransparent(tp: Type): Type = tp match {
case _: ExprType => tp
case _ => AnnotatedType(tp, Annotation(defn.TransparentParamAnnot))
}
def paramInfo(param: Symbol) = {
val paramType = param.info.annotatedToRepeated
if (param.is(Transparent)) translateTransparent(paramType) else paramType
}

def paramInfo(param: Symbol) =
param.info.annotatedToRepeated
apply(params.map(_.name.asTermName))(
tl => params.map(p => tl.integrate(params, paramInfo(p))),
tl => tl.integrate(params, resultType))
Expand Down
9 changes: 4 additions & 5 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1913,12 +1913,12 @@ object Parsers {
/** ClsParamClauses ::= {ClsParamClause} [[nl] `(' [FunArgMods] ClsParams `)']
* ClsParamClause ::= [nl] `(' [`erased'] [ClsParams] ')'
* ClsParams ::= ClsParam {`' ClsParam}
* ClsParam ::= {Annotation} [{Modifier} (`val' | `var') | `inline'] Param
* ClsParam ::= {Annotation} [{Modifier} (`val' | `var')] Param
* DefParamClauses ::= {DefParamClause} [[nl] `(' [FunArgMods] DefParams `)']
* DefParamClause ::= [nl] `(' [`erased'] [DefParams] ')'
* DefParams ::= DefParam {`,' DefParam}
* DefParam ::= {Annotation} [`inline'] Param
* Param ::= id `:' ParamType [`=' Expr]
* DefParam ::= {Annotation} Param
* Param ::= id :' ParamType [`=' Expr]
*/
def paramClauses(owner: Name, ofCaseClass: Boolean = false): List[List[ValDef]] = {
var imods: Modifiers = EmptyModifiers
Expand All @@ -1940,15 +1940,14 @@ object Parsers {
addMod(mods, mod)
}
else {
if (!(mods.flags &~ (ParamAccessor | Transparent)).isEmpty)
if (!(mods.flags &~ ParamAccessor).isEmpty)
syntaxError("`val' or `var' expected")
if (firstClauseOfCaseClass) mods
else mods | PrivateLocal
}
}
}
else {
if (in.token == TRANSPARENT) mods = addModifier(mods)
mods = atPos(start) { mods | Param }
}
atPos(start, nameStart) {
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
super.transform(_).asInstanceOf[Template]))
}
case tree: ValDef =>
if (tree.symbol.is(Param) && !tree.symbol.owner.is(Transparent) && tree.tpt.tpe.derivesFrom(defn.ConstantClass))
ctx.error("only parameters of transparent method can have Constant type", tree.pos)
val tree1 = cpy.ValDef(tree)(rhs = normalizeErasedRhs(tree.rhs, tree.symbol))
transformMemberDef(tree1)
super.transform(tree1)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ class ReifyQuotes extends MacroTransformWithImplicits {
val captured = mutable.LinkedHashMap.empty[Symbol, Tree]
val captured2 = capturer(captured)

outer.enteredSyms.foreach(sym => if (!sym.is(Transparent)) capturers.put(sym, captured2))
outer.enteredSyms.foreach(sym => if (!sym.info.derivesFrom(defn.ConstantClass)) capturers.put(sym, captured2))

val tree2 = transform(tree)
capturers --= outer.enteredSyms
Expand Down Expand Up @@ -550,7 +550,7 @@ class ReifyQuotes extends MacroTransformWithImplicits {
splice(ref(splicedType).select(tpnme.UNARY_~).withPos(tree.pos))
case tree: Select if tree.symbol.isSplice =>
splice(tree)
case tree: RefTree if tree.symbol.is(Transparent) && tree.symbol.is(Param) =>
case tree: RefTree if tree.symbol.is(Param) && tree.tpe.derivesFrom(defn.ConstantClass) =>
tree
case tree: RefTree if isCaptured(tree.symbol, level) =>
val t = capturers(tree.symbol).apply(tree)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/Splicer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,9 @@ object Splicer {

def unexpectedTree(tree: tpd.Tree)(implicit env: Env): Boolean = {
// Assuming that top-level splices can only be in transparent methods
// and splices are expanded at inline site, references to transparent values
// and splices are expanded at inline site, references to parameter wit constant type
// will be know literal constant trees.
tree.symbol.is(Transparent)
tree.symbol.is(Param) && tree.tpe.derivesFrom(defn.ConstantClass)
}
}

Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,9 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
bindingsBuf: mutable.ListBuffer[MemberDef]): MemberDef = {
val argtpe = arg.tpe.dealiasKeepAnnots
val isByName = paramtp.dealias.isInstanceOf[ExprType]
val inlineFlag = if (paramtp.hasAnnotation(defn.TransparentParamAnnot)) Transparent else EmptyFlags
val (bindingFlags, bindingType) =
if (isByName) (Method, ExprType(argtpe.widen))
else (inlineFlag, argtpe.widen)
else (EmptyFlags, argtpe.widen)
val boundSym = newSym(name, bindingFlags, bindingType).asTerm
val binding =
if (isByName) DefDef(boundSym, arg.changeOwner(ctx.owner, boundSym))
Expand Down
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2385,8 +2385,6 @@ class Typer extends Namer
readaptSimplified(Inliner.inlineCall(tree, pt))
}
else if (tree.tpe <:< pt) {
if (pt.hasAnnotation(defn.TransparentParamAnnot))
checkTransparentConformant(tree, isFinal = false, "argument to transparent parameter")
if (ctx.typeComparer.GADTused && pt.isValueType)
// Insert an explicit cast, so that -Ycheck in later phases succeeds.
// I suspect, but am not 100% sure that this might affect inferred types,
Expand Down
6 changes: 0 additions & 6 deletions library/src/scala/annotation/internal/TransparentParam.scala

This file was deleted.

20 changes: 20 additions & 0 deletions tests/neg/constant-types.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
object Constants {

def f[C <: Constant](c: C): Unit = ()
val x = 1
f(x) // error

def fBool(c: Boolean & Constant): Unit = ()
val b = true
fBool(b) // error


def fInt(c: Int & Constant): Unit = ()
val i = 3
fInt(i) // error

def fChar(c: Char & Constant): Unit = ()
val c = 'c'
fChar(c) // error

}
2 changes: 1 addition & 1 deletion tests/neg/i4433.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

object Foo {
transparent def g(transparent p: Int => Boolean): Boolean = ~{ // error
transparent def g(p: (Int => Boolean) & Constant): Boolean = ~{ // error
if(p(5)) '(true)
else '(false)
}
Expand Down
20 changes: 20 additions & 0 deletions tests/neg/inlinevals-2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
object Test {

def power0(x: Double, n: Int & Constant): Double = ??? // error

transparent def power(x: Double, n: Int & Constant): Double = ??? // ok

transparent val N = 10
def X = 20

class C(x: Int & Constant, private val y: Int & Constant) // error // error

transparent def foo(x: Int) = {

def f(xs: List[Int] & Constant) = xs // error

transparent val y = { println("hi"); 1 } // ok
transparent val z = x // ok

}
}
19 changes: 4 additions & 15 deletions tests/neg/inlinevals.scala
Original file line number Diff line number Diff line change
@@ -1,35 +1,24 @@
object Test {

def power0(x: Double, transparent n: Int): Double = ??? // error

transparent def power(x: Double, transparent n: Int): Double = ??? // ok
transparent def power(x: Double, n: Int & Constant): Double = ??? // ok

transparent val N = 10
def X = 20

transparent transparent val twice = 30 // error: repeated modifier

class C(transparent x: Int, private transparent val y: Int) { // error // error
class C {
transparent val foo: Int // error: abstract member may not be inline
transparent 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, N) // ok, since parameter is inlined
power(2.0, X) // error: argument to transparent parameter must be a constant expression

transparent val M = X // error: rhs must be constant expression

transparent val xs = List(1, 2, 3) // error: must be a constant expression

transparent def foo(x: Int) = {

def f(transparent xs: List[Int]) = xs // error

transparent val y = { println("hi"); 1 } // ok
transparent val z = x // ok

}

transparent def byname(transparent f: => String): Int = ??? // ok
transparent def byname(f: => String): Int = ??? // ok

}
2 changes: 1 addition & 1 deletion tests/neg/quote-error-2/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import quoted._

object Macro_1 {
transparent def foo(transparent b: Boolean): Unit = ~fooImpl(b)
transparent def foo(b: Boolean & Constant): Unit = ~fooImpl(b)
def fooImpl(b: Boolean): Expr[Unit] =
'(println(~msg(b)))

Expand Down
2 changes: 1 addition & 1 deletion tests/neg/quote-error/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import quoted._

object Macro_1 {
transparent def foo(transparent b: Boolean): Unit = ~fooImpl(b)
transparent def foo(b: Boolean & Constant): Unit = ~fooImpl(b)
def fooImpl(b: Boolean): Expr[Unit] =
if (b) '(println("foo(true)"))
else QuoteError("foo cannot be called with false")
Expand Down
2 changes: 1 addition & 1 deletion tests/neg/quote-exception/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import quoted._

object Macro_1 {
transparent def foo(transparent b: Boolean): Unit = ~fooImpl(b)
transparent def foo(b: Boolean & Constant): Unit = ~fooImpl(b)
def fooImpl(b: Boolean): Expr[Unit] =
if (b) '(println("foo(true)"))
else ???
Expand Down
2 changes: 1 addition & 1 deletion tests/neg/quote-macro-complex-arg-0.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import scala.quoted._

object Macros {
transparent def foo(transparent i: Int, dummy: Int, j: Int): Int = ~bar(i + 1, '(j)) // error: i + 1 is not a parameter or field reference
transparent def foo(i: Int & Constant, dummy: Int, j: Int): Int = ~bar(i + 1, '(j)) // error: i + 1 is not a parameter or field reference
def bar(x: Int, y: Expr[Int]): Expr[Int] = '{ ~x.toExpr + ~y }
}
2 changes: 1 addition & 1 deletion tests/neg/quote-splice-interpret-1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import scala.quoted._

object Macros {
transparent def isZero(transparent n: Int): Boolean = ~{ // error
transparent def isZero(n: Int & Constant): Boolean = ~{ // error
if (n == 0) '(true)
else '(false)
}
Expand Down
30 changes: 30 additions & 0 deletions tests/pos/constant-types.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
object Constants {

transparent def f[C <: Constant](c: C): Unit = ()
f(true)
f(1)
f(2L)
f(1.2f)
f(1.2d)
f('a')
f("abc")

transparent def fBool(c: Boolean & Constant): Unit = ()
fBool(true)
fBool(false)
transparent def bb: Boolean = true
fBool(bb)

transparent def fInt(c: Int & Constant): Unit = ()
fInt(1)
fInt(2)
transparent def ii: Int = 4
fInt(ii)

transparent def fChar(c: Char & Constant): Unit = ()
fChar('a')
fChar('b')
transparent def cc: Char = 'd'
fChar(cc)

}
4 changes: 2 additions & 2 deletions tests/pos/i1570.decompiled
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** Decompiled from out/posTestFromTasty/pos/i1570/Test.class */
object Test {
transparent def foo(n: scala.Int): scala.Int = Test.bar(n)
transparent def bar(n: scala.Int): scala.Int = n
transparent def foo(n: scala.Int & scala.Constant): scala.Int & scala.Constant = Test.bar(n)
transparent def bar(n: scala.Int & scala.Constant): scala.Int & scala.Constant = n
}
4 changes: 2 additions & 2 deletions tests/pos/i1570.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
object Test {
transparent def foo(transparent n: Int) = bar(n)
transparent def bar(transparent n: Int) = n
transparent def foo(n: Int & Constant) = bar(n)
transparent def bar(n: Int & Constant) = n
}
2 changes: 1 addition & 1 deletion tests/pos/i3898b/quoted_1.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import scala.quoted._
object Macro {
transparent def ff(x: Int, transparent y: Int): String = ~impl('(x))
transparent def ff(x: Int, y: Int & Constant): String = ~impl('(x))
def impl(x: Expr[Int]): Expr[String] = '("")
}
2 changes: 1 addition & 1 deletion tests/pos/i3898c/quoted_1.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import scala.quoted._
object Macro {
transparent def ff(x: Int, transparent y: Int): String = ~impl('(x))
transparent def ff(x: Int, y: Int & Constant): String = ~impl('(x))
def impl(x: Expr[Int]): Expr[String] = '("")
}
2 changes: 1 addition & 1 deletion tests/pos/i4846.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import scala.quoted._

object Test {
transparent def foo(transparent x: Int): Int = ~fooImpl(x, '(x), '( '(x) ), '( '( '(x) ) ))
transparent def foo(x: Int & Constant): Int = ~fooImpl(x, '(x), '( '(x) ), '( '( '(x) ) ))
def fooImpl(a: Int, b: Expr[Int], c: Expr[Expr[Int]], d: Expr[Expr[Expr[Int]]]): Expr[Int] = ???
}
Loading