Skip to content

Commit 5004928

Browse files
committed
Move capvars checks to typer & allow variances
1 parent 9658555 commit 5004928

File tree

9 files changed

+73
-61
lines changed

9 files changed

+73
-61
lines changed

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

+14-29
Original file line numberDiff line numberDiff line change
@@ -1620,10 +1620,7 @@ object Parsers {
16201620
/** CaptureSet ::= `{` CaptureRef {`,` CaptureRef} `}` -- under captureChecking
16211621
*/
16221622
def captureSet(): List[Tree] =
1623-
if in.token != LBRACE then
1624-
syntaxError(em"expected '{' to start capture set", in.offset)
1625-
Nil
1626-
else inBraces {
1623+
inBraces {
16271624
if in.token == RBRACE then Nil else commaSeparated(captureRef)
16281625
}
16291626

@@ -3507,22 +3504,22 @@ object Parsers {
35073504
/** ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’
35083505
* ClsTypeParam ::= {Annotation} [‘+’ | ‘-’]
35093506
* id [HkTypeParamClause] TypeAndCtxBounds
3510-
* | {Annotation} id [`^`] TypeAndCtxBounds -- under captureChecking
3507+
* | {Annotation} [‘+’ | ‘-’] id `^` TypeAndCtxBounds -- under captureChecking
35113508
*
35123509
* DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’
35133510
* DefTypeParam ::= {Annotation}
35143511
* id [HkTypeParamClause] TypeAndCtxBounds
3515-
* | {Annotation} id [`^`] TypeAndCtxBounds -- under captureChecking
3512+
* | {Annotation} id `^` TypeAndCtxBounds -- under captureChecking
35163513
*
35173514
* TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’
35183515
* TypTypeParam ::= {Annotation}
35193516
* (id | ‘_’) [HkTypeParamClause] TypeAndCtxBounds
3520-
* | {Annotation} (id | ‘_’) [`^`] TypeAndCtxBounds -- under captureChecking
3517+
* | {Annotation} (id | ‘_’) `^` TypeAndCtxBounds -- under captureChecking
35213518
*
35223519
* HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} ‘]’
35233520
* HkTypeParam ::= {Annotation} [‘+’ | ‘-’]
35243521
* (id | ‘_’) [HkTypePamClause] TypeBounds
3525-
* | {Annotation} (id | ‘_’) [`^`] TypeBounds -- under captureChecking
3522+
* | {Annotation} [‘+’ | ‘-’] (id | ‘_’) `^` TypeBounds -- under captureChecking
35263523
*/
35273524
def typeParamClause(paramOwner: ParamOwner): List[TypeDef] = inBracketsWithCommas {
35283525

@@ -3548,26 +3545,16 @@ object Parsers {
35483545
WildcardParamName.fresh().toTypeName
35493546
else ident().toTypeName
35503547
val isCap = gobbleHat()
3551-
if isCap then
3552-
if mods.isOneOf(Covariant | Contravariant) then
3553-
syntaxError(em"capture parameters cannot have `+/-` variance annotations") // TODO we might want to allow those
3554-
if in.token == LBRACKET then
3555-
syntaxError(em"capture parameters do not take type parameters")
3556-
in.nextToken()
3557-
end if
35583548
val hkparams = typeParamClauseOpt(ParamOwner.Hk)
35593549
val bounds =
3560-
if !isCap && paramOwner.acceptsCtxBounds then typeAndCtxBounds(name)
3561-
else if !isCap && sourceVersion.enablesNewGivens && paramOwner == ParamOwner.Type then typeAndCtxBounds(name)
3550+
if paramOwner.acceptsCtxBounds then typeAndCtxBounds(name)
3551+
else if sourceVersion.enablesNewGivens && paramOwner == ParamOwner.Type then typeAndCtxBounds(name)
35623552
else typeBounds()
35633553
val res = TypeDef(name, lambdaAbstract(hkparams, bounds)).withMods(mods)
35643554
if isCap then
35653555
res.pushAttachment(CaptureVar, ())
35663556
// putting the attachment here as well makes post-processing in the typer easier
35673557
bounds.pushAttachment(CaptureVar, ())
3568-
val t = contextBounds(name)
3569-
if t.nonEmpty then
3570-
syntaxError(em"capture parameters cannot have context bounds", t.head.span)
35713558
res
35723559
}
35733560
}
@@ -4090,7 +4077,7 @@ object Parsers {
40904077
}
40914078

40924079
/** TypeDef ::= id [HkTypeParamClause] {FunParamClause} TypeAndCtxBounds [‘=’ TypeDefRHS ]
4093-
* | id [`^`] TypeAndCtxBounds [‘=’ TypeDefRHS ] -- under captureChecking
4080+
* | id `^` TypeAndCtxBounds [‘=’ TypeDefRHS ] -- under captureChecking
40944081
* TypeDefRHS ::= Type
40954082
* | CaptureSet -- under captureChecking
40964083
*/
@@ -4105,21 +4092,19 @@ object Parsers {
41054092
atSpan(start, nameStart) {
41064093
val nameIdent = typeIdent()
41074094
val isCapDef = gobbleHat()
4108-
if isCapDef && in.token == LBRACKET then syntaxError(em"capture-set member declarations cannot have type parameters")
41094095
val tname = nameIdent.name.asTypeName
4110-
val tparams = if !isCapDef then typeParamClauseOpt(ParamOwner.Hk) else Nil
4111-
val vparamss = if !isCapDef then funParamClauses() else Nil
4096+
val tparams = typeParamClauseOpt(ParamOwner.Hk)
4097+
val vparamss = funParamClauses()
41124098

41134099
def makeTypeDef(rhs: Tree): Tree = {
41144100
val rhs1 = lambdaAbstractAll(tparams :: vparamss, rhs)
41154101
val tdef = TypeDef(nameIdent.name.toTypeName, rhs1)
41164102
if nameIdent.isBackquoted then
41174103
tdef.pushAttachment(Backquoted, ())
4118-
if isCapDef then rhs.match
4119-
case ContextBounds(_, _) => syntaxError(em"capture-set member declarations cannot have context bounds", rhs.span)
4120-
case rhs => tdef.pushAttachment(CaptureVar, ())
4121-
// putting the attachment here as well makes post-processing in the typer easier
4122-
rhs.pushAttachment(CaptureVar, ())
4104+
if isCapDef then
4105+
tdef.pushAttachment(CaptureVar, ())
4106+
// putting the attachment here as well makes post-processing in the typer easier
4107+
rhs.pushAttachment(CaptureVar, ())
41234108
finalizeDef(tdef, mods, start)
41244109
}
41254110

compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala

-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,6 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
227227
case MatchIsNotPartialFunctionID // errorNumber: 211
228228
case OnlyFullyDependentAppliedConstructorTypeID // errorNumber: 212
229229
case PointlessAppliedConstructorTypeID // errorNumber: 213
230-
case ExpectedCaptureBoundOrEqualsID // errorNumber: 214
231230

232231
def errorNumber = ordinal - 1
233232

compiler/src/dotty/tools/dotc/reporting/messages.scala

-17
Original file line numberDiff line numberDiff line change
@@ -1923,23 +1923,6 @@ class ExpectedTypeBoundOrEquals(found: Token)(using Context)
19231923
|"""
19241924
}
19251925

1926-
class ExpectedCaptureBoundOrEquals(found: Token)(using Context)
1927-
extends SyntaxMsg(ExpectedCaptureBoundOrEqualsID) {
1928-
def msg(using Context) = i"${hl("=")}, ${hl(">:")}, or ${hl("<:")} expected, but ${Tokens.showToken(found)} found"
1929-
1930-
def explain(using Context) =
1931-
i"""Capture parameters and abstract captures may be constrained by a capture bound.
1932-
|Such capture bounds limit the concrete values of the capture variables and possibly
1933-
|reveal more information about the members of such captures.
1934-
|
1935-
|A lower type bound ${hl("B >: A")} expresses that the capture variable ${hl("B")}
1936-
|refers to a super capture of capture ${hl("A")}.
1937-
|
1938-
|An upper capture bound ${hl("T <: A")} declares that capture variable ${hl("T")}
1939-
|refers to a subcapture of ${hl("A")}.
1940-
|"""
1941-
}
1942-
19431926
class ClassAndCompanionNameClash(cls: Symbol, other: Symbol)(using Context)
19441927
extends NamingMsg(ClassAndCompanionNameClashID) {
19451928
def msg(using Context) =

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

+5-3
Original file line numberDiff line numberDiff line change
@@ -2525,7 +2525,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
25252525
def spliced(tree: Tree) = untpd.TypedSplice(tree)
25262526
val tparam = untpd.Ident(tree.paramName).withSpan(tree.span.withEnd(tree.span.point))
25272527
if Feature.ccEnabled && typed(tparam).tpe.derivesFrom(defn.Caps_CapSet) then
2528-
errorTree(tree, em"Capture variable `${tree.paramName}` cannot have a context bound.")
2528+
report.error(em"Capture variable `${tree.paramName}` cannot have a context bound.", tycon.srcPos)
25292529
if tycon.tpe.typeParams.nonEmpty then
25302530
val tycon0 = tycon.withType(tycon.tpe.etaCollapse)
25312531
typed(untpd.AppliedTypeTree(spliced(tycon0), tparam :: Nil))
@@ -3058,9 +3058,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
30583058
rhs1 match
30593059
case TypeBoundsTree(lo, hi, _) =>
30603060
if !isCap && (lo.tpe.derivesFrom(defn.Caps_CapSet) ^ hi.tpe.derivesFrom(defn.Caps_CapSet)) then
3061-
report.error(em"Illegal type bounds: >: $lo <: $hi. Capture-set bounds cannot be mixed with type bounds of other kinds", tdef.srcPos)
3061+
report.error(em"Illegal type bounds: >: $lo <: $hi. Capture-set bounds cannot be mixed with type bounds of other kinds", rhs.srcPos)
30623062
if isCap && !(lo.tpe.derivesFrom(defn.Caps_CapSet) && hi.tpe.derivesFrom(defn.Caps_CapSet)) then
3063-
report.error(em"Illegal type bounds: >: $lo <: $hi. $name^ can only have capture sets as bounds", tdef.srcPos)
3063+
report.error(em"Illegal type bounds: >: $lo <: $hi. $name^ can only have capture sets as bounds", rhs.srcPos)
3064+
case LambdaTypeTree(_, _) if isCap =>
3065+
report.error(em"`$name` cannot have type parameters, because it ranges over capture sets", rhs.srcPos)
30643066
case _ =>
30653067
assignType(cpy.TypeDef(tdef)(name, rhs1), sym)
30663068
}

docs/_docs/internals/syntax.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -378,20 +378,20 @@ ArgumentPatterns ::= ‘(’ [Patterns] ‘)’
378378
ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’
379379
ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] TypeDef(Modifiers, name, tparams, bounds)
380380
id [HkTypeParamClause] TypeAndCtxBounds Bound(below, above, context)
381-
| {Annotation} id [`^`] TypeAndCtxBounds -- under captureChecking
381+
| {Annotation} [‘+’ | ‘-’] id `^` TypeAndCtxBounds -- under captureChecking
382382
383383
DefTypeParamClause::= [nl] ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’
384384
DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeAndCtxBounds
385-
| {Annotation} id [`^`] TypeAndCtxBounds -- under captureChecking
385+
| {Annotation} id `^` TypeAndCtxBounds -- under captureChecking
386386
387387
TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’
388388
TypTypeParam ::= {Annotation} (id | ‘_’) [HkTypeParamClause] TypeAndCtxBounds
389-
| {Annotation} id [`^`] TypeAndCtxBounds -- under captureChecking
389+
| {Annotation} id `^` TypeAndCtxBounds -- under captureChecking
390390
391391
HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} ‘]’
392392
HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (id | ‘_’) [HkTypeParamClause]
393393
TypeBounds
394-
| {Annotation} id [`^`] TypeBounds -- under captureChecking
394+
| {Annotation} [‘+’ | ‘-’] id `^` TypeBounds -- under captureChecking
395395
396396
ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’]
397397
ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’
@@ -481,7 +481,7 @@ DefDef ::= DefSig [‘:’ Type] [‘=’ Expr]
481481
DefSig ::= id [DefParamClauses] [DefImplicitClause]
482482
TypeDef ::= id [HkTypeParamClause] {FunParamClause} TypeAndCtxBounds TypeDefTree(_, name, tparams, bound
483483
[‘=’ TypeDefRHS]
484-
| id [`^`] TypeAndCtxBounds [‘=’ TypeDefRHS] -- under captureChecking
484+
| id `^` TypeAndCtxBounds [‘=’ TypeDefRHS] -- under captureChecking
485485
TypeDefRHS ::= Type
486486
| CaptureSet -- under captureChecking
487487
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
-- Error: tests/neg-custom-args/captures/cap-paramlists6.scala:10:74 ---------------------------------------------------
1+
-- Error: tests/neg-custom-args/captures/cap-paramlists6.scala:10:71 ---------------------------------------------------
22
10 | val baz = () => [C^, D^ <: {C}, E^ <: {C,x}, F^ >: {x,y} <: {C,E} : Ctx, // error
33
| ^^^
4-
| capture parameters cannot have context bounds
5-
-- Error: tests/neg-custom-args/captures/cap-paramlists6.scala:11:54 ---------------------------------------------------
4+
| Capture variable `F` cannot have a context bound.
5+
-- Error: tests/neg-custom-args/captures/cap-paramlists6.scala:11:51 ---------------------------------------------------
66
11 | G >: {} <: {}, H >: {} <: {} : Ctx] => (x: Int) => 1 // error
77
| ^^^
88
| Capture variable `H` cannot have a context bound.
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
-- Error: tests/neg-custom-args/captures/capset-members2.scala:5:9 -----------------------------------------------------
1+
-- Error: tests/neg-custom-args/captures/capset-members2.scala:5:10 ----------------------------------------------------
22
5 | type C^[T] // error
3-
| ^
4-
| capture-set member declarations cannot have type parameters
3+
| ^
4+
| `C` cannot have type parameters, because it ranges over capture sets
5+
-- Error: tests/neg-custom-args/captures/capset-members2.scala:7:13 ----------------------------------------------------
6+
7 | def foo[C^[_]]: Int // error
7+
| ^
8+
| `C` cannot have type parameters, because it ranges over capture sets
9+
-- Error: tests/neg-custom-args/captures/capset-members2.scala:9:20 ----------------------------------------------------
10+
9 | def bar [M[_], C^ <: M]: Int // error
11+
| ^^^^
12+
| Illegal type bounds: >: scala.caps.CapSet <: M. C^ can only have capture sets as bounds

tests/neg-custom-args/captures/capset-members2.scala

+3
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ import caps.*
44
trait Foo:
55
type C^[T] // error
66

7+
def foo[C^[_]]: Int // error
8+
9+
def bar [M[_], C^ <: M]: Int // error
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import language.experimental.captureChecking
2+
3+
trait Test:
4+
type T[-C^]
5+
type U[+C^]
6+
type C^
7+
type D^
8+
9+
def foo[C^](x: T[C]): Unit = ???
10+
def bar(x: T[{}]): Unit = ???
11+
def baz(x: T[{caps.cap}]): Unit = ???
12+
def foo2[C^](x: U[C]): Unit = ???
13+
def bar2(x: U[{}]): Unit = ???
14+
def baz2(x: U[{caps.cap}]): Unit = ???
15+
16+
def test =
17+
val t: T[{C}] = ???
18+
val t2: T[C] = ???
19+
val u: U[{D}] = ???
20+
val u2: U[D] = ???
21+
foo(t)
22+
bar(t)
23+
baz(t) // error
24+
foo2(u)
25+
bar2(u) // error
26+
baz2(u)
27+
foo(t2)
28+
bar(t2)
29+
baz(t2) // error
30+
foo2(u2)
31+
bar2(u2) // error
32+
baz2(u2)

0 commit comments

Comments
 (0)