Skip to content
Merged
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 compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ object Flags {
val (Local @ _, _, _) = newFlags(13, "<local>")

/** A field generated for a primary constructor parameter (no matter if it's a 'val' or not),
* or an accessor of such a field.
* or an accessor of such a field / An `into` modifier on a class
*/
val (ParamAccessorOrInto @ _, ParamAccessor @ _, Into @ _) = newFlags(14, "<paramaccessor>", "into")

Expand Down
90 changes: 43 additions & 47 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -858,64 +858,60 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) {
if (flags.is(ParamAccessor) && sym.isTerm && !sym.isSetter)
flags = flags &~ ParamAccessor // we only generate a tag for parameter setters
pickleFlags(flags, sym.isTerm)
if flags.is(Into) then
// Temporary measure until we can change TastyFormat to include an INTO tag
pickleAnnotation(sym, mdef, Annotation(defn.SilentIntoAnnot, util.Spans.NoSpan))
val annots = sym.annotations.foreach(pickleAnnotation(sym, mdef, _))
}

def pickleFlags(flags: FlagSet, isTerm: Boolean)(using Context): Unit = {
def pickleFlags(flags: FlagSet, isTerm: Boolean)(using Context): Unit =
import Flags.*
def writeModTag(tag: Int) = {
def writeModTag(tag: Int) =
assert(isModifierTag(tag))
writeByte(tag)
}

if flags.is(Scala2x) then assert(attributes.scala2StandardLibrary)
if (flags.is(Private)) writeModTag(PRIVATE)
if (flags.is(Protected)) writeModTag(PROTECTED)
if (flags.is(Final, butNot = Module)) writeModTag(FINAL)
if (flags.is(Case)) writeModTag(CASE)
if (flags.is(Override)) writeModTag(OVERRIDE)
if (flags.is(Inline)) writeModTag(INLINE)
if (flags.is(InlineProxy)) writeModTag(INLINEPROXY)
if (flags.is(Macro)) writeModTag(MACRO)
if (flags.is(JavaStatic)) writeModTag(STATIC)
if (flags.is(Module)) writeModTag(OBJECT)
if (flags.is(Enum)) writeModTag(ENUM)
if (flags.is(Local)) writeModTag(LOCAL)
if (flags.is(Synthetic)) writeModTag(SYNTHETIC)
if (flags.is(Artifact)) writeModTag(ARTIFACT)
if flags.is(Private) then writeModTag(PRIVATE)
if flags.is(Protected) then writeModTag(PROTECTED)
if flags.is(Final, butNot = Module) then writeModTag(FINAL)
if flags.is(Case) then writeModTag(CASE)
if flags.is(Override) then writeModTag(OVERRIDE)
if flags.is(Inline) then writeModTag(INLINE)
if flags.is(InlineProxy) then writeModTag(INLINEPROXY)
if flags.is(Macro) then writeModTag(MACRO)
if flags.is(JavaStatic) then writeModTag(STATIC)
if flags.is(Module) then writeModTag(OBJECT)
if flags.is(Enum) then writeModTag(ENUM)
if flags.is(Local) then writeModTag(LOCAL)
if flags.is(Synthetic) then writeModTag(SYNTHETIC)
if flags.is(Artifact) then writeModTag(ARTIFACT)
if flags.is(Transparent) then writeModTag(TRANSPARENT)
if flags.is(Infix) then writeModTag(INFIX)
if flags.is(Invisible) then writeModTag(INVISIBLE)
if (flags.is(Erased)) writeModTag(ERASED)
if (flags.is(Exported)) writeModTag(EXPORTED)
if (flags.is(Given)) writeModTag(GIVEN)
if (flags.is(Implicit)) writeModTag(IMPLICIT)
if (flags.is(Tracked)) writeModTag(TRACKED)
if (isTerm) {
if (flags.is(Lazy, butNot = Module)) writeModTag(LAZY)
if (flags.is(AbsOverride)) { writeModTag(ABSTRACT); writeModTag(OVERRIDE) }
if (flags.is(Mutable)) writeModTag(MUTABLE)
if (flags.is(Accessor)) writeModTag(FIELDaccessor)
if (flags.is(CaseAccessor)) writeModTag(CASEaccessor)
if (flags.is(HasDefault)) writeModTag(HASDEFAULT)
if flags.is(Erased) then writeModTag(ERASED)
if flags.is(Exported) then writeModTag(EXPORTED)
if flags.is(Given) then writeModTag(GIVEN)
if flags.is(Implicit) then writeModTag(IMPLICIT)
if flags.is(Tracked) then writeModTag(TRACKED)
if isTerm then
if flags.is(Lazy, butNot = Module) then writeModTag(LAZY)
if flags.is(AbsOverride) then { writeModTag(ABSTRACT); writeModTag(OVERRIDE) }
if flags.is(Mutable) then writeModTag(MUTABLE)
if flags.is(Accessor) then writeModTag(FIELDaccessor)
if flags.is(CaseAccessor) then writeModTag(CASEaccessor)
if flags.is(HasDefault) then writeModTag(HASDEFAULT)
if flags.isAllOf(StableMethod) then writeModTag(STABLE) // other StableRealizable flag occurrences are either implied or can be recomputed
if (flags.is(Extension)) writeModTag(EXTENSION)
if (flags.is(ParamAccessor)) writeModTag(PARAMsetter)
if (flags.is(SuperParamAlias)) writeModTag(PARAMalias)
assert(!(flags.is(Label)))
}
else {
if (flags.is(Sealed)) writeModTag(SEALED)
if (flags.is(Abstract)) writeModTag(ABSTRACT)
if (flags.is(Trait)) writeModTag(TRAIT)
if (flags.is(Covariant)) writeModTag(COVARIANT)
if (flags.is(Contravariant)) writeModTag(CONTRAVARIANT)
if (flags.is(Opaque)) writeModTag(OPAQUE)
if (flags.is(Open)) writeModTag(OPEN)
}
}
if flags.is(Extension) then writeModTag(EXTENSION)
if flags.is(ParamAccessor) then writeModTag(PARAMsetter)
if flags.is(SuperParamAlias) then writeModTag(PARAMalias)
assert(!flags.is(Label))
else
if flags.is(Sealed) then writeModTag(SEALED)
if flags.is(Abstract) then writeModTag(ABSTRACT)
if flags.is(Trait) then writeModTag(TRAIT)
if flags.is(Covariant) then writeModTag(COVARIANT)
if flags.is(Contravariant) then writeModTag(CONTRAVARIANT)
if flags.is(Opaque) then writeModTag(OPAQUE)
if flags.is(Open) then writeModTag(OPEN)
if flags.is(Into) then writeModTag(INTO)
end pickleFlags

