Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit e95afce

Browse files
authored
Make into a preview feature (#24090)
Since SIP 71 is now accepted, we can ship it as -preview in 3.8 and standard in 3.9. ## Note to tooling implementers There are two relevant pieces that need to be implemented in IDEs: - `into` is a new soft modifier for classes, traits, and enums. Types with `into` are legal conversion target types. See `isConversionTargetType` in `Types.scala`. - The `Conversion.into` type is a regular opaque type, so should not need any support by itself. However, there's a rule that no-flip covariant occurrences of `into[T]` in method parameters are mapped to`T @$into`, where `$into` is an annotation in `annotation.internal`. The annotation is "silent" which means it should not be shown in inferred types. The purpose of that mapping is that `into[...]` does not have to be explicitly stripped in method bodies. See `suppressInto` and `revealInto` in TypeOps. /cc @unkarjedy @tgodzik
2 parents cdcc5c1 + dd0c26d commit e95afce

30 files changed

+173
-185
lines changed

‎compiler/src/dotty/tools/dotc/core/Flags.scala‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ object Flags {
250250
val (Local @ _, _, _) = newFlags(13, "<local>")
251251

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

‎compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala‎

Lines changed: 43 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -858,64 +858,60 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) {
858858
if (flags.is(ParamAccessor) && sym.isTerm && !sym.isSetter)
859859
flags = flags &~ ParamAccessor // we only generate a tag for parameter setters
860860
pickleFlags(flags, sym.isTerm)
861-
if flags.is(Into) then
862-
// Temporary measure until we can change TastyFormat to include an INTO tag
863-
pickleAnnotation(sym, mdef, Annotation(defn.SilentIntoAnnot, util.Spans.NoSpan))
864861
val annots = sym.annotations.foreach(pickleAnnotation(sym, mdef, _))
865862
}
866863

867-
def pickleFlags(flags: FlagSet, isTerm: Boolean)(using Context): Unit = {
864+
def pickleFlags(flags: FlagSet, isTerm: Boolean)(using Context): Unit =
868865
import Flags.*
869-
def writeModTag(tag: Int) = {
866+
def writeModTag(tag: Int) =
870867
assert(isModifierTag(tag))
871868
writeByte(tag)
872-
}
869+
873870
if flags.is(Scala2x) then assert(attributes.scala2StandardLibrary)
874-
if (flags.is(Private)) writeModTag(PRIVATE)
875-
if (flags.is(Protected)) writeModTag(PROTECTED)
876-
if (flags.is(Final, butNot = Module)) writeModTag(FINAL)
877-
if (flags.is(Case)) writeModTag(CASE)
878-
if (flags.is(Override)) writeModTag(OVERRIDE)
879-
if (flags.is(Inline)) writeModTag(INLINE)
880-
if (flags.is(InlineProxy)) writeModTag(INLINEPROXY)
881-
if (flags.is(Macro)) writeModTag(MACRO)
882-
if (flags.is(JavaStatic)) writeModTag(STATIC)
883-
if (flags.is(Module)) writeModTag(OBJECT)
884-
if (flags.is(Enum)) writeModTag(ENUM)
885-
if (flags.is(Local)) writeModTag(LOCAL)
886-
if (flags.is(Synthetic)) writeModTag(SYNTHETIC)
887-
if (flags.is(Artifact)) writeModTag(ARTIFACT)
871+
if flags.is(Private)then writeModTag(PRIVATE)
872+
if flags.is(Protected)then writeModTag(PROTECTED)
873+
if flags.is(Final, butNot = Module)then writeModTag(FINAL)
874+
if flags.is(Case)then writeModTag(CASE)
875+
if flags.is(Override)then writeModTag(OVERRIDE)
876+
if flags.is(Inline)then writeModTag(INLINE)
877+
if flags.is(InlineProxy)then writeModTag(INLINEPROXY)
878+
if flags.is(Macro)then writeModTag(MACRO)
879+
if flags.is(JavaStatic)then writeModTag(STATIC)
880+
if flags.is(Module)then writeModTag(OBJECT)
881+
if flags.is(Enum)then writeModTag(ENUM)
882+
if flags.is(Local)then writeModTag(LOCAL)
883+
if flags.is(Synthetic)then writeModTag(SYNTHETIC)
884+
if flags.is(Artifact)then writeModTag(ARTIFACT)
888885
if flags.is(Transparent) then writeModTag(TRANSPARENT)
889886
if flags.is(Infix) then writeModTag(INFIX)
890887
if flags.is(Invisible) then writeModTag(INVISIBLE)
891-
if (flags.is(Erased)) writeModTag(ERASED)
892-
if (flags.is(Exported)) writeModTag(EXPORTED)
893-
if (flags.is(Given)) writeModTag(GIVEN)
894-
if (flags.is(Implicit)) writeModTag(IMPLICIT)
895-
if (flags.is(Tracked)) writeModTag(TRACKED)
896-
if (isTerm) {
897-
if (flags.is(Lazy, butNot = Module)) writeModTag(LAZY)
898-
if (flags.is(AbsOverride)) { writeModTag(ABSTRACT); writeModTag(OVERRIDE) }
899-
if (flags.is(Mutable)) writeModTag(MUTABLE)
900-
if (flags.is(Accessor)) writeModTag(FIELDaccessor)
901-
if (flags.is(CaseAccessor)) writeModTag(CASEaccessor)
902-
if (flags.is(HasDefault)) writeModTag(HASDEFAULT)
888+
if flags.is(Erased)then writeModTag(ERASED)
889+
if flags.is(Exported)then writeModTag(EXPORTED)
890+
if flags.is(Given)then writeModTag(GIVEN)
891+
if flags.is(Implicit)then writeModTag(IMPLICIT)
892+
if flags.is(Tracked)then writeModTag(TRACKED)
893+
if isTermthen
894+
if flags.is(Lazy, butNot = Module)then writeModTag(LAZY)
895+
if flags.is(AbsOverride)then { writeModTag(ABSTRACT); writeModTag(OVERRIDE) }
896+
if flags.is(Mutable)then writeModTag(MUTABLE)
897+
if flags.is(Accessor)then writeModTag(FIELDaccessor)
898+
if flags.is(CaseAccessor)then writeModTag(CASEaccessor)
899+
if flags.is(HasDefault)then writeModTag(HASDEFAULT)
903900
if flags.isAllOf(StableMethod) then writeModTag(STABLE) // other StableRealizable flag occurrences are either implied or can be recomputed
904-
if (flags.is(Extension)) writeModTag(EXTENSION)
905-
if (flags.is(ParamAccessor)) writeModTag(PARAMsetter)
906-
if (flags.is(SuperParamAlias)) writeModTag(PARAMalias)
907-
assert(!(flags.is(Label)))
908-
}
909-
else {
910-
if (flags.is(Sealed)) writeModTag(SEALED)
911-
if (flags.is(Abstract)) writeModTag(ABSTRACT)
912-
if (flags.is(Trait)) writeModTag(TRAIT)
913-
if (flags.is(Covariant)) writeModTag(COVARIANT)
914-
if (flags.is(Contravariant)) writeModTag(CONTRAVARIANT)
915-
if (flags.is(Opaque)) writeModTag(OPAQUE)
916-
if (flags.is(Open)) writeModTag(OPEN)
917-
}
918-
}
901+
if flags.is(Extension) then writeModTag(EXTENSION)
902+
if flags.is(ParamAccessor) then writeModTag(PARAMsetter)
903+
if flags.is(SuperParamAlias) then writeModTag(PARAMalias)
904+
assert(!flags.is(Label))
905+
else
906+
if flags.is(Sealed) then writeModTag(SEALED)
907+
if flags.is(Abstract) then writeModTag(ABSTRACT)
908+
if flags.is(Trait) then writeModTag(TRAIT)
909+
if flags.is(Covariant) then writeModTag(COVARIANT)
910+
if flags.is(Contravariant) then writeModTag(CONTRAVARIANT)
911+
if flags.is(Opaque) then writeModTag(OPAQUE)
912+
if flags.is(Open) then writeModTag(OPEN)
913+
if flags.is(Into) then writeModTag(INTO)
914+
end pickleFlags
919915

920916
private def isUnpicklable(owner: Symbol, ann: Annotation)(using Context) = ann match {
921917
case Annotation.Child(sym) => sym.isInaccessibleChildOf(owner)

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -670,12 +670,7 @@ class TreeUnpickler(reader: TastyReader,
670670
}
671671
val annotOwner =
672672
if sym.owner.isClass then newLocalDummy(sym.owner) else sym.owner
673-
var annots = annotFns.map(_(annotOwner))
674-
if annots.exists(_.hasSymbol(defn.SilentIntoAnnot)) then
675-
// Temporary measure until we can change TastyFormat to include an INTO tag
676-
sym.setFlag(Into)
677-
annots = annots.filterNot(_.symbol == defn.SilentIntoAnnot)
678-
sym.annotations = annots
673+
sym.annotations = annotFns.map(_(annotOwner))
679674
if sym.isOpaqueAlias then sym.setFlag(Deferred)
680675
val isScala2MacroDefinedInScala3 = flags.is(Macro, butNot = Inline) && flags.is(Erased)
681676
ctx.owner match {
@@ -765,6 +760,7 @@ class TreeUnpickler(reader: TastyReader,
765760
case TRANSPARENT => addFlag(Transparent)
766761
case INFIX => addFlag(Infix)
767762
case TRACKED => addFlag(Tracked)
763+
case INTO => addFlag(Into)
768764
case PRIVATEqualified =>
769765
readByte()
770766
privateWithin = readWithin

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3374,12 +3374,14 @@ object Parsers {
33743374
case IDENTIFIER =>
33753375
name match {
33763376
case nme.inline => Mod.Inline()
3377-
case nme.into => Mod.Into()
33783377
case nme.opaque => Mod.Opaque()
33793378
case nme.open => Mod.Open()
33803379
case nme.transparent => Mod.Transparent()
33813380
case nme.infix => Mod.Infix()
33823381
case nme.tracked => Mod.Tracked()
3382+
case nme.into =>
3383+
Feature.checkPreviewFeature("`into`", in.sourcePos())
3384+
Mod.Into()
33833385
case nme.erased if in.erasedEnabled => Mod.Erased()
33843386
case nme.update if Feature.ccEnabled => Mod.Update()
33853387
}

‎library/src/scala/Conversion.scala‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package scala
22

33
import language.experimental.captureChecking
4+
import annotation.internal.preview
45

56
/** A class for implicit values that can serve as implicit conversions.
67
* The implicit resolution algorithm will act as if there existed
@@ -41,10 +42,11 @@ object Conversion:
4142
* conversions are tried from the type of `t` to `T`. `into[T]` types are erased to `T`
4243
* in all covariant positions of the types of parameter symbols.
4344
*/
44-
@experimental
45+
@preview
4546
opaque type into[+T] >: T = T
4647

4748
/** Unwrap an `into` */
4849
extension [T](x: into[T])
49-
@experimental def underlying: T = x
50+
@preview
51+
def underlying: T = x
5052
end Conversion
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
package scala.annotation.internal
22

3-
import language.experimental.captureChecking
4-
5-
import annotation.experimental
6-
73
/** An internal annotation on (part of) a parameter type that serves as a marker where
84
* the original type was of the form `into[T]`. These annotated types are mapped back
95
* to `into[T]` types when forming a method types from the parameter types. The idea is
106
* that `T @$into` is equivalent to `T`, whereas `into[T]` is only a known supertype of
117
* `T`. Hence, we don't need to use `.underlying` to go from an into type to its
128
* underlying type in the types of local parameters.
139
*/
14-
@experimental
10+
@preview
1511
class $into extends annotation.StaticAnnotation

‎library/src/scala/language.scala‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ object language {
292292
* @see [[https://dotty.epfl.ch/docs/reference/experimental/into-modifier]]
293293
*/
294294
@compileTimeOnly("`into` can only be used at compile time in import statements")
295+
@deprecated("The into language import is no longer needed since the feature is now in preview", since = "3.8")
295296
object into
296297

297298
/** Experimental support for named tuples.

‎library/src/scala/runtime/stdLibPatches/language.scala‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ object language:
9999
* @see [[https://dotty.epfl.ch/docs/reference/experimental/into-modifier]]
100100
*/
101101
@compileTimeOnly("`into` can only be used at compile time in import statements")
102+
@deprecated("The into language import is no longer needed since the feature is now in preview", since = "3.8")
102103
object into
103104

104105
/** Experimental support for named tuples.

‎tasty/src/dotty/tools/tasty/TastyFormat.scala‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ Standard-Section: "ASTs" TopLevelStat*
229229
OPEN -- an open class
230230
INVISIBLE -- invisible during typechecking, except when resolving from TASTy
231231
TRACKED -- a tracked class parameter / a dependent class
232+
INTO -- a legal conversion target
232233
Annotation
233234
234235
Variance = STABLE -- invariant
@@ -512,6 +513,7 @@ object TastyFormat {
512513
final val SPLITCLAUSE = 46
513514
final val TRACKED = 47
514515
final val SUBMATCH = 48 // experimental.subCases
516+
final val INTO = 49
515517

516518
// Tree Cat. 2: tag Nat
517519
final val firstNatTreeTag = SHAREDterm
@@ -704,7 +706,8 @@ object TastyFormat {
704706
| ANNOTATION
705707
| PRIVATEqualified
706708
| PROTECTEDqualified
707-
| TRACKED => true
709+
| TRACKED
710+
| INTO => true
708711
case _ => false
709712
}
710713

‎tests/neg/i23400.scala‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import scala.language.experimental.into
1+
//> using options -preview
2+
// preview needed for into in 3.8
3+
24
import Conversion.into
35

46
import scala.deriving.Mirror

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /