Skip to content
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
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/CompilationUnit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,12 @@ class CompilationUnit protected (val source: SourceFile, val info: CompilationUn
/** Will be set to true if unit was compiled with `magic` language import. */
var magic: Boolean = false

/** Will be set to true if unit was compiled with `errorHandling` language import. */
var newErrorHandling: Boolean = false

/** Will be set to true if the unit contains a pureFunctions language import */
var knowsPureFuns: Boolean = false

/** Will be set to true if the unit contains an inlineTrait language import */
var knowsInlineTraits: Boolean = false

Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2416,13 +2416,16 @@ object desugar {
// This is a deliberate departure from scalac, where StringContext is not rooted (See #4732)
Apply(Select(Apply(scalaDot(nme.StringContext), strs), id).withSpan(tree.span), elems)
case PostfixOp(t, op) =>
if (ctx.mode is Mode.Type) && !isBackquoted(op) && op.name == tpnme.raw.STAR then
def isOp(name: TypeName) = op.name == name && !isBackquoted(op)
if isOp(tpnme.raw.STAR) then
if ctx.isJava then
AppliedTypeTree(ref(defn.RepeatedParamType), t)
else
Annotated(
AppliedTypeTree(ref(defn.SeqType), t),
New(ref(defn.RepeatedAnnot.typeRef), Nil :: Nil))
else if isOp(tpnme.?) then
AppliedTypeTree(ref(defn.MaybeClass.typeRef), t :: ref(defn.UnitClass.typeRef) :: Nil)
else
assert(ctx.mode.isExpr || ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive), ctx.mode)
Select(t, op.name)
Expand Down
20 changes: 20 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def If(cond: Tree, thenp: Tree, elsep: Tree)(using Context): If =
ta.assignType(untpd.If(cond, thenp, elsep), thenp, elsep)

/** A smart version of this that returns one of the branches if the condition
* is a boolean literal.
*/
def conditional(cond: Tree, thenp: Tree, elsep: Tree)(using Context): Tree =
cond match
case Literal(Constant(true)) => thenp
case Literal(Constant(false)) => elsep
case _ => If(cond, thenp, elsep)

def InlineIf(cond: Tree, thenp: Tree, elsep: Tree)(using Context): If =
ta.assignType(untpd.InlineIf(cond, thenp, elsep), thenp, elsep)

Expand Down Expand Up @@ -1094,6 +1103,13 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
receiver.select(defn.Object_ne).appliedTo(nullLit).withSpan(tree.span)
}

/** `null == tree` if cond, else `null != tree`
* Simpler than `testNotNull`. TODO: Can we replace testNotNull with this?
*/
def nullTest(cond: Boolean)(using Context) =
nullLiteral.select(if cond then defn.Any_== else defn.Any_!=)
.appliedTo(tree)

/** If inititializer tree is `_`, the default value of its type,
* otherwise the tree itself.
*/
Expand All @@ -1104,6 +1120,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def and(that: Tree)(using Context): Tree =
tree.select(defn.Boolean_&&).appliedTo(that)

/** `!this`, for boolean tree `this` */
def not(using Context): Tree =
tree.select(defn.Boolean_!)

/** `this || that`, for boolean trees `this`, `that` */
def or(that: Tree)(using Context): Tree =
tree.select(defn.Boolean_||).appliedTo(that)
Expand Down
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1535,9 +1535,10 @@ class CheckCaptures extends Recheck, SymTransformer:

val saved = curEnv
curEnv = Env(cls, EnvKind.Regular, localSet, curEnv)
val selfType = cls.classInfo.selfType
try
// (2) Capture set of self type includes capture set of class
val thisSet = cls.classInfo.selfType.captureSet.withDescription(i"of the self type of $cls")
val thisSet = selfType.captureSet.withDescription(i"of the self type of $cls")
withGlobalCapAsRoot: // OK? We need this here since self types use GlobalAny instead of a LocalCap
checkSubset(localSet, thisSet, tree.srcPos)

