Skip to content

Commit 2a05248

Browse files
committed
Change scheme to use an opaque type alias
1 parent 8aa9437 commit 2a05248

29 files changed

+305
-344
lines changed

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,9 @@ class Definitions {
753753
@tu lazy val StringBuilderClass: ClassSymbol = requiredClass("scala.collection.mutable.StringBuilder")
754754
@tu lazy val MatchErrorClass : ClassSymbol = requiredClass("scala.MatchError")
755755
@tu lazy val ConversionClass : ClassSymbol = requiredClass("scala.Conversion").typeRef.symbol.asClass
756+
@tu lazy val ConversionModule : Symbol = ConversionClass.companionModule
757+
@tu lazy val ConversionModuleClass: ClassSymbol = ConversionModule.moduleClass.asClass
758+
@tu lazy val Conversion_into : Symbol = ConversionModuleClass.requiredType("into")
756759

757760
@tu lazy val StringAddClass : ClassSymbol = requiredClass("scala.runtime.StringAdd")
758761
@tu lazy val StringAdd_+ : Symbol = StringAddClass.requiredMethod(nme.raw.PLUS)
@@ -1037,8 +1040,6 @@ class Definitions {
10371040
@tu lazy val ImplicitNotFoundAnnot: ClassSymbol = requiredClass("scala.annotation.implicitNotFound")
10381041
@tu lazy val InferredDepFunAnnot: ClassSymbol = requiredClass("scala.caps.internal.inferredDepFun")
10391042
@tu lazy val InlineParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.InlineParam")
1040-
@tu lazy val IntoAnnot: ClassSymbol = requiredClass("scala.annotation.into")
1041-
@tu lazy val IntoParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.$into")
10421043
@tu lazy val ErasedParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ErasedParam")
10431044
@tu lazy val MainAnnot: ClassSymbol = requiredClass("scala.main")
10441045
@tu lazy val MappedAlternativeAnnot: ClassSymbol = requiredClass("scala.annotation.internal.MappedAlternative")
@@ -1056,6 +1057,7 @@ class Definitions {
10561057
// @tu lazy val ScalaStrictFPAnnot: ClassSymbol = requiredClass("scala.annotation.strictfp")
10571058
@tu lazy val ScalaStaticAnnot: ClassSymbol = requiredClass("scala.annotation.static")
10581059
@tu lazy val SerialVersionUIDAnnot: ClassSymbol = requiredClass("scala.SerialVersionUID")
1060+
@tu lazy val SilentIntoAnnot: ClassSymbol = requiredClass("scala.annotation.internal.$into")
10591061
@tu lazy val TailrecAnnot: ClassSymbol = requiredClass("scala.annotation.tailrec")
10601062
@tu lazy val ThreadUnsafeAnnot: ClassSymbol = requiredClass("scala.annotation.threadUnsafe")
10611063
@tu lazy val ConstructorOnlyAnnot: ClassSymbol = requiredClass("scala.annotation.constructorOnly")
@@ -1115,7 +1117,7 @@ class Definitions {
11151117

11161118
// Set of annotations that are not printed in types except under -Yprint-debug
11171119
@tu lazy val SilentAnnots: Set[Symbol] =
1118-
Set(InlineParamAnnot, ErasedParamAnnot, RefineOverrideAnnot)
1120+
Set(InlineParamAnnot, ErasedParamAnnot, RefineOverrideAnnot, SilentIntoAnnot)
11191121

11201122
// A list of annotations that are commonly used to indicate that a field/method argument or return
11211123
// type is not null. These annotations are used by the nullification logic in JavaNullInterop to
@@ -1385,6 +1387,9 @@ class Definitions {
13851387
final def isNamedTuple_From(sym: Symbol)(using Context): Boolean =
13861388
sym.name == tpnme.From && sym.owner == NamedTupleModule.moduleClass
13871389

1390+
final def isInto(sym: Symbol)(using Context): Boolean =
1391+
sym.name == tpnme.into && sym.owner == ConversionModuleClass
1392+
13881393
private val compiletimePackageAnyTypes: Set[Name] = Set(
13891394
tpnme.Equals, tpnme.NotEquals, tpnme.IsConst, tpnme.ToString
13901395
)

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,4 +315,11 @@ object NamerOps:
315315
ann.tree match
316316
case ast.tpd.WitnessNamesAnnot(witnessNames) =>
317317
addContextBoundCompanionFor(sym, witnessNames, Nil)
318+
319+
/** if `sym` is a term parameter or parameter accessor, map all occurrences of
320+
* `into[T]` in its type to `T @$into`.
321+
*/
322+
extension (tp: Type)
323+
def suppressIntoIfParam(sym: Symbol)(using Context): Type =
324+
if sym.isOneOf(TermParamOrAccessor) then TypeOps.suppressInto(tp) else tp
318325
end NamerOps

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import typer.ForceDegree
1818
import typer.Inferencing.*
1919
import typer.IfBottom
2020
import reporting.TestingReporter
21+
import Annotations.Annotation
2122
import cc.{CapturingType, derivedCapturingType, CaptureSet, captureSet, isBoxed, isBoxedCapturing}
2223
import CaptureSet.{CompareResult, IdentityCaptRefMap, VarState}
2324

@@ -944,6 +945,28 @@ object TypeOps:
944945
class StripTypeVarsMap(using Context) extends TypeMap:
945946
def apply(tp: Type) = mapOver(tp).stripTypeVar
946947

948+
/** Map no-flip covariant occurrences of `into[T]` to `T @$into` */
949+
def suppressInto(using Context) = new FollowAliasesMap:
950+
def apply(t: Type): Type =
951+
if variance <= 0 then t
952+
else t match
953+
case AppliedType(tycon: TypeRef, arg :: Nil) if defn.isInto(tycon.symbol) =>
954+
AnnotatedType(arg, Annotation(defn.SilentIntoAnnot, util.Spans.NoSpan))
955+
case _ =>
956+
mapFollowingAliases(t)
957+
958+
/** Map no-flip covariant occurrences of `T @$into` to `into[T]` */
959+
def revealInto(using Context) = new FollowAliasesMap:
960+
def apply(t: Type): Type =
961+
if variance <= 0 then t
962+
else t match
963+
case AnnotatedType(t1, ann) if ann.symbol == defn.SilentIntoAnnot =>
964+
AppliedType(
965+
defn.ConversionModule.termRef.select(defn.Conversion_into), // the external reference to the opaque type
966+
t1 :: Nil)
967+
case _ =>
968+
mapFollowingAliases(t)
969+
947970
/** Apply [[Type.stripTypeVar]] recursively. */
948971
def stripTypeVars(tp: Type)(using Context): Type =
949972
new StripTypeVarsMap().apply(tp)

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

Lines changed: 10 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -446,9 +446,11 @@ object Types extends TypeUtils {
446446
def isRepeatedParam(using Context): Boolean =
447447
typeSymbol eq defn.RepeatedParamClass
448448

449-
/** Is this a parameter type that allows implicit argument converson? */
449+
/** Is this type of the form `compiletime.into[T]`, which means it can be the
450+
* target of an implicit converson without requiring a language import?
451+
*/
450452
def isInto(using Context): Boolean = this match
451-
case AnnotatedType(_, annot) => annot.symbol == defn.IntoParamAnnot
453+
case AppliedType(tycon: TypeRef, arg :: Nil) => defn.isInto(tycon.symbol)
452454
case _ => false
453455

454456
/** Is this the type of a method that has a repeated parameter type as
@@ -1967,8 +1969,7 @@ object Types extends TypeUtils {
19671969
}
19681970
defn.FunctionNOf(
19691971
mt.paramInfos.mapConserve:
1970-
_.translateFromRepeated(toArray = isJava)
1971-
.mapIntoAnnot(defn.IntoParamAnnot, null),
1972+
_.translateFromRepeated(toArray = isJava),
19721973
result1, isContextual)
19731974
if mt.hasErasedParams then
19741975
defn.PolyFunctionOf(mt)
@@ -2016,38 +2017,6 @@ object Types extends TypeUtils {
20162017
case _ => this
20172018
}
20182019

2019-
/** A mapping between mapping one kind of into annotation to another or
2020-
* dropping into annotations.
2021-
* @param from the into annotation to map
2022-
* @param to either the replacement annotation symbol, or `null`
2023-
* in which case the `from` annotations are dropped.
2024-
*/
2025-
def mapIntoAnnot(from: ClassSymbol, to: ClassSymbol | Null)(using Context): Type = this match
2026-
case self @ AnnotatedType(tp, annot) =>
2027-
val tp1 = tp.mapIntoAnnot(from, to)
2028-
if annot.symbol == from then
2029-
if to == null then tp1
2030-
else AnnotatedType(tp1, Annotation(to, annot.tree.span))
2031-
else self.derivedAnnotatedType(tp1, annot)
2032-
case AppliedType(tycon, arg :: Nil) if tycon.typeSymbol == defn.RepeatedParamClass =>
2033-
val arg1 = arg.mapIntoAnnot(from, to)
2034-
if arg1 eq arg then this
2035-
else AppliedType(tycon, arg1 :: Nil)
2036-
case defn.FunctionOf(argTypes, resType, isContextual) =>
2037-
val resType1 = resType.mapIntoAnnot(from, to)
2038-
if resType1 eq resType then this
2039-
else defn.FunctionOf(argTypes, resType1, isContextual)
2040-
case RefinedType(parent, rname, mt: MethodOrPoly) =>
2041-
val mt1 = mt.mapIntoAnnot(from, to)
2042-
if mt1 eq mt then this
2043-
else RefinedType(parent.mapIntoAnnot(from, to), rname, mt1)
2044-
case mt: MethodOrPoly =>
2045-
mt.derivedLambdaType(resType = mt.resType.mapIntoAnnot(from, to))
2046-
case tp: ExprType =>
2047-
tp.derivedExprType(tp.resType.mapIntoAnnot(from, to))
2048-
case _ =>
2049-
this
2050-
20512020
/** The set of distinct symbols referred to by this type, after all aliases are expanded */
20522021
def coveringSet(using Context): Set[Symbol] =
20532022
(new CoveringSetAccumulator).apply(Set.empty[Symbol], this)
@@ -4222,11 +4191,11 @@ object Types extends TypeUtils {
42224191

42234192
/** Produce method type from parameter symbols, with special mappings for repeated
42244193
* and inline parameters:
4225-
* - replace @repeated annotations on Seq or Array types by <repeated> types
4194+
* - replace `@repeated` annotations on Seq or Array types by <repeated> types
42264195
* - map into annotations to $into annotations
4227-
* - add @inlineParam to inline parameters
4228-
* - add @erasedParam to erased parameters
4229-
* - wrap types of parameters that have an @allowConversions annotation with Into[_]
4196+
* - add `@inlineParam` to inline parameters
4197+
* - add `@erasedParam` to erased parameters
4198+
* - map `T @$into` types to `into[T]`
42304199
*/
42314200
def fromSymbols(params: List[Symbol], resultType: Type)(using Context): MethodType =
42324201
apply(params.map(_.name.asTermName))(
@@ -4240,9 +4209,7 @@ object Types extends TypeUtils {
42404209
def addAnnotation(tp: Type, cls: ClassSymbol, param: Symbol): Type = tp match
42414210
case ExprType(resType) => ExprType(addAnnotation(resType, cls, param))
42424211
case _ => AnnotatedType(tp, Annotation(cls, param.span))
4243-
var paramType = pinfo
4244-
.annotatedToRepeated
4245-
.mapIntoAnnot(defn.IntoAnnot, defn.IntoParamAnnot)
4212+
var paramType = TypeOps.revealInto(pinfo).annotatedToRepeated
42464213
if param.is(Inline) then
42474214
paramType = addAnnotation(paramType, defn.InlineParamAnnot, param)
42484215
if param.is(Erased) then

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -933,7 +933,7 @@ class TreeUnpickler(reader: TastyReader,
933933
DefDef(paramDefss, tpt)
934934
case VALDEF =>
935935
val tpt = readTpt()(using localCtx)
936-
sym.info = tpt.tpe
936+
sym.info = tpt.tpe.suppressIntoIfParam(sym)
937937
ValDef(tpt)
938938
case TYPEDEF | TYPEPARAM =>
939939
if (sym.isClass) {
@@ -978,7 +978,7 @@ class TreeUnpickler(reader: TastyReader,
978978
case PARAM =>
979979
val tpt = readTpt()(using localCtx)
980980
assert(nothingButMods(end))
981-
sym.info = tpt.tpe
981+
sym.info = tpt.tpe.suppressIntoIfParam(sym)
982982
ValDef(tpt)
983983
}
984984
goto(end)

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

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,7 +1674,7 @@ object Parsers {
16741674
accept(ARROW)
16751675

16761676
val resultType =
1677-
if isPure then capturesAndResult(typ) else typ()
1677+
if isPure then capturesAndResult(() => typ()) else typ()
16781678
if token == TLARROW then
16791679
for case ValDef(_, tpt, _) <- params do
16801680
if isByNameType(tpt) then
@@ -1765,7 +1765,7 @@ object Parsers {
17651765
LambdaTypeTree(tparams.mapConserve(stripContextBounds("type lambdas")), toplevelTyp())
17661766
else if in.token == ARROW || isPureArrow(nme.PUREARROW) then
17671767
val arrowOffset = in.skipToken()
1768-
val body = toplevelTyp(nestedIntoOK(in.token))
1768+
val body = toplevelTyp()
17691769
makePolyFunction(tparams, body, "type", Ident(nme.ERROR.toTypeName), start, arrowOffset)
17701770
else
17711771
accept(TLARROW)
@@ -2181,7 +2181,7 @@ object Parsers {
21812181
* | `=>' Type
21822182
* | `->' [CaptureSet] Type
21832183
*/
2184-
val funArgType: () => Tree = () => paramTypeOf(typ)
2184+
val funArgType: () => Tree = () => paramTypeOf(() => typ())
21852185

21862186
/** ParamType ::= ParamValueType
21872187
* | `=>' ParamValueType
@@ -2194,11 +2194,6 @@ object Parsers {
21942194
def paramValueType(): Tree =
21952195
val t = toplevelTyp()
21962196
if isIdent(nme.raw.STAR) then
2197-
if !t.isInstanceOf[Parens] && isInto(t) then
2198-
syntaxError(
2199-
em"""`*` cannot directly follow `into` parameter
2200-
|the `into` parameter needs to be put in parentheses""",
2201-
in.offset)
22022197
in.nextToken()
22032198
atSpan(startOffset(t)):
22042199
PostfixOp(t, Ident(tpnme.raw.STAR))
@@ -3522,7 +3517,7 @@ object Parsers {
35223517
*/
35233518
def contextTypes(paramOwner: ParamOwner, numLeadParams: Int, impliedMods: Modifiers): List[ValDef] =
35243519
typesToParams(
3525-
commaSeparated(() => paramTypeOf(toplevelTyp)),
3520+
commaSeparated(() => paramTypeOf(() => toplevelTyp())),
35263521
paramOwner, numLeadParams, impliedMods)
35273522

35283523
def typesToParams(tps: List[Tree], paramOwner: ParamOwner, numLeadParams: Int, impliedMods: Modifiers): List[ValDef] =

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -334,10 +334,6 @@ class PlainPrinter(_ctx: Context) extends Printer {
334334
case AnnotatedType(tpe, annot) =>
335335
if defn.SilentAnnots.contains(annot.symbol) && !printDebug then
336336
toText(tpe)
337-
else if (annot.symbol == defn.IntoParamAnnot)
338-
&& !printDebug
339-
then atPrec(GlobalPrec):
340-
"into[" ~ toText(tpe) ~ "]"
341337
else if annot.isInstanceOf[CaptureAnnotation] then
342338
toTextLocal(tpe) ~ "^" ~ toText(annot)
343339
else

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,9 @@ class Namer { typer: Typer =>
833833
protected def typeSig(sym: Symbol): Type = original match
834834
case original: ValDef =>
835835
if (sym.is(Module)) moduleValSig(sym)
836-
else valOrDefDefSig(original, sym, Nil, identity)(using localContext(sym).setNewScope)
836+
else
837+
valOrDefDefSig(original, sym, Nil, identity)(using localContext(sym).setNewScope)
838+
.suppressIntoIfParam(sym)
837839
case original: DefDef =>
838840
// For the primary constructor DefDef, it is:
839841
// * indexed as a part of completing the class, with indexConstructor; and

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

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -374,52 +374,6 @@ object RefChecks {
374374
&& atPhase(typerPhase):
375375
loop(member.info.paramInfoss, other.info.paramInfoss)
376376

377-
/** A map of all occurrences of `into` in a member type.
378-
* Key: number of parameter carrying `into` annotation(s)
379-
* Value: A list of all depths of into annotations, where each
380-
* function arrow increases the depth.
381-
* Example:
382-
* def foo(x: into A, y: => [X] => into (x: X) => into B): C
383-
* produces the map
384-
* (0 -> List(0), 1 -> List(1, 2))
385-
*/
386-
type IntoOccurrenceMap = immutable.Map[Int, List[Int]]
387-
388-
def intoOccurrences(tp: Type): IntoOccurrenceMap =
389-
390-
def traverseInfo(depth: Int, tp: Type): List[Int] = tp match
391-
case AnnotatedType(tp, annot) if annot.symbol == defn.IntoParamAnnot =>
392-
depth :: traverseInfo(depth, tp)
393-
case AppliedType(tycon, arg :: Nil) if tycon.typeSymbol == defn.RepeatedParamClass =>
394-
traverseInfo(depth, arg)
395-
case defn.FunctionOf(_, resType, _) =>
396-
traverseInfo(depth + 1, resType)
397-
case RefinedType(parent, rname, mt: MethodOrPoly) =>
398-
traverseInfo(depth, mt)
399-
case tp: MethodOrPoly =>
400-
traverseInfo(depth + 1, tp.resType)
401-
case tp: ExprType =>
402-
traverseInfo(depth, tp.resType)
403-
case _ =>
404-
Nil
405-
406-
def traverseParams(n: Int, formals: List[Type], acc: IntoOccurrenceMap): IntoOccurrenceMap =
407-
if formals.isEmpty then acc
408-
else
409-
val occs = traverseInfo(0, formals.head)
410-
traverseParams(n + 1, formals.tail, if occs.isEmpty then acc else acc + (n -> occs))
411-
412-
def traverse(n: Int, tp: Type, acc: IntoOccurrenceMap): IntoOccurrenceMap = tp match
413-
case tp: PolyType =>
414-
traverse(n, tp.resType, acc)
415-
case tp: MethodType =>
416-
traverse(n + tp.paramInfos.length, tp.resType, traverseParams(n, tp.paramInfos, acc))
417-
case _ =>
418-
acc
419-
420-
traverse(0, tp, immutable.Map.empty)
421-
end intoOccurrences
422-
423377
val checker =
424378
if makeOverridingPairsChecker == null then OverridingPairsChecker(clazz, self)
425379
else makeOverridingPairsChecker(clazz, self)
@@ -653,8 +607,6 @@ object RefChecks {
653607
overrideError(i"needs to be declared with @targetName(${"\""}${other.targetName}${"\""}) so that external names match")
654608
else
655609
overrideError("cannot have a @targetName annotation since external names would be different")
656-
else if intoOccurrences(memberTp) != intoOccurrences(otherTp) then
657-
overrideError("has different occurrences of `into` modifiers", compareTypes = true)
658610
else if other.is(ParamAccessor) && !isInheritedAccessor(member, other)
659611
&& !member.is(Tracked) // see remark on tracked members above
660612
then // (1.12)

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2672,9 +2672,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
26722672
&& checkedArgs(1).tpe.derivesFrom(defn.RuntimeExceptionClass)
26732673
then
26742674
report.error(em"throws clause cannot be defined for RuntimeException", checkedArgs(1).srcPos)
2675-
else if tycon == defn.IntoType then
2676-
// <into> is defined in package scala but this should be hidden from user programs
2677-
report.error(em"not found: <into>", tpt1.srcPos)
26782675
else if (ctx.isJava)
26792676
if tycon eq defn.ArrayClass then
26802677
checkedArgs match {

0 commit comments

Comments
 (0)