-
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
add flexible types to deal with Java-defined signatures under -Yexplicit-nulls #18112
Conversation
@noti0na1 I've finished cleaning this up and it's ready for you to review again. Best reviewed all at once. Will need to be squashed before merging, but I wanted to maintain the history for review. |
83d60b7
to
45549b0
Compare
compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala
Outdated
Show resolved
Hide resolved
LGTM overall. |
@odersky suggested to define However, I don't think this is a good idea:
|
It would be better if we can change the definition of This can be useful if we are passing a nullable type to Java, for example, we may encounter cc @olhotak |
In many places a However, a |
Yes, we could. I thought it was unnecessary because we only apply Would this solve the currently failing |
What I would try is to do something like `narrow` in Types.scala
/** A prefix-less refined this or a termRef to a new skolem symbol
* that has the given type as info.
*/
def narrow(using Context): TermRef =
TermRef(NoPrefix, newSkolem(this))
Narrow creates a TermRef with a fresh skolem symbol. Likewise we could
define a flexible type to be a fresh typeref with the two bounds. We should
however be careful to re-use the same flexible type if we encounter the
same bounds (which should be often). This can be done either by relying on
the type hashing function
and treating flexible types as cached types or "by-hand" by adding a one
field flexible type cache to each TypeBounds type. TypeBoudns are hash
consed, so this would do the trick.
…On Thu, Sep 21, 2023 at 5:17 PM Ondřej Lhoták ***@***.***> wrote:
It would be better if we can change the definition of hi of
FlexableType(T) from T to T.stripNulls.
This can be useful if we are passing a nullable type to Java, for example,
we may encounter FlexableType(String | Null) . With the current
definition, FlexableType(String | Null) !<:< String.
cc @olhotak <https://github.com/olhotak>
Yes, we could. I thought it was unnecessary because we only apply
FlexibleType to Java types, which don't have | Null, but you're right
that we also apply it to type variables that could be instantiated with a
nullble type.
Would this solve the currently failing interop-match-src test?
—
Reply to this email directly, view it on GitHub
<#18112 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAGCKVSKJD27EFGTWEZEXBDX3RLCFANCNFSM6AAAAAAZ2GDIDA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
--
Martin Odersky
Professor, Programming Methods Group (LAMP)
Faculty IC, EPFL
Station 14, Lausanne, Switzerland
|
@odersky I have tried the Although creating a simple flexiable type is easy and its subtyping works automatically, we still need to add special cases to many places. For example in Therefore, I think it is better to just add a new type for flexiable types. |
@olhotak outermostLevelAlreadyNullable = tp.classSymbol.is(JavaDefined) && !ctx.flexibleTypes This causes adding extra flexible types to type arguments of Java-defined types. public class Jtest<T> {
public Jtest<T> j = this;
} def test[T](x: Jtest[T]) =
val y = x.j The correct type for The error in |
The Java signature of the <U> CompletableFuture<U> handle(BiFunction<? super T,Throwable,? extends U> fn) If we don't make |
e3ea462
to
24d6ba9
Compare
eb01981
to
7ba5ba1
Compare
ab9f7c5
to
3e7d5ae
Compare
8db3939
to
f7439ff
Compare
Fixed a bug related to |
This has been decided to be included in the 3.5.0 release. |
Can you please rebase over main instead of merging? With the merge in the history I am unable to review. What I try to do is squash all commits of flexible types because with the individual commits its impossible to see what remains to be done. But a merge in the middle makes that impossible. In fact I think it might be best if all flexible types commits were squashed into one. I don't see any increments that we want to handle separately. |
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.
Thanks for the squash. LGTM!
Let's just to a performance test to make sure we don't have slowdowns.
test performance please |
1 similar comment
test performance please |
performance test scheduled: 1 job(s) in queue, 1 running. |
Performance test finished successfully: Visit https://dotty-bench.epfl.ch/18112/ to see the changes. Benchmarks is based on merging with main (447cdf4) |
There is a small thing missing. def isLegalTag(tag: Int): Boolean =
firstSimpleTreeTag <= tag && tag <= SPLITCLAUSE ||
firstNatTreeTag <= tag && tag <= RENAMED ||
firstASTTreeTag <= tag && tag <= BOUNDED ||
firstNatASTTreeTag <= tag && tag <= NAMEDARG ||
- firstLengthTreeTag <= tag && tag <= MATCHCASEtype ||
+ firstLengthTreeTag <= tag && tag <= FLEXIBLEtype ||
tag == HOLE
|
@olhotak The added experimental function |
OK, that's fine. It is needed to port existing code (e.g. in the community build) that currently uses |
This is a continuation of #17369.
When dealing with reference types from Java, it's essential to address the implicit nullability of these types. The most accurate way to represent them in Scala is to use nullable types, though working with lots of nullable types
directly can be annoying. To streamline interactions with Java libraries, we introduce the concept of flexible types.
The flexible type, denoted by
T?
, functions as an abstract type with unique bounds:T | Null ... T
, ensuring thatT | Null <: T? <: T
. The subtyping rule treats a reference type coming from Java as either nullable or non-nullable depending on the context. This concept draws inspiration from Kotlin's platform types. By relaxing null checks for such types, Scala aligns its safety guarantees with those of Java. Notably, flexible types are non-denotable, meaning users cannot explicitly write them in the code; only the compiler can construct or infer these types.Consequently, a value with a flexible type can serve as both a nullable and non-nullable value. Additionally, both nullable and non-nullable values can be passed as parameters with flexible types during function calls. Invoking the member functions of a flexible type is allowed, but it can trigger a
NullPointerException
if the value is indeednull
during runtime.