Skip to content

Spec changes for explicit nulls - #26941

Open
olhotak wants to merge 4 commits into
scala:mainfrom
dotty-staging:explicit-nulls-spec-changes
Open

Spec changes for explicit nulls#26941
olhotak wants to merge 4 commits into
scala:mainfrom
dotty-staging:explicit-nulls-spec-changes

Conversation

@olhotak

@olhotak olhotak commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

In anticipation of a SIP for explicit nulls, these are proposed changes to the spec, for discussion.

It remains as a TODO to make changes to 08-pattern-matching.md and 12-the-scala-standard-library.md.

Have you relied on LLM-based tools in this contribution?

No

How was the solution tested?

Non-code change, no tests needed

In anticipation of a SIP for explicit nulls, these are proposed changes to the spec, for discussion.

It remains as a TODO to make changes to 08-pattern-matching.md and 12-the-scala-standard-library.md.
Comment thread docs/_spec/03-types.md
Types can be _concrete_ or _abstract_.
An abstract type ´T´ always has lower and upper bounds ´L´ and ´H´ such that ´L >: T´ and ´T <: H´.
A concrete type ´T´ is considered to have itself as both lower and upper bound.
A flexible type ´T?´ has lower bound `´T´ | scala.Null` and upper bound ´T´.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is not the right place for this information. We're talking about two very different Ts. In the existing text, T refers to a type definition, as in type T <: .... But in the new sentence, it refers to a type usage.

Since you added them in the Internal Types, Flexible Types are their own dedicated thing (not a special abstract type member with bounds). In that case, you should:

  • Add a new section "3.3.14 Flexible Types" to define what flexible types are, and notably say that T? is only valid when T is a proper type.
  • Add typing rules for T? <: T and T | Null <: T? (which you already did).

If instead, flexible types are supposed to be a mere abstract type with weird bounds, then they shouldn't even appear in the type system. They should only be in the standard library section, under "Fundamental Type Aliases":

type <flexible-type>[T] >: T | Null <: T

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We'll need more offline discussion about which way to go for the spec.

One issue is that if the go with the applied type formulation, the following rule has to be disabled for flexible types:

scala3/docs/_spec/03-types.md

Lines 1448 to 1457 in 1077009

- ´S = p.X[S_1, ..., S_n]´ and ´T = q.X[T_1, ..., T_n]´ are possibly parameterized type designators with ´n \geq 0´ and:
- `isSubPrefix(´p´, ´q´)`, and
- it is not the case that ´p.x´ and ´q.X´ are class type designators for different classes, and
- for each ´i \in \{ 1, ..., n \}´:
- the ´i´th type parameter of ´q.X´ is covariant and ´S_i <: T_i´ [^argisnotwildcard], or
- the ´i´th type parameter of ´q.X´ is contravariant and ´T_i <: S_i´ [^argisnotwildcard], or
- the ´i´th type parameter of ´q.X´ is invariant and:
- ´S_i´ and ´T_i´ are types and ´S_i =:= T_i´, or
- ´S_i´ is a type and ´T_i´ is a wildcard type argument of the form ´? >: L_2 <: H_2´ and ´L_2 <: S_i´ and ´S_i <: H_2´, or
- ´S_i´ is a wildcard type argument of the form ´? >: L_1 <: H_1´ and ´T_i´ is a wildcard type argument of the form ´? >: L_2 <: H_2´ and ´L_2 <: L_1´ and ´H_1 <: H_2´ (i.e., the ´S_i´ "interval" is contained in the ´T_i´ "interval").

- ´S = p.X[S_1, ..., S_n]´ and ´T = q.X[T_1, ..., T_n]´ are possibly parameterized type designators with ´n \geq 0´ and:
  - `isSubPrefix(´p´, ´q´)`, and
  - it is not the case that ´p.x´ and ´q.X´ are class type designators for different classes, and
  - for each ´i \in \{ 1, ..., n \}´:
      - the ´i´th type parameter of ´q.X´ is covariant and ´S_i <: T_i´ [^argisnotwildcard], or
      - the ´i´th type parameter of ´q.X´ is contravariant and ´T_i <: S_i´ [^argisnotwildcard], or
      - the ´i´th type parameter of ´q.X´ is invariant and:
          - ´S_i´ and ´T_i´ are types and ´S_i =:= T_i´, or
          - ´S_i´ is a type and ´T_i´ is a wildcard type argument of the form ´? >: L_2 <: H_2´ and ´L_2 <: S_i´ and ´S_i <: H_2´, or
          - ´S_i´ is a wildcard type argument of the form ´? >: L_1 <: H_1´ and ´T_i´ is a wildcard type argument of the form ´? >: L_2 <: H_2´ and ´L_2 <: L_1´ and ´H_1 <: H_2´ (i.e., the ´S_i´ "interval" is contained in the ´T_i´ "interval").

Otherwise we get a mess of kind mismatches because FlexibleType[T] <: T.

The PR #26245 that represents flexible types as applied types disables a big pile of complicated logic for applied types when it sees a flexible type:
https://github.com/scala/scala3/pull/26245/changes#diff-c163f9660fb06bb487b0120921d06353fc52154ae1b3066f58da1c73ecd00914R1290
https://github.com/scala/scala3/pull/26245/changes#diff-c163f9660fb06bb487b0120921d06353fc52154ae1b3066f58da1c73ecd00914R1512

So if we go with the applied type formulation for the spec, we'll need to reflect that disabling in the spec.

Ideally what we'd really want is for a flexible type to have a type bounds as underlying, like an applied type does, but without all the messy complexity that comes from the application to type arguments (and how subtyping between applied types imposes new subtyping constraints between those arguments).

cc @odersky for your thoughts.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The issue is more complicated than my comment suggests. A minimized example of what goes wrong without the exclusion of flexible types from the applied type handling is in the following test case in the PR:
https://github.com/scala/scala3/pull/26245/changes#diff-01ec396fd41d506d897e07f992e05506c89a349d4e5a22cba39a78dd5abf66b3

Comment thread docs/_spec/03-types.md Outdated
Comment thread docs/_spec/06-expressions.md Outdated
olhotak and others added 3 commits August 28, 2026 16:06
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.

2 participants