Skip to content

Commit 5fe047b

Browse files
authored
Backport "Make explicit arguments for context bounds an error from 3.5" (#19476)
Backports #19316 to 3.4.0
2 parents 8be96b0 + 48e0340 commit 5fe047b

File tree

7 files changed

+155
-73
lines changed

7 files changed

+155
-73
lines changed

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,8 @@ object desugar {
674674
val nu = vparamss.foldLeft(makeNew(classTypeRef)) { (nu, vparams) =>
675675
val app = Apply(nu, vparams.map(refOfDef))
676676
vparams match {
677-
case vparam :: _ if vparam.mods.is(Given) => app.setApplyKind(ApplyKind.Using)
677+
case vparam :: _ if vparam.mods.is(Given) || vparam.name.is(ContextBoundParamName) =>
678+
app.setApplyKind(ApplyKind.Using)
678679
case _ => app
679680
}
680681
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ object DesugarEnums {
9999
val clazzOf = TypeApply(ref(defn.Predef_classOf.termRef), tpt :: Nil)
100100
val ctag = Apply(TypeApply(ref(defn.ClassTagModule_apply.termRef), tpt :: Nil), clazzOf :: Nil)
101101
val apply = Select(ref(defn.ArrayModule.termRef), nme.apply)
102-
Apply(Apply(TypeApply(apply, tpt :: Nil), values), ctag :: Nil)
102+
Apply(Apply(TypeApply(apply, tpt :: Nil), values), ctag :: Nil).setApplyKind(ApplyKind.Using)
103103

104104
/** The following lists of definitions for an enum type E and known value cases e_0, ..., e_n:
105105
*

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

+11-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@ import SourceVersion.*
66
import Feature.*
77
import core.Contexts.Context
88

9-
class MigrationVersion(val warnFrom: SourceVersion, val errorFrom: SourceVersion):
10-
assert(warnFrom.ordinal <= errorFrom.ordinal)
9+
class MigrationVersion(
10+
val warnFrom: SourceVersion,
11+
val errorFrom: SourceVersion):
12+
require(warnFrom.ordinal <= errorFrom.ordinal)
13+
1114
def needsPatch(using Context): Boolean =
12-
sourceVersion.isMigrating && sourceVersion.isAtLeast(errorFrom)
15+
sourceVersion.isMigrating && sourceVersion.isAtLeast(warnFrom)
16+
17+
def patchFrom: SourceVersion =
18+
warnFrom.prevMigrating
1319

1420
object MigrationVersion:
1521

@@ -27,6 +33,8 @@ object MigrationVersion:
2733

2834
val AscriptionAfterPattern = MigrationVersion(`3.3`, future)
2935

36+
val ExplicitContextBoundArgument = MigrationVersion(`3.4`, future)
37+
3038
val AlphanumericInfix = MigrationVersion(`3.4`, future)
3139
val RemoveThisQualifier = MigrationVersion(`3.4`, future)
3240
val UninitializedVars = MigrationVersion(`3.4`, future)

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

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ enum SourceVersion:
1919
def stable: SourceVersion =
2020
if isMigrating then SourceVersion.values(ordinal + 1) else this
2121

22+
def prevMigrating: SourceVersion =
23+
if isMigrating then this else SourceVersion.values(ordinal - 1).prevMigrating
24+
2225
def isAtLeast(v: SourceVersion) = stable.ordinal >= v.ordinal
2326

2427
def isAtMost(v: SourceVersion) = stable.ordinal <= v.ordinal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package dotty.tools
2+
package dotc
3+
package typer
4+
5+
import core.*
6+
import ast.*
7+
import Contexts.*
8+
import Types.*
9+
import Flags.*
10+
import Names.*
11+
import StdNames.*
12+
import Symbols.*
13+
import Trees.*
14+
import ProtoTypes.*
15+
import Decorators.*
16+
import config.MigrationVersion as mv
17+
import config.Feature.{sourceVersion, migrateTo3}
18+
import config.SourceVersion.*
19+
import reporting.*
20+
import NameKinds.ContextBoundParamName
21+
import rewrites.Rewrites.patch
22+
import util.Spans.Span
23+
import rewrites.Rewrites
24+
25+
/** A utility trait containing source-dependent deprecation messages
26+
* and migrations.
27+
*/
28+
trait Migrations:
29+
this: Typer =>
30+
31+
import tpd.*
32+
33+
/** Run `migration`, asserting we are in the proper Typer (not a ReTyper) */
34+
inline def migrate[T](inline migration: T): T =
35+
assert(!this.isInstanceOf[ReTyper])
36+
migration
37+
38+
/** Run `migration`, provided we are in the proper Typer (not a ReTyper) */
39+
inline def migrate(inline migration: Unit): Unit =
40+
if !this.isInstanceOf[ReTyper] then migration
41+
42+
/** Flag & migrate `?` used as a higher-kinded type parameter
43+
* Warning in 3.0-migration, error from 3.0
44+
*/
45+
def kindProjectorQMark(tree: untpd.TypeDef, sym: Symbol)(using Context): Unit =
46+
if tree.name eq tpnme.? then
47+
val addendum = if sym.owner.is(TypeParam)
48+
then ", use `_` to denote a higher-kinded type parameter"
49+
else ""
50+
val namePos = tree.sourcePos.withSpan(tree.nameSpan)
51+
report.errorOrMigrationWarning(
52+
em"`?` is not a valid type name$addendum", namePos, mv.Scala2to3)
53+
54+
def typedAsFunction(tree: untpd.PostfixOp, pt: Type)(using Context): Tree = {
55+
val untpd.PostfixOp(qual, Ident(nme.WILDCARD)) = tree: @unchecked
56+
val pt1 = if (defn.isFunctionNType(pt)) pt else AnyFunctionProto
57+
val nestedCtx = ctx.fresh.setNewTyperState()
58+
val res = typed(qual, pt1)(using nestedCtx)
59+
res match {
60+
case closure(_, _, _) =>
61+
case _ =>
62+
val recovered = typed(qual)(using ctx.fresh.setExploreTyperState())
63+
val msg = OnlyFunctionsCanBeFollowedByUnderscore(recovered.tpe.widen, tree)
64+
report.errorOrMigrationWarning(msg, tree.srcPos, mv.Scala2to3)
65+
if mv.Scala2to3.needsPatch then
66+
// Under -rewrite, patch `x _` to `(() => x)`
67+
msg.actions
68+
.headOption
69+
.foreach(Rewrites.applyAction)
70+
return typed(untpd.Function(Nil, qual), pt)
71+
}
72+
nestedCtx.typerState.commit()
73+
74+
lazy val (prefix, suffix) = res match {
75+
case Block(mdef @ DefDef(_, vparams :: Nil, _, _) :: Nil, _: Closure) =>
76+
val arity = vparams.length
77+
if (arity > 0) ("", "") else ("(() => ", "())")
78+
case _ =>
79+
("(() => ", ")")
80+
}
81+
val mversion = mv.FunctionUnderscore
82+
def remedy =
83+
if ((prefix ++ suffix).isEmpty) "simply leave out the trailing ` _`"
84+
else s"use `$prefix<function>$suffix` instead"
85+
def rewrite = Message.rewriteNotice("This construct", mversion.patchFrom)
86+
report.errorOrMigrationWarning(
87+
em"""The syntax `<function> _` is no longer supported;
88+
|you can $remedy$rewrite""",
89+
tree.srcPos, mversion)
90+
if mversion.needsPatch then
91+
patch(Span(tree.span.start), prefix)
92+
patch(Span(qual.span.end, tree.span.end), suffix)
93+
94+
res
95+
}
96+
97+
/** Flag & migrate explicit normal arguments to parameters coming from context bounds
98+
* Warning in 3.4, error in 3.5, rewrite in 3.5-migration.
99+
*/
100+
def contextBoundParams(tree: Tree, tp: Type, pt: FunProto)(using Context): Unit =
101+
val mversion = mv.ExplicitContextBoundArgument
102+
def isContextBoundParams = tp.stripPoly match
103+
case MethodType(ContextBoundParamName(_) :: _) => true
104+
case _ => false
105+
if sourceVersion.isAtLeast(`3.4`)
106+
&& isContextBoundParams
107+
&& pt.applyKind != ApplyKind.Using
108+
then
109+
def rewriteMsg = Message.rewriteNotice("This code", mversion.patchFrom)
110+
report.errorOrMigrationWarning(
111+
em"""Context bounds will map to context parameters.
112+
|A `using` clause is needed to pass explicit arguments to them.$rewriteMsg""",
113+
tree.srcPos, mversion)
114+
if mversion.needsPatch then
115+
patch(Span(pt.args.head.span.start), "using ")
116+
end contextBoundParams
117+
118+
end Migrations

compiler/src/dotty/tools/dotc/typer/Typer.scala

+10-68
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import config.Printers.{gadts, typr}
4343
import config.Feature
4444
import config.Feature.{sourceVersion, migrateTo3}
4545
import config.SourceVersion.*
46-
import rewrites.Rewrites.patch
46+
import rewrites.Rewrites, Rewrites.patch
4747
import staging.StagingLevel
4848
import reporting.*
4949
import Nullables.*
@@ -53,7 +53,6 @@ import config.Config
5353
import config.MigrationVersion
5454

5555
import scala.annotation.constructorOnly
56-
import dotty.tools.dotc.rewrites.Rewrites
5756

5857
object Typer {
5958

@@ -127,7 +126,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
127126
with Dynamic
128127
with Checking
129128
with QuotesAndSplices
130-
with Deriving {
129+
with Deriving
130+
with Migrations {
131131

132132
import Typer.*
133133
import tpd.{cpy => _, _}
@@ -2978,48 +2978,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
29782978
else tree1
29792979
}
29802980

2981-
def typedAsFunction(tree: untpd.PostfixOp, pt: Type)(using Context): Tree = {
2982-
val untpd.PostfixOp(qual, Ident(nme.WILDCARD)) = tree: @unchecked
2983-
val pt1 = if (defn.isFunctionNType(pt)) pt else AnyFunctionProto
2984-
val nestedCtx = ctx.fresh.setNewTyperState()
2985-
val res = typed(qual, pt1)(using nestedCtx)
2986-
res match {
2987-
case closure(_, _, _) =>
2988-
case _ =>
2989-
val recovered = typed(qual)(using ctx.fresh.setExploreTyperState())
2990-
val msg = OnlyFunctionsCanBeFollowedByUnderscore(recovered.tpe.widen, tree)
2991-
report.errorOrMigrationWarning(msg, tree.srcPos, MigrationVersion.Scala2to3)
2992-
if MigrationVersion.Scala2to3.needsPatch then
2993-
// Under -rewrite, patch `x _` to `(() => x)`
2994-
msg.actions
2995-
.headOption
2996-
.foreach(Rewrites.applyAction)
2997-
return typed(untpd.Function(Nil, qual), pt)
2998-
}
2999-
nestedCtx.typerState.commit()
3000-
3001-
lazy val (prefix, suffix) = res match {
3002-
case Block(mdef @ DefDef(_, vparams :: Nil, _, _) :: Nil, _: Closure) =>
3003-
val arity = vparams.length
3004-
if (arity > 0) ("", "") else ("(() => ", "())")
3005-
case _ =>
3006-
("(() => ", ")")
3007-
}
3008-
def remedy =
3009-
if ((prefix ++ suffix).isEmpty) "simply leave out the trailing ` _`"
3010-
else s"use `$prefix<function>$suffix` instead"
3011-
def rewrite = Message.rewriteNotice("This construct", `3.4-migration`)
3012-
report.errorOrMigrationWarning(
3013-
em"""The syntax `<function> _` is no longer supported;
3014-
|you can $remedy$rewrite""",
3015-
tree.srcPos,
3016-
MigrationVersion.FunctionUnderscore)
3017-
if MigrationVersion.FunctionUnderscore.needsPatch then
3018-
patch(Span(tree.span.start), prefix)
3019-
patch(Span(qual.span.end, tree.span.end), suffix)
3020-
3021-
res
3022-
}
2981+
override def typedAsFunction(tree: untpd.PostfixOp, pt: Type)(using Context): Tree =
2982+
migrate(super.typedAsFunction(tree, pt))
30232983

30242984
/** Translate infix operation expression `l op r` to
30252985
*
@@ -3137,13 +3097,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
31373097
case tree: untpd.TypeDef =>
31383098
// separate method to keep dispatching method `typedNamed` short which might help the JIT
31393099
def typedTypeOrClassDef: Tree =
3140-
if tree.name eq tpnme.? then
3141-
val addendum = if sym.owner.is(TypeParam)
3142-
then ", use `_` to denote a higher-kinded type parameter"
3143-
else ""
3144-
val namePos = tree.sourcePos.withSpan(tree.nameSpan)
3145-
report.errorOrMigrationWarning(
3146-
em"`?` is not a valid type name$addendum", namePos, MigrationVersion.Scala2to3)
3100+
migrate(kindProjectorQMark(tree, sym))
31473101
if tree.isClassDef then
31483102
typedClassDef(tree, sym.asClass)(using ctx.localContext(tree, sym))
31493103
else
@@ -3818,24 +3772,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
38183772
def adaptToArgs(wtp: Type, pt: FunProto): Tree = wtp match {
38193773
case wtp: MethodOrPoly =>
38203774
def methodStr = methPart(tree).symbol.showLocated
3821-
if (matchingApply(wtp, pt))
3775+
if matchingApply(wtp, pt) then
3776+
migrate(contextBoundParams(tree, wtp, pt))
38223777
if needsTupledDual(wtp, pt) then adapt(tree, pt.tupledDual, locked)
38233778
else tree
38243779
else if wtp.isContextualMethod then
3825-
def isContextBoundParams = wtp.stripPoly match
3826-
case MethodType(ContextBoundParamName(_) :: _) => true
3827-
case _ => false
3828-
if sourceVersion == `future-migration` && isContextBoundParams && pt.args.nonEmpty
3829-
then // Under future-migration, don't infer implicit arguments yet for parameters
3830-
// coming from context bounds. Issue a warning instead and offer a patch.
3831-
def rewriteMsg = Message.rewriteNotice("This code", `future-migration`)
3832-
report.migrationWarning(
3833-
em"""Context bounds will map to context parameters.
3834-
|A `using` clause is needed to pass explicit arguments to them.$rewriteMsg""", tree.srcPos)
3835-
patch(Span(pt.args.head.span.start), "using ")
3836-
tree
3837-
else
3838-
adaptNoArgs(wtp) // insert arguments implicitly
3780+
adaptNoArgs(wtp) // insert arguments implicitly
38393781
else if (tree.symbol.isPrimaryConstructor && tree.symbol.info.firstParamTypes.isEmpty)
38403782
readapt(tree.appliedToNone) // insert () to primary constructors
38413783
else
@@ -4441,7 +4383,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
44414383
protected def matchingApply(methType: MethodOrPoly, pt: FunProto)(using Context): Boolean =
44424384
val isUsingApply = pt.applyKind == ApplyKind.Using
44434385
methType.isContextualMethod == isUsingApply
4444-
|| methType.isImplicitMethod && isUsingApply // for a transition allow `with` arguments for regular implicit parameters
4386+
|| methType.isImplicitMethod && isUsingApply // for a transition allow `using` arguments for regular implicit parameters
44454387

44464388
/** Check that `tree == x: pt` is typeable. Used when checking a pattern
44474389
* against a selector of type `pt`. This implementation accounts for
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//> using options -source 3.4
2+
3+
class C[T]
4+
def foo[X: C] = ()
5+
6+
given [T]: C[T] = C[T]()
7+
8+
def Test =
9+
foo(C[Int]()) // warn
10+
foo(using C[Int]()) // ok

0 commit comments

Comments
 (0)