-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Implement avoid in terms of AproximatingTypeMap #2945
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
Conversation
48def35
to
139b248
Compare
@@ -64,9 +64,19 @@ trait TypeAssigner { | |||
} | |||
|
|||
/** An upper approximation of the given type `tp` that does not refer to any symbol in `symsToAvoid`. | |||
* We need to approximate with ranges: | |||
* | |||
* term references to symbols in `symsToAvoid`, |
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.
This would be easier to read with -
in front of each list item.
/** Needs to handle the case where the prefix type does not have a member | ||
* named `tp.name` anymmore. | ||
/** Two deviations from standard derivedSelect: | ||
* 1. The teh approximation result is a singleton references C#x.type, we |
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.
Extra teh
in the sentence ?
val parentType = info.parentsWithArgs.reduceLeft(ctx.typeComparer.andType(_, _)) | ||
def addRefinement(parent: Type, decl: Symbol) = { | ||
val inherited = | ||
parentType.findMember(decl.name, info.cls.thisType, Private) |
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 would explicitly write excluded = Private
for clarity.
} | ||
avoided |
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 the intermediate avoided
was used for debugging, but its definition could be inlined here.
def apply(tp: Type): Type = { | ||
|
||
/** Map a `C.this` type to the right prefix. If the prefix is unstable and | ||
* the `C.this` occurs in nonvariant or contravariant position, mark the map |
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 don't think we mark the map to be unstable anymore.
case _ => | ||
if (thiscls.derivesFrom(cls) && pre.baseTypeRef(thiscls).exists) { | ||
if (theMap != null && theMap.currentVariance <= 0 && !isLegalPrefix(pre)) { | ||
ctx.base.unsafeNonvariant = ctx.runId |
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.
The field unsafeNonvariant
isn't used anymore and should be removed.
case tp: NamedType => // inlined for performance; TODO: factor out into inline method | ||
if (tp.symbol.isStatic) tp | ||
else { | ||
val saved = variance |
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.
Should use atVariance
like the base case does.
.mapOver(tp) | ||
case tp: ThisType => | ||
toPrefix(pre, cls, tp.cls) | ||
case _: BoundType | NoPrefix => |
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.
Add // Inlined for performance
? Same for the two cases below.
if (v1 > 0 && v2 > 0) propagate(infoLo, infoHi) | ||
else if (v1 < 0 && v2 < 0) propagate(infoHi, infoLo) | ||
else if (!infoLo.isAlias && !infoHi.isAlias) propagate(infoLo, infoHi) | ||
else range(tp.bottomType, tp.topType) |
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.
Could parent
be used instead of tp.topType
?
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 added a comment explaining why not:
// Using `parent` instead of `tp.topType` would be better for normal refinements,
// but it would also turn *-types to a hk-types, which is not what we want.
// We should revisit this point in case we represent applied types not as refinements anymore.
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.
it would also turn *-types to a hk-types
Would it? TypeApplications#isHK
only returns true for HKTypeLambda
, and before this PR avoid
used to return the parent for refinement types and it worked fine.
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.
Alternatively we could return derivedRefinedType(tp, parent, WildcardType) I think
if (distributeArgs(args, tp.typeParams)) | ||
range(tp.derivedAppliedType(tycon, loBuf.toList), | ||
tp.derivedAppliedType(tycon, hiBuf.toList)) | ||
else range(tp.bottomType, tp.topType) |
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.
Could tp.superType
be used instead of tp.topType
?
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.
Not sure. tp.superType
is not necessarily a supertype of the derived application because the arguments have changed. We could try to re-do the application on the upper bound of the constructor, but this looks murky with what we know right now. Theoretical underpinnings would be good to have here!
The new object Test {
def test = {
object Hi {
type A = Int
}
val x: Hi.A = 1
List(x)
}
val hi: List[Int] = test // Fails because `test` has type `List[Any]` instead of `List[Int]`
} Because when we avoid diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
index 4340cae22c..11849b870e 100644
--- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
+++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
@@ -49,7 +49,7 @@ trait TypeAssigner {
parentType.findMember(decl.name, info.cls.thisType, excluded = Private)
.suchThat(decl.matches(_))
val inheritedInfo = inherited.info
- if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info)) {
+ if (!inheritedInfo.exists || (decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info))) {
val r = RefinedType(parent, decl.name, decl.info)
typr.println(i"add ref $parent $decl --> " + r)
r Do you think that's a good way to go? |
137431b
to
de19138
Compare
@smarter I developed another fix, which is more direct. |
How come we don't avoid type symbols in TypeAssigner anyway? |
I think some of the later phases might allow anonymous classes defined in one block to be used in another. As long as they are still in the same method, that's OK. But I am not sure about specifics. |
OK, opened #2987 to track this. |
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.
LGTM!
* 1. We first try a widening conversion to the type's info with | ||
* the original prefix. Since the original prefix is known to | ||
* be a subtype of the returned prefix, this can improve results. | ||
* 2. IThen, if the approximation result is a singleton reference C#x.type, we |
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.
typo: IThen -> Then
e7e643c
to
45ffdf0
Compare
The logic before was overcomplicated since I did not take into account that Range refinedInfos could only happen at variance 0. On the othet hand, it did not take into account all the subtleties of alias types with variances.
Test case in i2945.scala.
For a Block, TypeAssigner does an avoid just for term symbols whereas Typer does one for all local symbols. Since Typer called TypeAssigner, we got a double avoidance pass. This is eliminated by assigning types directly in Typer without passing through TypeAssigner.
45ffdf0
to
491fe97
Compare
test performance please |
performance test scheduled: 1 jobs in total. |
Performance test finished successfully: Visit https://liufengyun.github.io/bench/2945 to see the changes. |
Based on #2908. Last two commits are new.