Skip to content

Change into scheme to be fully type-based #23014

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
7 changes: 2 additions & 5 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2268,11 +2268,8 @@ object desugar {
assert(ctx.mode.isExpr || ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive), ctx.mode)
Select(t, op.name)
case PrefixOp(op, t) =>
if op.name == tpnme.into then
Annotated(t, New(ref(defn.IntoAnnot.typeRef), Nil :: Nil))
else
val nspace = if (ctx.mode.is(Mode.Type)) tpnme else nme
Select(t, nspace.UNARY_PREFIX ++ op.name)
val nspace = if (ctx.mode.is(Mode.Type)) tpnme else nme
Select(t, nspace.UNARY_PREFIX ++ op.name)
case ForDo(enums, body) =>
makeFor(nme.foreach, nme.foreach, enums, body) orElse tree
case ForYield(enums, body) =>
Expand Down
6 changes: 0 additions & 6 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -573,12 +573,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
ValDef(nme.syntheticParamName(n), if (tpt == null) TypeTree() else tpt, EmptyTree)
.withFlags(flags)

def isInto(t: Tree)(using Context): Boolean = t match
case PrefixOp(Ident(tpnme.into), _) => true
case Function(_, res) => isInto(res)
case Parens(t) => isInto(t)
case _ => false

def lambdaAbstract(params: List[ValDef] | List[TypeDef], tpt: Tree)(using Context): Tree =
params match
case Nil => tpt
Expand Down
13 changes: 0 additions & 13 deletions compiler/src/dotty/tools/dotc/cc/CaptureOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -637,19 +637,6 @@ class CleanupRetains(using Context) extends TypeMap:
RetainingType(tp, Nil, byName = annot.symbol == defn.RetainsByNameAnnot)
case _ => mapOver(tp)

/** A typemap that follows aliases and keeps their transformed results if
* there is a change.
*/
trait FollowAliasesMap(using Context) extends TypeMap:
var follow = true // Used for debugging so that we can compare results with and w/o following.
def mapFollowingAliases(t: Type): Type =
val t1 = t.dealiasKeepAnnots
if follow && (t1 ne t) then
val t2 = apply(t1)
if t2 ne t1 then t2
else t
else mapOver(t)

