Skip to content
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 signature computation and caching involving type variables #18092

Merged
merged 7 commits into from
Jul 10, 2023

Conversation

smarter
Copy link
Member

@smarter smarter commented Jun 28, 2023

This fixes the logic taking the signature of a type uninstantiated type variables (using tpnme.Uninstantiated) and non-permanently instantiated type variables (which have a legitimate signature in the currenty TyperState but cannot be cached).
The test case was minimized from fmonniot/scala3mock#2.

@smarter smarter force-pushed the fix-undef-erasure branch 3 times, most recently from d84d6d6 to 47b2f48 Compare June 28, 2023 18:15
@smarter smarter marked this pull request as draft June 28, 2023 18:49
@smarter smarter changed the title Fix signature computation involving nested type variables Fix signature computation and caching involving type variables Jun 30, 2023
@smarter smarter force-pushed the fix-undef-erasure branch 2 times, most recently from 17012c8 to 429dff2 Compare June 30, 2023 15:01
@smarter smarter marked this pull request as ready for review June 30, 2023 16:17
@smarter smarter requested a review from odersky June 30, 2023 16:33
compiler/src/dotty/tools/dotc/core/TypeErasure.scala Outdated Show resolved Hide resolved
else if (arity <= Definitions.MaxTupleArity) defn.TupleType(arity).nn
private def erasePair(tp: Type)(using Context): Type | Null = {
val arity = tupleArity(tp)
if arity == -2 then null // erasure depends on an uninstantiated type variable or WildcardType
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Elsewhere we make sure that we return null only if inSigName is true. But here it seems no such check is done.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

erasePair is called from TypeErasure#apply which does the null check, elsewhere we check that our input is not null which isn't necessary here with non-nullable types.

compiler/src/dotty/tools/dotc/core/TypeErasure.scala Outdated Show resolved Hide resolved
compiler/src/dotty/tools/dotc/core/TypeErasure.scala Outdated Show resolved Hide resolved
@@ -630,7 +671,10 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
else if sourceLanguage.isScala2 then
this(Scala2Erasure.intersectionDominator(Scala2Erasure.flattenedParents(tp)))
else
erasedGlb(this(tp1), this(tp2))
val e1 = this(tp1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be streamlined by moving to WildcardType instead of null

val e1 = this(tp1)
val e2 = this(tp2)
if e1 == null || e2 == null then null
else TypeComparer.orType(e1, e2, isErased = true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be streamlined by moving to WildcardType instead of null

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I switched to WildcardType but didn't find a better place to check for the special value than here, let me know if you had something specific in mind.

compiler/src/dotty/tools/dotc/core/TypeErasure.scala Outdated Show resolved Hide resolved
@odersky odersky assigned smarter and unassigned odersky Jul 1, 2023
@smarter smarter force-pushed the fix-undef-erasure branch 2 times, most recently from 9a59970 to 4f1c26c Compare July 5, 2023 14:11
@smarter smarter assigned odersky and unassigned smarter Jul 5, 2023
Copy link
Contributor

@odersky odersky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

compiler/src/dotty/tools/dotc/core/TypeErasure.scala Outdated Show resolved Hide resolved
@odersky odersky assigned smarter and unassigned odersky Jul 10, 2023
Its meaning will be expanded in the next commit.
During typechecking, we sometimes need to compute the signature of a type
involving uninstantiated type variables or wildcards (in particular, because
signature matching is doen as part of subtype checking for methods), this is
handled with `tpnme.Uninstantiated` which is handled specially in `Signature`.

Before this commit, `sigName` only checked for wildcards and type variables
at the top-level of the type, even though nested types can still have
an impact on type erasure, in particular when they appear as part of:
- an intersection
- a union
- an underlying type of a derived value class
- the element type of an array type
- an element type of a tuple (... *: X *: ...)

We keep track of all these situations by returning `null` in
`TypeErasure#apply` when encountering a wildcard or uninstantiated type
variable, which we then propage upwards to the `sigName` call. This propagation
only happens for `sigName` calls (where `inSigName` is true), otherwise we
throw an assertion since erasure shouldn't normally be computed for
underdefined types.
Signatures should not be cached if they may change. The existing
`isUnderDefined` check is not enough to guarantee this because the signature
might have been computed based on a type variable instantiated in a TyperState
which was subsequently retracted.

To guard against this, we now rely on `Type#isProvisional`. This is a stricter
check than needed since the provisional part of the type does not always have
an impact on its signature, but in practice this is fine: when compiling Dotty,
signature cache misses increased by less than 2%.
I'm afraid we'll have to get rid of it if we want our caches to be correct, but
I don't have the time to look into it right now.
As requested in the review, this is slightly shorter but means we have to be
very careful to not accidentally forget to propagate a WildcardType to the
top-level when computing signatures.
@smarter smarter merged commit ed319e8 into scala:main Jul 10, 2023
17 checks passed
@smarter smarter deleted the fix-undef-erasure branch July 10, 2023 14:15
@Kordyjan Kordyjan added this to the 3.4.0 milestone Aug 1, 2023
@smarter smarter added the backport:nominated If we agree to backport this PR, replace this tag with "backport:accepted", otherwise delete it. label Aug 21, 2023
@Kordyjan Kordyjan removed the backport:nominated If we agree to backport this PR, replace this tag with "backport:accepted", otherwise delete it. label Oct 10, 2023
Kordyjan added a commit that referenced this pull request Dec 8, 2023
…les " to LTS (#19107)

Backports #18092 to the LTS branch.

PR submitted by the release tooling.
[skip ci]
@Kordyjan Kordyjan modified the milestones: 3.4.0, 3.3.2 Dec 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants