diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index b9960cbb4652..740b2e3d9ab8 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -70,13 +70,13 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class InterpolatedString(id: TermName, segments: List[Tree])(implicit @constructorOnly src: SourceFile) extends TermTree - /** A function type */ + /** A function type or closure */ case class Function(args: List[Tree], body: Tree)(implicit @constructorOnly src: SourceFile) extends Tree { override def isTerm: Boolean = body.isTerm override def isType: Boolean = body.isType } - /** A function type with `implicit`, `erased`, or `given` modifiers */ + /** A function type or closure with `implicit`, `erased`, or `given` modifiers */ class FunctionWithMods(args: List[Tree], body: Tree, val mods: Modifiers)(implicit @constructorOnly src: SourceFile) extends Function(args, body) @@ -217,6 +217,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class Transparent()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Transparent) case class Infix()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Infix) + + case class Impure()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Impure) } /** Modifiers and annotations for definitions diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala index 09064314b1bf..4c201d7edf54 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala @@ -17,9 +17,13 @@ def retainedElems(tree: Tree)(using Context): List[Tree] = tree match case Apply(_, Typed(SeqLiteral(elems, _), _) :: Nil) => elems case _ => Nil +class IllegalCaptureRef(tpe: Type) extends Exception + extension (tree: Tree) - def toCaptureRef(using Context): CaptureRef = tree.tpe.asInstanceOf[CaptureRef] + def toCaptureRef(using Context): CaptureRef = tree.tpe match + case ref: CaptureRef => ref + case tpe => throw IllegalCaptureRef(tpe) def toCaptureSet(using Context): CaptureSet = tree.getAttachment(Captures) match @@ -59,20 +63,6 @@ extension (tp: Type) def isBoxedCapturing(using Context) = !tp.boxedCaptured.isAlwaysEmpty - def canHaveInferredCapture(using Context): Boolean = tp match - case tp: TypeRef if tp.symbol.isClass => - !tp.symbol.isValueClass && tp.symbol != defn.AnyClass - case _: TypeVar | _: TypeParamRef => - false - case tp: TypeProxy => - tp.superType.canHaveInferredCapture - case tp: AndType => - tp.tp1.canHaveInferredCapture && tp.tp2.canHaveInferredCapture - case tp: OrType => - tp.tp1.canHaveInferredCapture || tp.tp2.canHaveInferredCapture - case _ => - false - def stripCapturing(using Context): Type = tp.dealiasKeepAnnots match case CapturingType(parent, _, _) => parent.stripCapturing diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index cd8d67399d8d..82e5e6e14a4b 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -56,6 +56,12 @@ sealed abstract class CaptureSet extends Showable: assert(v.isConst) Const(v.elems) + final def isUniversal(using Context) = + elems.exists { + case ref: TermRef => ref.symbol == defn.captureRoot + case _ => false + } + /** Cast to variable. @pre: !isConst */ def asVar: Var = assert(!isConst) diff --git a/compiler/src/dotty/tools/dotc/cc/CapturingType.scala b/compiler/src/dotty/tools/dotc/cc/CapturingType.scala index 2eeb1ff41b72..738e746d0178 100644 --- a/compiler/src/dotty/tools/dotc/cc/CapturingType.scala +++ b/compiler/src/dotty/tools/dotc/cc/CapturingType.scala @@ -12,10 +12,22 @@ object CapturingType: else AnnotatedType(parent, CaptureAnnotation(refs, boxed)) def unapply(tp: AnnotatedType)(using Context): Option[(Type, CaptureSet, Boolean)] = - if ctx.phase == Phases.checkCapturesPhase && tp.annot.symbol == defn.RetainsAnnot then + if ctx.phase == Phases.checkCapturesPhase then EventuallyCapturingType.unapply(tp) + else None + +end CapturingType + +object EventuallyCapturingType: + + def unapply(tp: AnnotatedType)(using Context): Option[(Type, CaptureSet, Boolean)] = + if tp.annot.symbol == defn.RetainsAnnot then tp.annot match case ann: CaptureAnnotation => Some((tp.parent, ann.refs, ann.boxed)) - case ann => Some((tp.parent, ann.tree.toCaptureSet, ann.tree.isBoxedCapturing)) + case ann => + try Some((tp.parent, ann.tree.toCaptureSet, ann.tree.isBoxedCapturing)) + catch case ex: IllegalCaptureRef => None else None -end CapturingType +end EventuallyCapturingType + + diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 513833ea2d12..e2eddcfaef21 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -8,7 +8,7 @@ import Flags._, Scopes._, Decorators._, NameOps._, Periods._, NullOpsDecorator._ import unpickleScala2.Scala2Unpickler.ensureConstructor import scala.collection.mutable import collection.mutable -import Denotations.SingleDenotation +import Denotations.{SingleDenotation, staticRef} import util.{SimpleIdentityMap, SourceFile, NoSource} import typer.ImportInfo.RootRef import Comments.CommentsContext @@ -86,7 +86,7 @@ class Definitions { * * FunctionN traits follow this template: * - * trait FunctionN[T0,...T{N-1}, R] extends Object { + * trait FunctionN[-T0,...-T{N-1}, +R] extends Object { * def apply($x0: T0, ..., $x{N_1}: T{N-1}): R * } * @@ -96,46 +96,65 @@ class Definitions { * * ContextFunctionN traits follow this template: * - * trait ContextFunctionN[T0,...,T{N-1}, R] extends Object { + * trait ContextFunctionN[-T0,...,-T{N-1}, +R] extends Object { * def apply(using $x0: T0, ..., $x{N_1}: T{N-1}): R * } * * ErasedFunctionN traits follow this template: * - * trait ErasedFunctionN[T0,...,T{N-1}, R] extends Object { + * trait ErasedFunctionN[-T0,...,-T{N-1}, +R] extends Object { * def apply(erased $x0: T0, ..., $x{N_1}: T{N-1}): R * } * * ErasedContextFunctionN traits follow this template: * - * trait ErasedContextFunctionN[T0,...,T{N-1}, R] extends Object { + * trait ErasedContextFunctionN[-T0,...,-T{N-1}, +R] extends Object { * def apply(using erased $x0: T0, ..., $x{N_1}: T{N-1}): R * } * * ErasedFunctionN and ErasedContextFunctionN erase to Function0. + * + * EffXYZFunctionN afollow this template: + * + * type EffXYZFunctionN[-T0,...,-T{N-1}, +R] = {*} XYZFunctionN[T0,...,T{N-1}, R] */ - def newFunctionNTrait(name: TypeName): ClassSymbol = { + private def newFunctionNType(name: TypeName): Symbol = { + val impure = name.startsWith("Impure") val completer = new LazyType { def complete(denot: SymDenotation)(using Context): Unit = { - val cls = denot.asClass.classSymbol - val decls = newScope val arity = name.functionArity - val paramNamePrefix = tpnme.scala ++ str.NAME_JOIN ++ name ++ str.EXPAND_SEPARATOR - val argParamRefs = List.tabulate(arity) { i => - enterTypeParam(cls, paramNamePrefix ++ "T" ++ (i + 1).toString, Contravariant, decls).typeRef - } - val resParamRef = enterTypeParam(cls, paramNamePrefix ++ "R", Covariant, decls).typeRef - val methodType = MethodType.companion( - isContextual = name.isContextFunction, - isImplicit = false, - isErased = name.isErasedFunction) - decls.enter(newMethod(cls, nme.apply, methodType(argParamRefs, resParamRef), Deferred)) - denot.info = - ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: Nil, decls) + if impure then + val argParamNames = List.tabulate(arity)(tpnme.syntheticTypeParamName) + val argVariances = List.fill(arity)(Contravariant) + val underlyingName = name.asSimpleName.drop(6) + val underlyingClass = ScalaPackageVal.requiredClass(underlyingName) + denot.info = TypeAlias( + HKTypeLambda(argParamNames :+ "R".toTypeName, argVariances :+ Covariant)( + tl => List.fill(arity + 1)(TypeBounds.empty), + tl => CapturingType(underlyingClass.typeRef.appliedTo(tl.paramRefs), + CaptureSet.universal, boxed = false) + )) + else + val cls = denot.asClass.classSymbol + val decls = newScope + val paramNamePrefix = tpnme.scala ++ str.NAME_JOIN ++ name ++ str.EXPAND_SEPARATOR + val argParamRefs = List.tabulate(arity) { i => + enterTypeParam(cls, paramNamePrefix ++ "T" ++ (i + 1).toString, Contravariant, decls).typeRef + } + val resParamRef = enterTypeParam(cls, paramNamePrefix ++ "R", Covariant, decls).typeRef + val methodType = MethodType.companion( + isContextual = name.isContextFunction, + isImplicit = false, + isErased = name.isErasedFunction) + decls.enter(newMethod(cls, nme.apply, methodType(argParamRefs, resParamRef), Deferred)) + denot.info = + ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: Nil, decls) } } - val flags = Trait | NoInits - newPermanentClassSymbol(ScalaPackageClass, name, flags, completer) + if impure then + newPermanentSymbol(ScalaPackageClass, name, EmptyFlags, completer) + else + newPermanentClassSymbol(ScalaPackageClass, name, Trait | NoInits, completer) } private def newMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol = @@ -209,7 +228,7 @@ class Definitions { val cls = ScalaPackageVal.moduleClass.asClass cls.info.decls.openForMutations.useSynthesizer( name => - if (name.isTypeName && name.isSyntheticFunction) newFunctionNTrait(name.asTypeName) + if (name.isTypeName && name.isSyntheticFunction) newFunctionNType(name.asTypeName) else NoSymbol) cls } @@ -1273,37 +1292,54 @@ class Definitions { @tu lazy val TupleType: Array[TypeRef] = mkArityArray("scala.Tuple", MaxTupleArity, 1) + /** Cached function types of arbitary arities. + * Function types are created on demand with newFunctionNTrait, which is + * called from a synthesizer installed in ScalaPackageClass. + */ private class FunType(prefix: String): private var classRefs: Array[TypeRef] = new Array(22) + def apply(n: Int): TypeRef = while n >= classRefs.length do val classRefs1 = new Array[TypeRef](classRefs.length * 2) Array.copy(classRefs, 0, classRefs1, 0, classRefs.length) classRefs = classRefs1 + val funName = s"scala.$prefix$n" if classRefs(n) == null then - classRefs(n) = requiredClassRef(prefix + n.toString) + classRefs(n) = + if prefix.startsWith("Impure") + then staticRef(funName.toTypeName).symbol.typeRef + else requiredClassRef(funName) classRefs(n) - - private val erasedContextFunType = FunType("scala.ErasedContextFunction") - private val contextFunType = FunType("scala.ContextFunction") - private val erasedFunType = FunType("scala.ErasedFunction") - private val funType = FunType("scala.Function") - - def FunctionClass(n: Int, isContextual: Boolean = false, isErased: Boolean = false)(using Context): Symbol = - ( if isContextual && isErased then erasedContextFunType(n) - else if isContextual then contextFunType(n) - else if isErased then erasedFunType(n) - else funType(n) - ).symbol.asClass + end FunType + + private def funTypeIdx(isContextual: Boolean, isErased: Boolean, isImpure: Boolean): Int = + (if isContextual then 1 else 0) + + (if isErased then 2 else 0) + + (if isImpure then 4 else 0) + + private val funTypeArray: IArray[FunType] = + val arr = Array.ofDim[FunType](8) + val choices = List(false, true) + for contxt <- choices; erasd <- choices; impure <- choices do + var str = "Function" + if contxt then str = "Context" + str + if erasd then str = "Erased" + str + if impure then str = "Impure" + str + arr(funTypeIdx(contxt, erasd, impure)) = FunType(str) + IArray.unsafeFromArray(arr) + + def FunctionSymbol(n: Int, isContextual: Boolean = false, isErased: Boolean = false, isImpure: Boolean = false)(using Context): Symbol = + funTypeArray(funTypeIdx(isContextual, isErased, isImpure))(n).symbol @tu lazy val Function0_apply: Symbol = Function0.requiredMethod(nme.apply) - @tu lazy val Function0: Symbol = FunctionClass(0) - @tu lazy val Function1: Symbol = FunctionClass(1) - @tu lazy val Function2: Symbol = FunctionClass(2) + @tu lazy val Function0: Symbol = FunctionSymbol(0) + @tu lazy val Function1: Symbol = FunctionSymbol(1) + @tu lazy val Function2: Symbol = FunctionSymbol(2) - def FunctionType(n: Int, isContextual: Boolean = false, isErased: Boolean = false)(using Context): TypeRef = - FunctionClass(n, isContextual && !ctx.erasedTypes, isErased).typeRef + def FunctionType(n: Int, isContextual: Boolean = false, isErased: Boolean = false, isImpure: Boolean = false)(using Context): TypeRef = + FunctionSymbol(n, isContextual && !ctx.erasedTypes, isErased, isImpure).typeRef lazy val PolyFunctionClass = requiredClass("scala.PolyFunction") def PolyFunctionType = PolyFunctionClass.typeRef @@ -1345,6 +1381,10 @@ class Definitions { */ def isFunctionClass(cls: Symbol): Boolean = scalaClassName(cls).isFunction + /** Is a function class, or an impure function type alias */ + def isFunctionSymbol(sym: Symbol): Boolean = + sym.isType && (sym.owner eq ScalaPackageClass) && sym.name.isFunction + /** Is a function class where * - FunctionN for N >= 0 and N != XXL */ @@ -1550,7 +1590,7 @@ class Definitions { new PerRun(Function2SpecializedReturnTypes.map(_.symbol)) def isSpecializableFunction(cls: ClassSymbol, paramTypes: List[Type], retType: Type)(using Context): Boolean = - paramTypes.length <= 2 && cls.derivesFrom(FunctionClass(paramTypes.length)) + paramTypes.length <= 2 && cls.derivesFrom(FunctionSymbol(paramTypes.length)) && isSpecializableFunctionSAM(paramTypes, retType) /** If the Single Abstract Method of a Function class has this type, is it specializable? */ diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index cb590e2384a0..f2682621a7bd 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -314,8 +314,8 @@ object Flags { /** A Scala 2x super accessor / an unpickled Scala 2.x class */ val (SuperParamAliasOrScala2x @ _, SuperParamAlias @ _, Scala2x @ _) = newFlags(26, "", "") - /** A parameter with a default value */ - val (_, HasDefault @ _, _) = newFlags(27, "") + /** A parameter with a default value / an impure untpd.Function type */ + val (_, HasDefault @ _, Impure @ _) = newFlags(27, "", "<{*}>") /** An extension method, or a collective extension instance */ val (Extension @ _, ExtensionMethod @ _, _) = newFlags(28, "") diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index fb35ac0ac91f..d7c224d90821 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -197,20 +197,25 @@ object NameOps { else collectDigits(acc * 10 + d, idx + 1) collectDigits(0, suffixStart + 8) - /** name[0..suffixStart) == `str` */ - private def isPreceded(str: String, suffixStart: Int) = - str.length == suffixStart && name.firstPart.startsWith(str) + private def isFunctionPrefix(suffixStart: Int, mustHave: String = ""): Boolean = + suffixStart >= 0 + && { + val first = name.firstPart + var found = mustHave.isEmpty + def skip(idx: Int, str: String) = + if first.startsWith(str, idx) then + if str == mustHave then found = true + idx + str.length + else idx + skip(skip(skip(0, "Impure"), "Erased"), "Context") == suffixStart + && found + } /** Same as `funArity`, except that it returns -1 if the prefix * is not one of "", "Context", "Erased", "ErasedContext" */ private def checkedFunArity(suffixStart: Int): Int = - if suffixStart == 0 - || isPreceded("Context", suffixStart) - || isPreceded("Erased", suffixStart) - || isPreceded("ErasedContext", suffixStart) - then funArity(suffixStart) - else -1 + if isFunctionPrefix(suffixStart) then funArity(suffixStart) else -1 /** Is a function name, i.e one of FunctionXXL, FunctionN, ContextFunctionN, ErasedFunctionN, ErasedContextFunctionN for N >= 0 */ @@ -222,19 +227,14 @@ object NameOps { */ def isPlainFunction: Boolean = functionArity >= 0 - /** Is an context function name, i.e one of ContextFunctionN or ErasedContextFunctionN for N >= 0 - */ - def isContextFunction: Boolean = + /** Is a function name that contains `mustHave` as a substring */ + private def isSpecificFunction(mustHave: String): Boolean = val suffixStart = functionSuffixStart - (isPreceded("Context", suffixStart) || isPreceded("ErasedContext", suffixStart)) - && funArity(suffixStart) >= 0 + isFunctionPrefix(suffixStart, mustHave) && funArity(suffixStart) >= 0 - /** Is an erased function name, i.e. one of ErasedFunctionN, ErasedContextFunctionN for N >= 0 - */ - def isErasedFunction: Boolean = - val suffixStart = functionSuffixStart - (isPreceded("Erased", suffixStart) || isPreceded("ErasedContext", suffixStart)) - && funArity(suffixStart) >= 0 + def isContextFunction: Boolean = isSpecificFunction("Context") + def isErasedFunction: Boolean = isSpecificFunction("Erased") + def isImpureFunction: Boolean = isSpecificFunction("Impure") /** Is a synthetic function name, i.e. one of * - FunctionN for N > 22 diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 9ff15ae53537..a12f6c478c72 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -741,6 +741,8 @@ object StdNames { val XOR : N = "^" val ZAND : N = "&&" val ZOR : N = "||" + val PUREARROW: N = "->" + val PURECTXARROW: N = "?->" // unary operators val UNARY_PREFIX: N = "unary_" diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 5a11c9d2e30f..32f31c95d783 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -2413,7 +2413,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling case tp1: TypeVar if tp1.isInstantiated => tp1.underlying & tp2 case CapturingType(parent1, refs1, _) => - if subCaptures(tp2.captureSet, refs1, frozenConstraint).isOK then + if subCaptures(tp2.captureSet, refs1, frozen = true).isOK then parent1 & tp2 else tp1.derivedCapturingType(parent1 & tp2, refs1) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index b528bd2137ae..746a66e2344d 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -183,7 +183,7 @@ object Types { case _ => false } - /** Is this type a (possibly refined or applied or aliased) type reference + /** Is this type a (possibly refined, applied, aliased or annotated) type reference * to the given type symbol? * @sym The symbol to compare to. It must be a class symbol or abstract type. * It makes no sense for it to be an alias type because isRef would always @@ -204,9 +204,7 @@ object Types { case this1: TypeVar => this1.instanceOpt.isRef(sym, skipRefined) case this1: AnnotatedType => - this1 match - case CapturingType(_, _, _) => false - case _ => this1.parent.isRef(sym, skipRefined) + this1.parent.isRef(sym, skipRefined) case _ => false } diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index f2e3cb16bc45..875d135c38a8 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1363,47 +1363,56 @@ object Parsers { * | InfixType * | CaptureSet Type * FunType ::= (MonoFunType | PolyFunType) - * MonoFunType ::= FunTypeArgs (‘=>’ | ‘?=>’) Type - * PolyFunType ::= HKTypeParamClause '=>' Type + * MonoFunType ::= FunTypeArgs (‘=>’ | ‘?=>’ | ‘->’ | ‘?->’ ) Type + * PolyFunType ::= HKTypeParamClause ('=>' | ‘->’_) Type * FunTypeArgs ::= InfixType * | `(' [ [ ‘[using]’ ‘['erased'] FunArgType {`,' FunArgType } ] `)' * | '(' [ ‘[using]’ ‘['erased'] TypedFunParam {',' TypedFunParam } ')' * CaptureSet ::= `{` CaptureRef {`,` CaptureRef} `}` * CaptureRef ::= Ident */ - def typ(): Tree = { + def typ(): Tree = val start = in.offset var imods = Modifiers() def functionRest(params: List[Tree]): Tree = val paramSpan = Span(start, in.lastOffset) atSpan(start, in.offset) { - if in.token == TLARROW then + var token = in.token + if in.isIdent(nme.PUREARROW) then + token = ARROW + else if in.isIdent(nme.PURECTXARROW) then + token = CTXARROW + else if token == TLARROW then if !imods.flags.isEmpty || params.isEmpty then syntaxError(em"illegal parameter list for type lambda", start) - in.token = ARROW - else - for case ValDef(_, tpt: ByNameTypeTree, _) <- params do - syntaxError(em"parameter of type lambda may not be call-by-name", tpt.span) - in.nextToken() - return TermLambdaTypeTree(params.asInstanceOf[List[ValDef]], typ()) + token = ARROW + else if ctx.settings.Ycc.value then + // `=>` means impure function under -Ycc whereas `->` is a regular function. + // Without -Ycc they both mean regular function. + imods |= Impure - if in.token == CTXARROW then + if token == CTXARROW then in.nextToken() imods |= Given + else if token == ARROW || token == TLARROW then + in.nextToken() else accept(ARROW) - val t = typ() - if imods.isOneOf(Given | Erased) then + val resultType = typ() + if token == TLARROW then + for case ValDef(_, tpt: ByNameTypeTree, _) <- params do + syntaxError(em"parameter of type lambda may not be call-by-name", tpt.span) + TermLambdaTypeTree(params.asInstanceOf[List[ValDef]], resultType) + else if imods.isOneOf(Given | Erased | Impure) then if imods.is(Given) && params.isEmpty then syntaxError("context function types require at least one parameter", paramSpan) - new FunctionWithMods(params, t, imods) + FunctionWithMods(params, resultType, imods) else if !ctx.settings.YkindProjector.isDefault then - val (newParams :+ newT, tparams) = replaceKindProjectorPlaceholders(params :+ t) - - lambdaAbstract(tparams, Function(newParams, newT)) + val (newParams :+ newResultType, tparams) = replaceKindProjectorPlaceholders(params :+ resultType) + lambdaAbstract(tparams, Function(newParams, newResultType)) else - Function(params, t) + Function(params, resultType) } def funTypeArgsRest(first: Tree, following: () => Tree) = { val buf = new ListBuffer[Tree] += first @@ -1461,7 +1470,7 @@ object Parsers { val tparams = typeParamClause(ParamOwner.TypeParam) if (in.token == TLARROW) atSpan(start, in.skipToken())(LambdaTypeTree(tparams, toplevelTyp())) - else if (in.token == ARROW) { + else if (in.token == ARROW || in.isIdent(nme.PUREARROW)) { val arrowOffset = in.skipToken() val body = toplevelTyp() atSpan(start, arrowOffset) { @@ -1482,16 +1491,18 @@ object Parsers { else if (in.token == INDENT) enclosed(INDENT, typ()) else infixType() - in.token match { + in.token match case ARROW | CTXARROW => functionRest(t :: Nil) case MATCH => matchType(t) case FORSOME => syntaxError(ExistentialTypesNoLongerSupported()); t case _ => - if (imods.is(Erased) && !t.isInstanceOf[FunctionWithMods]) - syntaxError(ErasedTypesCanOnlyBeFunctionTypes(), implicitKwPos(start)) - t - } - } + if isIdent(nme.PUREARROW) || isIdent(nme.PURECTXARROW) then + functionRest(t :: Nil) + else + if (imods.is(Erased) && !t.isInstanceOf[FunctionWithMods]) + syntaxError(ErasedTypesCanOnlyBeFunctionTypes(), implicitKwPos(start)) + t + end typ private def makeKindProjectorTypeDef(name: TypeName): TypeDef = { val isVarianceAnnotated = name.startsWith("+") || name.startsWith("-") @@ -1549,7 +1560,7 @@ object Parsers { def infixTypeRest(t: Tree): Tree = infixOps(t, canStartInfixTypeTokens, refinedTypeFn, Location.ElseWhere, isType = true, - isOperator = !followingIsVararg()) + isOperator = !followingIsVararg() && !isIdent(nme.PUREARROW) && !isIdent(nme.PURECTXARROW)) /** RefinedType ::= WithType {[nl] Refinement} */ diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 054fe62682dd..f2b5047da944 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -15,7 +15,7 @@ import util.SourcePosition import scala.util.control.NonFatal import scala.annotation.switch import config.Config -import cc.{CapturingType, CaptureSet} +import cc.{EventuallyCapturingType, CaptureSet} class PlainPrinter(_ctx: Context) extends Printer { @@ -140,6 +140,9 @@ class PlainPrinter(_ctx: Context) extends Printer { + defn.ObjectClass + defn.FromJavaObjectSymbol + def toText(cs: CaptureSet): Text = + "{" ~ Text(cs.elems.toList.map(toTextCaptureRef), ", ") ~ "}" + def toText(tp: Type): Text = controlled { homogenize(tp) match { case tp: TypeType => @@ -189,7 +192,7 @@ class PlainPrinter(_ctx: Context) extends Printer { keywordStr(" match ") ~ "{" ~ casesText ~ "}" ~ (" <: " ~ toText(bound) provided !bound.isAny) }.close - case CapturingType(parent, refs, boxed) => + case EventuallyCapturingType(parent, refs, boxed) => def box = Str("box ") provided boxed if printDebug && !refs.isConst then changePrec(GlobalPrec)(box ~ s"$refs " ~ toText(parent)) @@ -198,11 +201,7 @@ class PlainPrinter(_ctx: Context) extends Printer { else if !refs.isConst && refs.elems.isEmpty then changePrec(GlobalPrec)("?" ~ " " ~ toText(parent)) else if Config.printCaptureSetsAsPrefix then - changePrec(GlobalPrec)( - box ~ "{" - ~ Text(refs.elems.toList.map(toTextCaptureRef), ", ") - ~ "} " - ~ toText(parent)) + changePrec(GlobalPrec)(box ~ toText(refs) ~ " " ~ toText(parent)) else changePrec(InfixPrec)(toText(parent) ~ " retains " ~ box ~ toText(refs.toRetainsTypeArg)) case tp: PreviousErrorType if ctx.settings.XprintTypes.value => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index c7606acf3dd6..0424a9b5f767 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -15,7 +15,7 @@ import Annotations.Annotation import Denotations._ import SymDenotations._ import StdNames.{nme, tpnme} -import ast.{Trees, untpd} +import ast.{Trees, tpd, untpd} import typer.{Implicits, Namer, Applications} import typer.ProtoTypes._ import Trees._ @@ -25,10 +25,12 @@ import NameKinds.{WildcardParamName, DefaultGetterName} import util.Chars.isOperatorPart import transform.TypeUtils._ import transform.SymUtils._ +import config.Config import language.implicitConversions import dotty.tools.dotc.util.{NameTransformer, SourcePosition} import dotty.tools.dotc.ast.untpd.{MemberDef, Modifiers, PackageDef, RefTree, Template, TypeDef, ValOrDefDef} +import cc.{EventuallyCapturingType, CaptureSet, toCaptureSet, IllegalCaptureRef} class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { @@ -136,14 +138,14 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { else simpleNameString(tsym) } - private def arrow(isGiven: Boolean): String = - if isGiven then "?=>" else "=>" + private def arrow(isGiven: Boolean, isPure: Boolean): String = + (if isGiven then "?" else "") + (if isPure then "->" else "=>") override def toText(tp: Type): Text = controlled { def toTextTuple(args: List[Type]): Text = "(" ~ argsText(args) ~ ")" - def toTextFunction(args: List[Type], isGiven: Boolean, isErased: Boolean): Text = + def toTextFunction(args: List[Type], isGiven: Boolean, isErased: Boolean, isPure: Boolean): Text = changePrec(GlobalPrec) { val argStr: Text = if args.length == 2 @@ -156,26 +158,26 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { ~ keywordText("erased ").provided(isErased) ~ argsText(args.init) ~ ")" - argStr ~ " " ~ arrow(isGiven) ~ " " ~ argText(args.last) + argStr ~ " " ~ arrow(isGiven, isPure) ~ " " ~ argText(args.last) } - def toTextMethodAsFunction(info: Type): Text = info match + def toTextMethodAsFunction(info: Type, isPure: Boolean): Text = info match case info: MethodType => changePrec(GlobalPrec) { "(" ~ keywordText("erased ").provided(info.isErasedMethod) ~ paramsText(info) ~ ") " - ~ arrow(info.isImplicitMethod) + ~ arrow(info.isImplicitMethod, isPure) ~ " " - ~ toTextMethodAsFunction(info.resultType) + ~ toTextMethodAsFunction(info.resultType, isPure) } case info: PolyType => changePrec(GlobalPrec) { "[" ~ paramsText(info) ~ "] => " - ~ toTextMethodAsFunction(info.resultType) + ~ toTextMethodAsFunction(info.resultType, isPure) } case _ => toText(info) @@ -214,9 +216,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { def appliedText(tp: Type): Text = tp match case tp @ AppliedType(tycon, args) => - val cls = tycon.typeSymbol + val tsym = tycon.typeSymbol if tycon.isRepeatedParam then toTextLocal(args.head) ~ "*" - else if defn.isFunctionClass(cls) then toTextFunction(args, cls.name.isContextFunction, cls.name.isErasedFunction) + else if defn.isFunctionSymbol(tsym) then + toTextFunction(args, tsym.name.isContextFunction, tsym.name.isErasedFunction, + isPure = ctx.settings.Ycc.value && !tsym.name.isImpureFunction) else if tp.tupleArity >= 2 && !printDebug then toTextTuple(tp.tupleElementTypes) else if isInfixType(tp) then val l :: r :: Nil = args @@ -243,7 +247,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { // don't eta contract if the application would be printed specially toText(tycon) case tp: RefinedType if defn.isFunctionOrPolyType(tp) && !printDebug => - toTextMethodAsFunction(tp.refinedInfo) + toTextMethodAsFunction(tp.refinedInfo, + isPure = ctx.settings.Ycc.value && !tp.typeSymbol.name.isImpureFunction) case tp: TypeRef => if (tp.symbol.isAnonymousClass && !showUniqueIds) toText(tp.info) @@ -259,7 +264,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case tp: ClassInfo => if tp.cls.derivesFrom(defn.PolyFunctionClass) then tp.member(nme.apply).info match - case info: PolyType => return toTextMethodAsFunction(info) + case info: PolyType => return toTextMethodAsFunction(info, isPure = false) case _ => toTextParents(tp.parents) ~~ "{...}" case JavaArrayType(elemtp) => @@ -527,7 +532,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { changePrec(OrTypePrec) { toText(args(0)) ~ " | " ~ atPrec(OrTypePrec + 1) { toText(args(1)) } } else if (tpt.symbol == defn.andType && args.length == 2) changePrec(AndTypePrec) { toText(args(0)) ~ " & " ~ atPrec(AndTypePrec + 1) { toText(args(1)) } } - else if defn.isFunctionClass(tpt.symbol) + else if defn.isFunctionSymbol(tpt.symbol) && tpt.isInstanceOf[TypeTree] && tree.hasType && !printDebug then changePrec(GlobalPrec) { toText(tree.typeOpt) } else args match @@ -602,7 +607,17 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case tree: Template => toTextTemplate(tree) case Annotated(arg, annot) => - toTextLocal(arg) ~~ annotText(annot.symbol.enclosingClass, annot) + def captureSet = + annot.asInstanceOf[tpd.Tree].toCaptureSet + def toTextAnnot = + toTextLocal(arg) ~~ annotText(annot.symbol.enclosingClass, annot) + def toTextRetainsAnnot = + try changePrec(GlobalPrec)(toText(captureSet) ~ " " ~ toText(arg)) + catch case ex: IllegalCaptureRef => toTextAnnot + if annot.symbol.maybeOwner == defn.RetainsAnnot + && ctx.settings.Ycc.value && Config.printCaptureSetsAsPrefix && !printDebug + then toTextRetainsAnnot + else toTextAnnot case EmptyTree => "" case TypedSplice(t) => @@ -645,7 +660,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { ~ Text(args.map(argToText), ", ") ~ ")" } - argsText ~ " " ~ arrow(isGiven) ~ " " ~ toText(body) + val isPure = + ctx.settings.Ycc.value + && tree.match + case tree: FunctionWithMods => !tree.mods.is(Impure) + case _ => true + argsText ~ " " ~ arrow(isGiven, isPure) ~ " " ~ toText(body) case PolyFunction(targs, body) => val targsText = "[" ~ Text(targs.map((arg: Tree) => toText(arg)), ", ") ~ "]" changePrec(GlobalPrec) { diff --git a/compiler/src/dotty/tools/dotc/typer/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/typer/CheckCaptures.scala index 1f30dd989f3a..e78776cb0158 100644 --- a/compiler/src/dotty/tools/dotc/typer/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/typer/CheckCaptures.scala @@ -117,82 +117,150 @@ class CheckCaptures extends Recheck: override def transformType(tp: Type, inferred: Boolean, boxed: Boolean)(using Context): Type = - def addInnerVars(tp: Type): Type = tp match - case tp @ AppliedType(tycon, args) => - tp.derivedAppliedType(tycon, args.map(addVars(_, boxed = true))) - case tp @ RefinedType(core, rname, rinfo) => - val rinfo1 = addVars(rinfo) - if defn.isFunctionType(tp) then - rinfo1.toFunctionType(isJava = false, alwaysDependent = true) - else - tp.derivedRefinedType(addInnerVars(core), rname, rinfo1) - case tp: MethodType => - tp.derivedLambdaType( - paramInfos = tp.paramInfos.mapConserve(addVars(_)), - resType = addVars(tp.resType)) - case tp: PolyType => - tp.derivedLambdaType( - resType = addVars(tp.resType)) - case tp: ExprType => - tp.derivedExprType(resType = addVars(tp.resType)) - case _ => - tp - - def addFunctionRefinements(tp: Type): Type = tp match - case tp @ AppliedType(tycon, args) => - if defn.isNonRefinedFunction(tp) then - MethodType.companion( - isContextual = defn.isContextFunctionClass(tycon.classSymbol), - isErased = defn.isErasedFunctionClass(tycon.classSymbol) - )(args.init, addFunctionRefinements(args.last)) - .toFunctionType(isJava = false, alwaysDependent = true) - .showing(i"add function refinement $tp --> $result", capt) - else - tp.derivedAppliedType(tycon, args.map(addFunctionRefinements(_))) - case tp @ RefinedType(core, rname, rinfo) if !defn.isFunctionType(tp) => - tp.derivedRefinedType( - addFunctionRefinements(core), rname, addFunctionRefinements(rinfo)) - case tp: MethodOrPoly => - tp.derivedLambdaType(resType = addFunctionRefinements(tp.resType)) - case tp: ExprType => - tp.derivedExprType(resType = addFunctionRefinements(tp.resType)) - case _ => - tp - - /** Refine a possibly applied class type C where the class has tracked parameters - * x_1: T_1, ..., x_n: T_n to C { val x_1: CV_1 T_1, ..., val x_n: CV_n T_n } - * where CV_1, ..., CV_n are fresh capture sets. + def depFun(tycon: Type, argTypes: List[Type], resType: Type): Type = + MethodType.companion( + isContextual = defn.isContextFunctionClass(tycon.classSymbol), + isErased = defn.isErasedFunctionClass(tycon.classSymbol) + )(argTypes, resType) + .toFunctionType(isJava = false, alwaysDependent = true) + + def box(tp: Type): Type = tp match + case CapturingType(parent, refs, false) => CapturingType(parent, refs, true) + case _ => tp + + /** Perform the following transformation steps everywhere in a type: + * 1. Drop retains annotations + * 2. Turn plain function types into dependent function types, so that + * we can refer to their parameter in capture sets. Currently this is + * only done at the toplevel, i.e. for function types that are not + * themselves argument types of other function types. Without this restriction + * boxmap-paper.scala fails. Need to figure out why. + * 3. Refine other class types C by adding capture set variables to their parameter getters + * (see addCaptureRefinements) + * 4. Add capture set variables to all types that can be tracked + * + * Polytype bounds are only cleaned using step 1, but not otherwise transformed. */ - def addCaptureRefinements(tp: Type): Type = tp.stripped match - case _: TypeRef | _: AppliedType if tp.typeSymbol.isClass => - val cls = tp.typeSymbol.asClass - cls.paramGetters.foldLeft(tp) { (core, getter) => - if getter.termRef.isTracked then - val getterType = tp.memberInfo(getter).strippedDealias - RefinedType(core, getter.name, CapturingType(getterType, CaptureSet.Var(), boxed = false)) - .showing(i"add capture refinement $tp --> $result", capt) - else - core - } - case _ => - tp + def mapInferred = new TypeMap: - def addVars(tp: Type, boxed: Boolean = false): Type = - var tp1 = addInnerVars(tp) - val tp2 = addCaptureRefinements(tp1) - if tp1.canHaveInferredCapture - then CapturingType(tp2, CaptureSet.Var(), boxed) - else tp2 - - if inferred then - val cleanup = new TypeMap: + /** Drop @retains annotations everywhere */ + object cleanup extends TypeMap: def apply(t: Type) = t match case AnnotatedType(parent, annot) if annot.symbol == defn.RetainsAnnot => apply(parent) case _ => mapOver(t) - addVars(addFunctionRefinements(cleanup(tp)), boxed) - .showing(i"reinfer $tp --> $result", capt) + + /** Refine a possibly applied class type C where the class has tracked parameters + * x_1: T_1, ..., x_n: T_n to C { val x_1: CV_1 T_1, ..., val x_n: CV_n T_n } + * where CV_1, ..., CV_n are fresh capture sets. + */ + def addCaptureRefinements(tp: Type): Type = tp match + case _: TypeRef | _: AppliedType if tp.typeParams.isEmpty => + tp.typeSymbol match + case cls: ClassSymbol if !defn.isFunctionClass(cls) => + cls.paramGetters.foldLeft(tp) { (core, getter) => + if getter.termRef.isTracked then + val getterType = tp.memberInfo(getter).strippedDealias + RefinedType(core, getter.name, CapturingType(getterType, CaptureSet.Var(), boxed = false)) + .showing(i"add capture refinement $tp --> $result", capt) + else + core + } + case _ => tp + case _ => tp + + /** Should a capture set variable be added on type `tp`? */ + def canHaveInferredCapture(tp: Type): Boolean = + tp.typeParams.isEmpty && tp.match + case tp: (TypeRef | AppliedType) => + val sym = tp.typeSymbol + if sym.isClass then !sym.isValueClass && sym != defn.AnyClass + else canHaveInferredCapture(tp.superType.dealias) + case tp: (RefinedOrRecType | MatchType) => + canHaveInferredCapture(tp.underlying) + case tp: AndType => + canHaveInferredCapture(tp.tp1) && canHaveInferredCapture(tp.tp2) + case tp: OrType => + canHaveInferredCapture(tp.tp1) || canHaveInferredCapture(tp.tp2) + case _ => + false + + /** Add a capture set variable to `tp` if necessary, or maybe pull out + * an embedded capture set variables from a part of `tp`. + */ + def addVar(tp: Type) = tp match + case tp @ RefinedType(parent @ CapturingType(parent1, refs, boxed), rname, rinfo) => + CapturingType(tp.derivedRefinedType(parent1, rname, rinfo), refs, boxed) + case tp: RecType => + tp.parent match + case CapturingType(parent1, refs, boxed) => + CapturingType(tp.derivedRecType(parent1), refs, boxed) + case _ => + tp // can return `tp` here since unlike RefinedTypes, RecTypes are never created + // by `mapInferred`. Hence if the underlying type admits capture variables + // a variable was already added, and the first case above would apply. + case AndType(CapturingType(parent1, refs1, boxed1), CapturingType(parent2, refs2, boxed2)) => + assert(refs1.asVar.elems.isEmpty) + assert(refs2.asVar.elems.isEmpty) + assert(boxed1 == boxed2) + CapturingType(AndType(parent1, parent2), refs1, boxed1) + case tp @ OrType(CapturingType(parent1, refs1, boxed1), CapturingType(parent2, refs2, boxed2)) => + assert(refs1.asVar.elems.isEmpty) + assert(refs2.asVar.elems.isEmpty) + assert(boxed1 == boxed2) + CapturingType(OrType(parent1, parent2, tp.isSoft), refs1, boxed1) + case tp @ OrType(CapturingType(parent1, refs1, boxed1), tp2) => + CapturingType(OrType(parent1, tp2, tp.isSoft), refs1, boxed1) + case tp @ OrType(tp1, CapturingType(parent2, refs2, boxed2)) => + CapturingType(OrType(tp1, parent2, tp.isSoft), refs2, boxed2) + case _ if canHaveInferredCapture(tp) => + CapturingType(tp, CaptureSet.Var(), boxed = false) + case _ => + tp + + var isTopLevel = true + + def mapNested(ts: List[Type]): List[Type] = + val saved = isTopLevel + isTopLevel = false + try ts.mapConserve(this) finally isTopLevel = saved + + def apply(t: Type) = + val t1 = t match + case AnnotatedType(parent, annot) if annot.symbol == defn.RetainsAnnot => + apply(parent) + case tp @ AppliedType(tycon, args) => + val tycon1 = this(tycon) + if defn.isNonRefinedFunction(tp) then + val args1 = mapNested(args.init) + val res1 = this(args.last) + if isTopLevel then + depFun(tycon1, args1, res1) + .showing(i"add function refinement $tp --> $result", capt) + else + tp.derivedAppliedType(tycon1, args1 :+ res1) + else + tp.derivedAppliedType(tycon1, args.mapConserve(arg => box(this(arg)))) + case tp @ RefinedType(core, rname, rinfo) if defn.isFunctionType(tp) => + apply(rinfo).toFunctionType(isJava = false, alwaysDependent = true) + case tp: MethodType => + tp.derivedLambdaType( + paramInfos = mapNested(tp.paramInfos), + resType = this(tp.resType)) + case tp: TypeLambda => + // Don't recurse into parameter bounds, just cleanup any stray retains annotations + tp.derivedLambdaType( + paramInfos = tp.paramInfos.mapConserve(cleanup(_).bounds), + resType = this(tp.resType)) + case _ => + mapOver(t) + addVar(addCaptureRefinements(t1)) + end mapInferred + + if inferred then + val tp1 = mapInferred(tp) + if boxed then box(tp1) else tp1 else def setBoxed(t: Type) = t match case AnnotatedType(_, annot) if annot.symbol == defn.RetainsAnnot => diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index fecdf3eb6cef..0fc0f852d471 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -815,7 +815,7 @@ trait Implicits: def isOldStyleFunctionConversion(tpe: Type): Boolean = tpe match { case PolyType(_, resType) => isOldStyleFunctionConversion(resType) - case _ => tpe.derivesFrom(defn.FunctionClass(1)) && !tpe.derivesFrom(defn.ConversionClass) && !tpe.derivesFrom(defn.SubTypeClass) + case _ => tpe.derivesFrom(defn.FunctionSymbol(1)) && !tpe.derivesFrom(defn.ConversionClass) && !tpe.derivesFrom(defn.SubTypeClass) } try diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 86db24e14c4c..42889b23bb04 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1238,7 +1238,8 @@ class Typer extends Namer val numArgs = args.length val isContextual = funFlags.is(Given) val isErased = funFlags.is(Erased) - val funCls = defn.FunctionClass(numArgs, isContextual, isErased) + val isImpure = funFlags.is(Impure) + val funSym = defn.FunctionSymbol(numArgs, isContextual, isErased, isImpure) /** If `app` is a function type with arguments that are all erased classes, * turn it into an erased function type. @@ -1248,7 +1249,7 @@ class Typer extends Namer if !isErased && numArgs > 0 && args.indexWhere(!_.tpe.isErasedClass) == numArgs => - val tycon1 = TypeTree(defn.FunctionClass(numArgs, isContextual, isErased = true).typeRef) + val tycon1 = TypeTree(defn.FunctionSymbol(numArgs, isContextual, true, isImpure).typeRef) .withSpan(tycon.span) assignType(cpy.AppliedTypeTree(app)(tycon1, args), tycon1, args) case _ => @@ -1275,7 +1276,7 @@ class Typer extends Namer report.error(i"$mt is an illegal function type because it has inter-parameter dependencies", tree.srcPos) val resTpt = TypeTree(mt.nonDependentResultApprox).withSpan(body.span) val typeArgs = appDef.termParamss.head.map(_.tpt) :+ resTpt - val tycon = TypeTree(funCls.typeRef) + val tycon = TypeTree(funSym.typeRef) val core = propagateErased(AppliedTypeTree(tycon, typeArgs)) RefinedTypeTree(core, List(appDef), ctx.owner.asClass) end typedDependent @@ -1286,7 +1287,7 @@ class Typer extends Namer using ctx.fresh.setOwner(newRefinedClassSymbol(tree.span)).setNewScope) case _ => propagateErased( - typed(cpy.AppliedTypeTree(tree)(untpd.TypeTree(funCls.typeRef), args :+ body), pt)) + typed(cpy.AppliedTypeTree(tree)(untpd.TypeTree(funSym.typeRef), args :+ body), pt)) } } diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index d6e13fbbd168..a1c2367fba9b 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2692,7 +2692,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def SomeModule: Symbol = dotc.core.Symbols.defn.SomeClass.companionModule def ProductClass: Symbol = dotc.core.Symbols.defn.ProductClass def FunctionClass(arity: Int, isImplicit: Boolean = false, isErased: Boolean = false): Symbol = - dotc.core.Symbols.defn.FunctionClass(arity, isImplicit, isErased) + dotc.core.Symbols.defn.FunctionSymbol(arity, isImplicit, isErased) def TupleClass(arity: Int): Symbol = dotc.core.Symbols.defn.TupleType(arity).classSymbol.asClass def isTupleClass(sym: Symbol): Boolean = diff --git a/tests/neg-custom-args/capt-wf.scala b/tests/neg-custom-args/capt-wf.scala index dc4d6a0d4bff..3bd80e0d0f68 100644 --- a/tests/neg-custom-args/capt-wf.scala +++ b/tests/neg-custom-args/capt-wf.scala @@ -8,7 +8,7 @@ def test(c: Cap, other: String): Unit = val x2: {other} C = ??? // error: cs is empty val s1 = () => "abc" val x3: {s1} C = ??? // error: cs is empty - val x3a: () => String = s1 + val x3a: () -> String = s1 val s2 = () => if x1 == null then "" else "abc" val x4: {s2} C = ??? // OK val x5: {c, c} C = ??? // error: redundant @@ -26,8 +26,8 @@ def test(c: Cap, other: String): Unit = val y1: {e1} String = ??? // error cs is empty val y2: {o1} String = ??? // error cs is empty - lazy val ev: (Int => Boolean) = (n: Int) => - lazy val od: (Int => Boolean) = (n: Int) => + lazy val ev: (Int -> Boolean) = (n: Int) => + lazy val od: (Int -> Boolean) = (n: Int) => if n == 1 then true else ev(n - 1) if n == 0 then true else od(n - 1) val y3: {ev} String = ??? // error cs is empty diff --git a/tests/neg-custom-args/captures/bounded.scala b/tests/neg-custom-args/captures/bounded.scala index dc2621e95a65..fb6b198fb3a3 100644 --- a/tests/neg-custom-args/captures/bounded.scala +++ b/tests/neg-custom-args/captures/bounded.scala @@ -9,6 +9,6 @@ def test(c: Cap) = def f(x: Int): Int = if c == c then x else 0 val b = new B(f) val r1 = b.elem - val r1c: {c} Int => Int = r1 + val r1c: {c} Int -> Int = r1 val r2 = b.lateElem - val r2c: () => {c} Int => Int = r2 // error \ No newline at end of file + val r2c: () -> {c} Int -> Int = r2 // error \ No newline at end of file diff --git a/tests/neg-custom-args/captures/boxmap.check b/tests/neg-custom-args/captures/boxmap.check index 406077077af5..b3d6605989bf 100644 --- a/tests/neg-custom-args/captures/boxmap.check +++ b/tests/neg-custom-args/captures/boxmap.check @@ -1,7 +1,7 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/boxmap.scala:14:2 ---------------------------------------- -14 | () => b[Box[B]]((x: A) => box(f(x))) // error +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/boxmap.scala:12:2 ---------------------------------------- +12 | () => b[Box[B]]((x: A) => box(f(x))) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: {f} () => ? Box[B] - | Required: () => Box[B] + | Found: {f} () -> ? Box[B] + | Required: () -> Box[B] longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/boxmap.scala b/tests/neg-custom-args/captures/boxmap.scala index e335320ef9d4..114aaccb6bb5 100644 --- a/tests/neg-custom-args/captures/boxmap.scala +++ b/tests/neg-custom-args/captures/boxmap.scala @@ -1,14 +1,12 @@ type Top = Any @retains(*) -infix type ==> [A, B] = (A => B) @retains(*) - -type Box[+T <: Top] = ([K <: Top] => (T ==> K) => K) +type Box[+T <: Top] = ([K <: Top] -> (T => K) -> K) def box[T <: Top](x: T): Box[T] = - [K <: Top] => (k: T ==> K) => k(x) + [K <: Top] => (k: T => K) => k(x) -def map[A <: Top, B <: Top](b: Box[A])(f: A ==> B): Box[B] = +def map[A <: Top, B <: Top](b: Box[A])(f: A => B): Box[B] = b[Box[B]]((x: A) => box(f(x))) -def lazymap[A <: Top, B <: Top](b: Box[A])(f: A ==> B): () => Box[B] = +def lazymap[A <: Top, B <: Top](b: Box[A])(f: A => B): () -> Box[B] = () => b[Box[B]]((x: A) => box(f(x))) // error diff --git a/tests/neg-custom-args/captures/byname.scala b/tests/neg-custom-args/captures/byname.scala index ef5876be2c11..feb9461dc4c7 100644 --- a/tests/neg-custom-args/captures/byname.scala +++ b/tests/neg-custom-args/captures/byname.scala @@ -3,7 +3,7 @@ def test(cap1: Cap, cap2: Cap) = def f() = if cap1 == cap1 then g else g def g(x: Int) = if cap2 == cap2 then 1 else x - def h(ff: => {cap2} Int => Int) = ff + def h(ff: => {cap2} Int -> Int) = ff h(f()) // error diff --git a/tests/neg-custom-args/captures/capt-box-env.scala b/tests/neg-custom-args/captures/capt-box-env.scala index e9743054076e..605b446d5262 100644 --- a/tests/neg-custom-args/captures/capt-box-env.scala +++ b/tests/neg-custom-args/captures/capt-box-env.scala @@ -1,5 +1,4 @@ -class C -type Cap = {*} C +@annotation.capability class Cap class Pair[+A, +B](x: A, y: B): def fst: A = x @@ -9,4 +8,4 @@ def test(c: Cap) = def f(x: Cap): Unit = if c == x then () val p = Pair(f, f) val g = () => p.fst == p.snd - val gc: () => Boolean = g // error + val gc: () -> Boolean = g // error diff --git a/tests/neg-custom-args/captures/capt-box.scala b/tests/neg-custom-args/captures/capt-box.scala index 317fc064ec0b..634470704fc5 100644 --- a/tests/neg-custom-args/captures/capt-box.scala +++ b/tests/neg-custom-args/captures/capt-box.scala @@ -1,6 +1,4 @@ -//import scala.retains -class C -type Cap = {*} C +@annotation.capability class Cap def test(x: Cap) = @@ -10,4 +8,4 @@ def test(x: Cap) = val x2 = identity(x1) - val x3: Cap => Unit = x2 // error \ No newline at end of file + val x3: Cap -> Unit = x2 // error \ No newline at end of file diff --git a/tests/neg-custom-args/captures/capt-env.scala b/tests/neg-custom-args/captures/capt-env.scala index 84b4b57a7930..52fa4abfdaa8 100644 --- a/tests/neg-custom-args/captures/capt-env.scala +++ b/tests/neg-custom-args/captures/capt-env.scala @@ -9,5 +9,5 @@ def test(c: Cap) = def f(x: Cap): Unit = if c == x then () val p = Pair(f, f) val g = () => p.fst == p.snd - val gc: () => Boolean = g // error + val gc: () -> Boolean = g // error diff --git a/tests/neg-custom-args/captures/capt1.check b/tests/neg-custom-args/captures/capt1.check index ce7c4833bf9c..0b99f1bac09e 100644 --- a/tests/neg-custom-args/captures/capt1.check +++ b/tests/neg-custom-args/captures/capt1.check @@ -1,21 +1,21 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:3:2 ------------------------------------------ 3 | () => if x == null then y else y // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: {x} () => ? C - | Required: () => C + | Found: {x} () -> ? C + | Required: () -> C longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:6:2 ------------------------------------------ 6 | () => if x == null then y else y // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: {x} () => ? C + | Found: {x} () -> ? C | Required: Matchable longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:13:2 ----------------------------------------- 13 | def f(y: Int) = if x == null then y else y // error | ^ - | Found: {x} Int => Int + | Found: {x} Int -> Int | Required: Matchable 14 | f @@ -38,9 +38,9 @@ longer explanation available when compiling with `-explain` longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:31:24 ---------------------------------------- -31 | val z2 = h[() => Cap](() => x)(() => C()) // error +31 | val z2 = h[() -> Cap](() => x)(() => C()) // error | ^^^^^^^ - | Found: {x} () => ? Cap - | Required: () => Cap + | Found: {x} () -> Cap + | Required: () -> Cap longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/capt1.scala b/tests/neg-custom-args/captures/capt1.scala index 4da49c5f4f1e..e230defda170 100644 --- a/tests/neg-custom-args/captures/capt1.scala +++ b/tests/neg-custom-args/captures/capt1.scala @@ -1,5 +1,5 @@ class C -def f(x: C @retains(*), y: C): () => C = +def f(x: C @retains(*), y: C): () -> C = () => if x == null then y else y // error def g(x: C @retains(*), y: C): Matchable = @@ -28,7 +28,7 @@ def h4(x: Cap, y: Int): A = def foo() = val x: C @retains(*) = ??? def h[X](a: X)(b: X) = a - val z2 = h[() => Cap](() => x)(() => C()) // error - val z3 = h[(() => Cap) @retains(x)](() => x)(() => C()) // ok - val z4 = h[(() => Cap) @retains(x)](() => x)(() => C()) // what was inferred for z3 + val z2 = h[() -> Cap](() => x)(() => C()) // error + val z3 = h[(() -> Cap) @retains(x)](() => x)(() => C()) // ok + val z4 = h[(() -> Cap) @retains(x)](() => x)(() => C()) // what was inferred for z3 diff --git a/tests/neg-custom-args/captures/capt2.scala b/tests/neg-custom-args/captures/capt2.scala index 1eee53463f6d..8b08832dfdb9 100644 --- a/tests/neg-custom-args/captures/capt2.scala +++ b/tests/neg-custom-args/captures/capt2.scala @@ -2,8 +2,8 @@ class C type Cap = {*} C -def f1(c: Cap): (() => {c} C) = () => c // error, but would be OK under capture abbreciations for funciton types -def f2(c: Cap): ({c} () => C) = () => c // error +def f1(c: Cap): (() -> {c} C) = () => c // error, but would be OK under capture abbreciations for funciton types +def f2(c: Cap): ({c} () -> C) = () => c // error -def h5(x: Cap): () => C = +def h5(x: Cap): () -> C = f1(x) // error diff --git a/tests/neg-custom-args/captures/capt3.scala b/tests/neg-custom-args/captures/capt3.scala index 80b937276f73..6e9ea02fe8e3 100644 --- a/tests/neg-custom-args/captures/capt3.scala +++ b/tests/neg-custom-args/captures/capt3.scala @@ -5,22 +5,22 @@ def test1() = val x: Cap = C() val y = () => { x; () } val z = y - z: (() => Unit) // error + z: (() -> Unit) // error def test2() = val x: Cap = C() def y = () => { x; () } def z = y - z: (() => Unit) // error + z: (() -> Unit) // error def test3() = val x: Cap = C() def y = () => { x; () } val z = y - z: (() => Unit) // error + z: (() -> Unit) // error def test4() = val x: Cap = C() val y = () => { x; () } def z = y - z: (() => Unit) // error + z: (() -> Unit) // error diff --git a/tests/neg-custom-args/captures/io.scala b/tests/neg-custom-args/captures/io.scala index 17c22a2111e4..c0cb11686b32 100644 --- a/tests/neg-custom-args/captures/io.scala +++ b/tests/neg-custom-args/captures/io.scala @@ -4,13 +4,13 @@ sealed trait IO: def test1 = val IO : IO @retains(*) = new IO {} def foo = {IO; IO.puts("hello") } - val x : () => Unit = () => foo // error: Found: (() => Unit) retains IO; Required: () => Unit + val x : () -> Unit = () => foo // error: Found: (() -> Unit) retains IO; Required: () -> Unit def test2 = val IO : IO @retains(*) = new IO {} def puts(msg: Any, io: IO @retains(*)) = println(msg) def foo() = puts("hello", IO) - val x : () => Unit = () => foo() // error: Found: (() => Unit) retains IO; Required: () => Unit + val x : () -> Unit = () => foo() // error: Found: (() -> Unit) retains IO; Required: () -> Unit type Capability[T] = T @retains(*) @@ -18,5 +18,5 @@ def test3 = val IO : Capability[IO] = new IO {} def puts(msg: Any, io: Capability[IO]) = println(msg) def foo() = puts("hello", IO) - val x : () => Unit = () => foo() // error: Found: (() => Unit) retains IO; Required: () => Unit + val x : () -> Unit = () => foo() // error: Found: (() -> Unit) retains IO; Required: () -> Unit diff --git a/tests/neg-custom-args/captures/lazylist.check b/tests/neg-custom-args/captures/lazylist.check index 3a80de9bdf16..0de190df8f11 100644 --- a/tests/neg-custom-args/captures/lazylist.check +++ b/tests/neg-custom-args/captures/lazylist.check @@ -8,7 +8,7 @@ longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylist.scala:35:29 ------------------------------------- 35 | val ref1c: LazyList[Int] = ref1 // error | ^^^^ - | Found: (ref1 : {cap1} lazylists.LazyCons[Int]{xs: {cap1} () => {*} lazylists.LazyList[Int]}) + | Found: (ref1 : {cap1} lazylists.LazyCons[Int]{xs: {cap1} () -> {*} lazylists.LazyList[Int]}) | Required: lazylists.LazyList[Int] longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/lazylist.scala b/tests/neg-custom-args/captures/lazylist.scala index f7be43e8dc27..56bfc3ea6da2 100644 --- a/tests/neg-custom-args/captures/lazylist.scala +++ b/tests/neg-custom-args/captures/lazylist.scala @@ -7,11 +7,11 @@ abstract class LazyList[+T]: def head: T def tail: LazyList[T] - def map[U](f: {*} T => U): {f, this} LazyList[U] = + def map[U](f: T => U): {f, this} LazyList[U] = if isEmpty then LazyNil else LazyCons(f(head), () => tail.map(f)) -class LazyCons[+T](val x: T, val xs: {*} () => {*} LazyList[T]) extends LazyList[T]: +class LazyCons[+T](val x: T, val xs: () => {*} LazyList[T]) extends LazyList[T]: def isEmpty = false def head = x def tail = xs() // error: cannot have an inferred type @@ -21,7 +21,7 @@ object LazyNil extends LazyList[Nothing]: def head = ??? def tail: {*} LazyList[Nothing] = ??? // error overriding -def map[A, B](xs: {*} LazyList[A], f: {*} A => B): {f, xs} LazyList[B] = +def map[A, B](xs: {*} LazyList[A], f: A => B): {f, xs} LazyList[B] = xs.map(f) class CC diff --git a/tests/neg-custom-args/captures/lazylists1.scala b/tests/neg-custom-args/captures/lazylists1.scala index 02c7cb4ff3e5..4091ee2c62ae 100644 --- a/tests/neg-custom-args/captures/lazylists1.scala +++ b/tests/neg-custom-args/captures/lazylists1.scala @@ -14,7 +14,7 @@ object LazyNil extends LazyList[Nothing]: def tail = ??? extension [A](xs: {*} LazyList[A]) - def map[B](f: {*} A => B): {xs, f} LazyList[B] = + def map[B](f: A => B): {xs, f} LazyList[B] = final class Mapped extends LazyList[B]: this: ({xs, f} Mapped) => diff --git a/tests/neg-custom-args/captures/lazylists2.scala b/tests/neg-custom-args/captures/lazylists2.scala index c31a1ae5d04f..b9ebb0a7a9f0 100644 --- a/tests/neg-custom-args/captures/lazylists2.scala +++ b/tests/neg-custom-args/captures/lazylists2.scala @@ -14,7 +14,7 @@ object LazyNil extends LazyList[Nothing]: def tail = ??? extension [A](xs: {*} LazyList[A]) - def map[B](f: {*} A => B): {f} LazyList[B] = + def map[B](f: A => B): {f} LazyList[B] = final class Mapped extends LazyList[B]: // error this: ({xs, f} Mapped) => @@ -23,7 +23,7 @@ extension [A](xs: {*} LazyList[A]) def tail: {this} LazyList[B] = xs.tail.map(f) new Mapped - def map2[B](f: {*} A => B): {xs} LazyList[B] = + def map2[B](f: A => B): {xs} LazyList[B] = final class Mapped extends LazyList[B]: // error this: ({xs, f} Mapped) => @@ -32,7 +32,7 @@ extension [A](xs: {*} LazyList[A]) def tail: {this} LazyList[B] = xs.tail.map(f) new Mapped - def map3[B](f: {*} A => B): {xs} LazyList[B] = + def map3[B](f: A => B): {xs} LazyList[B] = final class Mapped extends LazyList[B]: this: ({xs} Mapped) => @@ -41,7 +41,7 @@ extension [A](xs: {*} LazyList[A]) def tail: {this} LazyList[B] = xs.tail.map(f) // error new Mapped - def map4[B](f: {*} A => B): {xs} LazyList[B] = + def map4[B](f: A => B): {xs} LazyList[B] = final class Mapped extends LazyList[B]: this: ({xs, f} Mapped) => @@ -50,7 +50,7 @@ extension [A](xs: {*} LazyList[A]) def tail: {xs, f} LazyList[B] = xs.tail.map(f) // error new Mapped - def map5[B](f: {*} A => B): LazyList[B] = + def map5[B](f: A => B): LazyList[B] = class Mapped extends LazyList[B]: this: ({xs, f} Mapped) => diff --git a/tests/neg-custom-args/captures/lazyref.check b/tests/neg-custom-args/captures/lazyref.check index 2affed020dec..e4e06f8c52cb 100644 --- a/tests/neg-custom-args/captures/lazyref.check +++ b/tests/neg-custom-args/captures/lazyref.check @@ -1,28 +1,28 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:19:28 -------------------------------------- 19 | val ref1c: LazyRef[Int] = ref1 // error | ^^^^ - | Found: (ref1 : {cap1} LazyRef[Int]{elem: {cap1} () => Int}) + | Found: (ref1 : {cap1} LazyRef[Int]{elem: {cap1} () -> Int}) | Required: LazyRef[Int] longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:21:35 -------------------------------------- 21 | val ref2c: {cap2} LazyRef[Int] = ref2 // error | ^^^^ - | Found: (ref2 : {cap2, ref1} LazyRef[Int]{elem: {*} () => Int}) + | Found: (ref2 : {cap2, ref1} LazyRef[Int]{elem: {*} () -> Int}) | Required: {cap2} LazyRef[Int] longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:23:35 -------------------------------------- 23 | val ref3c: {ref1} LazyRef[Int] = ref3 // error | ^^^^ - | Found: (ref3 : {cap2, ref1} LazyRef[Int]{elem: {*} () => Int}) + | Found: (ref3 : {cap2, ref1} LazyRef[Int]{elem: {*} () -> Int}) | Required: {ref1} LazyRef[Int] longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:25:35 -------------------------------------- 25 | val ref4c: {cap1} LazyRef[Int] = ref4 // error | ^^^^ - | Found: (ref4 : {cap2, cap1} LazyRef[Int]{elem: {*} () => Int}) + | Found: (ref4 : {cap2, cap1} LazyRef[Int]{elem: {*} () -> Int}) | Required: {cap1} LazyRef[Int] longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/lazyref.scala b/tests/neg-custom-args/captures/lazyref.scala index 1002f9685675..2b278fd51a43 100644 --- a/tests/neg-custom-args/captures/lazyref.scala +++ b/tests/neg-custom-args/captures/lazyref.scala @@ -1,15 +1,15 @@ class CC type Cap = {*} CC -class LazyRef[T](val elem: {*} () => T): +class LazyRef[T](val elem: () => T): val get = elem - def map[U](f: {*} T => U): {f, this} LazyRef[U] = + def map[U](f: T => U): {f, this} LazyRef[U] = new LazyRef(() => f(elem())) -def map[A, B](ref: {*} LazyRef[A], f: {*} A => B): {f, ref} LazyRef[B] = +def map[A, B](ref: {*} LazyRef[A], f: A => B): {f, ref} LazyRef[B] = new LazyRef(() => f(ref.elem())) -def mapc[A, B]: (ref: {*} LazyRef[A], f: {*} A => B) => {f, ref} LazyRef[B] = +def mapc[A, B]: (ref: {*} LazyRef[A], f: A => B) -> {f, ref} LazyRef[B] = (ref1, f1) => map[A, B](ref1, f1) def test(cap1: Cap, cap2: Cap) = diff --git a/tests/neg-custom-args/captures/try.check b/tests/neg-custom-args/captures/try.check index bd95835c6525..a2fe96016b80 100644 --- a/tests/neg-custom-args/captures/try.check +++ b/tests/neg-custom-args/captures/try.check @@ -1,8 +1,8 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:28:43 ------------------------------------------ -28 | val b = handle[Exception, () => Nothing] { // error +28 | val b = handle[Exception, () -> Nothing] { // error | ^ - | Found: ? (x: CanThrow[Exception]) => {x} () => ? Nothing - | Required: CanThrow[Exception] => () => Nothing + | Found: ? (x: CanThrow[Exception]) -> {x} () -> ? Nothing + | Required: CanThrow[Exception] => () -> Nothing 29 | (x: CanThrow[Exception]) => () => raise(new Exception)(using x) 30 | } { @@ -14,12 +14,12 @@ longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/try.scala:34:11 --------------------------------------------------------------- 34 | val xx = handle { // error | ^^^^^^ - | inferred type argument {*} () => Int is not allowed to capture the universal capability (* : Any) + | inferred type argument {*} () -> Int is not allowed to capture the universal capability (* : Any) | - | The inferred arguments are: [? Exception, {*} () => Int] + | The inferred arguments are: [? Exception, {*} () -> Int] -- Error: tests/neg-custom-args/captures/try.scala:46:13 --------------------------------------------------------------- 46 |val global = handle { // error | ^^^^^^ - | inferred type argument {*} () => Int is not allowed to capture the universal capability (* : Any) + | inferred type argument {*} () -> Int is not allowed to capture the universal capability (* : Any) | - | The inferred arguments are: [? Exception, {*} () => Int] + | The inferred arguments are: [? Exception, {*} () -> Int] diff --git a/tests/neg-custom-args/captures/try.scala b/tests/neg-custom-args/captures/try.scala index 804a16192be0..b128f82a2a3c 100644 --- a/tests/neg-custom-args/captures/try.scala +++ b/tests/neg-custom-args/captures/try.scala @@ -25,7 +25,7 @@ def test = (ex: Exception) => ??? } - val b = handle[Exception, () => Nothing] { // error + val b = handle[Exception, () -> Nothing] { // error (x: CanThrow[Exception]) => () => raise(new Exception)(using x) } { (ex: Exception) => ??? diff --git a/tests/neg-custom-args/captures/vars.check b/tests/neg-custom-args/captures/vars.check index 4eab5b6b2b3a..0df38b918862 100644 --- a/tests/neg-custom-args/captures/vars.check +++ b/tests/neg-custom-args/captures/vars.check @@ -1,21 +1,21 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/vars.scala:11:24 ----------------------------------------- -11 | val z2c: () => Unit = z2 // error +11 | val z2c: () -> Unit = z2 // error | ^^ - | Found: (z2 : {x, cap1} () => Unit) - | Required: () => Unit + | Found: (z2 : {x, cap1} () -> Unit) + | Required: () -> Unit longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/vars.scala:13:10 -------------------------------------------------------------- -13 | var a: {*} String => String = f // error - | ^^^^^^^^^^^^^^^^^^^ - | type of mutable variable box {*} String => String is not allowed to capture the universal capability (* : Any) +-- Error: tests/neg-custom-args/captures/vars.scala:13:16 -------------------------------------------------------------- +13 | var a: String => String = f // error + | ^^^^^^^^^^^^^^^^ + | type of mutable variable String => String is not allowed to capture the universal capability (* : Any) -- Error: tests/neg-custom-args/captures/vars.scala:14:9 --------------------------------------------------------------- -14 | var b: List[{*} String => String] = Nil // error - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - |type of mutable variable List[box {*} String => String] is not allowed to capture the universal capability (* : Any) +14 | var b: List[String => String] = Nil // error + | ^^^^^^^^^^^^^^^^^^^^^^ + | type of mutable variable List[String => String] is not allowed to capture the universal capability (* : Any) -- Error: tests/neg-custom-args/captures/vars.scala:29:2 --------------------------------------------------------------- 29 | local { cap3 => // error | ^^^^^ - |inferred type argument {*} (x$0: ? String) => ? String is not allowed to capture the universal capability (* : Any) + |inferred type argument {*} (x$0: ? String) -> ? String is not allowed to capture the universal capability (* : Any) | - |The inferred arguments are: [{*} (x$0: ? String) => ? String] + |The inferred arguments are: [{*} (x$0: ? String) -> ? String] diff --git a/tests/neg-custom-args/captures/vars.scala b/tests/neg-custom-args/captures/vars.scala index 4a58f79932b3..e85bcbe2db04 100644 --- a/tests/neg-custom-args/captures/vars.scala +++ b/tests/neg-custom-args/captures/vars.scala @@ -6,12 +6,12 @@ def test(cap1: Cap, cap2: Cap) = var x = f val y = x val z = () => if x("") == "" then "a" else "b" - val zc: {cap1} () => String = z + val zc: {cap1} () -> String = z val z2 = () => { x = identity } - val z2c: () => Unit = z2 // error + val z2c: () -> Unit = z2 // error - var a: {*} String => String = f // error - var b: List[{*} String => String] = Nil // error + var a: String => String = f // error + var b: List[String => String] = Nil // error def scope = val cap3: Cap = CC() @@ -22,9 +22,9 @@ def test(cap1: Cap, cap2: Cap) = g val s = scope - val sc: {*} String => String = scope + val sc: String => String = scope - def local[T](op: Cap => T): T = op(CC()) + def local[T](op: Cap -> T): T = op(CC()) local { cap3 => // error def g(x: String): String = if cap3 == cap3 then "" else "a" @@ -32,7 +32,7 @@ def test(cap1: Cap, cap2: Cap) = } class Ref: - var elem: {cap1} String => String = null + var elem: {cap1} String -> String = null val r = Ref() r.elem = f diff --git a/tests/pos-custom-args/captures/bounded.scala b/tests/pos-custom-args/captures/bounded.scala index fad0b50c2137..85c1a67387b5 100644 --- a/tests/pos-custom-args/captures/bounded.scala +++ b/tests/pos-custom-args/captures/bounded.scala @@ -9,6 +9,6 @@ def test(c: Cap) = def f(x: Int): Int = if c == c then x else 0 val b = new B(f) val r1 = b.elem - val r1c: {c} Int => Int = r1 + val r1c: {c} Int -> Int = r1 val r2 = b.lateElem - val r2c: {c} () => {c} Int => Int = r2 \ No newline at end of file + val r2c: {c} () -> {c} Int -> Int = r2 \ No newline at end of file diff --git a/tests/pos-custom-args/captures/boxmap-paper.scala b/tests/pos-custom-args/captures/boxmap-paper.scala index ed8c648526d1..aff4c38e1b9d 100644 --- a/tests/pos-custom-args/captures/boxmap-paper.scala +++ b/tests/pos-custom-args/captures/boxmap-paper.scala @@ -1,19 +1,18 @@ -infix type ==> [A, B] = {*} (A => B) -type Cell[+T] = [K] => (T ==> K) => K +type Cell[+T] = [K] -> (T => K) -> K def cell[T](x: T): Cell[T] = - [K] => (k: T ==> K) => k(x) + [K] => (k: T => K) => k(x) def get[T](c: Cell[T]): T = c[T](identity) -def map[A, B](c: Cell[A])(f: A ==> B): Cell[B] +def map[A, B](c: Cell[A])(f: A => B): Cell[B] = c[Cell[B]]((x: A) => cell(f(x))) -def pureMap[A, B](c: Cell[A])(f: A => B): Cell[B] +def pureMap[A, B](c: Cell[A])(f: A -> B): Cell[B] = c[Cell[B]]((x: A) => cell(f(x))) -def lazyMap[A, B](c: Cell[A])(f: A ==> B): {f} () => Cell[B] +def lazyMap[A, B](c: Cell[A])(f: A => B): {f} () -> Cell[B] = () => c[Cell[B]]((x: A) => cell(f(x))) trait IO: @@ -21,17 +20,17 @@ trait IO: def test(io: {*} IO) = - val loggedOne: {io} () => Int = () => { io.print("1"); 1 } + val loggedOne: {io} () -> Int = () => { io.print("1"); 1 } - val c: Cell[{io} () => Int] - = cell[{io} () => Int](loggedOne) + val c: Cell[{io} () -> Int] + = cell[{io} () -> Int](loggedOne) - val g = (f: {io} () => Int) => + val g = (f: {io} () -> Int) => val x = f(); io.print(" + ") val y = f(); io.print(s" = ${x + y}") - val r = lazyMap[{io} () => Int, Unit](c)(f => g(f)) - val r2 = lazyMap[{io} () => Int, Unit](c)(g) + val r = lazyMap[{io} () -> Int, Unit](c)(f => g(f)) + val r2 = lazyMap[{io} () -> Int, Unit](c)(g) val r3 = lazyMap(c)(g) val _ = r() val _ = r2() diff --git a/tests/pos-custom-args/captures/boxmap.scala b/tests/pos-custom-args/captures/boxmap.scala index a0dcade2b179..003e46804a9d 100644 --- a/tests/pos-custom-args/captures/boxmap.scala +++ b/tests/pos-custom-args/captures/boxmap.scala @@ -1,20 +1,18 @@ type Top = Any @retains(*) -infix type ==> [A, B] = (A => B) @retains(*) - -type Box[+T <: Top] = ([K <: Top] => (T ==> K) => K) +type Box[+T <: Top] = ([K <: Top] -> (T => K) -> K) def box[T <: Top](x: T): Box[T] = - [K <: Top] => (k: T ==> K) => k(x) + [K <: Top] => (k: T => K) => k(x) -def map[A <: Top, B <: Top](b: Box[A])(f: A ==> B): Box[B] = +def map[A <: Top, B <: Top](b: Box[A])(f: A => B): Box[B] = b[Box[B]]((x: A) => box(f(x))) -def lazymap[A <: Top, B <: Top](b: Box[A])(f: A ==> B): (() => Box[B]) @retains(f) = +def lazymap[A <: Top, B <: Top](b: Box[A])(f: A => B): (() -> Box[B]) @retains(f) = () => b[Box[B]]((x: A) => box(f(x))) def test[A <: Top, B <: Top] = - def lazymap[A <: Top, B <: Top](b: Box[A])(f: A ==> B) = + def lazymap[A <: Top, B <: Top](b: Box[A])(f: A => B) = () => b[Box[B]]((x: A) => box(f(x))) - val x: (b: Box[A]) => (f: A ==> B) => (() => Box[B]) @retains(f) = lazymap[A, B] + val x: (b: Box[A]) -> (f: A => B) -> (() -> Box[B]) @retains(f) = lazymap[A, B] () diff --git a/tests/pos-custom-args/captures/capt-capability.scala b/tests/pos-custom-args/captures/capt-capability.scala index 41da15d288f1..9990542a199d 100644 --- a/tests/pos-custom-args/captures/capt-capability.scala +++ b/tests/pos-custom-args/captures/capt-capability.scala @@ -1,15 +1,15 @@ import annotation.capability @capability class Cap -def f1(c: Cap): {c} () => c.type = () => c // ok +def f1(c: Cap): {c} () -> c.type = () => c // ok def f2: Int = - val g: {*} Boolean => Int = ??? + val g: Boolean => Int = ??? val x = g(true) x def f3: Int = - def g: {*} Boolean => Int = ??? + def g: Boolean => Int = ??? def h = g val x = g.apply(true) x @@ -17,10 +17,10 @@ def f3: Int = def foo() = val x: Cap = ??? val y: Cap = x - val x2: {x} () => Cap = ??? - val y2: {x} () => Cap = x2 + val x2: {x} () -> Cap = ??? + val y2: {x} () -> Cap = x2 - val z1: {*} () => Cap = f1(x) + val z1: () => Cap = f1(x) def h[X](a: X)(b: X) = a val z2 = diff --git a/tests/pos-custom-args/captures/capt-depfun.scala b/tests/pos-custom-args/captures/capt-depfun.scala index 6b99eff32692..072eaefd3e78 100644 --- a/tests/pos-custom-args/captures/capt-depfun.scala +++ b/tests/pos-custom-args/captures/capt-depfun.scala @@ -1,18 +1,18 @@ class C type Cap = C @retains(*) -type T = (x: Cap) => String @retains(x) +type T = (x: Cap) -> String @retains(x) -val aa: ((x: Cap) => String @retains(x)) = (x: Cap) => "" +val aa: ((x: Cap) -> String @retains(x)) = (x: Cap) => "" def f(y: Cap, z: Cap): String @retains(*) = - val a: ((x: Cap) => String @retains(x)) = (x: Cap) => "" + val a: ((x: Cap) -> String @retains(x)) = (x: Cap) => "" val b = a(y) val c: String @retains(y) = b def g(): C @retains(y, z) = ??? val d = a(g()) - val ac: ((x: Cap) => String @retains(x) => String @retains(x)) = ??? - val bc: (({y} String) => {y} String) = ac(y) - val dc: (String => {y, z} String) = ac(g()) + val ac: ((x: Cap) -> String @retains(x) -> String @retains(x)) = ??? + val bc: (({y} String) -> {y} String) = ac(y) + val dc: (String -> {y, z} String) = ac(g()) c diff --git a/tests/pos-custom-args/captures/capt-depfun2.scala b/tests/pos-custom-args/captures/capt-depfun2.scala index 17f98b4a1554..98ee9dbfdc6b 100644 --- a/tests/pos-custom-args/captures/capt-depfun2.scala +++ b/tests/pos-custom-args/captures/capt-depfun2.scala @@ -3,6 +3,6 @@ type Cap = C @retains(*) def f(y: Cap, z: Cap) = def g(): C @retains(y, z) = ??? - val ac: ((x: Cap) => Array[String @retains(x)]) = ??? + val ac: ((x: Cap) -> Array[String @retains(x)]) = ??? val dc: Array[? >: String <: {y, z} String] = ac(g()) // needs to be inferred val ec = ac(y) diff --git a/tests/pos-custom-args/captures/capt-test.scala b/tests/pos-custom-args/captures/capt-test.scala index f40bd2ff1746..6ee0d2a4d9f4 100644 --- a/tests/pos-custom-args/captures/capt-test.scala +++ b/tests/pos-custom-args/captures/capt-test.scala @@ -2,7 +2,7 @@ abstract class LIST[+T]: def isEmpty: Boolean def head: T def tail: LIST[T] - def map[U](f: {*} T => U): LIST[U] = + def map[U](f: T => U): LIST[U] = if isEmpty then NIL else CONS(f(head), tail.map(f)) @@ -15,7 +15,7 @@ object NIL extends LIST[Nothing]: def head = ??? def tail = ??? -def map[A, B](f: {*} A => B)(xs: LIST[A]): LIST[B] = +def map[A, B](f: A => B)(xs: LIST[A]): LIST[B] = xs.map(f) class C @@ -29,7 +29,7 @@ def test(c: Cap, d: Cap) = val zs = val z = g CONS(z, ys) - val zsc: LIST[{d, y} Cap => Unit] = zs + val zsc: LIST[{d, y} Cap -> Unit] = zs val a4 = zs.map(identity) - val a4c: LIST[{d, y} Cap => Unit] = a4 + val a4c: LIST[{d, y} Cap -> Unit] = a4 diff --git a/tests/pos-custom-args/captures/capt1.scala b/tests/pos-custom-args/captures/capt1.scala index 14c0855544d4..e8e217435f96 100644 --- a/tests/pos-custom-args/captures/capt1.scala +++ b/tests/pos-custom-args/captures/capt1.scala @@ -1,14 +1,14 @@ class C type Cap = {*} C -def f1(c: Cap): {c} () => c.type = () => c // ok +def f1(c: Cap): {c} () -> c.type = () => c // ok def f2: Int = - val g: {*} Boolean => Int = ??? + val g: {*} Boolean -> Int = ??? val x = g(true) x def f3: Int = - def g: {*} Boolean => Int = ??? + def g: Boolean => Int = ??? def h = g val x = g.apply(true) x @@ -16,10 +16,10 @@ def f3: Int = def foo() = val x: {*} C = ??? val y: {x} C = x - val x2: {x} () => C = ??? - val y2: {x} () => {x} C = x2 + val x2: {x} () -> C = ??? + val y2: {x} () -> {x} C = x2 - val z1: {*} () => Cap = f1(x) + val z1: () => Cap = f1(x) def h[X](a: X)(b: X) = a val z2 = diff --git a/tests/pos-custom-args/captures/capt2.scala b/tests/pos-custom-args/captures/capt2.scala index e3d4cd67b30c..11bb2d5eb7b5 100644 --- a/tests/pos-custom-args/captures/capt2.scala +++ b/tests/pos-custom-args/captures/capt2.scala @@ -9,12 +9,12 @@ def test1() = def test2() = val x: Cap = C() val y = () => { x; () } - def z: (() => Unit) @retains(x) = y - z: (() => Unit) @retains(x) - def z2: (() => Unit) @retains(y) = y - z2: (() => Unit) @retains(y) - val p: {*} () => String = () => "abc" + def z: (() -> Unit) @retains(x) = y + z: (() -> Unit) @retains(x) + def z2: (() -> Unit) @retains(y) = y + z2: (() -> Unit) @retains(y) + val p: {*} () -> String = () => "abc" val q: {p} C = ??? - p: ({p} () => String) + p: ({p} () -> String) diff --git a/tests/pos-custom-args/captures/cc-expand.scala b/tests/pos-custom-args/captures/cc-expand.scala index eedc95554b17..7bce1ea8387e 100644 --- a/tests/pos-custom-args/captures/cc-expand.scala +++ b/tests/pos-custom-args/captures/cc-expand.scala @@ -8,14 +8,14 @@ object Test: def test(ct: CT, dt: CT) = - def x0: A => {ct} B = ??? + def x0: A -> {ct} B = ??? - def x1: A => B @retains(ct) = ??? - def x2: A => B => C @retains(ct) = ??? - def x3: A => () => B => C @retains(ct) = ??? + def x1: A -> B @retains(ct) = ??? + def x2: A -> B -> C @retains(ct) = ??? + def x3: A -> () -> B -> C @retains(ct) = ??? - def x4: (x: A @retains(ct)) => B => C = ??? + def x4: (x: A @retains(ct)) -> B -> C = ??? - def x5: A => (x: B @retains(ct)) => () => C @retains(dt) = ??? - def x6: A => (x: B @retains(ct)) => (() => C @retains(dt)) @retains(x, dt) = ??? - def x7: A => (x: B @retains(ct)) => (() => C @retains(dt)) @retains(x) = ??? \ No newline at end of file + def x5: A -> (x: B @retains(ct)) -> () -> C @retains(dt) = ??? + def x6: A -> (x: B @retains(ct)) -> (() -> C @retains(dt)) @retains(x, dt) = ??? + def x7: A -> (x: B @retains(ct)) -> (() -> C @retains(dt)) @retains(x) = ??? \ No newline at end of file diff --git a/tests/pos-custom-args/captures/impurefun.scala b/tests/pos-custom-args/captures/impurefun.scala new file mode 100644 index 000000000000..6e31008fe54a --- /dev/null +++ b/tests/pos-custom-args/captures/impurefun.scala @@ -0,0 +1,8 @@ +object Test: + + val f: ImpureFunction1[Int, Int] = (x: Int) => x + 1 + + val g: Int -> Int = (x: Int) => x + 1 + + val h: Int => Int = (x: Int) => x + 1 + diff --git a/tests/pos-custom-args/captures/lazylists-mono.scala b/tests/pos-custom-args/captures/lazylists-mono.scala index 82c44abf703a..44ab36ded6a2 100644 --- a/tests/pos-custom-args/captures/lazylists-mono.scala +++ b/tests/pos-custom-args/captures/lazylists-mono.scala @@ -6,21 +6,21 @@ type Cap = {*} CC def test(E: Cap) = trait LazyList[+A]: - protected def contents: {E} () => (A, {E} LazyList[A]) + protected def contents: {E} () -> (A, {E} LazyList[A]) def isEmpty: Boolean def head: A = contents()._1 def tail: {E} LazyList[A] = contents()._2 - class LazyCons[+A](override val contents: {E} () => (A, {E} LazyList[A])) + class LazyCons[+A](override val contents: {E} () -> (A, {E} LazyList[A])) extends LazyList[A]: def isEmpty: Boolean = false object LazyNil extends LazyList[Nothing]: - def contents: {E} () => (Nothing, LazyList[Nothing]) = ??? + def contents: {E} () -> (Nothing, LazyList[Nothing]) = ??? def isEmpty: Boolean = true extension [A](xs: {E} LazyList[A]) - def map[B](f: {E} A => B): {E} LazyList[B] = + def map[B](f: {E} A -> B): {E} LazyList[B] = if xs.isEmpty then LazyNil else val cons = () => (f(xs.head), xs.tail.map(f)) diff --git a/tests/pos-custom-args/captures/lazylists.scala b/tests/pos-custom-args/captures/lazylists.scala index 17d5f8546edc..bf3e0300b5b5 100644 --- a/tests/pos-custom-args/captures/lazylists.scala +++ b/tests/pos-custom-args/captures/lazylists.scala @@ -14,7 +14,7 @@ object LazyNil extends LazyList[Nothing]: def tail = ??? extension [A](xs: {*} LazyList[A]) - def map[B](f: {*} A => B): {xs, f} LazyList[B] = + def map[B](f: A => B): {xs, f} LazyList[B] = final class Mapped extends LazyList[B]: this: ({xs, f} Mapped) => diff --git a/tests/pos-custom-args/captures/lazylists1.scala b/tests/pos-custom-args/captures/lazylists1.scala index 4c8006fb0e29..7fbdde87ad9b 100644 --- a/tests/pos-custom-args/captures/lazylists1.scala +++ b/tests/pos-custom-args/captures/lazylists1.scala @@ -13,23 +13,23 @@ object LazyNil extends LazyList[Nothing]: def head = ??? def tail = ??? -final class LazyCons[+T](val x: T, val xs: {*} () => {*} LazyList[T]) extends LazyList[T]: +final class LazyCons[+T](val x: T, val xs: Int => {*} LazyList[T]) extends LazyList[T]: this: ({*} LazyList[T]) => def isEmpty = false def head = x - def tail: {this} LazyList[T] = xs() + def tail: {this} LazyList[T] = xs(0) extension [A](xs: {*} LazyList[A]) - def map[B](f: {*} A => B): {xs, f} LazyList[B] = + def map[B](f: A => B): {xs, f} LazyList[B] = if xs.isEmpty then LazyNil - else LazyCons(f(xs.head), () => xs.tail.map(f)) + else LazyCons(f(xs.head), x => xs.tail.map(f)) def test(cap1: Cap, cap2: Cap) = def f(x: String): String = if cap1 == cap1 then "" else "a" def g(x: String): String = if cap2 == cap2 then "" else "a" - val xs = LazyCons("", () => if f("") == f("") then LazyNil else LazyNil) + val xs = new LazyCons("", x => if f("") == f("") then LazyNil else LazyNil) val xsc: {cap1} LazyList[String] = xs val ys = xs.map(g) val ysc: {cap1, cap2} LazyList[String] = ys diff --git a/tests/pos-custom-args/captures/lazyref.scala b/tests/pos-custom-args/captures/lazyref.scala index 39748b00506b..2ab770178a16 100644 --- a/tests/pos-custom-args/captures/lazyref.scala +++ b/tests/pos-custom-args/captures/lazyref.scala @@ -1,15 +1,14 @@ -class CC -type Cap = {*} CC +@annotation.capability class Cap -class LazyRef[T](val elem: {*} () => T): +class LazyRef[T](val elem: () => T): val get = elem - def map[U](f: {*} T => U): {f, this} LazyRef[U] = + def map[U](f: T => U): {f, this} LazyRef[U] = new LazyRef(() => f(elem())) -def map[A, B](ref: {*} LazyRef[A], f: {*} A => B): {f, ref} LazyRef[B] = +def map[A, B](ref: {*} LazyRef[A], f: A => B): {f, ref} LazyRef[B] = new LazyRef(() => f(ref.elem())) -def mapc[A, B]: (ref: {*} LazyRef[A], f: {*} A => B) => {f, ref} LazyRef[B] = +def mapc[A, B]: (ref: {*} LazyRef[A], f: A => B) => {f, ref} LazyRef[B] = (ref1, f1) => map[A, B](ref1, f1) def test(cap1: Cap, cap2: Cap) = diff --git a/tests/pos-custom-args/captures/list-encoding.scala b/tests/pos-custom-args/captures/list-encoding.scala index 74bc8bd2b099..59ae61273af7 100644 --- a/tests/pos-custom-args/captures/list-encoding.scala +++ b/tests/pos-custom-args/captures/list-encoding.scala @@ -3,10 +3,10 @@ package listEncoding class Cap type Op[T, C] = - {*} (v: T) => {*} (s: C) => C + (v: T) => (s: C) => C type List[T] = - [C] => (op: Op[T, C]) => {op} (s: C) => C + [C] -> (op: Op[T, C]) -> {op} (s: C) -> C def nil[T]: List[T] = [C] => (op: Op[T, C]) => (s: C) => s diff --git a/tests/pos-custom-args/captures/lists.scala b/tests/pos-custom-args/captures/lists.scala index 139f885ec87a..f52727af7b94 100644 --- a/tests/pos-custom-args/captures/lists.scala +++ b/tests/pos-custom-args/captures/lists.scala @@ -2,7 +2,7 @@ abstract class LIST[+T]: def isEmpty: Boolean def head: T def tail: LIST[T] - def map[U](f: {*} T => U): LIST[U] = + def map[U](f: {*} T -> U): LIST[U] = if isEmpty then NIL else CONS(f(head), tail.map(f)) @@ -15,11 +15,10 @@ object NIL extends LIST[Nothing]: def head = ??? def tail = ??? -def map[A, B](f: {*} A => B)(xs: LIST[A]): LIST[B] = +def map[A, B](f: A => B)(xs: LIST[A]): LIST[B] = xs.map(f) -class C -type Cap = {*} C +@annotation.capability class Cap def test(c: Cap, d: Cap, e: Cap) = def f(x: Cap): Unit = if c == x then () @@ -29,63 +28,63 @@ def test(c: Cap, d: Cap, e: Cap) = val zs = val z = g CONS(z, ys) - val zsc: LIST[{d, y} Cap => Unit] = zs + val zsc: LIST[{d, y} Cap -> Unit] = zs val z1 = zs.head - val z1c: {y, d} Cap => Unit = z1 + val z1c: {y, d} Cap -> Unit = z1 val ys1 = zs.tail val y1 = ys1.head def m1[A, B] = - (f: {*} A => B) => (xs: LIST[A]) => xs.map(f) + (f: A => B) => (xs: LIST[A]) => xs.map(f) - def m1c: (f: {*} String => Int) => {f} LIST[String] => LIST[Int] = m1[String, Int] + def m1c: (f: String => Int) -> {f} LIST[String] -> LIST[Int] = m1[String, Int] def m2 = [A, B] => - (f: {*} A => B) => (xs: LIST[A]) => xs.map(f) + (f: A => B) => (xs: LIST[A]) => xs.map(f) - def m2c: [A, B] => (f: {*} A => B) => {f} LIST[A] => LIST[B] = m2 + def m2c: [A, B] -> (f: A => B) -> {f} LIST[A] -> LIST[B] = m2 def eff[A](x: A) = if x == e then x else x val eff2 = [A] => (x: A) => if x == e then x else x - val a0 = identity[{d, y} Cap => Unit] - val a0c: ({d, y} Cap => Unit) => {d, y} Cap => Unit = a0 - val a1 = zs.map[{d, y} Cap => Unit](a0) - val a1c: LIST[{d, y} Cap => Unit] = a1 - val a2 = zs.map[{d, y} Cap => Unit](identity[{d, y} Cap => Unit]) - val a2c: LIST[{d, y} Cap => Unit] = a2 - val a3 = zs.map(identity[{d, y} Cap => Unit]) - val a3c: LIST[{d, y} Cap => Unit] = a3 + val a0 = identity[{d, y} Cap -> Unit] + val a0c: ({d, y} Cap -> Unit) -> {d, y} Cap -> Unit = a0 + val a1 = zs.map[{d, y} Cap -> Unit](a0) + val a1c: LIST[{d, y} Cap -> Unit] = a1 + val a2 = zs.map[{d, y} Cap -> Unit](identity[{d, y} Cap -> Unit]) + val a2c: LIST[{d, y} Cap -> Unit] = a2 + val a3 = zs.map(identity[{d, y} Cap -> Unit]) + val a3c: LIST[{d, y} Cap -> Unit] = a3 val a4 = zs.map(identity) - val a4c: LIST[{d, c} Cap => Unit] = a4 - val a5 = map[{d, y} Cap => Unit, {d, y} Cap => Unit](identity)(zs) - val a5c: LIST[{d, c} Cap => Unit] = a5 - val a6 = m1[{d, y} Cap => Unit, {d, y} Cap => Unit](identity)(zs) - val a6c: LIST[{d, c} Cap => Unit] = a6 + val a4c: LIST[{d, c} Cap -> Unit] = a4 + val a5 = map[{d, y} Cap -> Unit, {d, y} Cap -> Unit](identity)(zs) + val a5c: LIST[{d, c} Cap -> Unit] = a5 + val a6 = m1[{d, y} Cap -> Unit, {d, y} Cap -> Unit](identity)(zs) + val a6c: LIST[{d, c} Cap -> Unit] = a6 - val b0 = eff[{d, y} Cap => Unit] - val b0c: {e} ({d, y} Cap => Unit) => {d, y} Cap => Unit = b0 - val b1 = zs.map[{d, y} Cap => Unit](a0) - val b1c: {e} LIST[{d, y} Cap => Unit] = b1 - val b2 = zs.map[{d, y} Cap => Unit](eff[{d, y} Cap => Unit]) - val b2c: {e} LIST[{d, y} Cap => Unit] = b2 - val b3 = zs.map(eff[{d, y} Cap => Unit]) - val b3c: {e} LIST[{d, y} Cap => Unit] = b3 + val b0 = eff[{d, y} Cap -> Unit] + val b0c: {e} ({d, y} Cap -> Unit) -> {d, y} Cap -> Unit = b0 + val b1 = zs.map[{d, y} Cap -> Unit](a0) + val b1c: {e} LIST[{d, y} Cap -> Unit] = b1 + val b2 = zs.map[{d, y} Cap -> Unit](eff[{d, y} Cap -> Unit]) + val b2c: {e} LIST[{d, y} Cap -> Unit] = b2 + val b3 = zs.map(eff[{d, y} Cap -> Unit]) + val b3c: {e} LIST[{d, y} Cap -> Unit] = b3 val b4 = zs.map(eff) - val b4c: {e} LIST[{d, c} Cap => Unit] = b4 - val b5 = map[{d, y} Cap => Unit, {d, y} Cap => Unit](eff)(zs) - val b5c: {e} LIST[{d, c} Cap => Unit] = b5 - val b6 = m1[{d, y} Cap => Unit, {d, y} Cap => Unit](eff)(zs) - val b6c: {e} LIST[{d, c} Cap => Unit] = b6 + val b4c: {e} LIST[{d, c} Cap -> Unit] = b4 + val b5 = map[{d, y} Cap -> Unit, {d, y} Cap -> Unit](eff)(zs) + val b5c: {e} LIST[{d, c} Cap -> Unit] = b5 + val b6 = m1[{d, y} Cap -> Unit, {d, y} Cap -> Unit](eff)(zs) + val b6c: {e} LIST[{d, c} Cap -> Unit] = b6 - val c0 = eff2[{d, y} Cap => Unit] - val c0c: {e} ({d, y} Cap => Unit) => {d, y} Cap => Unit = c0 - val c1 = zs.map[{d, y} Cap => Unit](a0) - val c1c: {e} LIST[{d, y} Cap => Unit] = c1 - val c2 = zs.map[{d, y} Cap => Unit](eff2[{d, y} Cap => Unit]) - val c2c: {e} LIST[{d, y} Cap => Unit] = c2 - val c3 = zs.map(eff2[{d, y} Cap => Unit]) - val c3c: {e} LIST[{d, y} Cap => Unit] = c3 + val c0 = eff2[{d, y} Cap -> Unit] + val c0c: {e} ({d, y} Cap -> Unit) -> {d, y} Cap -> Unit = c0 + val c1 = zs.map[{d, y} Cap -> Unit](a0) + val c1c: {e} LIST[{d, y} Cap -> Unit] = c1 + val c2 = zs.map[{d, y} Cap -> Unit](eff2[{d, y} Cap -> Unit]) + val c2c: {e} LIST[{d, y} Cap -> Unit] = c2 + val c3 = zs.map(eff2[{d, y} Cap -> Unit]) + val c3c: {e} LIST[{d, y} Cap -> Unit] = c3 diff --git a/tests/pos-custom-args/captures/pairs.scala b/tests/pos-custom-args/captures/pairs.scala index 4f23a086a075..14d484ff21b1 100644 --- a/tests/pos-custom-args/captures/pairs.scala +++ b/tests/pos-custom-args/captures/pairs.scala @@ -1,6 +1,5 @@ -class C -type Cap = {*} C +@annotation.capability class Cap object Generic: @@ -13,13 +12,13 @@ object Generic: def g(x: Cap): Unit = if d == x then () val p = Pair(f, g) val x1 = p.fst - val x1c: {c} Cap => Unit = x1 + val x1c: {c} Cap -> Unit = x1 val y1 = p.snd - val y1c: {d} Cap => Unit = y1 + val y1c: {d} Cap -> Unit = y1 object Monomorphic: - class Pair(val x: {*} Cap => Unit, val y: {*} Cap => Unit): + class Pair(val x: Cap => Unit, val y: {*} Cap -> Unit): def fst = x def snd = y @@ -28,6 +27,6 @@ object Monomorphic: def g(x: Cap): Unit = if d == x then () val p = Pair(f, g) val x1 = p.fst - val x1c: {c} Cap => Unit = x1 + val x1c: {c} Cap -> Unit = x1 val y1 = p.snd - val y1c: {d} Cap => Unit = y1 + val y1c: {d} Cap -> Unit = y1 diff --git a/tests/pos-custom-args/captures/try.scala b/tests/pos-custom-args/captures/try.scala index a50eeabfb3a3..14773cd5be0f 100644 --- a/tests/pos-custom-args/captures/try.scala +++ b/tests/pos-custom-args/captures/try.scala @@ -3,7 +3,7 @@ import language.experimental.erasedDefinitions class CT[E <: Exception] type CanThrow[E <: Exception] = CT[E] @retains(*) -infix type throws[R, E <: Exception] = (erased CanThrow[E]) ?=> R +infix type throws[R, E <: Exception] = (erased CanThrow[E]) ?-> R class Fail extends Exception @@ -12,7 +12,7 @@ def raise[E <: Exception](e: E): Nothing throws E = throw e 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 = +def handle[E <: Exception, R](op: (erased CanThrow[E]) -> R)(handler: E -> R): R = erased val x: CanThrow[E] = ??? try op(x) catch case ex: E => handler(ex) diff --git a/tests/pos-custom-args/captures/try3.scala b/tests/pos-custom-args/captures/try3.scala index b29ad2d4b352..b8937bec00f3 100644 --- a/tests/pos-custom-args/captures/try3.scala +++ b/tests/pos-custom-args/captures/try3.scala @@ -2,8 +2,7 @@ import language.experimental.erasedDefinitions import annotation.capability import java.io.IOException -class CT[-E] // variance is needed for correct rechecking inference -type CanThrow[E] = {*} CT[E] +@annotation.capability class CanThrow[-E] def handle[E <: Exception, T](op: CanThrow[E] ?=> T)(handler: E => T): T = val x: CanThrow[E] = ??? @@ -14,7 +13,7 @@ def raise[E <: Exception](ex: E)(using CanThrow[E]): Nothing = throw ex def test1: Int = - def f(a: Boolean): Boolean => CanThrow[IOException] ?=> Int = + def f(a: Boolean): Boolean -> CanThrow[IOException] ?-> Int = handle { if !a then raise(IOException()) (b: Boolean) => (_: CanThrow[IOException]) ?=> diff --git a/tests/pos-custom-args/captures/vars.scala b/tests/pos-custom-args/captures/vars.scala index aca56c55f386..12721158a2bb 100644 --- a/tests/pos-custom-args/captures/vars.scala +++ b/tests/pos-custom-args/captures/vars.scala @@ -1,18 +1,17 @@ -class CC -type Cap = {*} CC +@annotation.capability class Cap def test(cap1: Cap, cap2: Cap) = def f(x: String): String = if cap1 == cap1 then "" else "a" var x = f val y = x val z = () => if x("") == "" then "a" else "b" - val zc: {cap1} () => String = z + val zc: {cap1} () -> String = z val z2 = () => { x = identity } - val z2c: {cap1} () => Unit = z2 + val z2c: {cap1} () -> Unit = z2 class Ref: - var elem: {cap1} String => String = null + var elem: {cap1} String -> String = null val r = Ref() r.elem = f - val fc: {cap1} String => String = r.elem + val fc: {cap1} String -> String = r.elem diff --git a/tests/pos/i12723.scala b/tests/pos/i12723.scala index d1cab3ede638..022a3a458f04 100644 --- a/tests/pos/i12723.scala +++ b/tests/pos/i12723.scala @@ -1,10 +1,10 @@ class Fun[|*|[_, _]] { - enum ->[A, B] { - case BiId[X, Y]() extends ((X |*| Y) -> (X |*| Y)) + enum -->[A, B] { + case BiId[X, Y]() extends ((X |*| Y) --> (X |*| Y)) } - def go[A, B](f: A -> B): Unit = + def go[A, B](f: A --> B): Unit = f match { - case ->.BiId() => () + case -->.BiId() => () } } diff --git a/tests/pos/impurefun.scala b/tests/pos/impurefun.scala new file mode 100644 index 000000000000..c9f4c54a0b90 --- /dev/null +++ b/tests/pos/impurefun.scala @@ -0,0 +1,4 @@ +object Test: + + val f: ImpureFunction1[Int, Int] = (x: Int) => x + 1 +