Skip to content

Commit 3cabdea

Browse files
committed
Parsing and desugaring of new into modifier on parameter types
1 parent 8b8caa9 commit 3cabdea

File tree

14 files changed

+107
-98
lines changed

14 files changed

+107
-98
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 6 additions & 17 deletions
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) =>

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 1 addition & 0 deletions
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

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 6 additions & 8 deletions
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(_, _) =>

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 1 addition & 4 deletions
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,7 @@ 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.internal.into")
10211019
@tu lazy val ErasedParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ErasedParam")
10221020
@tu lazy val MainAnnot: ClassSymbol = requiredClass("scala.main")
10231021
@tu lazy val MappedAlternativeAnnot: ClassSymbol = requiredClass("scala.annotation.internal.MappedAlternative")
@@ -2137,7 +2135,6 @@ class Definitions {
21372135
orType,
21382136
RepeatedParamClass,
21392137
ByNameParamClass2x,
2140-
IntoType,
21412138
AnyValClass,
21422139
NullClass,
21432140
NothingClass,

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 1 addition & 1 deletion
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>"

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -419,8 +419,8 @@ 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 =
423+
typeSymbol eq ???
424424

425425
/** Is this the type of a method that has a repeated parameter type as
426426
* last parameter type?
@@ -4131,34 +4131,14 @@ object Types extends TypeUtils {
41314131
case ExprType(resType) => ExprType(addAnnotation(resType, cls, param))
41324132
case _ => AnnotatedType(tp, Annotation(cls, param.span))
41334133

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-
41544134
def paramInfo(param: Symbol) =
4155-
var paramType = param.info.annotatedToRepeated
4135+
var paramType = param.info
4136+
.annotatedToRepeated
4137+
//.mapIntoAnnot(defn.IntoType.appliedTo(_))
41564138
if param.is(Inline) then
41574139
paramType = addAnnotation(paramType, defn.InlineParamAnnot, param)
41584140
if param.is(Erased) then
41594141
paramType = addAnnotation(paramType, defn.ErasedParamAnnot, param)
4160-
if param.hasAnnotation(defn.AllowConversionsAnnot) then
4161-
paramType = addInto(paramType)
41624142
paramType
41634143

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

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ object Parsers {
7373
enum ParseKind:
7474
case Expr, Type, Pattern
7575

76+
enum IntoOK:
77+
case Yes, No, Nested
78+
7679
type StageKind = Int
7780
object StageKind {
7881
val None = 0
@@ -1484,7 +1487,7 @@ object Parsers {
14841487
/** Same as [[typ]], but if this results in a wildcard it emits a syntax error and
14851488
* returns a tree for type `Any` instead.
14861489
*/
1487-
def toplevelTyp(): Tree = rejectWildcardType(typ())
1490+
def toplevelTyp(intoOK: IntoOK = IntoOK.No): Tree = rejectWildcardType(typ(intoOK))
14881491

14891492
private def getFunction(tree: Tree): Option[Function] = tree match {
14901493
case Parens(tree1) => getFunction(tree1)
@@ -1535,12 +1538,21 @@ object Parsers {
15351538
* | `(' [ FunArgType {`,' FunArgType } ] `)'
15361539
* | '(' [ TypedFunParam {',' TypedFunParam } ')'
15371540
* MatchType ::= InfixType `match` <<< TypeCaseClauses >>>
1541+
* IntoType ::= [‘into’] IntoTargetType
1542+
* | ‘( IntoType ‘)’
1543+
* IntoTargetType ::= Type
1544+
* | FunTypeArgs (‘=>’ | ‘?=>’) IntoType
15381545
*/
1539-
def typ(): Tree =
1546+
def typ(intoOK: IntoOK = IntoOK.No): Tree =
15401547
val start = in.offset
15411548
var imods = Modifiers()
15421549
val erasedArgs: ListBuffer[Boolean] = ListBuffer()
15431550

1551+
def nestedIntoOK(token: Int) =
1552+
if token == TLARROW then IntoOK.No
1553+
else if intoOK == IntoOK.Nested then IntoOK.Yes
1554+
else intoOK
1555+
15441556
def functionRest(params: List[Tree]): Tree =
15451557
val paramSpan = Span(start, in.lastOffset)
15461558
atSpan(start, in.offset) {
@@ -1569,8 +1581,9 @@ object Parsers {
15691581
else
15701582
accept(ARROW)
15711583

1584+
def resType() = typ(nestedIntoOK(token))
15721585
val resultType =
1573-
if isPure then capturesAndResult(typ) else typ()
1586+
if isPure then capturesAndResult(resType) else resType()
15741587
if token == TLARROW then
15751588
for case ValDef(_, tpt, _) <- params do
15761589
if isByNameType(tpt) then
@@ -1605,6 +1618,12 @@ object Parsers {
16051618
syntaxError(ErasedTypesCanOnlyBeFunctionTypes(), implicitKwPos(start))
16061619
t
16071620

1621+
def isIntoPrefix: Boolean =
1622+
intoOK == IntoOK.Yes
1623+
&& in.isIdent(nme.into)
1624+
&& in.featureEnabled(Feature.into)
1625+
&& canStartTypeTokens.contains(in.lookahead.token)
1626+
16081627
var isValParamList = false
16091628
if in.token == LPAREN then
16101629
in.nextToken()
@@ -1635,17 +1654,36 @@ object Parsers {
16351654
funArgType()
16361655
commaSeparatedRest(t, funArg)
16371656
accept(RPAREN)
1657+
1658+
val intoAllowed =
1659+
intoOK == IntoOK.Yes
1660+
&& args.lengthCompare(1) == 0
1661+
&& (!canFollowSimpleTypeTokens.contains(in.token) || followingIsVararg())
1662+
val byNameAllowed = in.isArrow || isPureArrow
1663+
1664+
def sanitize(arg: Tree): Tree = arg match
1665+
case ByNameTypeTree(t) if !byNameAllowed =>
1666+
syntaxError(ByNameParameterNotSupported(t), t.span)
1667+
t
1668+
case PrefixOp(id @ Ident(tpnme.into), t) if !intoAllowed =>
1669+
syntaxError(em"no `into` modifier allowed here", id.span)
1670+
t
1671+
case Parens(t) =>
1672+
cpy.Parens(arg)(sanitize(t))
1673+
case arg: FunctionWithMods =>
1674+
val body1 = sanitize(arg.body)
1675+
if body1 eq arg.body then arg
1676+
else FunctionWithMods(arg.args, body1, arg.mods, arg.erasedParams).withSpan(arg.span)
1677+
case Function(args, res) if !intoAllowed =>
1678+
cpy.Function(arg)(args, sanitize(res))
1679+
case arg =>
1680+
arg
1681+
1682+
val args1 = args.mapConserve(sanitize)
16381683
if isValParamList || in.isArrow || isPureArrow then
16391684
functionRest(args)
16401685
else
1641-
val args1 = args.mapConserve: t =>
1642-
if isByNameType(t) then
1643-
syntaxError(ByNameParameterNotSupported(t), t.span)
1644-
stripByNameType(t)
1645-
else
1646-
t
1647-
val tuple = atSpan(start):
1648-
makeTupleOrParens(args1)
1686+
val tuple = atSpan(start)(makeTupleOrParens(args1))
16491687
typeRest:
16501688
infixTypeRest:
16511689
refinedTypeRest:
@@ -1660,7 +1698,7 @@ object Parsers {
16601698
LambdaTypeTree(tparams, toplevelTyp())
16611699
else if in.token == ARROW || isPureArrow(nme.PUREARROW) then
16621700
val arrowOffset = in.skipToken()
1663-
val body = toplevelTyp()
1701+
val body = toplevelTyp(nestedIntoOK(in.token))
16641702
atSpan(start, arrowOffset):
16651703
getFunction(body) match
16661704
case Some(f) =>
@@ -1673,6 +1711,8 @@ object Parsers {
16731711
typ()
16741712
else if in.token == INDENT then
16751713
enclosed(INDENT, typ())
1714+
else if isIntoPrefix then
1715+
PrefixOp(typeIdent(), typ(IntoOK.Nested))
16761716
else
16771717
typeRest(infixType())
16781718
end typ
@@ -2047,18 +2087,13 @@ object Parsers {
20472087
else
20482088
core()
20492089

2050-
private def maybeInto(tp: () => Tree) =
2051-
if in.isIdent(nme.into)
2052-
&& in.featureEnabled(Feature.into)
2053-
&& canStartTypeTokens.contains(in.lookahead.token)
2054-
then atSpan(in.skipToken()) { Into(tp()) }
2055-
else tp()
2056-
20572090
/** FunArgType ::= Type
20582091
* | `=>' Type
20592092
* | `->' [CaptureSet] Type
20602093
*/
2061-
val funArgType: () => Tree = () => paramTypeOf(typ)
2094+
val funArgType: () => Tree =
2095+
() => paramTypeOf(() => typ(IntoOK.Yes))
2096+
// We allow intoOK and filter out afterwards in typ()
20622097

20632098
/** ParamType ::= ParamValueType
20642099
* | `=>' ParamValueType
@@ -2067,15 +2102,21 @@ object Parsers {
20672102
def paramType(): Tree = paramTypeOf(paramValueType)
20682103

20692104
/** ParamValueType ::= Type [`*']
2105+
* | IntoType
2106+
* | ‘(’ IntoType ‘)’ `*'
20702107
*/
2071-
def paramValueType(): Tree = {
2072-
val t = maybeInto(toplevelTyp)
2073-
if (isIdent(nme.raw.STAR)) {
2108+
def paramValueType(): Tree =
2109+
val t = toplevelTyp(IntoOK.Yes)
2110+
if isIdent(nme.raw.STAR) then
2111+
if !t.isInstanceOf[Parens] && isInto(t) then
2112+
syntaxError(
2113+
em"""`*` cannot directly follow `into` parameter
2114+
|the `into` parameter needs to be put in parentheses""",
2115+
in.offset)
20742116
in.nextToken()
2075-
atSpan(startOffset(t)) { PostfixOp(t, Ident(tpnme.raw.STAR)) }
2076-
}
2117+
atSpan(startOffset(t)):
2118+
PostfixOp(t, Ident(tpnme.raw.STAR))
20772119
else t
2078-
}
20792120

20802121
/** TypeArgs ::= `[' Type {`,' Type} `]'
20812122
* NamedTypeArgs ::= `[' NamedTypeArg {`,' NamedTypeArg} `]'
@@ -3315,7 +3356,7 @@ object Parsers {
33153356
/** ContextTypes ::= FunArgType {‘,’ FunArgType}
33163357
*/
33173358
def contextTypes(paramOwner: ParamOwner, numLeadParams: Int, impliedMods: Modifiers): List[ValDef] =
3318-
val tps = commaSeparated(() => paramTypeOf(toplevelTyp))
3359+
val tps = commaSeparated(() => paramTypeOf(() => toplevelTyp()))
33193360
var counter = numLeadParams
33203361
def nextIdx = { counter += 1; counter }
33213362
val paramFlags = if paramOwner.isClass then LocalParamAccessor else Param

compiler/src/dotty/tools/dotc/parsing/Tokens.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,9 @@ object Tokens extends TokensCommon {
238238

239239
final val canStartPatternTokens = atomicExprTokens | openParensTokens | BitSet(USCORE, QUOTE)
240240

241+
val canFollowSimpleTypeTokens =
242+
BitSet(AT, WITH, COLONop, COLONfollow, COLONeol, LBRACE, IDENTIFIER, BACKQUOTED_IDENT, ARROW, CTXARROW, MATCH, FORSOME)
243+
241244
final val templateIntroTokens: TokenSet = BitSet(CLASS, TRAIT, OBJECT, ENUM, CASECLASS, CASEOBJECT)
242245

243246
final val dclIntroTokens: TokenSet = BitSet(DEF, VAL, VAR, TYPE, GIVEN)

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,11 @@ class PlainPrinter(_ctx: Context) extends Printer {
285285
toTextGlobal(tp.resultType)
286286
}
287287
case AnnotatedType(tpe, annot) =>
288-
if annot.symbol == defn.InlineParamAnnot || annot.symbol == defn.ErasedParamAnnot then toText(tpe)
288+
if annot.symbol == defn.InlineParamAnnot || annot.symbol == defn.ErasedParamAnnot
289+
then toText(tpe)
290+
else if annot.symbol == defn.IntoAnnot && !printDebug then
291+
atPrec(GlobalPrec):
292+
Str("into ") ~ toText(tpe)
289293
else toTextLocal(tpe) ~ " " ~ toText(annot)
290294
case tp: TypeVar =>
291295
def toTextCaret(tp: Type) = if printDebug then toTextLocal(tp) ~ Str("^") else toText(tp)

0 commit comments

Comments
 (0)