Skip to content

WIP: Change namedtypes #3147

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

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,8 @@ object Trees {
* which implements copy-on-write. Another use-case is in method interpolateAndAdapt in Typer,
* where we overwrite with a simplified version of the type itself.
*/
private[dotc] def overwriteType(tpe: T) = {
if (this.isInstanceOf[Template[_]]) assert(tpe.isInstanceOf[WithFixedSym], s"$this <--- $tpe")
private[dotc] def overwriteType(tpe: T) =
myTpe = tpe
}

/** The type of the tree. In case of an untyped tree,
* an UnAssignedTypeException is thrown. (Overridden by empty trees)
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,8 @@ object Flags {

// Flags following this one are not pickled

/** Symbol always defines a fresh named type */
final val Fresh = commonFlag(45, "<fresh>")
/** Symbol is not a member of its owner */
final val NonMember = commonFlag(45, "<non-member>")

/** Denotation is in train of being loaded and completed, used to catch cyclic dependencies */
final val Touched = commonFlag(48, "<touched>")
Expand Down Expand Up @@ -446,7 +446,7 @@ object Flags {
Module | Package | Deferred | MethodOrHKCommon | Param | ParamAccessor.toCommonFlags |
Scala2ExistentialCommon | Mutable.toCommonFlags | Touched | JavaStatic |
CovariantOrOuter | ContravariantOrLabel | CaseAccessor.toCommonFlags |
Fresh | Erroneous | ImplicitCommon | Permanent | Synthetic |
NonMember | Erroneous | ImplicitCommon | Permanent | Synthetic |
SuperAccessorOrScala2x | Inline

/** Flags guaranteed to be set upon symbol creation, or, if symbol is a top-level
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Phases.scala
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ object Phases {
myErasedTypes = prev.getClass == classOf[Erasure] || prev.erasedTypes
myFlatClasses = prev.getClass == classOf[Flatten] || prev.flatClasses
myRefChecked = prev.getClass == classOf[RefChecks] || prev.refChecked
mySymbolicRefs = prev.getClass == classOf[ResolveSuper] || prev.symbolicRefs
mySymbolicRefs = getClass == classOf[Erasure] || prev.symbolicRefs
myLabelsReordered = prev.getClass == classOf[LabelDefs] || prev.labelsReordered
mySameMembersStartId = if (changesMembers) id else prev.sameMembersStartId
mySameParentsStartId = if (changesParents) id else prev.sameMembersStartId
Expand Down
5 changes: 1 addition & 4 deletions compiler/src/dotty/tools/dotc/core/Substituters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,7 @@ trait Substituters { this: Context =>
var ts = to
while (fs.nonEmpty) {
if (fs.head eq sym)
return tp match {
case tp: WithFixedSym => NamedType.withFixedSym(tp.prefix, ts.head)
case _ => substSym(tp.prefix, from, to, theMap) select ts.head
}
return substSym(tp.prefix, from, to, theMap) select ts.head
fs = fs.tail
ts = ts.tail
}
Expand Down
9 changes: 9 additions & 0 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,15 @@ object SymDenotations {
(this is (ModuleVal, butNot = Package)) && moduleClass.isAbsent
}

final def isValidInCurrentRun(implicit ctx: Context): Boolean =
validFor.runId == ctx.runId || ctx.stillValid(this)

final def isReferencedSymbolically(implicit ctx: Context) =
is(NonMember) || isTerm && ctx.phase.symbolicRefs

final def isOverridable(implicit ctx: Context) =
!(isReferencedSymbolically || is(Private) || isConstructor || maybeOwner.isTerm)

/** Is this symbol the root class or its companion object? */
final def isRoot: Boolean =
(name.toTermName == nme.ROOT || name == nme.ROOTPKG) && (owner eq NoSymbol)
Expand Down
18 changes: 11 additions & 7 deletions compiler/src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -245,11 +245,15 @@ trait Symbols { this: Context =>
* would be `fld2`. There is a single local dummy per template.
*/
def newLocalDummy(cls: Symbol, coord: Coord = NoCoord) =
newSymbol(cls, nme.localDummyName(cls), EmptyFlags, NoType)
newSymbol(cls, nme.localDummyName(cls), NonMember, NoType)

/** Create an import symbol pointing back to given qualifier `expr`. */
def newImportSymbol(owner: Symbol, expr: Tree, coord: Coord = NoCoord) =
newSymbol(owner, nme.IMPORT, EmptyFlags, ImportType(expr), coord = coord)
def newImportSymbol(owner: Symbol, expr: Tree, coord: Coord = NoCoord): TermSymbol =
newImportSymbol(owner, ImportType(expr), coord = coord)

/** Create an import symbol with given `info`. */
def newImportSymbol(owner: Symbol, info: Type, coord: Coord): TermSymbol =
newSymbol(owner, nme.IMPORT, Synthetic | NonMember, info, coord = coord)

/** Create a class constructor symbol for given class `cls`. */
def newConstructor(cls: ClassSymbol, flags: FlagSet, paramNames: List[TermName], paramTypes: List[Type], privateWithin: Symbol = NoSymbol, coord: Coord = NoCoord) =
Expand Down Expand Up @@ -343,7 +347,7 @@ trait Symbols { this: Context =>
copy.denot = odenot.copySymDenotation(
symbol = copy,
owner = ttmap1.mapOwner(odenot.owner),
initFlags = odenot.flags &~ Touched | Fresh,
initFlags = odenot.flags &~ Touched,
info = completer,
privateWithin = ttmap1.mapOwner(odenot.privateWithin), // since this refers to outer symbols, need not include copies (from->to) in ownermap here.
annotations = odenot.annotations)
Expand Down Expand Up @@ -421,7 +425,7 @@ object Symbols {
}

final def isValidInCurrentRun(implicit ctx: Context): Boolean =
lastDenot.validFor.runId == ctx.runId || ctx.stillValid(lastDenot)
lastDenot.isValidInCurrentRun

/** Subclass tests and casts */
final def isTerm(implicit ctx: Context): Boolean =
Expand All @@ -436,8 +440,8 @@ object Symbols {
final def asType(implicit ctx: Context): TypeSymbol = { assert(isType, s"isType called on not-a-Type $this"); asInstanceOf[TypeSymbol] }
final def asClass: ClassSymbol = asInstanceOf[ClassSymbol]

final def isFresh(implicit ctx: Context) =
lastDenot != null && (lastDenot is Fresh)
final def isReferencedSymbolically(implicit ctx: Context) =
lastDenot != null && lastDenot.isReferencedSymbolically

/** Special cased here, because it may be used on naked symbols in substituters */
final def isStatic(implicit ctx: Context): Boolean =
Expand Down
7 changes: 4 additions & 3 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
tp1.symbol.companionModule
else
tp1.symbol
if ((sym1 ne NoSymbol) && (sym1 eq tp2.symbol))
val sym2 = tp2.symbol
if ((sym1 ne NoSymbol) && (sym1 eq sym2))
ctx.erasedTypes ||
sym1.isStaticOwner ||
isSubType(tp1.prefix, tp2.prefix) ||
Expand All @@ -198,8 +199,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
( (tp1.name eq tp2.name)
&& isSubType(tp1.prefix, tp2.prefix)
&& tp1.signature == tp2.signature
&& !tp1.isInstanceOf[WithFixedSym]
&& !tp2.isInstanceOf[WithFixedSym]
&& sym1.isOverridable
&& sym2.isOverridable
) ||
thirdTryNamed(tp1, tp2)
case _ =>
Expand Down
40 changes: 17 additions & 23 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1572,9 +1572,8 @@ object Types {
val sym = lastSymbol
if (sym != null && sym.isValidInCurrentRun) denotOfSym(sym) else loadDenot
case d: SymDenotation =>
if (this.isInstanceOf[WithFixedSym]) d.current
else if (d.validFor.runId == ctx.runId || ctx.stillValid(d))
if (d.exists && prefix.isTightPrefix(d.owner) || d.isConstructor) d.current
if (d.isValidInCurrentRun)
if (!d.isOverridable || d.exists && prefix.isTightPrefix(d.owner)) d.current
else recomputeMember(d) // symbol could have been overridden, recompute membership
else {
val newd = loadDenot
Expand Down Expand Up @@ -1680,13 +1679,10 @@ object Types {
checkedPeriod = Nowhere
}

private[dotc] def withSym(sym: Symbol, signature: Signature)(implicit ctx: Context): ThisType =
if (sig != signature)
withSig(signature).withSym(sym, signature).asInstanceOf[ThisType]
else {
setSym(sym)
this
}
private[Types] def withSym(sym: Symbol)(implicit ctx: Context): ThisType = {
setSym(sym)
this
}

private[dotc] final def setSym(sym: Symbol)(implicit ctx: Context): Unit = {
if (Config.checkNoDoubleBindings)
Expand Down Expand Up @@ -2000,7 +1996,7 @@ object Types {
this
}

override def withSym(sym: Symbol, signature: Signature)(implicit ctx: Context): ThisType =
override def withSym(sym: Symbol)(implicit ctx: Context): ThisType =
unsupported("withSym")

override def newLikeThis(prefix: Type)(implicit ctx: Context): NamedType =
Expand Down Expand Up @@ -2054,8 +2050,6 @@ object Types {

object TermRef {

private def symbolicRefs(implicit ctx: Context) = ctx.phase.symbolicRefs

/** Create term ref with given name, without specifying a signature.
* Its meaning is the (potentially multi-) denotation of the member(s)
* of prefix with given name.
Expand All @@ -2076,7 +2070,7 @@ object Types {
* signature, if denotation is not yet completed.
*/
def apply(prefix: Type, name: TermName, denot: Denotation)(implicit ctx: Context): TermRef = {
if ((prefix eq NoPrefix) || denot.symbol.isFresh || symbolicRefs)
if ((prefix eq NoPrefix) || denot.symbol.isReferencedSymbolically)
apply(prefix, denot.symbol.asTerm)
else denot match {
case denot: SymDenotation if denot.isCompleted => withSig(prefix, name, denot.signature)
Expand All @@ -2098,31 +2092,31 @@ object Types {
* (2) The name in the term ref need not be the same as the name of the Symbol.
*/
def withSymAndName(prefix: Type, sym: TermSymbol, name: TermName)(implicit ctx: Context): TermRef =
if ((prefix eq NoPrefix) || sym.isFresh || symbolicRefs)
if ((prefix eq NoPrefix) || sym.isReferencedSymbolically)
withFixedSym(prefix, name, sym)
else if (sym.defRunId != NoRunId && sym.isCompleted)
withSig(prefix, name, sym.signature) withSym (sym, sym.signature)
withSig(prefix, name, sym.signature).withSym(sym)
// Linker note:
// this is problematic, as withSig method could return a hash-consed refference
// that could have symbol already set making withSym trigger a double-binding error
// ./tests/run/absoverride.scala demonstates this
else
all(prefix, name) withSym (sym, Signature.NotAMethod)
all(prefix, name) withSym (sym)

/** Create a term ref to given symbol, taking the signature from the symbol
* (which must be completed).
*/
def withSig(prefix: Type, sym: TermSymbol)(implicit ctx: Context): TermRef =
if ((prefix eq NoPrefix) || sym.isFresh || symbolicRefs) withFixedSym(prefix, sym.name, sym)
else withSig(prefix, sym.name, sym.signature).withSym(sym, sym.signature)
if ((prefix eq NoPrefix) || sym.isReferencedSymbolically) withFixedSym(prefix, sym.name, sym)
else withSig(prefix, sym.name, sym.signature).withSym(sym)

/** Create a term ref with given prefix, name and signature */
def withSig(prefix: Type, name: TermName, sig: Signature)(implicit ctx: Context): TermRef =
unique(new TermRefWithSignature(prefix, name, sig))

/** Create a term ref with given prefix, name, signature, and initial denotation */
def withSigAndDenot(prefix: Type, name: TermName, sig: Signature, denot: Denotation)(implicit ctx: Context): TermRef = {
if ((prefix eq NoPrefix) || denot.symbol.isFresh || symbolicRefs)
if ((prefix eq NoPrefix) || denot.symbol.isReferencedSymbolically)
withFixedSym(prefix, denot.symbol.asTerm.name, denot.symbol.asTerm)
else
withSig(prefix, name, sig)
Expand Down Expand Up @@ -2151,12 +2145,12 @@ object Types {
* (2) The name in the type ref need not be the same as the name of the Symbol.
*/
def withSymAndName(prefix: Type, sym: TypeSymbol, name: TypeName)(implicit ctx: Context): TypeRef =
if ((prefix eq NoPrefix) || sym.isFresh) withFixedSym(prefix, name, sym)
else apply(prefix, name).withSym(sym, Signature.NotAMethod)
if ((prefix eq NoPrefix) || sym.isReferencedSymbolically) withFixedSym(prefix, name, sym)
else apply(prefix, name).withSym(sym)

/** Create a type ref with given name and initial denotation */
def apply(prefix: Type, name: TypeName, denot: Denotation)(implicit ctx: Context): TypeRef = {
if ((prefix eq NoPrefix) || denot.symbol.isFresh) apply(prefix, denot.symbol.asType)
if ((prefix eq NoPrefix) || denot.symbol.isReferencedSymbolically) apply(prefix, denot.symbol.asType)
else apply(prefix, name)
} withDenot denot
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -983,7 +983,7 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi
UnApply(fn, implicitArgs, argPats, patType)
case REFINEDtpt =>
val refineCls = ctx.newCompleteClassSymbol(
ctx.owner, tpnme.REFINE_CLASS, Fresh, parents = Nil)
ctx.owner, tpnme.REFINE_CLASS, NonMember, parents = Nil)
typeAtAddr(start) = refineCls.typeRef
val parent = readTpt()
val refinements = readStats(refineCls, end)(localContext(refineCls))
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/transform/TreeChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,7 @@ class TreeChecker extends Phase with SymTransformer {
assert(tree.isTerm || !ctx.isAfterTyper, tree.show + " at " + ctx.phase)
val tpe = tree.typeOpt
val sym = tree.symbol
if (!tpe.isInstanceOf[WithFixedSym] &&
sym.exists && !sym.is(Private) &&
if (sym.exists && sym.isOverridable &&
!tree.name.is(OuterSelectName) // outer selects have effectively fixed symbols
) {
val qualTpe = tree.qualifier.typeOpt
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,8 @@ trait Implicits { self: Typer =>
case TypeBounds(lo, hi) if lo ne hi => apply(hi)
case _ => t
}
case t: RefinedType =>
apply(t.parent)
case _ =>
if (variance > 0) mapOver(t) else t
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
val ptDefined = isFullyDefined(pt, ForceDegree.none)
if (ptDefined && !(avoidingType <:< pt)) avoidingType = pt
val tree1 = ascribeType(tree, avoidingType)
assert(ptDefined || noLeaks(tree1), // `ptDefined` needed because of special case of anonymous classes
assert(ptDefined || noLeaks(tree1) || tree1.tpe.widen.isErroneous,
// `ptDefined` needed because of special case of anonymous classes
i"leak: ${escapingRefs(tree1, localSyms).toList}%, % in $tree1")
tree1
}
Expand Down
4 changes: 3 additions & 1 deletion docs/docs/reference/multiversal-equality.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ The precise rules for equality checking are as follows.

1. A comparison using `x == y` or `x != y` between values `x: T` and `y: U`
is legal if either `T` and `U` are the same, or one of the types is a subtype
of the other, or an implicit value of type `scala.Eq[T, U]` is found.
of the "lifted" version of the other type, or an implicit value of type `scala.Eq[T, U]` is found.
See the [description on Github](https://github.com/lampepfl/dotty/issues/1247) for
a definition of lifting.

2. The usual rules for implicit search apply also to `Eq` instances,
with one modification: An instance of `scala.Eq.eqAny[T, U]` is
Expand Down
15 changes: 15 additions & 0 deletions docs/docs/reference/union-intersection-subtyping.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

### Subtyping Rules

An intesection type `A & B` is a subtype of its constituents `A`, and `B`

A & B <: A
A & B <: B

T <: A & B provided T <: A and T <: B

If `C` is a covariant type constructor,

C[A & B] =:= C[A] & C[B]
C[A & B] <: C[B]