Skip to content
Draft
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: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import CCState.*
import StdNames.nme
import NameKinds.{DefaultGetterName, WildcardParamName, UniqueNameKind}
import reporting.{trace, Message, OverrideError}
import scala.util.boundary

/** The capture checker */
object CheckCaptures:
Expand Down Expand Up @@ -1290,7 +1291,7 @@ class CheckCaptures extends Recheck, SymTransformer:
*/
def adaptBoxed(actual: Type, expected: Type, pos: SrcPos, covariant: Boolean, alwaysConst: Boolean, boxErrors: BoxErrors)(using Context): Type =

def recur(actual: Type, expected: Type, covariant: Boolean): Type =
def recur(actual: Type, expected: Type, covariant: Boolean): Type = boundary:

/** Adapt the inner shape type: get the adapted shape type, and the capture set leaked during adaptation
* @param boxed if true we adapt to a boxed expected type
Expand Down Expand Up @@ -1384,7 +1385,7 @@ class CheckCaptures extends Recheck, SymTransformer:
if boxErrors != null then boxErrors += msg
if ctx.settings.YccDebug.value then
println(i"cannot box/unbox $actual vs $expected")
return actual
boundary.break(actual)
Copy link
Member Author

@bishabosha bishabosha Jan 22, 2025

Choose a reason for hiding this comment

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

if you enable tracing then this return starts to warn, due to the surrounding trace call, so this is here

// Disallow future addition of `cap` to `criticalSet`.
criticalSet.disallowRootCapability: () =>
report.error(msg, pos)
Expand Down
14 changes: 8 additions & 6 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -962,7 +962,8 @@ class Definitions {
def TupleXXL_fromIterator(using Context): Symbol = TupleXXLModule.requiredMethod("fromIterator")
def TupleXXL_unapplySeq(using Context): Symbol = TupleXXLModule.requiredMethod(nme.unapplySeq)

@tu lazy val NamedTupleModule = requiredModule("scala.NamedTuple")
@tu lazy val NamedTupleModule: Symbol = requiredModule("scala.NamedTuple")
@tu lazy val NamedTupleModuleClass: Symbol = NamedTupleModule.moduleClass
@tu lazy val NamedTupleTypeRef: TypeRef = NamedTupleModule.termRef.select(tpnme.NamedTuple).asInstanceOf

@tu lazy val RuntimeTupleMirrorTypeRef: TypeRef = requiredClassRef("scala.runtime.TupleMirror")
Expand Down Expand Up @@ -1360,9 +1361,9 @@ class Definitions {
final def isCompiletime_S(sym: Symbol)(using Context): Boolean =
sym.name == tpnme.S && sym.owner == CompiletimeOpsIntModuleClass

final def isNamedTuple_From(sym: Symbol)(using Context): Boolean =
sym.name == tpnme.From && sym.owner == NamedTupleModule.moduleClass

private val namedTupleTypes: Set[Name] = Set(
tpnme.From, tpnme.OptFrom, tpnme.IsNamedTuple
)
private val compiletimePackageAnyTypes: Set[Name] = Set(
tpnme.Equals, tpnme.NotEquals, tpnme.IsConst, tpnme.ToString
)
Expand Down Expand Up @@ -1391,7 +1392,8 @@ class Definitions {
tpnme.Plus, tpnme.Length, tpnme.Substring, tpnme.Matches, tpnme.CharAt
)
private val compiletimePackageOpTypes: Set[Name] =
Set(tpnme.S, tpnme.From)
Set(tpnme.S)
++ namedTupleTypes
++ compiletimePackageAnyTypes
++ compiletimePackageIntTypes
++ compiletimePackageLongTypes
Expand All @@ -1404,7 +1406,7 @@ class Definitions {
compiletimePackageOpTypes.contains(sym.name)
&& (
isCompiletime_S(sym)
|| isNamedTuple_From(sym)
|| sym.owner == NamedTupleModuleClass && namedTupleTypes.contains(sym.name)
|| sym.owner == CompiletimeOpsAnyModuleClass && compiletimePackageAnyTypes.contains(sym.name)
|| sym.owner == CompiletimeOpsIntModuleClass && compiletimePackageIntTypes.contains(sym.name)
|| sym.owner == CompiletimeOpsLongModuleClass && compiletimePackageLongTypes.contains(sym.name)
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ object StdNames {
val Flag : N = "Flag"
val Fields: N = "Fields"
val From: N = "From"
val IsNamedTuple: N = "IsNamedTuple"
val Ident: N = "Ident"
val Import: N = "Import"
val Literal: N = "Literal"
Expand All @@ -385,6 +386,7 @@ object StdNames {
val NoPrefix: N = "NoPrefix"
val NoSymbol: N = "NoSymbol"
val NoType: N = "NoType"
val OptFrom: N = "OptFrom"
val Pair: N = "Pair"
val Ref: N = "Ref"
val RootPackage: N = "RootPackage"
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import parsing.Parsers.OutlineParser
import dotty.tools.tasty.{TastyHeaderUnpickler, UnpickleException, UnpicklerConfig, TastyVersion}
import dotty.tools.dotc.core.tasty.TastyUnpickler
import dotty.tools.tasty.besteffort.BestEffortTastyHeaderUnpickler
import dotty.tools.dotc.util.SourceFile

object SymbolLoaders {
import ast.untpd.*
Expand Down Expand Up @@ -450,7 +451,7 @@ class TastyLoader(val tastyFile: AbstractFile) extends SymbolLoader {
val (classRoot, moduleRoot) = rootDenots(root.asClass)
if (!isBestEffortTasty || ctx.withBestEffortTasty) then
val tastyBytes = tastyFile.toByteArray
unpickler.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule))(using ctx.withSource(util.NoSource))
unpickler.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule))(using ctx.withSource(util.NoSource : SourceFile))
if mayLoadTreesFromTasty || isBestEffortTasty then
classRoot.classSymbol.rootTreeOrProvider = unpickler
moduleRoot.classSymbol.rootTreeOrProvider = unpickler
Expand Down
43 changes: 41 additions & 2 deletions compiler/src/dotty/tools/dotc/core/TypeEval.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@ object TypeEval:
case ConstantType(Constant(n: String)) => Some(n)
case _ => None

/** is the type concrete? if not then the compiletime op should not reduce */
def isKnown(tp: Type): Boolean = tp.dealias match
// currently not a concrete known type
case TypeRef(NoPrefix,_) => false
// currently not a concrete known type
case _: TypeParamRef => false
// constant if the term is constant
case t: TermRef =>
if t.denot.symbol.flagsUNSAFE.is(Flags.Param) then
// might be substituted later
false
else
isKnown(t.underlying)
// an operation type => recursively check all argument compositions
case applied: AppliedType if defn.isCompiletimeAppliedType(applied.typeSymbol) =>
applied.args.forall(isKnown)
// all other types are considered known
case _ => true

