Skip to content

Commit e994cf0

Browse files
authored
Change workings of experimental into modifier on parameter types (#19673)
`into` is now a modifier that sits on the precise parameter part where a conversion is allowed. Hint for reviewers: - Start with the doc page `into-modifier.md`, to see what changed. - Then, take a look at the doc comments of the `into` and `$into` annotations. These explain what role these annotations play in type checking. - Then the rest should make sense.
2 parents de25aa3 + cea5af1 commit e994cf0

28 files changed

+467
-132
lines changed

Diff for: compiler/src/dotty/tools/dotc/ast/Desugar.scala

+6-17
Original file line numberDiff line numberDiff line change
@@ -178,21 +178,7 @@ object desugar {
178178
val valName = normalizeName(vdef, tpt).asTermName
179179
var mods1 = vdef.mods
180180

181-
def dropInto(tpt: Tree): Tree = tpt match
182-
case Into(tpt1) =>
183-
mods1 = vdef.mods.withAddedAnnotation(
184-
TypedSplice(
185-
Annotation(defn.AllowConversionsAnnot, tpt.span.startPos).tree))
186-
tpt1
187-
case ByNameTypeTree(tpt1) =>
188-
cpy.ByNameTypeTree(tpt)(dropInto(tpt1))
189-
case PostfixOp(tpt1, op) if op.name == tpnme.raw.STAR =>
190-
cpy.PostfixOp(tpt)(dropInto(tpt1), op)
191-
case _ =>
192-
tpt
193-
194-
val vdef1 = cpy.ValDef(vdef)(name = valName, tpt = dropInto(tpt))
195-
.withMods(mods1)
181+
val vdef1 = cpy.ValDef(vdef)(name = valName).withMods(mods1)
196182

197183
if isSetterNeeded(vdef) then
198184
val setterParam = makeSyntheticParameter(tpt = SetterParamTree().watching(vdef))
@@ -1876,8 +1862,11 @@ object desugar {
18761862
assert(ctx.mode.isExpr || ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive), ctx.mode)
18771863
Select(t, op.name)
18781864
case PrefixOp(op, t) =>
1879-
val nspace = if (ctx.mode.is(Mode.Type)) tpnme else nme
1880-
Select(t, nspace.UNARY_PREFIX ++ op.name)
1865+
if op.name == tpnme.into then
1866+
Annotated(t, New(ref(defn.IntoAnnot.typeRef), Nil :: Nil))
1867+
else
1868+
val nspace = if (ctx.mode.is(Mode.Type)) tpnme else nme
1869+
Select(t, nspace.UNARY_PREFIX ++ op.name)
18811870
case ForDo(enums, body) =>
18821871
makeFor(nme.foreach, nme.foreach, enums, body) orElse tree
18831872
case ForYield(enums, body) =>

Diff for: compiler/src/dotty/tools/dotc/ast/Trees.scala

+1
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ object Trees {
304304

305305
def withFlags(flags: FlagSet): ThisTree[Untyped] = withMods(untpd.Modifiers(flags))
306306
def withAddedFlags(flags: FlagSet): ThisTree[Untyped] = withMods(rawMods | flags)
307+
def withAddedAnnotation(annot: Tree[Untyped]): ThisTree[Untyped] = withMods(rawMods.withAddedAnnotation(annot))
307308

308309
/** Destructively update modifiers. To be used with care. */
309310
def setMods(mods: untpd.Modifiers): Unit = myMods = mods

Diff for: compiler/src/dotty/tools/dotc/ast/untpd.scala

+6-8
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
118118
case class ContextBounds(bounds: TypeBoundsTree, cxBounds: List[Tree])(implicit @constructorOnly src: SourceFile) extends TypTree
119119
case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree)(implicit @constructorOnly src: SourceFile) extends DefTree
120120
case class ExtMethods(paramss: List[ParamClause], methods: List[Tree])(implicit @constructorOnly src: SourceFile) extends Tree
121-
case class Into(tpt: Tree)(implicit @constructorOnly src: SourceFile) extends Tree
122121
case class MacroTree(expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree
123122

124123
case class ImportSelector(imported: Ident, renamed: Tree = EmptyTree, bound: Tree = EmptyTree)(implicit @constructorOnly src: SourceFile) extends Tree {
@@ -552,6 +551,12 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
552551
ValDef(nme.syntheticParamName(n), if (tpt == null) TypeTree() else tpt, EmptyTree)
553552
.withFlags(flags)
554553

554+
def isInto(t: Tree)(using Context): Boolean = t match
555+
case PrefixOp(Ident(tpnme.into), _) => true
556+
case Function(_, res) => isInto(res)
557+
case Parens(t) => isInto(t)
558+
case _ => false
559+
555560
def lambdaAbstract(params: List[ValDef] | List[TypeDef], tpt: Tree)(using Context): Tree =
556561
params match
557562
case Nil => tpt
@@ -666,9 +671,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
666671
def ExtMethods(tree: Tree)(paramss: List[ParamClause], methods: List[Tree])(using Context): Tree = tree match
667672
case tree: ExtMethods if (paramss eq tree.paramss) && (methods == tree.methods) => tree
668673
case _ => finalize(tree, untpd.ExtMethods(paramss, methods)(tree.source))
669-
def Into(tree: Tree)(tpt: Tree)(using Context): Tree = tree match
670-
case tree: Into if tpt eq tree.tpt => tree
671-
case _ => finalize(tree, untpd.Into(tpt)(tree.source))
672674
def ImportSelector(tree: Tree)(imported: Ident, renamed: Tree, bound: Tree)(using Context): Tree = tree match {
673675
case tree: ImportSelector if (imported eq tree.imported) && (renamed eq tree.renamed) && (bound eq tree.bound) => tree
674676
case _ => finalize(tree, untpd.ImportSelector(imported, renamed, bound)(tree.source))
@@ -734,8 +736,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
734736
cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs))
735737
case ExtMethods(paramss, methods) =>
736738
cpy.ExtMethods(tree)(transformParamss(paramss), transformSub(methods))
737-
case Into(tpt) =>
738-
cpy.Into(tree)(transform(tpt))
739739
case ImportSelector(imported, renamed, bound) =>
740740
cpy.ImportSelector(tree)(transformSub(imported), transform(renamed), transform(bound))
741741
case Number(_, _) | TypedSplice(_) =>
@@ -791,8 +791,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
791791
this(this(this(x, pats), tpt), rhs)
792792
case ExtMethods(paramss, methods) =>
793793
this(paramss.foldLeft(x)(apply), methods)
794-
case Into(tpt) =>
795-
this(x, tpt)
796794
case ImportSelector(imported, renamed, bound) =>
797795
this(this(this(x, imported), renamed), bound)
798796
case Number(_, _) =>

Diff for: compiler/src/dotty/tools/dotc/core/Definitions.scala

+2-4
Original file line numberDiff line numberDiff line change
@@ -642,8 +642,6 @@ class Definitions {
642642

643643
@tu lazy val RepeatedParamClass: ClassSymbol = enterSpecialPolyClass(tpnme.REPEATED_PARAM_CLASS, Covariant, Seq(ObjectType, SeqType))
644644

645-
@tu lazy val IntoType: TypeSymbol = enterAliasType(tpnme.INTO, HKTypeLambda(TypeBounds.empty :: Nil)(_.paramRefs(0)))
646-
647645
// fundamental classes
648646
@tu lazy val StringClass: ClassSymbol = requiredClass("java.lang.String")
649647
def StringType: Type = StringClass.typeRef
@@ -1002,7 +1000,6 @@ class Definitions {
10021000
@tu lazy val JavaAnnotationClass: ClassSymbol = requiredClass("java.lang.annotation.Annotation")
10031001

10041002
// Annotation classes
1005-
@tu lazy val AllowConversionsAnnot: ClassSymbol = requiredClass("scala.annotation.allowConversions")
10061003
@tu lazy val AnnotationDefaultAnnot: ClassSymbol = requiredClass("scala.annotation.internal.AnnotationDefault")
10071004
@tu lazy val AssignedNonLocallyAnnot: ClassSymbol = requiredClass("scala.annotation.internal.AssignedNonLocally")
10081005
@tu lazy val BeanPropertyAnnot: ClassSymbol = requiredClass("scala.beans.BeanProperty")
@@ -1018,6 +1015,8 @@ class Definitions {
10181015
@tu lazy val ImplicitAmbiguousAnnot: ClassSymbol = requiredClass("scala.annotation.implicitAmbiguous")
10191016
@tu lazy val ImplicitNotFoundAnnot: ClassSymbol = requiredClass("scala.annotation.implicitNotFound")
10201017
@tu lazy val InlineParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.InlineParam")
1018+
@tu lazy val IntoAnnot: ClassSymbol = requiredClass("scala.annotation.into")
1019+
@tu lazy val IntoParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.$into")
10211020
@tu lazy val ErasedParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ErasedParam")
10221021
@tu lazy val MainAnnot: ClassSymbol = requiredClass("scala.main")
10231022
@tu lazy val MappedAlternativeAnnot: ClassSymbol = requiredClass("scala.annotation.internal.MappedAlternative")
@@ -2137,7 +2136,6 @@ class Definitions {
21372136
orType,
21382137
RepeatedParamClass,
21392138
ByNameParamClass2x,
2140-
IntoType,
21412139
AnyValClass,
21422140
NullClass,
21432141
NothingClass,

Diff for: compiler/src/dotty/tools/dotc/core/StdNames.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ object StdNames {
131131
val EXCEPTION_RESULT_PREFIX: N = "exceptionResult"
132132
val EXPAND_SEPARATOR: N = str.EXPAND_SEPARATOR
133133
val IMPORT: N = "<import>"
134-
val INTO: N = "<into>"
134+
val INTO: N = "$into"
135135
val MODULE_SUFFIX: N = str.MODULE_SUFFIX
136136
val OPS_PACKAGE: N = "<special-ops>"
137137
val OVERLOADED: N = "<overloaded>"

Diff for: compiler/src/dotty/tools/dotc/core/Types.scala

+42-26
Original file line numberDiff line numberDiff line change
@@ -419,8 +419,9 @@ object Types extends TypeUtils {
419419
typeSymbol eq defn.RepeatedParamClass
420420

421421
/** Is this a parameter type that allows implicit argument converson? */
422-
def isConvertibleParam(using Context): Boolean =
423-
typeSymbol eq defn.IntoType
422+
def isInto(using Context): Boolean = this match
423+
case AnnotatedType(_, annot) => annot.symbol == defn.IntoParamAnnot
424+
case _ => false
424425

425426
/** Is this the type of a method that has a repeated parameter type as
426427
* last parameter type?
@@ -1927,7 +1928,9 @@ object Types extends TypeUtils {
19271928
case res => res
19281929
}
19291930
defn.FunctionNOf(
1930-
mt.paramInfos.mapConserve(_.translateFromRepeated(toArray = isJava)),
1931+
mt.paramInfos.mapConserve:
1932+
_.translateFromRepeated(toArray = isJava)
1933+
.mapIntoAnnot(defn.IntoParamAnnot, null),
19311934
result1, isContextual)
19321935
if mt.hasErasedParams then
19331936
defn.PolyFunctionOf(mt)
@@ -1975,6 +1978,38 @@ object Types extends TypeUtils {
19751978
case _ => this
19761979
}
19771980

1981+
/** A mapping between mapping one kind of into annotation to another or
1982+
* dropping into annotations.
1983+
* @param from the into annotation to map
1984+
* @param to either the replacement annotation symbol, or `null`
1985+
* in which case the `from` annotations are dropped.
1986+
*/
1987+
def mapIntoAnnot(from: ClassSymbol, to: ClassSymbol | Null)(using Context): Type = this match
1988+
case self @ AnnotatedType(tp, annot) =>
1989+
val tp1 = tp.mapIntoAnnot(from, to)
1990+
if annot.symbol == from then
1991+
if to == null then tp1
1992+
else AnnotatedType(tp1, Annotation(to, annot.tree.span))
1993+
else self.derivedAnnotatedType(tp1, annot)
1994+
case AppliedType(tycon, arg :: Nil) if tycon.typeSymbol == defn.RepeatedParamClass =>
1995+
val arg1 = arg.mapIntoAnnot(from, to)
1996+
if arg1 eq arg then this
1997+
else AppliedType(tycon, arg1 :: Nil)
1998+
case defn.FunctionOf(argTypes, resType, isContextual) =>
1999+
val resType1 = resType.mapIntoAnnot(from, to)
2000+
if resType1 eq resType then this
2001+
else defn.FunctionOf(argTypes, resType1, isContextual)
2002+
case RefinedType(parent, rname, mt: MethodOrPoly) =>
2003+
val mt1 = mt.mapIntoAnnot(from, to)
2004+
if mt1 eq mt then this
2005+
else RefinedType(parent.mapIntoAnnot(from, to), rname, mt1)
2006+
case mt: MethodOrPoly =>
2007+
mt.derivedLambdaType(resType = mt.resType.mapIntoAnnot(from, to))
2008+
case tp: ExprType =>
2009+
tp.derivedExprType(tp.resType.mapIntoAnnot(from, to))
2010+
case _ =>
2011+
this
2012+
19782013
/** A type capturing `ref` */
19792014
def capturing(ref: CaptureRef)(using Context): Type =
19802015
if captureSet.accountsFor(ref) then this
@@ -4122,6 +4157,7 @@ object Types extends TypeUtils {
41224157
/** Produce method type from parameter symbols, with special mappings for repeated
41234158
* and inline parameters:
41244159
* - replace @repeated annotations on Seq or Array types by <repeated> types
4160+
* - map into annotations to $into annotations
41254161
* - add @inlineParam to inline parameters
41264162
* - add @erasedParam to erased parameters
41274163
* - wrap types of parameters that have an @allowConversions annotation with Into[_]
@@ -4131,34 +4167,14 @@ object Types extends TypeUtils {
41314167
case ExprType(resType) => ExprType(addAnnotation(resType, cls, param))
41324168
case _ => AnnotatedType(tp, Annotation(cls, param.span))
41334169

4134-
def wrapConvertible(tp: Type) =
4135-
AppliedType(defn.IntoType.typeRef, tp :: Nil)
4136-
4137-
/** Add `Into[..] to the type itself and if it is a function type, to all its
4138-
* curried result type(s) as well.
4139-
*/
4140-
def addInto(tp: Type): Type = tp match
4141-
case tp @ AppliedType(tycon, args) if tycon.typeSymbol == defn.RepeatedParamClass =>
4142-
tp.derivedAppliedType(tycon, addInto(args.head) :: Nil)
4143-
case tp @ AppliedType(tycon, args) if defn.isFunctionNType(tp) =>
4144-
wrapConvertible(tp.derivedAppliedType(tycon, args.init :+ addInto(args.last)))
4145-
case tp @ defn.RefinedFunctionOf(rinfo) =>
4146-
wrapConvertible(tp.derivedRefinedType(refinedInfo = addInto(rinfo)))
4147-
case tp: MethodOrPoly =>
4148-
tp.derivedLambdaType(resType = addInto(tp.resType))
4149-
case ExprType(resType) =>
4150-
ExprType(addInto(resType))
4151-
case _ =>
4152-
wrapConvertible(tp)
4153-
41544170
def paramInfo(param: Symbol) =
4155-
var paramType = param.info.annotatedToRepeated
4171+
var paramType = param.info
4172+
.annotatedToRepeated
4173+
.mapIntoAnnot(defn.IntoAnnot, defn.IntoParamAnnot)
41564174
if param.is(Inline) then
41574175
paramType = addAnnotation(paramType, defn.InlineParamAnnot, param)
41584176
if param.is(Erased) then
41594177
paramType = addAnnotation(paramType, defn.ErasedParamAnnot, param)
4160-
if param.hasAnnotation(defn.AllowConversionsAnnot) then
4161-
paramType = addInto(paramType)
41624178
paramType
41634179

41644180
apply(params.map(_.name.asTermName))(

0 commit comments

Comments
 (0)