Skip to content

Commit a2dd16f

Browse files
committed
Convert -Ycc to language import
Two new experimental language imports: - captureChecking: replaces -Ycc - pureFunctions: Enables pure function syntax A -> B
1 parent 62d3875 commit a2dd16f

File tree

26 files changed

+128
-73
lines changed

26 files changed

+128
-73
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ class Compiler {
8181
new PatternMatcher) :: // Compile pattern matches
8282
List(new TestRecheck.Pre) :: // Test only: run rechecker, enabled under -Yrecheck-test
8383
List(new TestRecheck) :: // Test only: run rechecker, enabled under -Yrecheck-test
84-
List(new CheckCaptures.Pre) :: // Preparations for check captures phase, enabled under -Ycc
85-
List(new CheckCaptures) :: // Check captures, enabled under -Ycc
84+
List(new CheckCaptures.Pre) :: // Preparations for check captures phase, enabled under captureChecking
85+
List(new CheckCaptures) :: // Check captures, enabled under captureChecking
8686
List(new ElimOpaque, // Turn opaque into normal aliases
8787
new sjs.ExplicitJSClasses, // Make all JS classes explicit (Scala.js only)
8888
new ExplicitOuter, // Add accessors to outer classes from nested ones.

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,11 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
195195
case arg => arg.typeOpt.widen.isRepeatedParam
196196
}
197197

198-
/** Is tree a type tree of the form `=> T` or (under -Ycc) `{refs}-> T`? */
198+
/** Is tree a type tree of the form `=> T` or (under pureFunctions) `{refs}-> T`? */
199199
def isByNameType(tree: Tree)(using Context): Boolean =
200200
stripByNameType(tree) ne tree
201201

202-
/** Strip `=> T` to `T` and (under -Ycc) `{refs}-> T` to `T` */
202+
/** Strip `=> T` to `T` and (under pureFunctions) `{refs}-> T` to `T` */
203203
def stripByNameType(tree: Tree)(using Context): Tree = unsplice(tree) match
204204
case ByNameTypeTree(t1) => t1
205205
case untpd.CapturingTypeTree(_, parent) =>
@@ -400,12 +400,12 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped]
400400
}
401401
}
402402

403-
/** Under -Ycc: A builder and extractor for `=> T`, which is an alias for `{*}-> T`.
403+
/** Under pureFunctions: A builder and extractor for `=> T`, which is an alias for `{*}-> T`.
404404
* Only trees of the form `=> T` are matched; trees written directly as `{*}-> T`
405405
* are ignored by the extractor.
406406
*/
407407
object ImpureByNameTypeTree:
408-
408+
409409
def apply(tp: ByNameTypeTree)(using Context): untpd.CapturingTypeTree =
410410
untpd.CapturingTypeTree(
411411
Ident(nme.CAPTURE_ROOT).withSpan(tp.span.startPos) :: Nil, tp)

compiler/src/dotty/tools/dotc/ast/untpd.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
217217

218218
case class Infix()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Infix)
219219

220-
/** Used under -Ycc to mark impure function types `A => B` in `FunctionWithMods` */
220+
/** Used under pureFunctions to mark impure function types `A => B` in `FunctionWithMods` */
221221
case class Impure()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Impure)
222222
}
223223

compiler/src/dotty/tools/dotc/cc/CaptureOps.scala

+4-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Decorators.*, NameOps.*
99
import config.Printers.capt
1010
import util.Property.Key
1111
import tpd.*
12+
import config.Feature
1213

1314
private val Captures: Key[CaptureSet] = Key()
1415
private val BoxedType: Key[BoxedTypeCache] = Key()
@@ -120,11 +121,11 @@ extension (tp: Type)
120121
case _ =>
121122
tp
122123