// Returns Some(true) if the type is a constant.
// Returns Some(false) if the type is not a constant.
// Returns None if there is not enough information to determine if the type is a constant.
Expand Down Expand Up @@ -113,6 +132,23 @@ object TypeEval:
case arg @ defn.NamedTuple(_, _) => Some(arg)
case _ => None

def optFieldsOf: Option[Type] =
if isKnown(tp) then fieldsOf match
case Some(tp) => Some(defn.SomeClass.typeRef.appliedTo(tp))
case None => Some(defn.NoneModule.termRef)
else
None

def isNamedTupleType: Option[Type] =
if isKnown(tp) then
expectArgsNum(1)
val arg = tp.args.head
arg.widenDealias match
case arg @ defn.NamedTuple(_, _) => Some(ConstantType(Constant(true)))
case _ => Some(ConstantType(Constant(false)))
else
None

def constantFold1[T](extractor: Type => Option[T], op: T => Any): Option[Type] =
expectArgsNum(1)
extractor(tp.args.head).map(a => runConstantOp(op(a)))
Expand Down Expand Up @@ -147,8 +183,11 @@ object TypeEval:
val constantType =
if defn.isCompiletime_S(sym) then
constantFold1(natValue, _ + 1)
else if defn.isNamedTuple_From(sym) then
fieldsOf
else if owner == defn.NamedTupleModuleClass then name match
case tpnme.From => fieldsOf
case tpnme.OptFrom => optFieldsOf
case tpnme.IsNamedTuple => isNamedTupleType
case _ => None
else if owner == defn.CompiletimeOpsAnyModuleClass then name match
case tpnme.Equals => constantFold2(constValue, _ == _)
case tpnme.NotEquals => constantFold2(constValue, _ != _)
Expand Down
6 changes: 6 additions & 0 deletions library/src/scala/NamedTuple.scala
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ object NamedTuple:
*/
type From[T] <: AnyNamedTuple

@experimental // extra experimental for now
type OptFrom[T] <: Option[AnyNamedTuple]

@experimental // extra experimental for now
type IsNamedTuple[T] <: Boolean

/** The type of the empty named tuple */
type Empty = NamedTuple[EmptyTuple, EmptyTuple]

Expand Down
Loading