Expand Down Expand Up @@ -1576,8 +1577,9 @@ class CheckCaptures extends Recheck, SymTransformer:
finally
if cls.is(ModuleClass) then
interpolate(cls.sourceModule.info, cls.sourceModule)
interpolate(selfType, cls)
else
capt.println(i"Use set of $cls = ${cls.useSet}, self type: ${cls.classInfo.selfType}")
capt.println(i"Use set of $cls = ${cls.useSet}, self type: $selfType")
completed += cls
curEnv = saved
end recheckClassDef
Expand Down
8 changes: 5 additions & 3 deletions compiler/src/dotty/tools/dotc/cc/Setup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,11 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
CapturingType(OrType(parent1, tp2, tp.isSoft), refs1, tp1.isBoxed)
case tp @ OrType(tp1, tp2 @ CapturingType(parent2, refs2)) =>
CapturingType(OrType(tp1, parent2, tp.isSoft), refs2, tp2.isBoxed)
case tp @ AppliedType(tycon, args)
if !defn.isFunctionClass(tp.dealias.typeSymbol) && (tp.dealias eq tp) =>
tp.derivedAppliedType(tycon, args.mapConserve(_.boxDeeply))
case tp @ AppliedType(tycon, args) if tp.dealias eq tp =>
val sym = tycon.typeSymbol
if !defn.isFunctionClass(sym) && sym != defn.MaybeClass
then tp.derivedAppliedType(tycon, args.mapConserve(_.boxDeeply))
else tp
case tp: RealTypeBounds =>
tp.derivedTypeBounds(tp.lo, tp.hi.boxDeeply)
case tp: LazyRef =>
Expand Down
35 changes: 24 additions & 11 deletions compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ object Feature:
val magic = experimental("magic")
val inlineTraits = experimental("inlineTraits")
val specializedTraits = experimental("specializedTraits")
val errorHandling = experimental("errorHandling")

val nonViralExperimentalFeatures: Set[TermName] =
Set(captureChecking, separationChecking, safe)
Expand Down Expand Up @@ -86,6 +87,7 @@ object Feature:
(magic, "Enable extensions for working with coding agents"),
(inlineTraits, "Allow inline traits"),
(specializedTraits, "Allow specialized traits"),
(errorHandling, "Allow error handling using T? and T ? E types"),
)

/** Features that are now standard; the language import / -language choice is
Expand Down Expand Up @@ -187,16 +189,21 @@ object Feature:

def quotedPatternsWithPolymorphicFunctionsEnabled(using Context) =
enabled(quotedPatternsWithPolymorphicFunctions)
def inlineTraitsEnabled(using Context) =

def inlineTraitsEnabled(using Context) =
enabledBySetting(inlineTraits)
|| enabledBySetting(specializedTraits)
|| ctx.compilationUnit.knowsInlineTraits


def errorHandlingEnabled(using Context) =
ctx.originalCompilationUnit.newErrorHandling
|| enabledBySetting(errorHandling)
|| enabledBySetting(magic)

/** Is pureFunctions enabled for this compilation unit? */
def pureFunsEnabled(using Context) =
enabledBySetting(pureFunctions)
|| ctx.compilationUnit.knowsPureFuns
|| ctx.originalCompilationUnit.knowsPureFuns
|| ccEnabled

