-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix variance loophole for private vars #18693
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -109,6 +109,13 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => | |||||||||
try op finally noCheckNews = saved | ||||||||||
} | ||||||||||
|
||||||||||
/** The set of all private class variables that are assigned | ||||||||||
* when selected with a qualifier other than the `this` of the owning class. | ||||||||||
* Such variables can contain only invariant type parameters in | ||||||||||
* their types. | ||||||||||
*/ | ||||||||||
private var privateVarsSetNonLocally: Set[Symbol] = Set() | ||||||||||
|
||||||||||
def isCheckable(t: New): Boolean = !inJavaAnnot && !noCheckNews.contains(t) | ||||||||||
|
||||||||||
/** Mark parameter accessors that are aliases of like-named parameters | ||||||||||
|
@@ -149,6 +156,8 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => | |||||||||
private def processMemberDef(tree: Tree)(using Context): tree.type = { | ||||||||||
val sym = tree.symbol | ||||||||||
Checking.checkValidOperator(sym) | ||||||||||
if sym.isClass then | ||||||||||
VarianceChecker.check(tree, privateVarsSetNonLocally) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this work if we find an illegal access from the companion object, defined after the class? IIUC the flow of this phase, we won't explore the A test case would be class BoxWithCompanion[+A](value: A) {
private var cached: A = value // error
def get: A = cached
}
object BoxWithCompanion {
def put[A](box: BoxWithCompanion[A], value: A): Unit = {
box.cached = value
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch; it does not. |
||||||||||
sym.transformAnnotations(transformAnnot) | ||||||||||
sym.defTree = tree | ||||||||||
tree | ||||||||||
|
@@ -257,6 +266,14 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => | |||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
/** Update privateVarsSetNonLocally is symbol is a private variable | ||||||||||
* that is selected from something other than `this` when assigned | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo:
Suggested change
|
||||||||||
*/ | ||||||||||
private def markVarAccess(tree: Tree, qual: Tree)(using Context): Unit = | ||||||||||
val sym = tree.symbol | ||||||||||
if sym.is(Private, butNot = Local) && !sym.isCorrectThisType(qual.tpe) then | ||||||||||
privateVarsSetNonLocally += sym | ||||||||||
|
||||||||||
def checkNoConstructorProxy(tree: Tree)(using Context): Unit = | ||||||||||
if tree.symbol.is(ConstructorProxy) then | ||||||||||
report.error(em"constructor proxy ${tree.symbol} cannot be used as a value", tree.srcPos) | ||||||||||
|
@@ -395,7 +412,6 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => | |||||||||
registerIfHasMacroAnnotations(tree) | ||||||||||
val sym = tree.symbol | ||||||||||
if (sym.isClass) | ||||||||||
VarianceChecker.check(tree) | ||||||||||
annotateExperimental(sym) | ||||||||||
checkMacroAnnotation(sym) | ||||||||||
if sym.isOneOf(GivenOrImplicit) then | ||||||||||
|
@@ -472,6 +488,9 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => | |||||||||
case tpe => tpe | ||||||||||
} | ||||||||||
) | ||||||||||
case Assign(lhs @ Select(qual, _), _) => | ||||||||||
markVarAccess(lhs, qual) | ||||||||||
super.transform(tree) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do I understand correctly that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The setter gets added in a later phase, |
||||||||||
case Typed(Ident(nme.WILDCARD), _) => | ||||||||||
withMode(Mode.Pattern)(super.transform(tree)) | ||||||||||
// The added mode signals that bounds in a pattern need not | ||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
-- Error: tests/neg/i18588.scala:7:14 ---------------------------------------------------------------------------------- | ||
7 | private var cached: A = value // error | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| covariant type A occurs in invariant position in type A of variable cached |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
class ROBox[+A](value: A) { | ||
private var cached: A = value | ||
def get: A = ROBox[A](value).cached | ||
} | ||
|
||
class Box[+A](value: A) { | ||
private var cached: A = value // error | ||
def get: A = cached | ||
|
||
def put[AA >: A](value: AA): Unit = { | ||
val box: Box[AA] = this | ||
box.cached = value | ||
} | ||
} | ||
odersky marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
trait Animal | ||
object Dog extends Animal | ||
object Cat extends Animal | ||
|
||
val dogBox: Box[Dog.type] = new Box(Dog) | ||
val _ = dogBox.put(Cat) | ||
val dog: Dog.type = dogBox.get |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that it's available outside of
isAccessibleFrom
, the nameisCorrectThisType
is not so clear anymore. Consider renaming toisAccessPrivilegedThisType
or something like that?