123-
/** Under -Ycc, map regular function type to impure function type
124+
/** Under pureFunctions, map regular function type to impure function type
124125
*/
125-
def adaptFunctionTypeUnderCC(using Context): Type = tp match
126+
def adaptFunctionTypeUnderPureFuns(using Context): Type = tp match
126127
case AppliedType(fn, args)
127-
if ctx.settings.Ycc.value && defn.isFunctionClass(fn.typeSymbol) =>
128+
if Feature.pureFunsEnabled && defn.isFunctionClass(fn.typeSymbol) =>
128129
val fname = fn.typeSymbol.name
129130
defn.FunctionType(
130131
fname.functionArity,

compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import Phases.*, DenotTransformers.*, SymDenotations.*
77
import Contexts.*, Names.*, Flags.*, Symbols.*, Decorators.*
88
import Types.*, StdNames.*, Denotations.*
99
import config.Printers.{capt, recheckr}
10-
import config.Config
10+
import config.{Config, Feature}
1111
import ast.{tpd, untpd, Trees}
1212
import Trees.*
1313
import typer.RefChecks.{checkAllOverrides, checkParents}
@@ -26,7 +26,7 @@ object CheckCaptures:
2626

2727
class Pre extends PreRecheck, SymTransformer:
2828

29-
override def isEnabled(using Context) = ctx.settings.Ycc.value
29+
override def isEnabled(using Context) = Feature.ccEnabled
3030

3131
/** Reset `private` flags of parameter accessors so that we can refine them
3232
* in Setup if they have non-empty capture sets. Special handling of some
@@ -133,7 +133,7 @@ class CheckCaptures extends Recheck, SymTransformer:
133133
import CheckCaptures.*
134134

135135
def phaseName: String = "cc"
136-
override def isEnabled(using Context) = ctx.settings.Ycc.value
136+
override def isEnabled(using Context) = Feature.ccEnabled
137137

138138
def newRechecker()(using Context) = CaptureChecker(ctx)
139139

@@ -148,7 +148,7 @@ class CheckCaptures extends Recheck, SymTransformer:
148148
/** Check overrides again, taking capture sets into account.
149149
* TODO: Can we avoid doing overrides checks twice?
150150
* We need to do them here since only at this phase CaptureTypes are relevant
151-
* But maybe we can then elide the check during the RefChecks phase if -Ycc is set?
151+
* But maybe we can then elide the check during the RefChecks phase under captureChecking?
152152
*/
153153
def checkOverrides = new TreeTraverser:
154154
def traverse(t: Tree)(using Context) =

compiler/src/dotty/tools/dotc/config/Config.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ object Config {
240240
*/
241241
inline val printCaptureSetsAsPrefix = true
242242

243-
/** If true, allow mappping capture set variables under -Ycc with maps that are neither
243+
/** If true, allow mappping capture set variables under captureChecking with maps that are neither
244244
* bijective nor idempotent. We currently do now know how to do this correctly in all
245245
* cases, though.
246246
*/

compiler/src/dotty/tools/dotc/config/Feature.scala

+7
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ object Feature:
2828
val symbolLiterals = deprecated("symbolLiterals")
2929
val fewerBraces = experimental("fewerBraces")
3030
val saferExceptions = experimental("saferExceptions")
31+
val pureFunctions = experimental("pureFunctions")
32+
val captureChecking = experimental("captureChecking")
3133

3234
/** Is `feature` enabled by by a command-line setting? The enabling setting is
3335
*
@@ -75,6 +77,11 @@ object Feature:
7577

7678
def scala2ExperimentalMacroEnabled(using Context) = enabled(scala2macros)
7779

80+
def pureFunsEnabled(using Context) =
81+
enabled(pureFunctions) || ccEnabled
82+
83+
def ccEnabled(using Context) = enabled(captureChecking)
84+
7885
def sourceVersionSetting(using Context): SourceVersion =
7986
SourceVersion.valueOf(ctx.settings.source.value)
8087

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

+2-3
Original file line numberDiff line numberDiff line change
@@ -328,9 +328,8 @@ private sealed trait YSettings:
328328
val YcheckInit: Setting[Boolean] = BooleanSetting("-Ysafe-init", "Ensure safe initialization of objects")
329329
val YrequireTargetName: Setting[Boolean] = BooleanSetting("-Yrequire-targetName", "Warn if an operator is defined without a @targetName annotation")
330330
val YrecheckTest: Setting[Boolean] = BooleanSetting("-Yrecheck-test", "Run basic rechecking (internal test only)")
331-
val Ycc: Setting[Boolean] = BooleanSetting("-Ycc", "Check captured references (warning: extremely experimental and unstable)")
332-
val YccDebug: Setting[Boolean] = BooleanSetting("-Ycc-debug", "Used in conjunction with -Ycc, debug info for captured references")
333-
val YccNoAbbrev: Setting[Boolean] = BooleanSetting("-Ycc-no-abbrev", "Used in conjunction with -Ycc, suppress type abbreviations")
331+
val YccDebug: Setting[Boolean] = BooleanSetting("-Ycc-debug", "Used in conjunction with captureChecking language import, debug info for captured references")
332+
val YccNoAbbrev: Setting[Boolean] = BooleanSetting("-Ycc-no-abbrev", "Used in conjunction with captureChecking language import, suppress type abbreviations")
334333

335334
/** Area-specific debug output */
336335
val YexplainLowlevel: Setting[Boolean] = BooleanSetting("-Yexplain-lowlevel", "When explaining type errors, show types at a lower level.")

compiler/src/dotty/tools/dotc/core/Definitions.scala

+4-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import typer.ImportInfo.RootRef
1414
import Comments.CommentsContext
1515
import Comments.Comment
1616
import util.Spans.NoSpan
17+
import config.Feature
1718
import Symbols.requiredModuleRef
1819
import cc.{CapturingType, CaptureSet, EventuallyCapturingType}
1920

@@ -976,7 +977,6 @@ class Definitions {
976977
@tu lazy val BooleanBeanPropertyAnnot: ClassSymbol = requiredClass("scala.beans.BooleanBeanProperty")
977978
@tu lazy val BodyAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Body")
978979
@tu lazy val CapabilityAnnot: ClassSymbol = requiredClass("scala.annotation.capability")
979-
@tu lazy val CaptureCheckedAnnot: ClassSymbol = requiredClass("scala.annotation.internal.CaptureChecked")
980980
@tu lazy val ChildAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Child")
981981
@tu lazy val ContextResultCountAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ContextResultCount")
982982
@tu lazy val ProvisionalSuperClassAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ProvisionalSuperClass")
@@ -1012,6 +1012,7 @@ class Definitions {
10121012
@tu lazy val UncheckedStableAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedStable")
10131013
@tu lazy val UncheckedVarianceAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedVariance")
10141014
@tu lazy val VolatileAnnot: ClassSymbol = requiredClass("scala.volatile")
1015+
@tu lazy val WithPureFunsAnnot: ClassSymbol = requiredClass("scala.annotation.internal.WithPureFuns")
10151016
@tu lazy val FieldMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.field")
10161017
@tu lazy val GetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.getter")
10171018
@tu lazy val ParamMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.param")
@@ -1160,7 +1161,7 @@ class Definitions {
11601161

11611162
/** Extractor for context function types representing by-name parameters, of the form
11621163
* `() ?=> T`.
1163-
* Under -Ycc, this becomes `() ?-> T` or `{r1, ..., rN} () ?-> T`.
1164+
* Under purefunctions, this becomes `() ?-> T` or `{r1, ..., rN} () ?-> T`.
11641165
*/
11651166
object ByNameFunction:
11661167
def apply(tp: Type)(using Context): Type = tp match
@@ -1984,7 +1985,7 @@ class Definitions {
19841985
if (!isInitialized) {
19851986
// force initialization of every symbol that is synthesized or hijacked by the compiler
19861987
val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses()
1987-
++ (JavaEnumClass :: (if ctx.settings.Ycc.value then captureRoot :: Nil else Nil))
1988+
++ (JavaEnumClass :: (if Feature.ccEnabled then captureRoot :: Nil else Nil))
19881989

19891990
isInitialized = true
19901991
}

compiler/src/dotty/tools/dotc/core/NameOps.scala

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import scala.io.Codec
88
import Int.MaxValue
99
import Names._, StdNames._, Contexts._, Symbols._, Flags._, NameKinds._, Types._
1010
import util.Chars.{isOperatorPart, digit2int}
11+
import config.Feature
1112
import Decorators.*
1213
import Definitions._
1314
import nme._
@@ -208,14 +209,14 @@ object NameOps {
208209
if str == mustHave then found = true
209210
idx + str.length
210211
else idx
211-
val start = if ctx.settings.Ycc.value then skip(0, "Impure") else 0
212+
val start = if Feature.pureFunsEnabled then skip(0, "Impure") else 0
212213
skip(skip(start, "Erased"), "Context") == suffixStart
213214
&& found
214215
}
215216

216217
/** Same as `funArity`, except that it returns -1 if the prefix
217218
* is not one of a (possibly empty) concatenation of a subset of
218-
* "Impure" (only under -Ycc), "Erased" and "Context" (in that order).
219+
* "Impure" (only under pureFunctions), "Erased" and "Context" (in that order).
219220
*/
220221
private def checkedFunArity(suffixStart: Int)(using Context): Int =
221222
if isFunctionPrefix(suffixStart) then funArity(suffixStart) else -1

compiler/src/dotty/tools/dotc/core/Types.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -4238,7 +4238,7 @@ object Types {
42384238
final val Unknown: DependencyStatus = 0 // not yet computed
42394239
final val NoDeps: DependencyStatus = 1 // no dependent parameters found
42404240
final val FalseDeps: DependencyStatus = 2 // all dependent parameters are prefixes of non-depended alias types
4241-
final val CaptureDeps: DependencyStatus = 3 // dependencies in capture sets under -Ycc, otherwise only false dependencoes
4241+
final val CaptureDeps: DependencyStatus = 3 // dependencies in capture sets under captureChecking, otherwise only false dependencoes
42424242
final val TrueDeps: DependencyStatus = 4 // some truly dependent parameters exist
42434243
final val StatusMask: DependencyStatus = 7 // the bits indicating actual dependency status
42444244
final val Provisional: DependencyStatus = 8 // set if dependency status can still change due to type variable instantiations

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

+8-8
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import ast.{Trees, tpd, untpd}
3232
import Trees._
3333
import Decorators._
3434
import transform.SymUtils._
35-
import cc.adaptFunctionTypeUnderCC
35+
import cc.adaptFunctionTypeUnderPureFuns
3636

3737
import dotty.tools.tasty.{TastyBuffer, TastyReader}
3838
import TastyBuffer._
@@ -87,8 +87,8 @@ class TreeUnpickler(reader: TastyReader,
8787
/** The root owner tree. See `OwnerTree` class definition. Set by `enterTopLevel`. */
8888
private var ownerTree: OwnerTree = _
8989

90-
/** Was unpickled class compiled with -Ycc? */
91-
private var wasCaptureChecked: Boolean = false
90+
/** Was unpickled class compiled with pureFunctions? */
91+
private var knowsPureFuns: Boolean = false
9292

9393
private def registerSym(addr: Addr, sym: Symbol) =
9494
symAtAddr(addr) = sym
@@ -489,11 +489,11 @@ class TreeUnpickler(reader: TastyReader,
489489
def readTermRef()(using Context): TermRef =
490490
readType().asInstanceOf[TermRef]
491491

492-
/** Under -Ycc, map all function types to impure function types,
493-
* unless the unpickled class was also compiled with -Ycc.
492+
/** Under pureFunctions, map all function types to impure function types,
493+
* unless the unpickled class was also compiled with pureFunctions.
494494
*/
495495
private def postProcessFunction(tp: Type)(using Context): Type =
496-
if wasCaptureChecked then tp else tp.adaptFunctionTypeUnderCC
496+
if knowsPureFuns then tp else tp.adaptFunctionTypeUnderPureFuns
497497

498498
// ------ Reading definitions -----------------------------------------------------
499499

@@ -642,8 +642,8 @@ class TreeUnpickler(reader: TastyReader,
642642
}
643643
registerSym(start, sym)
644644
if (isClass) {
645-
if sym.owner.is(Package) && annots.exists(_.hasSymbol(defn.CaptureCheckedAnnot)) then
646-
wasCaptureChecked = true
645+
if sym.owner.is(Package) && annots.exists(_.hasSymbol(defn.WithPureFunsAnnot)) then
646+
knowsPureFuns = true
647647
sym.completer.withDecls(newScope)
648648
forkAt(templateStart).indexTemplateParams()(using localContext(sym))
649649
}

compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import scala.collection.mutable
3333
import scala.collection.mutable.ListBuffer
3434
import scala.annotation.switch
3535
import reporting._
36-
import cc.adaptFunctionTypeUnderCC
36+
import cc.adaptFunctionTypeUnderPureFuns
3737

3838
object Scala2Unpickler {
3939

@@ -826,7 +826,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
826826
}
827827
else if args.nonEmpty then
828828
tycon.safeAppliedTo(EtaExpandIfHK(sym.typeParams, args.map(translateTempPoly)))
829-
.adaptFunctionTypeUnderCC
829+
.adaptFunctionTypeUnderPureFuns
830830
else if (sym.typeParams.nonEmpty) tycon.EtaExpand(sym.typeParams)
831831
else tycon
832832
case TYPEBOUNDStpe =>

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

+12-11
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ object Parsers {
196196

197197
def isIdent = in.isIdent
198198
def isIdent(name: Name) = in.isIdent(name)
199-
def isPureArrow(name: Name): Boolean = ctx.settings.Ycc.value && isIdent(name)
199+
def isPureArrow(name: Name): Boolean = Feature.pureFunsEnabled && isIdent(name)
200200
def isPureArrow: Boolean = isPureArrow(nme.PUREARROW) || isPureArrow(nme.PURECTXARROW)
201201
def isErased = isIdent(nme.erased) && in.erasedEnabled
202202
def isSimpleLiteral =
@@ -968,11 +968,11 @@ object Parsers {
968968
isArrowIndent()
969969
else false
970970

971-
/** Under -Ycc: is the following token sequuence a capture set `{ref1, ..., refN}`
972-
* followed by a token that can start a type?
971+
/** Under captureChecking language import: is the following token sequence a
972+
* capture set `{ref1, ..., refN}` followed by a token that can start a type?
973973
*/
974974
def followingIsCaptureSet(): Boolean =
975-
ctx.settings.Ycc.value && {
975+
Feature.ccEnabled && {
976976
val lookahead = in.LookaheadScanner()
977977
def followingIsTypeStart() =
978978
lookahead.nextToken()
@@ -1446,7 +1446,7 @@ object Parsers {
14461446
def captureRef(): Tree =
14471447
if in.token == THIS then simpleRef() else termIdent()
14481448

1449-
/** CaptureSet ::= `{` CaptureRef {`,` CaptureRef} `}` -- under -Ycc
1449+
/** CaptureSet ::= `{` CaptureRef {`,` CaptureRef} `}` -- under captureChecking
14501450
*/
14511451
def captureSet(): List[Tree] = inBraces {
14521452
if in.token == RBRACE then Nil else commaSeparated(captureRef)
@@ -1457,12 +1457,12 @@ object Parsers {
14571457
* | FunParamClause ‘=>>’ Type
14581458
* | MatchType
14591459
* | InfixType
1460-
* | CaptureSet Type -- under -Ycc
1460+
* | CaptureSet Type -- under captureChecking
14611461
* FunType ::= (MonoFunType | PolyFunType)
14621462
* MonoFunType ::= FunTypeArgs (‘=>’ | ‘?=>’) Type
1463-
* | (‘->’ | ‘?->’ ) Type -- under -Ycc
1463+
* | (‘->’ | ‘?->’ ) Type -- under pureFunctions
14641464
* PolyFunType ::= HKTypeParamClause '=>' Type
1465-
* | HKTypeParamClause ‘->’ Type -- under -Ycc
1465+
* | HKTypeParamClause ‘->’ Type -- under pureFunctions
14661466
* FunTypeArgs ::= InfixType
14671467
* | `(' [ [ ‘[using]’ ‘['erased'] FunArgType {`,' FunArgType } ] `)'
14681468
* | '(' [ ‘[using]’ ‘['erased'] TypedFunParam {',' TypedFunParam } ')'
@@ -1482,8 +1482,9 @@ object Parsers {
14821482
if !imods.flags.isEmpty || params.isEmpty then
14831483
syntaxError(em"illegal parameter list for type lambda", start)
14841484
token = ARROW
1485-
else if ctx.settings.Ycc.value then
1486-
// `=>` means impure function under -Ycc whereas `->` is a regular function.
1485+
else if Feature.pureFunsEnabled then
1486+
// `=>` means impure function under pureFunctions or captureChecking
1487+
// language imports, whereas `->` is then a regular function.
14871488
imods |= Impure
14881489

14891490
if token == CTXARROW then
@@ -1887,7 +1888,7 @@ object Parsers {
18871888
if in.token == ARROW || isPureArrow(nme.PUREARROW) then
18881889
val isImpure = in.token == ARROW
18891890
val tp = atSpan(in.skipToken()) { ByNameTypeTree(core()) }
1890-
if isImpure && ctx.settings.Ycc.value then ImpureByNameTypeTree(tp) else tp
1891+
if isImpure && Feature.pureFunsEnabled then ImpureByNameTypeTree(tp) else tp
18911892
else if in.token == LBRACE && followingIsCaptureSet() then
18921893
val start = in.offset
18931894
val cs = captureSet()

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import Variances.varianceSign
1414
import util.SourcePosition
1515
import scala.util.control.NonFatal
1616
import scala.annotation.switch
17-
import config.Config
17+
import config.{Config, Feature}
1818
import cc.{CapturingType, EventuallyCapturingType, CaptureSet, isBoxed}
1919

2020
class PlainPrinter(_ctx: Context) extends Printer {
@@ -242,7 +242,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
242242
else toText(CapturingType(ExprType(parent), refs))
243243
case ExprType(restp) =>
244244
changePrec(GlobalPrec) {
245-
(if ctx.settings.Ycc.value then "-> " else "=> ") ~ toText(restp)
245+
(if Feature.pureFunsEnabled then "-> " else "=> ") ~ toText(restp)
246246
}
247247
case tp: HKTypeLambda =>
248248
changePrec(GlobalPrec) {

0 commit comments

Comments
 (0)