private def isUnpicklable(owner: Symbol, ann: Annotation)(using Context) = ann match {
case Annotation.Child(sym) => sym.isInaccessibleChildOf(owner)
Expand Down
8 changes: 2 additions & 6 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -670,12 +670,7 @@ class TreeUnpickler(reader: TastyReader,
}
val annotOwner =
if sym.owner.isClass then newLocalDummy(sym.owner) else sym.owner
var annots = annotFns.map(_(annotOwner))
if annots.exists(_.hasSymbol(defn.SilentIntoAnnot)) then
// Temporary measure until we can change TastyFormat to include an INTO tag
sym.setFlag(Into)
annots = annots.filterNot(_.symbol == defn.SilentIntoAnnot)
sym.annotations = annots
sym.annotations = annotFns.map(_(annotOwner))
if sym.isOpaqueAlias then sym.setFlag(Deferred)
val isScala2MacroDefinedInScala3 = flags.is(Macro, butNot = Inline) && flags.is(Erased)
ctx.owner match {
Expand Down Expand Up @@ -765,6 +760,7 @@ class TreeUnpickler(reader: TastyReader,
case TRANSPARENT => addFlag(Transparent)
case INFIX => addFlag(Infix)
case TRACKED => addFlag(Tracked)
case INTO => addFlag(Into)
case PRIVATEqualified =>
readByte()
privateWithin = readWithin
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3367,12 +3367,14 @@ object Parsers {
case IDENTIFIER =>
name match {
case nme.inline => Mod.Inline()
case nme.into => Mod.Into()
case nme.opaque => Mod.Opaque()
case nme.open => Mod.Open()
case nme.transparent => Mod.Transparent()
case nme.infix => Mod.Infix()
case nme.tracked => Mod.Tracked()
case nme.into =>
Feature.checkPreviewFeature("`into`", in.sourcePos())
Mod.Into()
case nme.erased if in.erasedEnabled => Mod.Erased()
case nme.update if Feature.ccEnabled => Mod.Update()
}
Expand Down
6 changes: 4 additions & 2 deletions library/src/scala/Conversion.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package scala

import language.experimental.captureChecking
import annotation.internal.preview

/** A class for implicit values that can serve as implicit conversions.
* The implicit resolution algorithm will act as if there existed
Expand Down Expand Up @@ -41,10 +42,11 @@ object Conversion:
* conversions are tried from the type of `t` to `T`. `into[T]` types are erased to `T`
* in all covariant positions of the types of parameter symbols.
*/
@experimental
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is important, if we don't mark this alias with @preview, we will not be able to enforce it.

@preview
opaque type into[+T] >: T = T

/** Unwrap an `into` */
extension [T](x: into[T])
@experimental def underlying: T = x
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one should also be marked as @preview, otherwise we will be compiling the library with -preview

@preview
def underlying: T = x
end Conversion
6 changes: 1 addition & 5 deletions library/src/scala/annotation/internal/$into.scala
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package scala.annotation.internal

import language.experimental.captureChecking

import annotation.experimental

/** An internal annotation on (part of) a parameter type that serves as a marker where
* the original type was of the form `into[T]`. These annotated types are mapped back
* to `into[T]` types when forming a method types from the parameter types. The idea is
* that `T @$into` is equivalent to `T`, whereas `into[T]` is only a known supertype of
* `T`. Hence, we don't need to use `.underlying` to go from an into type to its
* underlying type in the types of local parameters.
*/
@experimental
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it now use @scala.annotation.internal.preview annotation instead of making it stable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not really needed. It is an internal annotation that people will not use manually.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, even then. In theory it's part of a preview feature, and could be altered. (Very very unlikely in this case, but let's follow the procedure, so that the next person who comes to this PR in search for a reference of how a feature becomes preview doesn't get misled.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, if we were to be pedantic, we should put it. I just claimed that from an implementation point of view, it is not really needed.

@preview
class $into extends annotation.StaticAnnotation
1 change: 1 addition & 0 deletions library/src/scala/language.scala
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ object language {
* @see [[https://dotty.epfl.ch/docs/reference/experimental/into-modifier]]
*/
@compileTimeOnly("`into` can only be used at compile time in import statements")
@deprecated("The into language import is no longer needed since the feature is now in preview", since = "3.8")
object into

/** Experimental support for named tuples.
Expand Down
1 change: 1 addition & 0 deletions library/src/scala/runtime/stdLibPatches/language.scala
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ object language:
* @see [[https://dotty.epfl.ch/docs/reference/experimental/into-modifier]]
*/
@compileTimeOnly("`into` can only be used at compile time in import statements")
@deprecated("The into language import is no longer needed since the feature is now in preview", since = "3.8")
object into

/** Experimental support for named tuples.
Expand Down
5 changes: 4 additions & 1 deletion tasty/src/dotty/tools/tasty/TastyFormat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ Standard-Section: "ASTs" TopLevelStat*
OPEN -- an open class
INVISIBLE -- invisible during typechecking, except when resolving from TASTy
TRACKED -- a tracked class parameter / a dependent class
INTO -- a legal conversion target
Annotation

Variance = STABLE -- invariant
Expand Down Expand Up @@ -512,6 +513,7 @@ object TastyFormat {
final val SPLITCLAUSE = 46
final val TRACKED = 47
final val SUBMATCH = 48 // experimental.subCases
final val INTO = 49

// Tree Cat. 2: tag Nat
final val firstNatTreeTag = SHAREDterm
Expand Down Expand Up @@ -704,7 +706,8 @@ object TastyFormat {
| ANNOTATION
| PRIVATEqualified
| PROTECTEDqualified
| TRACKED => true
| TRACKED
| INTO => true
case _ => false
}

Expand Down
4 changes: 3 additions & 1 deletion tests/neg/i23400.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import scala.language.experimental.into
//> using options -preview
// preview needed for into in 3.8

import Conversion.into

import scala.deriving.Mirror
Expand Down
Loading
Loading