-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix #5279: Be more careful where we create symbolic refs from named ones #5287
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
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1917,6 +1917,10 @@ object Types { | |
|| | ||
lastSymbol.infoOrCompleter.isInstanceOf[ErrorType] | ||
|| | ||
!sym.exists | ||
|| | ||
!lastSymbol.exists | ||
|| | ||
sym.isPackageObject // package objects can be visited before we get around to index them | ||
|| | ||
sym.owner != lastSymbol.owner && | ||
|
@@ -2070,17 +2074,36 @@ object Types { | |
else this | ||
|
||
/** A reference like this one, but with the given denotation, if it exists. | ||
* If the symbol of `denot` is the same as the current symbol, the denotation | ||
* is re-used, otherwise a new one is created. | ||
* Returns a new named type with the denotation's symbol if that symbol exists, and | ||
* one of the following alternatives applies: | ||
* 1. The current designator is a symbol and the symbols differ, or | ||
* 2. The current designator is a name and the new symbolic named type | ||
* does not have a currently known denotation. | ||
* 3. The current designator is a name and the new symbolic named type | ||
* has the same info as the current info | ||
* Otherwise the current denotation is overwritten with the given one. | ||
* | ||
* Note: (2) and (3) are a "lock in mechanism" where a reference with a name as | ||
* designator can turn into a symbolic reference. | ||
* | ||
* Note: This is a subtle dance to keep the balance between going to symbolic | ||
* references as much as we can (since otherwise we'd risk getting cycles) | ||
* and to still not lose any type info in the denotation (since symbolic | ||
* references often recompute their info directly from the symbol's info). | ||
* A test case is neg/opaque-self-encoding.scala. | ||
*/ | ||
final def withDenot(denot: Denotation)(implicit ctx: Context): ThisType = | ||
if (denot.exists) { | ||
val adapted = withSym(denot.symbol) | ||
if (adapted ne this) adapted.withDenot(denot).asInstanceOf[ThisType] | ||
else { | ||
setDenot(denot) | ||
this | ||
} | ||
val result = | ||
if (adapted.eq(this) | ||
|| designator.isInstanceOf[Symbol] | ||
|| !adapted.denotationIsCurrent | ||
|| adapted.info.eq(denot.info)) | ||
adapted | ||
else this | ||
result.setDenot(denot) | ||
result.asInstanceOf[ThisType] | ||
} | ||
else // don't assign NoDenotation, we might need to recover later. Test case is pos/avoid.scala. | ||
this | ||
|
@@ -2181,6 +2204,11 @@ object Types { | |
override protected def designator_=(d: Designator): Unit = myDesignator = d | ||
|
||
override def underlying(implicit ctx: Context): Type = info | ||
|
||
/** Hook that can be called from creation methods in TermRef and TypeRef */ | ||
def validated(implicit ctx: Context): this.type = { | ||
this | ||
} | ||
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.
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. Yes, I do use if occasionally for debugging, and decided to leave it in for others to use as well if needed. |
||
} | ||
|
||
final class CachedTermRef(prefix: Type, designator: Designator, hc: Int) extends TermRef(prefix, designator) { | ||
|
@@ -2197,6 +2225,23 @@ object Types { | |
private def assertUnerased()(implicit ctx: Context) = | ||
if (Config.checkUnerased) assert(!ctx.phase.erasedTypes) | ||
|
||
/** The designator to be used for a named type creation with given prefix, name, and denotation. | ||
* This is the denotation's symbol, if it exists and the prefix is not the this type | ||
* of the class owning the symbol. The reason for the latter qualification is that | ||
* when re-computing the denotation of a `this.<symbol>` reference we read the | ||
* type directly off the symbol. But the given denotation might contain a more precise | ||
* type than what can be computed from the symbol's info. We have to create in this case | ||
* a reference with a name as designator so that the denotation will be correctly updated in | ||
* the future. See also NamedType#withDenot. Test case is neg/opaque-self-encoding.scala. | ||
*/ | ||
private def designatorFor(prefix: Type, name: Name, denot: Denotation)(implicit ctx: Context): Designator = { | ||
val sym = denot.symbol | ||
if (sym.exists && (prefix.eq(NoPrefix) || prefix.ne(sym.owner.thisType))) | ||
sym | ||
else | ||
name | ||
} | ||
|
||
object NamedType { | ||
def isType(desig: Designator)(implicit ctx: Context): Boolean = desig match { | ||
case sym: Symbol => sym.isType | ||
|
@@ -2220,7 +2265,7 @@ object Types { | |
* from the denotation's symbol if the latter exists, or else it is the given name. | ||
*/ | ||
def apply(prefix: Type, name: TermName, denot: Denotation)(implicit ctx: Context): TermRef = | ||
apply(prefix, if (denot.symbol.exists) denot.symbol.asTerm else name).withDenot(denot) | ||
apply(prefix, designatorFor(prefix, name, denot)).withDenot(denot) | ||
} | ||
|
||
object TypeRef { | ||
|
@@ -2233,7 +2278,7 @@ object Types { | |
* from the denotation's symbol if the latter exists, or else it is the given name. | ||
*/ | ||
def apply(prefix: Type, name: TypeName, denot: Denotation)(implicit ctx: Context): TypeRef = | ||
apply(prefix, if (denot.symbol.exists) denot.symbol.asType else name).withDenot(denot) | ||
apply(prefix, designatorFor(prefix, name, denot)).withDenot(denot) | ||
} | ||
|
||
// --- Other SingletonTypes: ThisType/SuperType/ConstantType --------------------------- | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
trait TS { type TX = Int } | ||
trait TT { self: { type TX = Int } => | ||
type TX | ||
def lift(x: Int): TX = x | ||
} | ||
|
||
class Test { | ||
val t = new TT {} | ||
t.lift(1): Int // error | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
trait TS { type TX = Int } | ||
trait TT { self: { type TX = Int } => | ||
type TX | ||
def lift(x: Int): TX = x | ||
} | ||
|
||
// A more direct version | ||
|
||
trait UU { | ||
type UX | ||
val u: UX | ||
val x: this.type & { type UX = Int } | ||
val y: Int = x.u | ||
val z: x.UX = y | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
I guess
adapted.eq(this)
is a performance tweak, semantically we can drop it without changing the result.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.
Exact.