/** Is capture checking enabled by a command-line setting? */
Expand Down Expand Up @@ -324,32 +331,38 @@ object Feature:
* @return true iff the import was handled
*/
def handleGlobalLanguageImport(prefix: TermName, imported: Name)(using Context): Boolean =
def enableCC() =
ctx.compilationUnit.needsCaptureChecking = true
if ctx.run != null then ctx.run.nn.ccEnabledSomewhere = true
QualifiedName(prefix, imported.asTermName) match
case `pureFunctions` =>
ctx.compilationUnit.knowsPureFuns = true
if ctx.run != null then ctx.run.nn.pureFunsImportEncountered = true
true
case `captureChecking` =>
ctx.compilationUnit.needsCaptureChecking = true
if ctx.run != null then ctx.run.nn.ccEnabledSomewhere = true
enableCC()
true
case `separationChecking` =>
ctx.compilationUnit.needsCaptureChecking = true
enableCC()
ctx.compilationUnit.needsSeparationChecking = true
if ctx.run != null then ctx.run.nn.ccEnabledSomewhere = true
true
case `safe` =>
ctx.compilationUnit.needsCaptureChecking = true
enableCC()
ctx.compilationUnit.safeMode = true
if ctx.run != null then ctx.run.nn.ccEnabledSomewhere = true
true
case `magic` =>
enableCC()
ctx.compilationUnit.magic = true
ctx.compilationUnit.newErrorHandling = true
ctx.compilationUnit.sourceVersion = Some(SourceVersion.future)
true
case `inlineTraits` =>
ctx.compilationUnit.knowsInlineTraits = true
ctx.compilationUnit.knowsInlineTraits = true
if ctx.run != null then ctx.run.nn.inlineTraitsImportEncountered = true
true
case `errorHandling` =>
ctx.compilationUnit.newErrorHandling = true
true
case _ =>
false