/** An extractor for `caps.reachCapability(ref)`, which is used to express a reach
* capability as a tree in a @retains annotation.
*/
Expand Down
11 changes: 8 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,9 @@ class Definitions {
@tu lazy val StringBuilderClass: ClassSymbol = requiredClass("scala.collection.mutable.StringBuilder")
@tu lazy val MatchErrorClass : ClassSymbol = requiredClass("scala.MatchError")
@tu lazy val ConversionClass : ClassSymbol = requiredClass("scala.Conversion").typeRef.symbol.asClass
@tu lazy val ConversionModule : Symbol = ConversionClass.companionModule
@tu lazy val ConversionModuleClass: ClassSymbol = ConversionModule.moduleClass.asClass
@tu lazy val Conversion_into : Symbol = ConversionModuleClass.requiredType("into")

@tu lazy val StringAddClass : ClassSymbol = requiredClass("scala.runtime.StringAdd")
@tu lazy val StringAdd_+ : Symbol = StringAddClass.requiredMethod(nme.raw.PLUS)
Expand Down Expand Up @@ -1039,8 +1042,6 @@ class Definitions {
@tu lazy val ImplicitAmbiguousAnnot: ClassSymbol = requiredClass("scala.annotation.implicitAmbiguous")
@tu lazy val ImplicitNotFoundAnnot: ClassSymbol = requiredClass("scala.annotation.implicitNotFound")
@tu lazy val InlineParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.InlineParam")
@tu lazy val IntoAnnot: ClassSymbol = requiredClass("scala.annotation.into")
@tu lazy val IntoParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.$into")
@tu lazy val ErasedParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ErasedParam")
@tu lazy val MainAnnot: ClassSymbol = requiredClass("scala.main")
@tu lazy val MappedAlternativeAnnot: ClassSymbol = requiredClass("scala.annotation.internal.MappedAlternative")
Expand All @@ -1058,6 +1059,7 @@ class Definitions {
// @tu lazy val ScalaStrictFPAnnot: ClassSymbol = requiredClass("scala.annotation.strictfp")
@tu lazy val ScalaStaticAnnot: ClassSymbol = requiredClass("scala.annotation.static")
@tu lazy val SerialVersionUIDAnnot: ClassSymbol = requiredClass("scala.SerialVersionUID")
@tu lazy val SilentIntoAnnot: ClassSymbol = requiredClass("scala.annotation.internal.$into")
@tu lazy val TailrecAnnot: ClassSymbol = requiredClass("scala.annotation.tailrec")
@tu lazy val ThreadUnsafeAnnot: ClassSymbol = requiredClass("scala.annotation.threadUnsafe")
@tu lazy val ConstructorOnlyAnnot: ClassSymbol = requiredClass("scala.annotation.constructorOnly")
Expand Down Expand Up @@ -1117,7 +1119,7 @@ class Definitions {

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

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

final def isInto(sym: Symbol)(using Context): Boolean =
sym.name == tpnme.into && sym.owner == ConversionModuleClass

private val compiletimePackageAnyTypes: Set[Name] = Set(
tpnme.Equals, tpnme.NotEquals, tpnme.IsConst, tpnme.ToString
)
Expand Down
7 changes: 7 additions & 0 deletions compiler/src/dotty/tools/dotc/core/NamerOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -317,4 +317,11 @@ object NamerOps:
ann.tree match
case ast.tpd.WitnessNamesAnnot(witnessNames) =>
addContextBoundCompanionFor(sym, witnessNames, Nil)

/** if `sym` is a term parameter or parameter accessor, map all occurrences of
* `into[T]` in its type to `T @$into`.
*/
extension (tp: Type)
def suppressIntoIfParam(sym: Symbol)(using Context): Type =
if sym.isOneOf(TermParamOrAccessor) then TypeOps.suppressInto(tp) else tp
end NamerOps
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ object StdNames {
val EXCEPTION_RESULT_PREFIX: N = "exceptionResult"
val EXPAND_SEPARATOR: N = str.EXPAND_SEPARATOR
val IMPORT: N = "<import>"
val INTO: N = "$into"
val MODULE_SUFFIX: N = str.MODULE_SUFFIX
val OPS_PACKAGE: N = "<special-ops>"
val OVERLOADED: N = "<overloaded>"
Expand Down
23 changes: 23 additions & 0 deletions compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import typer.ForceDegree
import typer.Inferencing.*
import typer.IfBottom
import reporting.TestingReporter
import Annotations.Annotation
import cc.{CapturingType, derivedCapturingType, CaptureSet, captureSet, isBoxed, isBoxedCapturing}
import CaptureSet.{CompareResult, IdentityCaptRefMap, VarState}

Expand Down Expand Up @@ -936,6 +937,28 @@ object TypeOps:
class StripTypeVarsMap(using Context) extends TypeMap:
def apply(tp: Type) = mapOver(tp).stripTypeVar

/** Map no-flip covariant occurrences of `into[T]` to `T @$into` */
def suppressInto(using Context) = new FollowAliasesMap:
def apply(t: Type): Type =
if variance <= 0 then t
else t match
case AppliedType(tycon: TypeRef, arg :: Nil) if defn.isInto(tycon.symbol) =>
AnnotatedType(arg, Annotation(defn.SilentIntoAnnot, util.Spans.NoSpan))
case _ =>
mapFollowingAliases(t)

/** Map no-flip covariant occurrences of `T @$into` to `into[T]` */
def revealInto(using Context) = new FollowAliasesMap:
def apply(t: Type): Type =
if variance <= 0 then t
else t match
case AnnotatedType(t1, ann) if ann.symbol == defn.SilentIntoAnnot =>
AppliedType(
defn.ConversionModule.termRef.select(defn.Conversion_into), // the external reference to the opaque type
t1 :: Nil)
case _ =>
mapFollowingAliases(t)

/** Apply [[Type.stripTypeVar]] recursively. */
def stripTypeVars(tp: Type)(using Context): Type =
new StripTypeVarsMap().apply(tp)
Expand Down
66 changes: 22 additions & 44 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ import CaptureSet.{CompareResult, IdentityCaptRefMap}

import scala.annotation.internal.sharable
import scala.annotation.threadUnsafe
import dotty.tools.dotc.cc.ccConfig

object Types extends TypeUtils {

Expand Down Expand Up @@ -446,9 +445,11 @@ object Types extends TypeUtils {
def isRepeatedParam(using Context): Boolean =
typeSymbol eq defn.RepeatedParamClass

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

/** Is this the type of a method that has a repeated parameter type as
Expand Down Expand Up @@ -1966,8 +1967,7 @@ object Types extends TypeUtils {
}
defn.FunctionNOf(
mt.paramInfos.mapConserve:
_.translateFromRepeated(toArray = isJava)
.mapIntoAnnot(defn.IntoParamAnnot, null),
_.translateFromRepeated(toArray = isJava),
result1, isContextual)
if mt.hasErasedParams then
defn.PolyFunctionOf(mt)
Expand Down Expand Up @@ -2015,38 +2015,6 @@ object Types extends TypeUtils {
case _ => this
}

/** A mapping between mapping one kind of into annotation to another or
* dropping into annotations.
* @param from the into annotation to map
* @param to either the replacement annotation symbol, or `null`
* in which case the `from` annotations are dropped.
*/
def mapIntoAnnot(from: ClassSymbol, to: ClassSymbol | Null)(using Context): Type = this match
case self @ AnnotatedType(tp, annot) =>
val tp1 = tp.mapIntoAnnot(from, to)
if annot.symbol == from then
if to == null then tp1
else AnnotatedType(tp1, Annotation(to, annot.tree.span))
else self.derivedAnnotatedType(tp1, annot)
case AppliedType(tycon, arg :: Nil) if tycon.typeSymbol == defn.RepeatedParamClass =>
val arg1 = arg.mapIntoAnnot(from, to)
if arg1 eq arg then this
else AppliedType(tycon, arg1 :: Nil)
case defn.FunctionOf(argTypes, resType, isContextual) =>
val resType1 = resType.mapIntoAnnot(from, to)
if resType1 eq resType then this
else defn.FunctionOf(argTypes, resType1, isContextual)
case RefinedType(parent, rname, mt: MethodOrPoly) =>
val mt1 = mt.mapIntoAnnot(from, to)
if mt1 eq mt then this
else RefinedType(parent.mapIntoAnnot(from, to), rname, mt1)
case mt: MethodOrPoly =>
mt.derivedLambdaType(resType = mt.resType.mapIntoAnnot(from, to))
case tp: ExprType =>
tp.derivedExprType(tp.resType.mapIntoAnnot(from, to))
case _ =>
this

/** The set of distinct symbols referred to by this type, after all aliases are expanded */
def coveringSet(using Context): Set[Symbol] =
(new CoveringSetAccumulator).apply(Set.empty[Symbol], this)
Expand Down Expand Up @@ -4175,11 +4143,11 @@ object Types extends TypeUtils {

/** Produce method type from parameter symbols, with special mappings for repeated
* and inline parameters:
* - replace @repeated annotations on Seq or Array types by <repeated> types
* - replace `@repeated` annotations on Seq or Array types by <repeated> types
* - map into annotations to $into annotations
* - add @inlineParam to inline parameters
* - add @erasedParam to erased parameters
* - wrap types of parameters that have an @allowConversions annotation with Into[_]
* - add `@inlineParam` to inline parameters
* - add `@erasedParam` to erased parameters
* - map `T @$into` types to `into[T]`
*/
def fromSymbols(params: List[Symbol], resultType: Type)(using Context): MethodType =
apply(params.map(_.name.asTermName))(
Expand All @@ -4193,9 +4161,7 @@ object Types extends TypeUtils {
def addAnnotation(tp: Type, cls: ClassSymbol, param: Symbol): Type = tp match
case ExprType(resType) => ExprType(addAnnotation(resType, cls, param))
case _ => AnnotatedType(tp, Annotation(cls, param.span))
var paramType = pinfo
.annotatedToRepeated
.mapIntoAnnot(defn.IntoAnnot, defn.IntoParamAnnot)
var paramType = TypeOps.revealInto(pinfo).annotatedToRepeated
if param.is(Inline) then
paramType = addAnnotation(paramType, defn.InlineParamAnnot, param)
if param.is(Erased) then
Expand Down Expand Up @@ -6130,6 +6096,18 @@ object Types extends TypeUtils {

end BiTypeMap

/** A typemap that follows aliases and keeps their transformed results if
* there is a change.
*/
trait FollowAliasesMap(using Context) extends TypeMap:
def mapFollowingAliases(t: Type): Type =
val t1 = t.dealiasKeepAnnots
if t1 ne t then
val t2 = apply(t1)
if t2 ne t1 then t2
else t
else mapOver(t)

abstract class TypeMap(implicit protected var mapCtx: Context)
extends VariantTraversal with (Type => Type) { thisMap =>

Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -933,7 +933,7 @@ class TreeUnpickler(reader: TastyReader,
DefDef(paramDefss, tpt)
case VALDEF =>
val tpt = readTpt()(using localCtx)
sym.info = tpt.tpe
sym.info = tpt.tpe.suppressIntoIfParam(sym)
ValDef(tpt)
case TYPEDEF | TYPEPARAM =>
if (sym.isClass) {
Expand Down Expand Up @@ -978,7 +978,7 @@ class TreeUnpickler(reader: TastyReader,
case PARAM =>
val tpt = readTpt()(using localCtx)
assert(nothingButMods(end))
sym.info = tpt.tpe
sym.info = tpt.tpe.suppressIntoIfParam(sym)
ValDef(tpt)
}
goto(end)
Expand Down
Loading
Loading