diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 0a2c0c850e5d..9debb8651be5 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -588,9 +588,13 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => case New(_) | Closure(_, _, _) => Pure case TypeApply(fn, _) => + val sym = fn.symbol if tree.tpe.isInstanceOf[MethodOrPoly] then exprPurity(fn) - else if fn.symbol == defn.QuotedTypeModule_of || fn.symbol == defn.Predef_classOf then Pure - else if fn.symbol == defn.Compiletime_erasedValue && tree.tpe.dealias.isInstanceOf[ConstantType] then Pure + else if sym == defn.QuotedTypeModule_of + || sym == defn.Predef_classOf + || sym == defn.Compiletime_erasedValue && tree.tpe.dealias.isInstanceOf[ConstantType] + || defn.capsErasedValueMethods.contains(sym) + then Pure else Impure case Apply(fn, args) => val factorPurity = minOf(exprPurity(fn), args.map(exprPurity)) @@ -634,6 +638,15 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => def isPureBinding(tree: Tree)(using Context): Boolean = statPurity(tree) >= Pure + def isPureSyntheticCaseApply(sym: Symbol)(using Context): Boolean = + sym.isAllOf(SyntheticMethod) + && sym.name == nme.apply + && sym.owner.is(Module) + && { + val cls = sym.owner.companionClass + cls.is(Case) && cls.isNoInitsRealClass + } + /** Is the application `tree` with function part `fn` known to be pure? * Function value and arguments can still be impure. */ @@ -645,6 +658,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => tree.tpe.isInstanceOf[ConstantType] && tree.symbol != NoSymbol && isKnownPureOp(tree.symbol) // A constant expression with pure arguments is pure. || fn.symbol.isStableMember && fn.symbol.isConstructor // constructors of no-inits classes are stable + || isPureSyntheticCaseApply(fn.symbol) /** The purity level of this reference. * @return diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 991309293c0c..92c20afe7a73 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -301,7 +301,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { assert(vparams.hasSameLengthAs(tp.paramNames) && vparams.head.isTerm) (vparams.asInstanceOf[List[TermSymbol]], remaining1) case nil => - (tp.paramNames.lazyZip(tp.paramInfos).lazyZip(tp.erasedParams).map(valueParam), Nil) + (tp.paramNames.lazyZip(tp.paramInfos).lazyZip(tp.paramErasureStatuses).map(valueParam), Nil) val (rtp, paramss) = recur(tp.instantiate(vparams.map(_.termRef)), remaining1) (rtp, vparams :: paramss) case _ => diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala index faae83fd3456..070b1bb1f8e5 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala @@ -462,15 +462,13 @@ extension (sym: Symbol) /** Does this symbol allow results carrying the universal capability? * Currently this is true only for function type applies (since their - * results are unboxed) and `erasedValue` since this function is magic in - * that is allows to conjure global capabilies from nothing (aside: can we find a - * more controlled way to achieve this?). + * results are unboxed) and `caps.{$internal,unsafe}.erasedValue` since + * these function are magic in that they allow to conjure global capabilies from nothing. * But it could be generalized to other functions that so that they can take capability * classes as arguments. */ def allowsRootCapture(using Context): Boolean = - sym == defn.Compiletime_erasedValue - || defn.isFunctionClass(sym.maybeOwner) + defn.capsErasedValueMethods.contains(sym) || defn.isFunctionClass(sym.maybeOwner) /** When applying `sym`, would the result type be unboxed? * This is the case if the result type contains a top-level reference to an enclosing diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index fb0a5b8b59b2..9f0e285e27c7 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -577,7 +577,7 @@ class CheckCaptures extends Recheck, SymTransformer: * @param args the type arguments */ def disallowCapInTypeArgs(fn: Tree, sym: Symbol, args: List[Tree])(using Context): Unit = - def isExempt = sym.isTypeTestOrCast || sym == defn.Compiletime_erasedValue + def isExempt = sym.isTypeTestOrCast || defn.capsErasedValueMethods.contains(sym) if !isExempt then val paramNames = atPhase(thisPhase.prev): fn.tpe.widenDealias match diff --git a/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala b/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala index 81b03d765676..f2e426612eeb 100644 --- a/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala +++ b/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala @@ -48,8 +48,6 @@ object CheckRealizable { def boundsRealizability(tp: Type)(using Context): Realizability = new CheckRealizable().boundsRealizability(tp) - - private val LateInitializedFlags = Lazy | Erased } /** Compute realizability status. @@ -72,7 +70,7 @@ class CheckRealizable(using Context) { /** Is symbol's definitition a lazy or erased val? * (note we exclude modules here, because their realizability is ensured separately) */ - private def isLateInitialized(sym: Symbol) = sym.isOneOf(LateInitializedFlags, butNot = Module) + private def isLateInitialized(sym: Symbol) = sym.is(Lazy, butNot = Module) /** The realizability status of given type `tp`*/ def realizability(tp: Type): Realizability = tp.dealias match { @@ -184,7 +182,7 @@ class CheckRealizable(using Context) { private def memberRealizability(tp: Type) = { def checkField(sofar: Realizability, fld: SingleDenotation): Realizability = sofar andAlso { - if (checkedFields.contains(fld.symbol) || fld.symbol.isOneOf(Private | Mutable | LateInitializedFlags)) + if (checkedFields.contains(fld.symbol) || fld.symbol.isOneOf(Private | Mutable | Lazy)) // if field is private it cannot be part of a visible path // if field is mutable it cannot be part of a path // if field is lazy or erased it does not need to be initialized when the owning object is diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 381caa775dbd..3aaf966597c3 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1004,9 +1004,11 @@ class Definitions { @tu lazy val Caps_Capability: ClassSymbol = requiredClass("scala.caps.Capability") @tu lazy val Caps_CapSet: ClassSymbol = requiredClass("scala.caps.CapSet") @tu lazy val CapsInternalModule: Symbol = requiredModule("scala.caps.internal") + @tu lazy val Caps_erasedValue: Symbol = CapsInternalModule.requiredMethod("erasedValue") @tu lazy val CapsUnsafeModule: Symbol = requiredModule("scala.caps.unsafe") @tu lazy val Caps_unsafeAssumePure: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumePure") @tu lazy val Caps_unsafeAssumeSeparate: Symbol = CapsUnsafeModule.requiredMethod("unsafeAssumeSeparate") + @tu lazy val Caps_unsafeErasedValue: Symbol = CapsUnsafeModule.requiredMethod("unsafeErasedValue") @tu lazy val Caps_ContainsTrait: TypeSymbol = CapsModule.requiredType("Contains") @tu lazy val Caps_ContainsModule: Symbol = requiredModule("scala.caps.Contains") @tu lazy val Caps_containsImpl: TermSymbol = Caps_ContainsModule.requiredMethod("containsImpl") @@ -1558,6 +1560,11 @@ class Definitions { @tu lazy val pureSimpleClasses = Set(StringClass, NothingClass, NullClass) ++ ScalaValueClasses() + @tu lazy val capsErasedValueMethods = + Set(Caps_erasedValue, Caps_unsafeErasedValue) + @tu lazy val erasedValueMethods = + capsErasedValueMethods + Compiletime_erasedValue + @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) @@ -2001,7 +2008,9 @@ class Definitions { /** A allowlist of Scala-2 classes that are known to be pure */ def isAssuredNoInits(sym: Symbol): Boolean = - (sym `eq` SomeClass) || isTupleClass(sym) + (sym `eq` SomeClass) + || isTupleClass(sym) + || sym.is(Module) && isAssuredNoInits(sym.companionClass) /** If `cls` is Tuple1..Tuple22, add the corresponding *: type as last parent to `parents` */ def adjustForTuple(cls: ClassSymbol, tparams: List[TypeSymbol], parents: List[Type]): List[Type] = { diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index bd43578e3d53..8155db9f01ef 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1054,6 +1054,7 @@ object SymDenotations { def isEffectivelyErased(using Context): Boolean = isOneOf(EffectivelyErased) || is(Inline) && !isRetainedInline && !hasAnnotation(defn.ScalaStaticAnnot) + || defn.erasedValueMethods.contains(symbol) /** Is this a member that will become public in the generated binary */ def hasPublicInBinary(using Context): Boolean = diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index eb03a2b1c05d..5753d311baa9 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -2387,7 +2387,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling formals2.isEmpty } // If methods have erased parameters, then the erased parameters must match - val erasedValid = (!tp1.hasErasedParams && !tp2.hasErasedParams) || (tp1.erasedParams == tp2.erasedParams) + val erasedValid = (!tp1.hasErasedParams && !tp2.hasErasedParams) || (tp1.paramErasureStatuses == tp2.paramErasureStatuses) erasedValid && loop(tp1.paramInfos, tp2.paramInfos) } diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 83f087239477..2e6fa7d94d43 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -697,7 +697,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst val (names, formals0) = if tp.hasErasedParams then tp.paramNames .zip(tp.paramInfos) - .zip(tp.erasedParams) + .zip(tp.paramErasureStatuses) .collect{ case (param, isErased) if !isErased => param } .unzip else (tp.paramNames, tp.paramInfos) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index d9ea63267c0b..d6fcbf74b561 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3931,7 +3931,7 @@ object Types extends TypeUtils { case tp: MethodType => val params = if (hasErasedParams) tp.paramInfos - .zip(tp.erasedParams) + .zip(tp.paramErasureStatuses) .collect { case (param, isErased) if !isErased => param } else tp.paramInfos resultSignature.prependTermParams(params, sourceLanguage) @@ -4163,7 +4163,7 @@ object Types extends TypeUtils { final override def isContextualMethod: Boolean = companion.eq(ContextualMethodType) - def erasedParams(using Context): List[Boolean] = + def paramErasureStatuses(using Context): List[Boolean] = paramInfos.map(p => p.hasAnnotation(defn.ErasedParamAnnot)) def nonErasedParamCount(using Context): Int = diff --git a/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala b/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala index 3edb323e6b3b..80516047426f 100644 --- a/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala +++ b/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala @@ -341,6 +341,17 @@ class InlineReducer(inliner: Inliner)(using Context): val scrutineeSym = newSym(InlineScrutineeName.fresh(), Synthetic, scrutType).asTerm val scrutineeBinding = normalizeBinding(ValDef(scrutineeSym, scrutinee)) + // If scrutinee has embedded `compiletime.erasedValue[T]` expressions, convert them to + // mark scrutineeSym as Erased. This means that the scrutinee cannot be referenced in + // the reduced term. It is NOT checked that scrutinee is a pure expression, since + // there is a special case in Erase that exempts the RHS of an erased scrutinee definition. + if scrutinee.existsSubTree: + case tree @ TypeApply(fn, args) => tree.symbol == defn.Compiletime_erasedValue + case _ => false + then + scrutineeSym.setFlag(Erased) + + def reduceCase(cdef: CaseDef): MatchReduxWithGuard = { val caseBindingMap = new mutable.ListBuffer[(Symbol, MemberDef)]() diff --git a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala index dbc2694dc891..816bac14ddd2 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala @@ -127,8 +127,8 @@ class Interpreter(pos: SrcPos, classLoader0: ClassLoader)(using Context): case fnType: MethodType => val argTypes = fnType.paramInfos assert(argss.head.size == argTypes.size) - val nonErasedArgs = argss.head.lazyZip(fnType.erasedParams).collect { case (arg, false) => arg }.toList - val nonErasedArgTypes = fnType.paramInfos.lazyZip(fnType.erasedParams).collect { case (arg, false) => arg }.toList + val nonErasedArgs = argss.head.lazyZip(fnType.paramErasureStatuses).collect { case (arg, false) => arg }.toList + val nonErasedArgTypes = fnType.paramInfos.lazyZip(fnType.paramErasureStatuses).collect { case (arg, false) => arg }.toList assert(nonErasedArgs.size == nonErasedArgTypes.size) interpretArgsGroup(nonErasedArgs, nonErasedArgTypes) ::: interpretArgs(argss.tail, fnType.resType) case fnType: AppliedType if defn.isContextFunctionType(fnType) => diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index ecde64a720aa..361fa419bbf3 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -228,6 +228,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case OnlyFullyDependentAppliedConstructorTypeID // errorNumber: 212 case PointlessAppliedConstructorTypeID // errorNumber: 213 case IllegalContextBoundsID // errorNumber: 214 + case ErasedNotPureID // errornumber 215 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 5faec1fafcdf..83e97e7dc5eb 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -3535,3 +3535,35 @@ final class IllegalContextBounds(using Context) extends SyntaxMsg(IllegalContext override protected def explain(using Context): String = "" end IllegalContextBounds + +final class ErasedNotPure(tree: tpd.Tree, isArgument: Boolean, isImplicit: Boolean)(using Context) extends TypeMsg(ErasedNotPureID): + def what = + if isArgument then s"${if isImplicit then "implicit " else ""}argument to an erased parameter" + else "right-hand-side of an erased value" + override protected def msg(using Context): String = + i"$what fails to be a pure expression" + + override protected def explain(using Context): String = + def alternatives = + if tree.symbol == defn.Compiletime_erasedValue then + i"""An accepted (but unsafe) alternative for this expression uses function + | + | caps.unsafe.unsafeErasedValue + | + |instead.""" + else + """A pure expression is an expression that is clearly side-effect free and terminating. + |Some examples of pure expressions are: + | - literals, + | - references to values, + | - side-effect-free instance creations, + | - applications of inline functions to pure arguments.""" + + i"""The $what must be a pure expression, but I found: + | + | $tree + | + |This expression is not classified to be pure. + |$alternatives""" +end ErasedNotPure + diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 3503c707aed9..d0a89c9564f4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -14,7 +14,7 @@ import core.Names.* import core.StdNames.* import core.NameOps.* import core.Periods.currentStablePeriod -import core.NameKinds.{AdaptedClosureName, BodyRetainerName, DirectMethName} +import core.NameKinds.{AdaptedClosureName, BodyRetainerName, DirectMethName, InlineScrutineeName} import core.Scopes.newScopeWith import core.Decorators.* import core.Constants.* @@ -583,6 +583,12 @@ object Erasure { checkNotErasedClass(tree) end checkNotErased + def checkPureErased(tree: untpd.Tree, isArgument: Boolean, isImplicit: Boolean = false)(using Context): Unit = + val tree1 = tree.asInstanceOf[tpd.Tree] + inContext(preErasureCtx): + if !tpd.isPureExpr(tree1) then + report.error(ErasedNotPure(tree1, isArgument, isImplicit), tree1.srcPos) + private def checkNotErasedClass(tp: Type, tree: untpd.Tree)(using Context): Unit = tp match case JavaArrayType(et) => checkNotErasedClass(et, tree) @@ -848,7 +854,13 @@ object Erasure { val origFunType = origFun.tpe.widen(using preErasureCtx) val ownArgs = origFunType match case mt: MethodType if mt.hasErasedParams => - args.zip(mt.erasedParams).collect { case (arg, false) => arg } + args.lazyZip(mt.paramErasureStatuses).flatMap: (arg, isErased) => + if isErased then + checkPureErased(arg, isArgument = true, + isImplicit = mt.isImplicitMethod && arg.span.isSynthetic) + Nil + else + arg :: Nil case _ => args val fun1 = typedExpr(fun, AnyFunctionProto) fun1.tpe.widen match @@ -916,8 +928,11 @@ object Erasure { } override def typedValDef(vdef: untpd.ValDef, sym: Symbol)(using Context): Tree = - if (sym.isEffectivelyErased) erasedDef(sym) - else + if sym.isEffectivelyErased then + if !sym.name.is(InlineScrutineeName) then + checkPureErased(vdef.rhs, isArgument = false) + erasedDef(sym) + else trace(i"erasing $vdef"): checkNotErasedClass(sym.info, vdef) super.typedValDef(untpd.cpy.ValDef(vdef)( tpt = untpd.TypedSplice(TypeTree(sym.info).withSpan(vdef.tpt.span))), sym) diff --git a/compiler/src/dotty/tools/dotc/transform/Getters.scala b/compiler/src/dotty/tools/dotc/transform/Getters.scala index 6e6d84a9eaae..11adf4da83d5 100644 --- a/compiler/src/dotty/tools/dotc/transform/Getters.scala +++ b/compiler/src/dotty/tools/dotc/transform/Getters.scala @@ -90,7 +90,7 @@ class Getters extends MiniPhase with SymTransformer { thisPhase => d1 } - private val NoGetterNeededFlags = Method | Param | JavaDefined | JavaStatic | PhantomSymbol + private val NoGetterNeededFlags = Method | Param | JavaDefined | JavaStatic | PhantomSymbol | Erased val newSetters = util.HashSet[Symbol]() diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 5e2ff2d43283..2f1e4a8b2b4f 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -408,7 +408,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => case app: Apply => val methType = app.fun.tpe.widen.asInstanceOf[MethodType] if (methType.hasErasedParams) - for (arg, isErased) <- app.args.lazyZip(methType.erasedParams) do + for (arg, isErased) <- app.args.lazyZip(methType.paramErasureStatuses) do if isErased then if methType.isResultDependent then Checking.checkRealizable(arg.tpe, arg.srcPos, "erased argument") @@ -475,7 +475,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => case tree: ValDef => annotateExperimentalCompanion(tree.symbol) registerIfHasMacroAnnotations(tree) - checkErasedDef(tree) + //checkErasedDef(tree) Checking.checkPolyFunctionType(tree.tpt) val tree1 = cpy.ValDef(tree)(tpt = makeOverrideTypeDeclared(tree.symbol, tree.tpt)) if tree1.removeAttachment(desugar.UntupledParam).isDefined then @@ -483,7 +483,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => processValOrDefDef(super.transform(tree1)) case tree: DefDef => registerIfHasMacroAnnotations(tree) - checkErasedDef(tree) + //checkErasedDef(tree) Checking.checkPolyFunctionType(tree.tpt) annotateContextResults(tree) val tree1 = cpy.DefDef(tree)(tpt = makeOverrideTypeDeclared(tree.symbol, tree.tpt)) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index df4350f1eb05..56599a489732 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -607,10 +607,12 @@ object Checking { fail(ModifierNotAllowedForDefinition(Flags.Infix, s"A top-level ${sym.showKind} cannot be infix.")) if sym.isUpdateMethod && !sym.owner.derivesFrom(defn.Caps_Mutable) then fail(em"Update methods can only be used as members of classes extending the `Mutable` trait") - checkApplicable(Erased, - !sym.is(Lazy, butNot = Given) - && !sym.isMutableVarOrAccessor - && (!sym.isType || sym.isClass)) + val unerasable = + sym.is(Lazy, butNot = Given) + || sym.is(Method, butNot = Macro) + || sym.is(Mutable) + || sym.isType && !sym.isClass + checkApplicable(Erased, !unerasable) checkCombination(Final, Open) checkCombination(Sealed, Open) checkCombination(Final, Sealed) diff --git a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala index 26d03db4b7dc..55778017b76f 100644 --- a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -300,9 +300,9 @@ object EtaExpansion extends LiftImpure { val body = Apply(lifted, ids) if (mt.isContextualMethod) body.setApplyKind(ApplyKind.Using) val fn = - if (mt.isContextualMethod) new untpd.FunctionWithMods(params, body, Modifiers(Given), mt.erasedParams) - else if (mt.isImplicitMethod) new untpd.FunctionWithMods(params, body, Modifiers(Implicit), mt.erasedParams) - else if (mt.hasErasedParams) new untpd.FunctionWithMods(params, body, Modifiers(), mt.erasedParams) + if (mt.isContextualMethod) new untpd.FunctionWithMods(params, body, Modifiers(Given), mt.paramErasureStatuses) + else if (mt.isImplicitMethod) new untpd.FunctionWithMods(params, body, Modifiers(Implicit), mt.paramErasureStatuses) + else if (mt.hasErasedParams) new untpd.FunctionWithMods(params, body, Modifiers(), mt.paramErasureStatuses) else untpd.Function(params, body) if (defs.nonEmpty) untpd.Block(defs.toList map (untpd.TypedSplice(_)), fn) else fn } diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 4f596776d497..362866866e4a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -231,7 +231,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): case PreciseConstrained(tp, true) => if tp.isSingletonBounded(frozen = false) then withNoErrors: - ref(defn.Compiletime_erasedValue).appliedToType(formal).withSpan(span) + ref(defn.Caps_erasedValue).appliedToType(formal).withSpan(span) else withErrors(i"$tp is not a singleton") case _ => @@ -240,7 +240,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): val synthesizedPrecise: SpecialHandler = (formal, span) => formal match case PreciseConstrained(tp, false) => withNoErrors: - ref(defn.Compiletime_erasedValue).appliedToType(formal).withSpan(span) + ref(defn.Caps_erasedValue).appliedToType(formal).withSpan(span) case _ => EmptyTreeNoError diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 0af1685d0857..3efab85b5561 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1761,7 +1761,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if (mt.isParamDependent) report.error(em"$mt is an illegal function type because it has inter-parameter dependencies", tree.srcPos) // Restart typechecking if there are erased classes that we want to mark erased - if mt.erasedParams.zip(mt.paramInfos.map(_.isErasedClass)).exists((paramErased, classErased) => classErased && !paramErased) then + if mt.paramErasureStatuses.zip(mt.paramInfos.map(_.isErasedClass)).exists((paramErased, classErased) => classErased && !paramErased) then val newParams = params3.zipWithConserve(mt.paramInfos.map(_.isErasedClass)) { (arg, isErasedClass) => if isErasedClass then arg.withAddedFlags(Erased) else arg } @@ -2384,7 +2384,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer untpd.ValDef( CanThrowEvidenceName.fresh(), untpd.TypeTree(defn.CanThrowClass.typeRef.appliedTo(tp)), - untpd.ref(defn.Compiletime_erasedValue)) + untpd.ref(defn.Caps_erasedValue)) .withFlags(Given | Final | Erased) .withSpan(expr.span) val caughtExceptions = @@ -2950,14 +2950,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer EmptyTree def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(using Context): Tree = if !sym.info.exists then retractDefDef(sym) else ctx.profiler.onTypedDef(sym) { - - // TODO: - Remove this when `scala.language.experimental.erasedDefinitions` is no longer experimental. - // - Modify signature to `erased def erasedValue[T]: T` - if sym.eq(defn.Compiletime_erasedValue) then - // scala.compiletime.erasedValue should be `erased` but we cannot add this in the source. - // The library cannot use experimental language features, - // hence we special case it until `erased` is no longer experimental. - sym.setFlag(Erased) val DefDef(name, paramss, tpt, _) = ddef checkNonRootName(ddef.name, ddef.nameSpan) completeAnnotations(ddef, sym) @@ -3788,7 +3780,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } val erasedParams = pt match { - case defn.PolyFunctionOf(mt: MethodType) => mt.erasedParams + case defn.PolyFunctionOf(mt: MethodType) => mt.paramErasureStatuses case _ => paramTypes.map(_ => false) } diff --git a/compiler/src/dotty/tools/dotc/util/DiffUtil.scala b/compiler/src/dotty/tools/dotc/util/DiffUtil.scala index 31acc91caa2e..126cff9b9c65 100644 --- a/compiler/src/dotty/tools/dotc/util/DiffUtil.scala +++ b/compiler/src/dotty/tools/dotc/util/DiffUtil.scala @@ -103,7 +103,6 @@ object DiffUtil { case Deleted(str) => deleted(str) }.mkString - (expectedDiff, actualDiff) val pad = " " * 0.max(expectedSize - expected.length) expectedDiff + pad + " | " + actualDiff diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 3790174526b3..e3d5355eaffa 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -448,7 +448,7 @@ class QuoteMatcher(debug: Boolean) { def matchErasedParams(sctype: Type, pttype: Type): optional[MatchingExprs] = (sctype, pttype) match case (sctpe: MethodType, pttpe: MethodType) => - if sctpe.erasedParams.sameElements(pttpe.erasedParams) then + if sctpe.paramErasureStatuses.sameElements(pttpe.paramErasureStatuses) then matchErasedParams(sctpe.resType, pttpe.resType) else notMatched diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 852d7ee8b20f..fdd16963d33b 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2288,7 +2288,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler case _ => MethodTypeKind.Plain def param(idx: Int): TypeRepr = self.newParamRef(idx) - def erasedParams: List[Boolean] = self.erasedParams + def erasedParams: List[Boolean] = self.paramErasureStatuses def hasErasedParams: Boolean = self.hasErasedParams end extension end MethodTypeMethods diff --git a/compiler/test-resources/type-printer/test-definitions b/compiler/test-resources/type-printer/test-definitions index cdda5f65cb0e..6566496d3181 100644 --- a/compiler/test-resources/type-printer/test-definitions +++ b/compiler/test-resources/type-printer/test-definitions @@ -18,8 +18,3 @@ scala> trait E scala> implicit def x: Int = 1 def x: Int - -scala> import scala.language.experimental.erasedDefinitions - -scala> erased def y: Int = 1 -def y: Int diff --git a/docs/_docs/reference/experimental/erased-defs.md b/docs/_docs/reference/experimental/erased-defs.md index d266cd6c9d19..4be4498e1058 100644 --- a/docs/_docs/reference/experimental/erased-defs.md +++ b/docs/_docs/reference/experimental/erased-defs.md @@ -4,7 +4,7 @@ title: "Erased Definitions" nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/erased-defs.html --- -`erased` is a modifier that expresses that some definition or expression is erased by the compiler instead of being represented in the compiled output. It is not yet part of the Scala language standard. To enable `erased`, turn on the language feature +`erased` is a modifier that expresses that some value or parameter is erased by the compiler instead of being represented in the compiled output. It is not yet part of the Scala language standard. To enable `erased`, turn on the language feature [`experimental.erasedDefinitions`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$experimental$$erasedDefinitions$.html). This can be done with a language import ```scala import scala.language.experimental.erasedDefinitions @@ -74,9 +74,9 @@ def methodWithErasedInt2(erased i: Int): Int = methodWithErasedInt1(i) // OK ``` -Not only parameters can be marked as erased, `val` and `def` can also be marked -with `erased`. These will also only be usable as arguments to `erased` -parameters. +Besides parameters, `val` definitions can also be marked with `erased`. +These will also only be usable as arguments to `erased` parameters or +as part of the definitions of other erased `val`s. ```scala erased val erasedEvidence: Ev = ... @@ -85,7 +85,7 @@ methodWithErasedEv(erasedEvidence, 40) // 42 ## What happens with erased values at runtime? -As `erased` are guaranteed not to be used in computations, they can and will be +As `erased` vals and parameters are guaranteed not to be used in computations, they can and will be erased. ```scala @@ -130,7 +130,7 @@ class IsOff[S <: State] object IsOff: // will not be called at runtime for turnedOn, the // compiler will only require that this evidence exists - given IsOff[Off] = new IsOff[Off] + erased given IsOff[Off] = new IsOff[Off] @implicitNotFound("State must be On") class IsOn[S <: State] @@ -151,14 +151,7 @@ object Machine: val m = Machine.newMachine() m.turnedOn m.turnedOn.turnedOff - - // m.turnedOff - // ^ - // State must be On - - // m.turnedOn.turnedOn - // ^ - // State must be Off + m.turnedOn.turnedOn // error: State must be Off ``` Note that in [Compile-time operations](../metaprogramming/compiletime-ops.md#erasedvalue) we discussed `erasedValue` and inline diff --git a/library/src/scala/CanThrow.scala b/library/src/scala/CanThrow.scala index 485dcecb37df..46164d0133ea 100644 --- a/library/src/scala/CanThrow.scala +++ b/library/src/scala/CanThrow.scala @@ -12,5 +12,5 @@ erased class CanThrow[-E <: Exception] extends caps.SharedCapability @experimental object unsafeExceptions: - given canThrowAny: CanThrow[Exception] = compiletime.erasedValue + inline given canThrowAny: CanThrow[Exception] = caps.unsafe.unsafeErasedValue diff --git a/library/src/scala/caps/package.scala b/library/src/scala/caps/package.scala index fedfd7400e25..98df6ce5219c 100644 --- a/library/src/scala/caps/package.scala +++ b/library/src/scala/caps/package.scala @@ -110,6 +110,13 @@ object internal: */ final class inferredDepFun extends annotation.StaticAnnotation + /** An erasedValue issued internally by the compiler. Unlike the + * user-accessible compiletime.erasedValue, this version is assumed + * to be a pure expression, hence capability safe. The compiler generates this + * version only where it is known that a value can be generated. + */ + def erasedValue[T]: T = ??? + end internal @experimental @@ -135,4 +142,11 @@ object unsafe: */ def unsafeAssumeSeparate(op: Any): op.type = op + /** An unsafe variant of erasedValue that can be used as an escape hatch. Unlike the + * user-accessible compiletime.erasedValue, this version is assumed + * to be a pure expression, hence capability safe. But there is no proof + * of realizability, hence it is unsafe. + */ + def unsafeErasedValue[T]: T = ??? + end unsafe diff --git a/library/src/scala/compiletime/package.scala b/library/src/scala/compiletime/package.scala index 8215ae2452a3..1a161ebd4a03 100644 --- a/library/src/scala/compiletime/package.scala +++ b/library/src/scala/compiletime/package.scala @@ -23,7 +23,6 @@ import annotation.{compileTimeOnly, experimental} * the branches. * @syntax markdown */ -// TODO add `erased` once it is not an experimental feature anymore def erasedValue[T]: T = erasedValue[T] /** Used as the initializer of a mutable class or object field, like this: diff --git a/tests/init/warn/inner30.scala b/tests/init/warn/inner30.scala index d9b1eec3d6b1..1fd112579263 100644 --- a/tests/init/warn/inner30.scala +++ b/tests/init/warn/inner30.scala @@ -8,7 +8,7 @@ class Scanners { class Scanner { def foo() = - Conc(Run('a', 3), Run('b', 4)) + Conc(Run('a', 3), Run('b', 4)) // warn new LookAheadScanner class LookAheadScanner() extends Scanner diff --git a/tests/neg/erased-1.scala b/tests/invalid/neg/erased-1.scala similarity index 100% rename from tests/neg/erased-1.scala rename to tests/invalid/neg/erased-1.scala diff --git a/tests/neg/erased-2.scala b/tests/invalid/neg/erased-2.scala similarity index 100% rename from tests/neg/erased-2.scala rename to tests/invalid/neg/erased-2.scala diff --git a/tests/neg/erased-3.scala b/tests/invalid/neg/erased-3.scala similarity index 100% rename from tests/neg/erased-3.scala rename to tests/invalid/neg/erased-3.scala diff --git a/tests/neg/erased-args-lifted.scala b/tests/invalid/neg/erased-args-lifted.scala similarity index 87% rename from tests/neg/erased-args-lifted.scala rename to tests/invalid/neg/erased-args-lifted.scala index dfa7b74ee3d4..a2f6a654429f 100644 --- a/tests/neg/erased-args-lifted.scala +++ b/tests/invalid/neg/erased-args-lifted.scala @@ -2,7 +2,7 @@ object Test { def foo(a: Int)(b: Int, c: Int) = 42 - erased def bar(erased i: Int): Int = { + inline def bar(erased i: Int): Int = { println(1) 42 } diff --git a/tests/neg/erased-implicit.scala b/tests/invalid/neg/erased-implicit.scala similarity index 100% rename from tests/neg/erased-implicit.scala rename to tests/invalid/neg/erased-implicit.scala diff --git a/tests/neg/erased-params.scala b/tests/invalid/neg/erased-params.scala similarity index 100% rename from tests/neg/erased-params.scala rename to tests/invalid/neg/erased-params.scala diff --git a/tests/pos/i11743.scala b/tests/invalid/pos/i11743.scala similarity index 81% rename from tests/pos/i11743.scala rename to tests/invalid/pos/i11743.scala index ae524ca01ad6..3114383b3433 100644 --- a/tests/pos/i11743.scala +++ b/tests/invalid/pos/i11743.scala @@ -2,7 +2,7 @@ import language.experimental.erasedDefinitions import scala.compiletime.erasedValue type UnivEq[A] object UnivEq: - erased def force[A]: UnivEq[A] = erasedValue + inline def force[A]: UnivEq[A] = erasedValue extension [A](erased proof: UnivEq[A]) inline def univEq(a: A, b: A): Boolean = a == b diff --git a/tests/pos/i17584a.scala b/tests/invalid/pos/i17584a.scala similarity index 100% rename from tests/pos/i17584a.scala rename to tests/invalid/pos/i17584a.scala diff --git a/tests/run/erased-1.check b/tests/invalid/run/erased-1.check similarity index 100% rename from tests/run/erased-1.check rename to tests/invalid/run/erased-1.check diff --git a/tests/run/erased-1.scala b/tests/invalid/run/erased-1.scala similarity index 100% rename from tests/run/erased-1.scala rename to tests/invalid/run/erased-1.scala diff --git a/tests/run/erased-10.check b/tests/invalid/run/erased-10.check similarity index 100% rename from tests/run/erased-10.check rename to tests/invalid/run/erased-10.check diff --git a/tests/run/erased-10.scala b/tests/invalid/run/erased-10.scala similarity index 92% rename from tests/run/erased-10.scala rename to tests/invalid/run/erased-10.scala index 004d07b4de37..ce8c8a42de4c 100644 --- a/tests/run/erased-10.scala +++ b/tests/invalid/run/erased-10.scala @@ -10,7 +10,7 @@ object Test { println("pacFun4") } - erased def inky: Int = { + inline def inky: Int = { println("inky") // in erased function 42 } diff --git a/tests/run/erased-11.check b/tests/invalid/run/erased-11.check similarity index 100% rename from tests/run/erased-11.check rename to tests/invalid/run/erased-11.check diff --git a/tests/run/erased-11.scala b/tests/invalid/run/erased-11.scala similarity index 100% rename from tests/run/erased-11.scala rename to tests/invalid/run/erased-11.scala diff --git a/tests/run/erased-12.check b/tests/invalid/run/erased-12.check similarity index 100% rename from tests/run/erased-12.check rename to tests/invalid/run/erased-12.check diff --git a/tests/run/erased-12.scala b/tests/invalid/run/erased-12.scala similarity index 100% rename from tests/run/erased-12.scala rename to tests/invalid/run/erased-12.scala diff --git a/tests/run/erased-13.check b/tests/invalid/run/erased-13.check similarity index 100% rename from tests/run/erased-13.check rename to tests/invalid/run/erased-13.check diff --git a/tests/run/erased-13.scala b/tests/invalid/run/erased-13.scala similarity index 100% rename from tests/run/erased-13.scala rename to tests/invalid/run/erased-13.scala diff --git a/tests/run/erased-14.check b/tests/invalid/run/erased-14.check similarity index 100% rename from tests/run/erased-14.check rename to tests/invalid/run/erased-14.check diff --git a/tests/run/erased-14.scala b/tests/invalid/run/erased-14.scala similarity index 100% rename from tests/run/erased-14.scala rename to tests/invalid/run/erased-14.scala diff --git a/tests/run/erased-16.check b/tests/invalid/run/erased-16.check similarity index 100% rename from tests/run/erased-16.check rename to tests/invalid/run/erased-16.check diff --git a/tests/run/erased-16.scala b/tests/invalid/run/erased-16.scala similarity index 100% rename from tests/run/erased-16.scala rename to tests/invalid/run/erased-16.scala diff --git a/tests/run/erased-17.check b/tests/invalid/run/erased-17.check similarity index 100% rename from tests/run/erased-17.check rename to tests/invalid/run/erased-17.check diff --git a/tests/run/erased-17.scala b/tests/invalid/run/erased-17.scala similarity index 100% rename from tests/run/erased-17.scala rename to tests/invalid/run/erased-17.scala diff --git a/tests/run/erased-22.check b/tests/invalid/run/erased-22.check similarity index 100% rename from tests/run/erased-22.check rename to tests/invalid/run/erased-22.check diff --git a/tests/run/erased-22.scala b/tests/invalid/run/erased-22.scala similarity index 100% rename from tests/run/erased-22.scala rename to tests/invalid/run/erased-22.scala diff --git a/tests/run/erased-27.check b/tests/invalid/run/erased-27.check similarity index 100% rename from tests/run/erased-27.check rename to tests/invalid/run/erased-27.check diff --git a/tests/run/erased-27.scala b/tests/invalid/run/erased-27.scala similarity index 100% rename from tests/run/erased-27.scala rename to tests/invalid/run/erased-27.scala diff --git a/tests/run/erased-28.check b/tests/invalid/run/erased-28.check similarity index 100% rename from tests/run/erased-28.check rename to tests/invalid/run/erased-28.check diff --git a/tests/run/erased-28.scala b/tests/invalid/run/erased-28.scala similarity index 100% rename from tests/run/erased-28.scala rename to tests/invalid/run/erased-28.scala diff --git a/tests/run/erased-3.check b/tests/invalid/run/erased-3.check similarity index 100% rename from tests/run/erased-3.check rename to tests/invalid/run/erased-3.check diff --git a/tests/run/erased-3.scala b/tests/invalid/run/erased-3.scala similarity index 100% rename from tests/run/erased-3.scala rename to tests/invalid/run/erased-3.scala diff --git a/tests/run/erased-4.check b/tests/invalid/run/erased-4.check similarity index 100% rename from tests/run/erased-4.check rename to tests/invalid/run/erased-4.check diff --git a/tests/run/erased-4.scala b/tests/invalid/run/erased-4.scala similarity index 100% rename from tests/run/erased-4.scala rename to tests/invalid/run/erased-4.scala diff --git a/tests/run/erased-5.check b/tests/invalid/run/erased-5.check similarity index 100% rename from tests/run/erased-5.check rename to tests/invalid/run/erased-5.check diff --git a/tests/run/erased-5.scala b/tests/invalid/run/erased-5.scala similarity index 100% rename from tests/run/erased-5.scala rename to tests/invalid/run/erased-5.scala diff --git a/tests/run/erased-6.check b/tests/invalid/run/erased-6.check similarity index 100% rename from tests/run/erased-6.check rename to tests/invalid/run/erased-6.check diff --git a/tests/run/erased-6.scala b/tests/invalid/run/erased-6.scala similarity index 100% rename from tests/run/erased-6.scala rename to tests/invalid/run/erased-6.scala diff --git a/tests/run/erased-8.check b/tests/invalid/run/erased-8.check similarity index 100% rename from tests/run/erased-8.check rename to tests/invalid/run/erased-8.check diff --git a/tests/run/erased-8.scala b/tests/invalid/run/erased-8.scala similarity index 100% rename from tests/run/erased-8.scala rename to tests/invalid/run/erased-8.scala diff --git a/tests/run/erased-9.check b/tests/invalid/run/erased-9.check similarity index 100% rename from tests/run/erased-9.check rename to tests/invalid/run/erased-9.check diff --git a/tests/run/erased-9.scala b/tests/invalid/run/erased-9.scala similarity index 100% rename from tests/run/erased-9.scala rename to tests/invalid/run/erased-9.scala diff --git a/tests/run/erased-class-are-erased.check b/tests/invalid/run/erased-class-are-erased.check similarity index 100% rename from tests/run/erased-class-are-erased.check rename to tests/invalid/run/erased-class-are-erased.check diff --git a/tests/run/erased-class-are-erased.scala b/tests/invalid/run/erased-class-are-erased.scala similarity index 100% rename from tests/run/erased-class-are-erased.scala rename to tests/invalid/run/erased-class-are-erased.scala diff --git a/tests/run/erased-frameless.check b/tests/invalid/run/erased-frameless.check similarity index 100% rename from tests/run/erased-frameless.check rename to tests/invalid/run/erased-frameless.check diff --git a/tests/run/erased-frameless.scala b/tests/invalid/run/erased-frameless.scala similarity index 88% rename from tests/run/erased-frameless.scala rename to tests/invalid/run/erased-frameless.scala index fe654639492a..a366e705840c 100644 --- a/tests/run/erased-frameless.scala +++ b/tests/invalid/run/erased-frameless.scala @@ -28,7 +28,7 @@ trait Dataset[T] { // Use c.label to do an untyped select on actual Spark Dataset, and // cast the result to TypedDataset[A] - def col[S <: String, A](s: S) (using erased ev: Exists[T, s.type, A]) = + inline def col[S <: String, A](s: S) (using erased ev: Exists[T, s.type, A]) = new Column[T, A](s) // ev is only here to check than this is safe, it's never used at runtime! def collect(): Vector[T] @@ -71,17 +71,17 @@ case class Column[T, A](label: String) trait Exists[T, K, V] object Exists { - implicit def derive[T, H <: HList, K, V](implicit g: LabelledGeneric[T] { type Repr = H }, s: Selector[H, K, V]): Exists[T, K, V] = { + inline implicit def derive[T, H <: HList, K, V](implicit g: LabelledGeneric[T] { type Repr = H }, s: Selector[H, K, V]): Exists[T, K, V] = { println("Exists.derive") null } - implicit def caseFound[T <: HList, K <: String, V]: Selector[R[K, V] :: T, K, V] = { + inline implicit def caseFound[T <: HList, K <: String, V]: Selector[R[K, V] :: T, K, V] = { println("Selector.caseFound") null } - implicit def caseRecur[H, T <: HList, K <: String, V](implicit i: Selector[T, K, V]): Selector[H :: T, K, V] = { + inline implicit def caseRecur[H, T <: HList, K <: String, V](implicit i: Selector[T, K, V]): Selector[H :: T, K, V] = { println("Selector.caseRecur") null } diff --git a/tests/run/erased-select-prefix.check b/tests/invalid/run/erased-select-prefix.check similarity index 100% rename from tests/run/erased-select-prefix.check rename to tests/invalid/run/erased-select-prefix.check diff --git a/tests/run/erased-select-prefix.scala b/tests/invalid/run/erased-select-prefix.scala similarity index 77% rename from tests/run/erased-select-prefix.scala rename to tests/invalid/run/erased-select-prefix.scala index b877a0d209d7..06ed46d5ccce 100644 --- a/tests/run/erased-select-prefix.scala +++ b/tests/invalid/run/erased-select-prefix.scala @@ -29,9 +29,9 @@ object Test { def bar(erased i: Int): Unit = () - erased def foo0: Int = 0 - erased def foo1(): Int = 1 - erased def foo2[T]: Int = 2 - erased def foo3[T](): Int = 3 + inline def foo0: Int = 0 + inline def foo1(): Int = 1 + inline def foo2[T]: Int = 2 + inline def foo3[T](): Int = 3 } diff --git a/tests/run/erased-value-class.check b/tests/invalid/run/erased-value-class.check similarity index 100% rename from tests/run/erased-value-class.check rename to tests/invalid/run/erased-value-class.check diff --git a/tests/run/erased-value-class.scala b/tests/invalid/run/erased-value-class.scala similarity index 100% rename from tests/run/erased-value-class.scala rename to tests/invalid/run/erased-value-class.scala diff --git a/tests/run/polymorphic-erased-functions.scala b/tests/invalid/run/polymorphic-erased-functions.scala similarity index 100% rename from tests/run/polymorphic-erased-functions.scala rename to tests/invalid/run/polymorphic-erased-functions.scala diff --git a/tests/neg/erased-6.scala b/tests/neg/erased-6.scala index 4585ab876b3d..76fa1b937f00 100644 --- a/tests/neg/erased-6.scala +++ b/tests/neg/erased-6.scala @@ -1,7 +1,7 @@ //> using options -language:experimental.erasedDefinitions object Test { - erased def foo: Foo = new Foo + erased val foo: Foo = new Foo // error, Foo is not noInits foo.x() // error foo.y // error foo.z // error diff --git a/tests/neg/erased-assign.scala b/tests/neg/erased-assign.scala index 5026ca3f1856..61c8802e576e 100644 --- a/tests/neg/erased-assign.scala +++ b/tests/neg/erased-assign.scala @@ -4,7 +4,7 @@ object Test { var i: Int = 1 def foo(erased a: Int): Int = { i = a // error - erased def r = { + inline def r = { i = a () } diff --git a/tests/neg/erased-class.scala b/tests/neg/erased-class.scala index 96a1c8769bb1..aede15b9ef70 100644 --- a/tests/neg/erased-class.scala +++ b/tests/neg/erased-class.scala @@ -4,7 +4,7 @@ erased class AA erased class BB extends AA // ok @main def Test = - val f1: Array[AA] = compiletime.erasedValue // error // error - def f2(x: Int): Array[AA] = compiletime.erasedValue // error // error - def bar: AA = compiletime.erasedValue // ok - val baz: AA = compiletime.erasedValue // ok + val f1: Array[AA] = caps.unsafe.unsafeErasedValue // error // error + def f2(x: Int): Array[AA] = caps.unsafe.unsafeErasedValue // error // error + def bar: AA = caps.unsafe.unsafeErasedValue // ok + val baz: AA = caps.unsafe.unsafeErasedValue // ok diff --git a/tests/neg/erased-path.scala b/tests/neg/erased-path.scala index ece90e563483..6666165d5cc6 100644 --- a/tests/neg/erased-path.scala +++ b/tests/neg/erased-path.scala @@ -6,6 +6,6 @@ trait Obj { erased val s: Sys lazy val t: Sys - type S = s.X // error: not a legal path, since nonfinal + type S = s.X // now OK, was error: not a legal path, since nonfinal type T = t.X // error: not a legal path, since nonfinal } \ No newline at end of file diff --git a/tests/neg/erasedValueb.check b/tests/neg/erasedValueb.check new file mode 100644 index 000000000000..c38b5feba33d --- /dev/null +++ b/tests/neg/erasedValueb.check @@ -0,0 +1,10 @@ +-- Error: tests/neg/erasedValueb.scala:7:7 ----------------------------------------------------------------------------- +7 | foo0(erasedValue[Int]) // error + | ^^^^^^^^^^^ + | method erasedValue is declared as `erased`, but is in fact used +-- [E215] Type Error: tests/neg/erasedValueb.scala:8:18 ---------------------------------------------------------------- +8 | foo1(erasedValue[Int]) // error + | ^^^^^^^^^^^^^^^^ + | argument to an erased parameter fails to be a pure expression + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/erasedValueb.scala b/tests/neg/erasedValueb.scala index 5c1f1d359e93..a25cf66ec3cb 100644 --- a/tests/neg/erasedValueb.scala +++ b/tests/neg/erasedValueb.scala @@ -5,5 +5,5 @@ object Test { def foo0(a: Int): Int = 3 def foo1(erased a: Int): Int = 3 foo0(erasedValue[Int]) // error - foo1(erasedValue[Int]) + foo1(erasedValue[Int]) // error } diff --git a/tests/neg/experimental-imports.scala b/tests/neg/experimental-imports.scala index e3a91be45f08..10e655ddf3b2 100644 --- a/tests/neg/experimental-imports.scala +++ b/tests/neg/experimental-imports.scala @@ -8,14 +8,14 @@ object Object1: import language.experimental.namedTypeArguments import language.experimental.genericNumberLiterals import language.experimental.erasedDefinitions - erased def f = 1 + erased val f = 1 object Object2: import language.experimental.fewerBraces // error import language.experimental.namedTypeArguments // error import language.experimental.genericNumberLiterals // error import language.experimental.erasedDefinitions // error - erased def f = 1 + erased val f = 1 @experimental object Class1: @@ -23,14 +23,14 @@ object Class1: import language.experimental.namedTypeArguments import language.experimental.genericNumberLiterals import language.experimental.erasedDefinitions - erased def f = 1 + erased val f = 1 object Class2: import language.experimental.fewerBraces // error import language.experimental.namedTypeArguments // error import language.experimental.genericNumberLiterals // error import language.experimental.erasedDefinitions // error - erased def f = 1 + erased val f = 1 @experimental def fun1 = @@ -38,11 +38,11 @@ def fun1 = import language.experimental.namedTypeArguments import language.experimental.genericNumberLiterals import language.experimental.erasedDefinitions - erased def f = 1 + erased val f = 1 def fun2 = import language.experimental.fewerBraces // error import language.experimental.namedTypeArguments // error import language.experimental.genericNumberLiterals // error import language.experimental.erasedDefinitions // error - erased def f = 1 + erased val f = 1 diff --git a/tests/neg/experimental.scala b/tests/neg/experimental.scala index f35a7ca19d7f..583a8c5aa183 100644 --- a/tests/neg/experimental.scala +++ b/tests/neg/experimental.scala @@ -13,7 +13,7 @@ class Test1 { import scala.compiletime.erasedValue type UnivEq[A] object UnivEq: - erased def force[A]: UnivEq[A] = erasedValue + inline def force[A]: UnivEq[A] = erasedValue extension [A](erased proof: UnivEq[A]) inline def univEq(a: A, b: A): Boolean = a == b diff --git a/tests/neg/i23406.scala b/tests/neg/i23406.scala new file mode 100644 index 000000000000..79049f4eddff --- /dev/null +++ b/tests/neg/i23406.scala @@ -0,0 +1,5 @@ +inline def funny[T]: String = + inline compiletime.erasedValue[T] match + case x: String => x + +@main def Test = funny[String] // error diff --git a/tests/neg/i4060.scala b/tests/neg/i4060.scala index bd16ed867966..b85c1190cc3e 100644 --- a/tests/neg/i4060.scala +++ b/tests/neg/i4060.scala @@ -6,7 +6,7 @@ object App { trait A { type L >: Any} def upcast(erased a: A)(x: Any): a.L = x erased val p: A { type L <: Nothing } = p - def coerce(x: Any): Int = upcast(p)(x) // error + def coerce(x: Any): Int = upcast(p)(x) // ok? def coerceInline(x: Any): Int = upcast(compiletime.erasedValue[A {type L <: Nothing}])(x) // error @@ -14,7 +14,7 @@ object App { def upcast_dep_parameter(erased a: B)(x: a.L) : Int = x erased val q : B { type L >: Any } = compiletime.erasedValue - def coerceInlineWithB(x: Any): Int = upcast_dep_parameter(q)(x) // error + def coerceInlineWithB(x: Any): Int = upcast_dep_parameter(q)(x) // ok? def main(args: Array[String]): Unit = { println(coerce("Uh oh!")) diff --git a/tests/neg/magic-canthrow.scala b/tests/neg/magic-canthrow.scala new file mode 100644 index 000000000000..ceca68fd233a --- /dev/null +++ b/tests/neg/magic-canthrow.scala @@ -0,0 +1,11 @@ +import language.experimental.erasedDefinitions +import java.io.IOException + +class CanThrow[-E <: Exception] + +def foo[E <: Exception](e: E)(using erased CanThrow[E]): Nothing = throw e + +erased def magic[E]: E = magic // error + +def Test = foo(new IOException)(using magic) + diff --git a/tests/neg/typeclass-derivation2.scala b/tests/neg/typeclass-derivation2.scala index eca11fb326ed..ba89fb4c39c8 100644 --- a/tests/neg/typeclass-derivation2.scala +++ b/tests/neg/typeclass-derivation2.scala @@ -119,7 +119,7 @@ object TypeLevel { type Subtype[t] = Type[_, t] type Supertype[t] = Type[t, _] type Exactly[t] = Type[t, t] - erased def typeOf[T]: Type[T, T] = compiletime.erasedValue + inline def typeOf[T]: Type[T, T] = compiletime.erasedValue } // An algebraic datatype diff --git a/tests/new/test.scala b/tests/new/test.scala index d350e15a8c9f..dc0b40d6a755 100644 --- a/tests/new/test.scala +++ b/tests/new/test.scala @@ -1,15 +1,12 @@ +import java.io.IOException -package foo - -package object bar: - opaque type O[X] >: X = X - -class Test: - import bar.O - - val x = "abc" - val y: O[String] = x - //val z: String = y +class CanThrow[-E <: Exception] +def foo[E <: Exception](e: E)(using erased CanThrow[E]): Nothing = throw e +erased def magic[E]: E = magic // error +inline def moreMagic[E]: E = moreMagic +def Test = + foo(new IOException)(using magic) + foo(new IOException)(using moreMagic) // should be error diff --git a/tests/pending/neg/erased-impure.check b/tests/pending/neg/erased-impure.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/pending/neg/erased-impure.scala b/tests/pending/neg/erased-impure.scala new file mode 100644 index 000000000000..8dd668bbc529 --- /dev/null +++ b/tests/pending/neg/erased-impure.scala @@ -0,0 +1,44 @@ +//> using options -explain +import language.experimental.erasedDefinitions +import java.io.IOException +import caps.unsafe.unsafeErasedValue + +class CanThrow[-E <: Exception] + +def foo[E <: Exception](e: E)(using erased CanThrow[E]): Nothing = throw e + +erased val magic1: IOException = ??? // error +erased val magic2: IOException = compiletime.erasedValue[IOException] // error +erased val magic3: IOException = null.asInstanceOf[IOException] // error + +inline def inlineId[T](x: T) = x + +class C() + +def testPure[T](erased x: T): Unit = () + +case class Pair[A, B](x: A, y: B) +object Pair: + def apply(x: Int): Pair2[Int, Int] = + println("Pair2") + Pair2(x, x + 1) + +case class Box[A](x: A): + println(x) + +def Test = + foo(new IOException)(using ???) // error + foo(new IOException)(using inlineId(???)) // error + + testPure(C()) // OK + testPure(inlineId(C())) // OK + testPure(identity(C())) // error, identity is not an inline function + + testPure(Pair(unsafeErasedValue[Int], unsafeErasedValue[String])) // OK + testPure(Pair(unsafeErasedValue[Int])) // error + testPure(Box(unsafeErasedValue[Int])) // error + + + + + diff --git a/tests/pos-custom-args/captures/try.scala b/tests/pos-custom-args/captures/try.scala index 5faabecc411c..c88c842babc5 100644 --- a/tests/pos-custom-args/captures/try.scala +++ b/tests/pos-custom-args/captures/try.scala @@ -14,7 +14,7 @@ def foo(x: Boolean): Int throws Fail = if x then 1 else raise(Fail()) def handle[E <: Exception, R](op: (erased CanThrow[E]) -> R)(handler: E -> R): R = - erased val x: CanThrow[E] = ??? : CanThrow[E] + erased val x = caps.unsafe.unsafeErasedValue[CanThrow[E]] try op(x) catch case ex: E => handler(ex) diff --git a/tests/neg/erased-24.scala b/tests/pos/erased-24.scala similarity index 77% rename from tests/neg/erased-24.scala rename to tests/pos/erased-24.scala index bf2f1d21435e..410a1900a1c1 100644 --- a/tests/neg/erased-24.scala +++ b/tests/pos/erased-24.scala @@ -12,8 +12,8 @@ object Test { null.asInstanceOf[foo.X] // ok } - def fun2(erased foo: Foo)(erased bar: foo.B): bar.X = { // error - null.asInstanceOf[bar.X] // error + def fun2(erased foo: Foo)(erased bar: foo.B): bar.X = { // was error + null.asInstanceOf[bar.X] // was error } } diff --git a/tests/pos/erased-asInstanceOf.scala b/tests/pos/erased-asInstanceOf.scala index 692ff3a16b05..7029c298452c 100644 --- a/tests/pos/erased-asInstanceOf.scala +++ b/tests/pos/erased-asInstanceOf.scala @@ -11,7 +11,7 @@ object Test { val ds: Dataset = ??? - lazy val collD = new Column + val collD = new Column ds.select(collD) diff --git a/tests/pos/erased-conforms.scala b/tests/pos/erased-conforms.scala index 426490d5a53a..311b244b9872 100644 --- a/tests/pos/erased-conforms.scala +++ b/tests/pos/erased-conforms.scala @@ -5,7 +5,7 @@ erased class <::<[-From, +To] extends ErasedTerm erased class =::=[From, To] extends (From <::< To) -erased given [X] => (X =::= X) = scala.compiletime.erasedValue +inline given [X] => (X =::= X) = scala.caps.unsafe.unsafeErasedValue extension [From](x: From) inline def cast[To](using From <::< To): To = x.asInstanceOf[To] // Safe cast because we know `From <:< To` diff --git a/tests/neg/erased-pathdep-1.scala b/tests/pos/erased-pathdep-1.scala similarity index 67% rename from tests/neg/erased-pathdep-1.scala rename to tests/pos/erased-pathdep-1.scala index 422ceb5e37fe..e696c48df328 100644 --- a/tests/neg/erased-pathdep-1.scala +++ b/tests/pos/erased-pathdep-1.scala @@ -1,16 +1,14 @@ //> using options -language:experimental.erasedDefinitions -// Could become a neg test if we had totality checking for erased arguments - object Test { fun1(new Bar) val _ = fun2(new Bar) val _ = fun3(new Bar) - def fun1[F >: Bar <: Foo](erased f: F): f.X = null.asInstanceOf[f.X] // error // error - def fun2[F >: Bar <: Foo](erased f: F)(erased bar: f.B): f.B = null.asInstanceOf[f.B] // error // error // error - def fun3[F >: Bar <: Foo](erased f: F)(erased b: f.B): b.X = null.asInstanceOf[b.X] // error // error // error + def fun1[F >: Bar <: Foo](erased f: F): f.X = null.asInstanceOf[f.X] + def fun2[F >: Bar <: Foo](erased f: F)(erased bar: f.B): f.B = null.asInstanceOf[f.B] + def fun3[F >: Bar <: Foo](erased f: F)(erased b: f.B): b.X = null.asInstanceOf[b.X] } class Foo { diff --git a/tests/neg/erased-pathdep-2.scala b/tests/pos/erased-pathdep-2.scala similarity index 81% rename from tests/neg/erased-pathdep-2.scala rename to tests/pos/erased-pathdep-2.scala index 0b50acbf3b30..8c9f7b414a98 100644 --- a/tests/neg/erased-pathdep-2.scala +++ b/tests/pos/erased-pathdep-2.scala @@ -7,8 +7,8 @@ object Test { type F >: Bar <: Foo class A(erased val f: F) { - type F1 <: f.X // error - type F2[Z <: f.X] // error + type F1 <: f.X // was error + type F2[Z <: f.X] // was error } } diff --git a/tests/pos/erased-pure.scala b/tests/pos/erased-pure.scala index 9d2b54ac02b4..e62563669e66 100644 --- a/tests/pos/erased-pure.scala +++ b/tests/pos/erased-pure.scala @@ -1,4 +1,5 @@ import language.experimental.erasedDefinitions +import caps.unsafe.unsafeErasedValue inline def id[T](x: T) = x @@ -8,6 +9,7 @@ def foo[T](erased x: T): Unit = () class Pair[A, B](x: A, y: B) +case class Pair2[A, B](x: A, y: B) def Test = foo(C()) @@ -17,7 +19,8 @@ def Test = foo(Pair(C(), "hello" + "world")) foo(id(Pair(id(C()), id("hello" + "world")))) - - + //erased val x1 = Pair(unsafeErasedValue[Int], unsafeErasedValue[String]) + //erased val x2 = Pair2(unsafeErasedValue[Int], unsafeErasedValue[String]) + erased val x3 = Tuple2(unsafeErasedValue[Int], unsafeErasedValue[String]) diff --git a/tests/neg/erased-singleton.scala b/tests/pos/erased-singleton.scala similarity index 67% rename from tests/neg/erased-singleton.scala rename to tests/pos/erased-singleton.scala index 5ffa78e24b07..f7ad5165ec0a 100644 --- a/tests/neg/erased-singleton.scala +++ b/tests/pos/erased-singleton.scala @@ -5,5 +5,5 @@ trait Sys trait Obj { erased val s: Sys - type S = s.type // error: non final + type S = s.type // now OK, was error: non final } diff --git a/tests/pos/expeimental-flag-with-lang-feature.scala b/tests/pos/expeimental-flag-with-lang-feature.scala deleted file mode 100644 index 96069c332e02..000000000000 --- a/tests/pos/expeimental-flag-with-lang-feature.scala +++ /dev/null @@ -1,10 +0,0 @@ -//> using options -experimental - -import scala.language.experimental.erasedDefinitions -import scala.language.experimental.namedTypeArguments - -erased def erasedFun(erased x: Int): Int = x - -def namedTypeArgumentsFun[T, U]: Int = - namedTypeArgumentsFun[T = Int, U = Int] - namedTypeArgumentsFun[U = Int, T = Int] diff --git a/tests/pos/experimental-imports-top.scala b/tests/pos/experimental-imports-top.scala index 9ba2b5cd2c99..595caac66fe7 100644 --- a/tests/pos/experimental-imports-top.scala +++ b/tests/pos/experimental-imports-top.scala @@ -4,4 +4,4 @@ import language.experimental.erasedDefinitions import annotation.experimental @experimental -erased def f = 1 +erased val f = 1 diff --git a/tests/pos/experimentalErased.scala b/tests/pos/experimentalErased.scala index 358c134c714a..4a504e3d8a80 100644 --- a/tests/pos/experimentalErased.scala +++ b/tests/pos/experimentalErased.scala @@ -6,11 +6,6 @@ erased class Foo erased class Bar -@experimental -erased def foo = 2 - -erased def bar = 2 - @experimental erased val foo2 = 2 diff --git a/tests/pos/i11864.scala b/tests/pos/i11864.scala index ba43336e13ca..0301b50d7021 100644 --- a/tests/pos/i11864.scala +++ b/tests/pos/i11864.scala @@ -40,7 +40,7 @@ final class CallbackTo[+A] { object CallbackTo { type MapGuard[A] = { type Out = A } - erased given MapGuard: [A] => MapGuard[A] = compiletime.erasedValue + inline given MapGuard: [A] => MapGuard[A] = caps.unsafe.unsafeErasedValue def traverse[A, B](ta: List[A]): CallbackTo[List[B]] = val x: CallbackTo[List[A] => List[B]] = ??? diff --git a/tests/pos/i11896.scala b/tests/pos/i11896.scala index 49e5307f1a49..a4816eb5ad18 100644 --- a/tests/pos/i11896.scala +++ b/tests/pos/i11896.scala @@ -1,7 +1,7 @@ import scala.language.experimental.erasedDefinitions type X -erased def x: X = compiletime.erasedValue +inline def x: X = caps.unsafe.unsafeErasedValue def foo(using erased X): Unit = () diff --git a/tests/pos/i5938.scala b/tests/pos/i5938.scala index 17a20dcd0f1f..f392de153b4c 100644 --- a/tests/pos/i5938.scala +++ b/tests/pos/i5938.scala @@ -1,7 +1,6 @@ import scala.language.experimental.erasedDefinitions import compiletime.summonFrom -import compiletime.erasedValue trait Link[T, A] @@ -15,7 +14,7 @@ transparent inline def link[T] = class Foo object Foo { - erased implicit val barLink: Link[Foo, Bar.type] = erasedValue + erased implicit val barLink: Link[Foo, Bar.type] = caps.unsafe.unsafeErasedValue } implicit object Bar { diff --git a/tests/pos/i6419.scala b/tests/pos/i6419.scala index 550922f48d76..44136d9e48a3 100644 --- a/tests/pos/i6419.scala +++ b/tests/pos/i6419.scala @@ -9,8 +9,4 @@ class Foo { inline def bar: Unit = { foo } - - erased def baz: Unit = { - foo - } } diff --git a/tests/pos/i7741.scala b/tests/pos/i7741.scala index af9912915cc0..981789f14e2a 100644 --- a/tests/pos/i7741.scala +++ b/tests/pos/i7741.scala @@ -3,9 +3,6 @@ import scala.language.experimental.erasedDefinitions class A1 { @native private def a: Unit } -trait A2 { - erased def i(erased a: Int): Int -} trait A3 { erased val a: Int } \ No newline at end of file diff --git a/tests/pos/i7868.scala b/tests/pos/i7868.scala deleted file mode 100644 index fa31bd131b0c..000000000000 --- a/tests/pos/i7868.scala +++ /dev/null @@ -1,42 +0,0 @@ -//> using options -language:experimental.erasedDefinitions - -import language.experimental.namedTypeArguments -import scala.compiletime.* -import scala.compiletime.ops.int.* - -final case class Coproduct[+Set, +Value, Index <: Int](value: Value & Set, index: Index) - -object Coproduct { - opaque type +:[+A, +B] = A | B - - trait At[+Set, -Value, Index <: Int] { - def cast: Value <:< Set - } - - object At { - - given atHead: [Head, Tail] => At[Head +: Tail, Head, 0]: - def cast: Head <:< Head +: Tail = summon[Head <:< Head +: Tail] - - given atTail[Head, Tail, Value, NextIndex <: Int] - (using atNext: At[Tail, Value, NextIndex]) - : At[Head +: Tail, Value, S[NextIndex]] with - val cast: Value <:< Head +: Tail = atNext.cast - - given [A] => A => (() => A) = { () => summon[A] } - } - - def upCast[A, B](a: A)(using erased evidence: (A <:< B) ): B = a.asInstanceOf[B] - - def from[Set, Value, Index <: Int](value: Value)(using erased at: At[Set, Value, Index]) : ValueOf[Index] ?=> Coproduct[Set, Value, Index] = { - Coproduct[Set, Value, Index](upCast(value: Value)(using at.cast.liftCo[[X] =>> Value & X]), valueOf[Index]) - } - -} - -object Test extends App { - import Coproduct.* - - // Error: No singleton value available for scala.compiletime.ops.int.S[scala.compiletime.ops.int.S[(0 : Int)]]. - val c = from[Set = Int +: String +: Seq[Double] +: Nothing](Nil) -} diff --git a/tests/pos/inline-match-gadt.scala b/tests/pos/inline-match-gadt.scala index cf2aae00b402..7c966f33bf48 100644 --- a/tests/pos/inline-match-gadt.scala +++ b/tests/pos/inline-match-gadt.scala @@ -2,7 +2,7 @@ import scala.language.experimental.erasedDefinitions object `inline-match-gadt` { class Exactly[T] - erased def exactType[T]: Exactly[T] = compiletime.erasedValue + inline def exactType[T]: Exactly[T] = compiletime.erasedValue inline def foo[T](t: T): T = inline exactType[T] match { diff --git a/tests/pos/matchtype.scala b/tests/pos/matchtype.scala index 21c074deafd7..90d8f0dc6400 100644 --- a/tests/pos/matchtype.scala +++ b/tests/pos/matchtype.scala @@ -1,5 +1,5 @@ import scala.language.experimental.erasedDefinitions -import compiletime.erasedValue +import caps.unsafe.unsafeErasedValue as erasedValue import compiletime.ops.int.S object Test { type T[X] = X match { diff --git a/tests/pos/phantom-Eq.scala b/tests/pos/phantom-Eq.scala index d844c4b110c6..f3a4af02a186 100644 --- a/tests/pos/phantom-Eq.scala +++ b/tests/pos/phantom-Eq.scala @@ -16,18 +16,19 @@ object PhantomEq { object EqUtil { - type PhantomEq[-L, -R] + class PhantomEq[-L, -R] type PhantomEqEq[T] = PhantomEq[T, T] + erased val phantomEq = PhantomEq[Any, Any]() extension [T](x: T) def ===[U](y: U)(using erased PhantomEq[T, U]) = x.equals(y) - erased given eqString: PhantomEqEq[String] = compiletime.erasedValue - erased given eqInt: PhantomEqEq[Int] = compiletime.erasedValue - erased given eqDouble: PhantomEqEq[Double] = compiletime.erasedValue + inline given eqString: PhantomEqEq[String] = phantomEq + inline given eqInt: PhantomEqEq[Int] = phantomEq + inline given eqDouble: PhantomEqEq[Double] = phantomEq - erased given eqByteNum: PhantomEq[Byte, Number] = compiletime.erasedValue - erased given eqNumByte: PhantomEq[Number, Byte] = compiletime.erasedValue + inline given eqByteNum: PhantomEq[Byte, Number] = phantomEq + inline given eqNumByte: PhantomEq[Number, Byte] = phantomEq - erased given eqSeq: [T, U] => (erased PhantomEq[T, U]) => PhantomEq[Seq[T], Seq[U]] = compiletime.erasedValue + inline given eqSeq: [T, U] => (erased PhantomEq[T, U]) => PhantomEq[Seq[T], Seq[U]] = phantomEq } diff --git a/tests/pos/phantom-Eq2/Phantom-Eq_1.scala b/tests/pos/phantom-Eq2/Phantom-Eq_1.scala index b041a4a87efe..b5021a30b09b 100644 --- a/tests/pos/phantom-Eq2/Phantom-Eq_1.scala +++ b/tests/pos/phantom-Eq2/Phantom-Eq_1.scala @@ -1,19 +1,20 @@ import scala.language.experimental.erasedDefinitions +import scala.annotation.publicInBinary /* This is a version of ../pos/phantomEq.scala that tests phantom with separate compilation */ object EqUtil { - final class PhantomEq[-L, -R] private[EqUtil]() + final class PhantomEq[-L, -R] @publicInBinary private[EqUtil]() type PhantomEqEq[T] = PhantomEq[T, T] extension [T](x: T) def ===[U] (y: U) (using erased PhantomEq[T, U]) = x.equals(y) - erased given eqString: PhantomEqEq[String] = new PhantomEq[String, String] - erased given eqInt: PhantomEqEq[Int] = new PhantomEq[Int, Int] - erased given eqDouble: PhantomEqEq[Double] = new PhantomEq[Double, Double] - erased given eqByteNum: PhantomEq[Byte, Number] = new PhantomEq[Byte, Number] - erased given eqNumByte: PhantomEq[Number, Byte] = new PhantomEq[Number, Byte] - erased given eqSeq: [T, U] => (erased eq: PhantomEq[T, U]) => PhantomEq[Seq[T], Seq[U]] = + inline given eqString: PhantomEqEq[String] = new PhantomEq[String, String] + inline given eqInt: PhantomEqEq[Int] = new PhantomEq[Int, Int] + inline given eqDouble: PhantomEqEq[Double] = new PhantomEq[Double, Double] + inline given eqByteNum: PhantomEq[Byte, Number] = new PhantomEq[Byte, Number] + inline given eqNumByte: PhantomEq[Number, Byte] = new PhantomEq[Number, Byte] + inline given eqSeq: [T, U] => (erased eq: PhantomEq[T, U]) => PhantomEq[Seq[T], Seq[U]] = new PhantomEq[Seq[T], Seq[U]] } diff --git a/tests/pos/phantom-Evidence.scala b/tests/pos/phantom-Evidence.scala index f56ce3b798ee..db39a7b4659e 100644 --- a/tests/pos/phantom-Evidence.scala +++ b/tests/pos/phantom-Evidence.scala @@ -1,4 +1,5 @@ import scala.language.experimental.erasedDefinitions +import annotation.publicInBinary /** In this implementation variant of =:= (called =::=) we erase all instantiations and definitions of =::= */ object WithNormalState { @@ -11,9 +12,9 @@ object WithNormalState { object Instance { def newInstance(): Instance[Off] = new Instance[Off] } - class Instance[S <: State] private { - def getOnInstance (using erased ev: S =::= Off): Instance[On] = new Instance[On] // phantom parameter ev is erased - def getOffInstance (using erased ev: S =::= On): Instance[Off] = new Instance[Off] // phantom parameter ev is erased + class Instance[S <: State] @publicInBinary private { + inline def getOnInstance (using erased ev: S =::= Off): Instance[On] = new Instance[On] // phantom parameter ev is erased + inline def getOffInstance (using erased ev: S =::= On): Instance[Off] = new Instance[Off] // phantom parameter ev is erased } def run() = { @@ -26,5 +27,5 @@ object WithNormalState { object Utils { type =::=[From, To] - erased given tpEquals: [A] => (A =::= A) = compiletime.erasedValue + inline given tpEquals: [A] => (A =::= A) = caps.unsafe.unsafeErasedValue } diff --git a/tests/run/erased-18.scala b/tests/run/erased-18.scala index 46f7e44c7309..2e5275690ea2 100644 --- a/tests/run/erased-18.scala +++ b/tests/run/erased-18.scala @@ -11,8 +11,8 @@ object Test { )(foo) } - def foo = { - println("foo") + inline def foo = { + //println("foo") 42 } } diff --git a/tests/run/erased-machine-state.scala b/tests/run/erased-machine-state.scala index c84f1619366d..2431d34e9dfe 100644 --- a/tests/run/erased-machine-state.scala +++ b/tests/run/erased-machine-state.scala @@ -9,8 +9,7 @@ final class Off extends State @implicitNotFound("State must be Off") class IsOff[S <: State] object IsOff { - implicit def isOff: IsOff[Off] = { - println("isOff") + inline implicit def isOff: IsOff[Off] = { new IsOff[Off] } } @@ -18,8 +17,7 @@ object IsOff { @implicitNotFound("State must be On") class IsOn[S <: State] object IsOn { - implicit def isOn: IsOn[On] = { - println("isOn") + inline implicit def isOn: IsOn[On] = { new IsOn[On] } } diff --git a/tests/run/erased-poly-ref.scala b/tests/run/erased-poly-ref.scala index 59badb71255d..975a576cc15b 100644 --- a/tests/run/erased-poly-ref.scala +++ b/tests/run/erased-poly-ref.scala @@ -8,10 +8,9 @@ object Test { def fun(erased a: Int): Unit = println("fun") - def foo[P](erased x: Int)(erased y: Int): Int = 0 + inline def foo[P](erased x: Int)(erased y: Int): Int = 0 - def bar(x: Int) = { - println(x) + inline def bar(x: Int) = { x } } diff --git a/tests/run/i11996.scala b/tests/run/i11996.scala index 9724e12b575e..a4318ace6c86 100644 --- a/tests/run/i11996.scala +++ b/tests/run/i11996.scala @@ -3,8 +3,8 @@ final class UnivEq[A] object UnivEq: - erased def force[A]: UnivEq[A] = - compiletime.erasedValue + inline def force[A]: UnivEq[A] = + caps.unsafe.unsafeErasedValue extension [A](a: A) inline def ==*[B >: A](b: B)(using erased UnivEq[B]): Boolean = a == b diff --git a/tests/run/i16943.scala b/tests/run/i16943.scala index 68e1f8fb5aa3..697e9a2f38b7 100644 --- a/tests/run/i16943.scala +++ b/tests/run/i16943.scala @@ -1,6 +1,6 @@ @main @annotation.experimental -def Test(): Unit = fail(compiletime.erasedValue, 1) +def Test(): Unit = fail(caps.unsafe.unsafeErasedValue, 1) @annotation.experimental def fail(dumb: CanThrow[Exception], x: Int) = println(x) diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 3f904b6bdda0..5f755e375ec3 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -2974,6 +2974,7 @@ Text => empty Language => Scala Symbols => 16 entries Occurrences => 12 entries +Diagnostics => 2 entries Symbols: example/NamedArguments# => class NamedArguments extends Object { self: NamedArguments => +4 decls } @@ -3007,6 +3008,10 @@ Occurrences: [5:7..5:12): apply -> example/NamedArguments#User.apply(). [5:13..5:17): name -> example/NamedArguments#User.apply().(name) +Diagnostics: +[4:2..4:21): [warning] A pure expression does nothing in statement position +[5:2..5:27): [warning] A pure expression does nothing in statement position + expect/NewModifiers.scala ------------------------- @@ -3654,7 +3659,7 @@ Text => empty Language => Scala Symbols => 62 entries Occurrences => 165 entries -Diagnostics => 3 entries +Diagnostics => 4 entries Synthetics => 39 entries Symbols: @@ -3890,6 +3895,7 @@ Occurrences: Diagnostics: [19:21..19:22): [warning] unused pattern variable +[28:4..28:9): [warning] A pure expression does nothing in statement position [41:4..41:5): [warning] unused pattern variable [63:10..63:11): [warning] unused explicit parameter