Skip to content

Commit edf26d3

Browse files
committed
Fix cyclic complete and inference
1 parent ad580ce commit edf26d3

File tree

6 files changed

+36
-17
lines changed

6 files changed

+36
-17
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import collection.{immutable, mutable}
1414
import util.{Property, SourceFile, NoSource}
1515
import NameKinds.{TempResultName, OuterSelectName}
1616
import typer.ConstFold
17+
import typer.Nullables
1718

1819
import scala.annotation.tailrec
1920
import scala.io.Codec
@@ -935,7 +936,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
935936

936937
/** The current tree applied to given type argument list: `tree[targs(0), ..., targs(targs.length - 1)]` */
937938
def appliedToTypeTrees(targs: List[Tree])(using Context): Tree =
938-
if (targs.isEmpty) tree else TypeApply(tree, targs)
939+
if targs.isEmpty then tree else
940+
val app = TypeApply(tree, targs)
941+
app.putAttachment(Nullables.UnsafeNullsKey, config.Feature.unsafeNullsEnabled)
942+
app
939943

940944
/** Apply to `()` unless tree's widened type is parameterless */
941945
def ensureApplied(using Context): Tree =

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,13 @@ object Types {
214214
def isAnyRef(using Context): Boolean = isRef(defn.ObjectClass, skipRefined = false)
215215
def isAnyKind(using Context): Boolean = isRef(defn.AnyKindClass, skipRefined = false)
216216

217+
/** Is this type exactly Null (no vars, aliases, refinements etc allowed)? */
218+
def isExactlyNull(using Context): Boolean = this match {
219+
case tp: TypeRef =>
220+
tp.name == tpnme.Null && (tp.symbol eq defn.NullClass)
221+
case _ => false
222+
}
223+
217224
/** Is this type exactly Nothing (no vars, aliases, refinements etc allowed)? */
218225
def isExactlyNothing(using Context): Boolean = this match {
219226
case tp: TypeRef =>

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import core._
77
import dotty.tools.dotc.typer.Checking
88
import dotty.tools.dotc.typer.Inliner
99
import dotty.tools.dotc.typer.VarianceChecker
10+
import dotty.tools.dotc.typer.Nullables
1011
import Types._, Contexts._, Names._, Flags._, DenotTransformers._, Phases._
1112
import SymDenotations._, StdNames._, Annotations._, Trees._, Scopes._
1213
import Decorators._
@@ -299,7 +300,11 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
299300
args.foreach(checkInferredWellFormed)
300301
if (fn.symbol != defn.ChildAnnot.primaryConstructor)
301302
// Make an exception for ChildAnnot, which should really have AnyKind bounds
302-
Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType])
303+
val checkCtx = if tree.attachmentOrElse(Nullables.UnsafeNullsKey, false) then
304+
ctx.addMode(Mode.UnsafeNullsSubType)
305+
else
306+
ctx
307+
Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType])(using checkCtx)
303308
fn match {
304309
case sel: Select =>
305310
val args1 = transform(args)
@@ -359,7 +364,11 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
359364
else if (tree.tpt.symbol == defn.orType)
360365
() // nothing to do
361366
else
362-
Checking.checkAppliedType(tree)
367+
val checkCtx = if tree.attachmentOrElse(Nullables.UnsafeNullsKey, false) then
368+
ctx.addMode(Mode.UnsafeNullsSubType)
369+
else
370+
ctx
371+
Checking.checkAppliedType(tree)(using checkCtx)
363372
super.transform(tree)
364373
case SingletonTypeTree(ref) =>
365374
Checking.checkRealizable(ref.tpe, ref.srcPos)

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1075,7 +1075,7 @@ trait Applications extends Compatibility {
10751075
val isNamed = hasNamedArg(tree.args)
10761076
val typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_))
10771077
record("typedTypeApply")
1078-
typedFunPart(tree.fun, PolyProto(typedArgs, pt)) match {
1078+
val app = typedFunPart(tree.fun, PolyProto(typedArgs, pt)) match {
10791079
case IntegratedTypeArgs(app) =>
10801080
app
10811081
case _: TypeApply if !ctx.isAfterTyper =>
@@ -1098,6 +1098,8 @@ trait Applications extends Compatibility {
10981098
if (typedFn.tpe eq TryDynamicCallType) tryDynamicTypeApply()
10991099
else assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs)
11001100
}
1101+
app.putAttachment(Nullables.UnsafeNullsKey, config.Feature.unsafeNullsEnabled)
1102+
app
11011103
}
11021104

11031105
/** Rewrite `new Array[T](....)` if T is an unbounded generic to calls to newGenericArray.

compiler/src/dotty/tools/dotc/typer/Nullables.scala

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,29 +22,24 @@ object Nullables:
2222

2323
private def needNullifyHi(lo: Type, hi: Type)(using Context): Boolean =
2424
ctx.explicitNulls
25-
&& lo.isBottomTypeAfterErasure
26-
&& !hi.isAny
27-
&& !hi.isBottomTypeAfterErasure
25+
&& lo.isExactlyNull
2826
&& hi.isValueType
29-
&& hi.isNullableAfterErasure
3027

3128
def createNullableTypeBounds(lo: Type, hi: Type)(using Context): TypeBounds =
32-
TypeBounds(lo,
33-
if needNullifyHi(lo, hi)
34-
then OrNull(hi)
35-
else hi)
29+
val newHi = if needNullifyHi(lo, hi) then OrType(hi, defn.NullType, soft = false) else hi
30+
TypeBounds(lo, newHi)
3631

3732
def createNullableTypeBoundsTree(lo: Tree, hi: Tree, alias: Tree = EmptyTree)(using Context): TypeBoundsTree =
3833
val hiTpe = hi.typeOpt
39-
TypeBoundsTree(lo,
40-
if needNullifyHi(lo.typeOpt, hiTpe)
41-
then TypeTree(OrNull(hiTpe))
42-
else hi, alias)
34+
val newHi = if needNullifyHi(lo.typeOpt, hiTpe) then TypeTree(OrType(hiTpe, defn.NullType, soft = false)) else hi
35+
TypeBoundsTree(lo, newHi, alias)
4336

4437
inline def useUnsafeNullsSubTypeIf[T](cond: Boolean)(inline op: Context ?=> T)(using Context): T =
4538
val c = if cond then ctx.addMode(Mode.UnsafeNullsSubType) else ctx
4639
op(using c)
4740

41+
val UnsafeNullsKey = Property.StickyKey[Boolean]
42+
4843
/** A set of val or var references that are known to be not null, plus a set of
4944
* variable references that are not known (anymore) to be not null
5045
*/

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1785,7 +1785,9 @@ class Typer extends Namer
17851785
checkedArgs = List(TypeTree(elemtp).withSpan(arg.span))
17861786
case _ =>
17871787
}
1788-
assignType(cpy.AppliedTypeTree(tree)(tpt1, checkedArgs), tpt1, checkedArgs)
1788+
val appTpTree =assignType(cpy.AppliedTypeTree(tree)(tpt1, checkedArgs), tpt1, checkedArgs)
1789+
appTpTree.putAttachment(Nullables.UnsafeNullsKey, config.Feature.unsafeNullsEnabled)
1790+
appTpTree
17891791
}
17901792
}
17911793

0 commit comments

Comments
 (0)