Expand Down
35 changes: 32 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ class Definitions {
@tu lazy val SysPackage : Symbol = requiredModule("scala.sys.package")
@tu lazy val Sys_error: Symbol = SysPackage.moduleClass.requiredMethod(nme.error)


@tu lazy val ScalaXmlPackageClass: Symbol = getPackageClassIfDefined("scala.xml")

@tu lazy val CompiletimePackageClass: Symbol = requiredPackage("scala.compiletime").moduleClass
Expand All @@ -267,8 +268,6 @@ class Definitions {
@tu lazy val Compiletime_summonFrom : Symbol = CompiletimePackageClass.requiredMethod("summonFrom")
@tu lazy val Compiletime_summonInline : Symbol = CompiletimePackageClass.requiredMethod("summonInline")
@tu lazy val Compiletime_summonAll : Symbol = CompiletimePackageClass.requiredMethod("summonAll")
@tu lazy val Compiletime_spec : Symbol = CompiletimePackageClass.requiredMethod("$spec")
@tu lazy val Compiletime_wrappedType : Symbol = CompiletimePackageClass.requiredMethod("$wrappedType")
@tu lazy val CompiletimeTestingPackage: Symbol = requiredPackage("scala.compiletime.testing")
@tu lazy val CompiletimeTesting_typeChecks: Symbol = CompiletimeTestingPackage.requiredMethod("typeChecks")
@tu lazy val CompiletimeTesting_typeCheckErrors: Symbol = CompiletimeTestingPackage.requiredMethod("typeCheckErrors")
Expand Down Expand Up @@ -482,6 +481,33 @@ class Definitions {
}
def AnyKindType: TypeRef = AnyKindClass.typeRef

// Magic stuff
@tu lazy val MagicPackage: Symbol = requiredPackage("scala.magic")
@tu lazy val MagicPackageClass: ClassSymbol = MagicPackage.moduleClass.asClass

@tu lazy val MagicCompiletimePackage: Symbol = requiredPackage("scala.magic.compiletime")
@tu lazy val Magic_spec: Symbol = MagicCompiletimePackage.requiredMethod("$spec")
@tu lazy val Magic_wrappedType: Symbol = MagicCompiletimePackage.requiredMethod("$wrappedType")

@tu lazy val MagicRuntimePackageClass = requiredPackage("scala.magic.runtime").moduleClass.asClass

// Maybe-related
@tu lazy val MaybeClass: ClassSymbol = requiredClass("scala.compiletime.Maybe")
@tu lazy val ValidClass: ClassSymbol = requiredClass("scala.runtime.Valid")
@tu lazy val FailClass: ClassSymbol = requiredClass("scala.runtime.Fail")
@tu lazy val maybeModule: Symbol = requiredModule("scala.maybe")
@tu lazy val maybe_provided1: Symbol = maybeModule.info.member(termName("provided")).suchThat(_.info.isInstanceOf[MethodType]).symbol
@tu lazy val maybe_provided2: Symbol = maybeModule.info.member(termName("provided")).suchThat(_.info.isInstanceOf[PolyType]).symbol
@tu lazy val maybe_CanErr: Symbol = maybeModule.requiredType("CanErr")

@tu lazy val OkModule: Symbol = requiredModule("scala.util.Ok")
@tu lazy val Ok_apply: Symbol = OkModule.requiredMethod(nme.apply)
@tu lazy val Ok_unapply: Symbol = OkModule.requiredMethod(nme.unapply)

@tu lazy val ErrModule: Symbol = requiredModule("scala.util.Err")
@tu lazy val Err_unapply: Symbol = ErrModule.requiredMethod(nme.unapply)

// More synthetic symbols
@tu lazy val andType: TypeSymbol = enterBinaryAlias(tpnme.AND, AndType(_, _))
@tu lazy val orType: TypeSymbol = enterBinaryAlias(tpnme.OR, OrType(_, _, soft = false))

Expand Down Expand Up @@ -1594,6 +1620,8 @@ class Definitions {
@tu lazy val erasedValueMethods =
capsErasedValueMethods + Compiletime_erasedValue

@tu lazy val unitSuperClasses: Set[Symbol] = Set(UnitClass, AnyValClass, AnyClass)

@tu lazy val AbstractFunctionType: Array[TypeRef] = mkArityArray("scala.runtime.AbstractFunction", MaxImplementedFunctionArity, 0).asInstanceOf[Array[TypeRef]]
val AbstractFunctionClassPerRun: PerRun[Array[Symbol]] = new PerRun(AbstractFunctionType.map(_.symbol.asClass))
def AbstractFunctionClass(n: Int)(using Context): Symbol = AbstractFunctionClassPerRun()(using ctx)(n)
Expand Down Expand Up @@ -1770,7 +1798,7 @@ class Definitions {
private val PredefImportFns: RootRef =
RootRef(() => ScalaPredefModule.termRef)

// The new Specialized lives in scala.specialize.
// The new Specialized lives in scala.specialize.
// This is to avoid conflict with the Scala2 specialized annotation.
// It is not imported by default with the scala package, so we additionally import it here.
private val SpecializeImportFns: RootRef =
Expand Down Expand Up @@ -2217,6 +2245,7 @@ class Definitions {
m(TupleClass) = ProductClass
m(NonEmptyTupleClass) = ProductClass
m(PairClass) = ObjectClass
m(MaybeClass) = ObjectClass
m

// ----- Initialization ---------------------------------------------------
Expand Down
18 changes: 11 additions & 7 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,8 @@ object SymDenotations {
case myInfo: ModuleCompleter =>
// Instead of completing the ModuleCompleter, we can check whether
// the module class is absent, which might require less completions.
myInfo.moduleClass.isAbsent(canForce)
val mcls: Symbol = myInfo.moduleClass
!mcls.exists || mcls.isAbsent(canForce)
case _: SymbolLoader if canForce =>
// Completing a SymbolLoader might call `markAbsent()`
completeOnce()
Expand Down Expand Up @@ -675,9 +676,9 @@ object SymDenotations {
final def isSpecializedTraitImplementationClass(using Context): Boolean =
isClass && name.isSpecializedTraitImplementationName

/** Is this symbol a specialized trait implementation class that
/** Is this symbol a specialized trait implementation class that
* was generated from a specialization using only top classes / Nothing
* and is therefore not subject to a specialized interface */
* and is therefore not subject to a specialized interface */
final def isRawSpecializedTraitImplementationClass(using Context): Boolean =
isClass && name.isSpecializedTraitImplementationName

Expand Down Expand Up @@ -933,7 +934,10 @@ object SymDenotations {
/** Is this symbol a class of which `null` is a value? */
final def isNullableClass(using Context): Boolean =
if ctx.mode.is(Mode.SafeNulls) && !ctx.phase.erasedTypes
then symbol == defn.NullClass || symbol == defn.AnyClass || symbol == defn.AnyValClass || symbol == defn.MatchableClass
then symbol == defn.NullClass
|| symbol == defn.AnyClass
|| symbol == defn.AnyValClass
|| symbol == defn.MatchableClass
else isNullableClassAfterErasure

/** Is this symbol a class of which `null` is a value after erasure?
Expand Down Expand Up @@ -1086,11 +1090,11 @@ object SymDenotations {

def isInlineTrait(using Context): Boolean =
isAllOf(InlineTrait)
def isSpecializedMethod(using Context): Boolean =

def isSpecializedMethod(using Context): Boolean =
Specialization.isSpecializedMethod(symbol)

def isSpecializedTrait(using Context): Boolean =
def isSpecializedTrait(using Context): Boolean =
Specialization.isSpecializedTrait(symbol)

/** Does this method or field need to be retained at runtime */
Expand Down
23 changes: 21 additions & 2 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,15 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
return recur(tp1, OrType(tp21, tp221, tp2.isSoft)) && recur(tp1, OrType(tp21, tp222, tp2.isSoft))
case _ =>
}
// T? <: T | Null if T disjoint from null or Err
tp2 match
case OrNull(tp2a) =>
tp1w match
case MaybeType(tp1a, errArg) =>
if errArg.isRef(defn.UnitClass) && tp1a.isNotNullNorMaybe then
return recur(tp1a, tp2a)
case _ =>
case _ =>
either(recur(tp1, tp21), recur(tp1, tp22)) || fourthTry
case tp2: MatchType =>
val reduced = tp2.reduced
Expand Down Expand Up @@ -1041,6 +1050,9 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
// Same as above; this.type is also a singleton type in spec language
!ctx.explicitNulls && isNullable(tp.underlying)
case tp: RefinedOrRecType => isNullable(tp.parent)
case AppliedType(tycon, _ :: errArg :: Nil) if tycon.isRef(defn.MaybeClass) =>
// null <: T ? Unit
isSubType(defn.UnitType, errArg)
case tp: AppliedType => isNullable(tp.tycon)
case AndType(tp1, tp2) => isNullable(tp1) && isNullable(tp2)
case OrType(tp1, tp2) => isNullable(tp1) || isNullable(tp2)
Expand Down Expand Up @@ -1505,6 +1517,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
case _ => false
} && recordGadtUsageIf(true)

/** T <: T? if T is not null or Err */
def byMaybeWidening: Boolean = tp2 match
case MaybeType(res2, err2) if tp1.isNotNullNorMaybe =>
recur(tp1, res2)
case _ => false

tycon2 match {
case param2: TypeParamRef =>
isMatchingApply(tp1) ||
Expand All @@ -1513,6 +1531,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
case tycon2: TypeRef =>
isMatchingApply(tp1)
|| byGadtBounds
|| byMaybeWidening
|| defn.isCompiletimeAppliedType(tycon2.symbol)
&& compareCompiletimeAppliedType(tp2, tp1, fromBelow = true)
|| tycon2.info.match
Expand Down Expand Up @@ -1975,7 +1994,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
&& defn.isByNameFunction(arg2.dealias) =>
isSubArg(arg1res, arg2.argInfos.head)
case _ =>
if v < 0 then
if v < 0 then
val isValidSubtype = isSubType(arg2, arg1)
// Specialized traits have special variance rules because they have special erasure
if tp1.classSymbol.isSpecializedTrait
Expand All @@ -1987,7 +2006,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
false
else // Normal contravariance case
isValidSubtype
else if v > 0 then
else if v > 0 then
val isValidSubtype = isSubType(arg1, arg2)
// Specialized traits have special variance rules because they have special erasure
if tp1.classSymbol.isSpecializedTrait
Expand Down
Loading
Loading