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

Use narrowest type for pt of override under -Xsource:3 #10198

Merged
merged 1 commit into from Jan 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38 changes: 29 additions & 9 deletions src/compiler/scala/tools/nsc/typechecker/Namers.scala
Expand Up @@ -1368,10 +1368,11 @@ trait Namers extends MethodSynthesis {
deskolemizedPolySig(vparamSymss, resTpGiven)

// Must be lazy about the schema to avoid cycles in neg/t5093.scala
val overridden =
def computeOverridden(immediate: Boolean) =
if (!canOverride) NoSymbol
else safeNextOverriddenSymbolLazySchema(meth, methodSigApproxUnknownArgs _)
else safeNextOverriddenSymbolLazySchema(meth, methodSigApproxUnknownArgs _, immediate)

val overridden = computeOverridden(immediate = false)
/*
* If `meth` doesn't have an explicit return type, extract the return type from the method
* overridden by `meth` (if there's an unique one). This type is later used as the expected
Expand Down Expand Up @@ -1454,10 +1455,15 @@ trait Namers extends MethodSynthesis {

// Add a () parameter section if this overrides some method with () parameters
val vparamSymssOrEmptyParamsFromOverride = {
// must check `.info.isInstanceOf[MethodType]`, not `.isMethod`!
// Note that matching MethodType of NullaryMethodType must be nilary not nelary.
// must check `.info.isInstanceOf[MethodType]`, not `.isMethod`, to exclude NullaryMethodType.
// Note that the matching MethodType of a NullaryMethodType must be nilary not nelary.
def overriddenNilary(sym: Symbol) = sym.info.isInstanceOf[MethodType]
if (overridden != NoSymbol && vparamSymss.isEmpty && overridden.alternatives.exists(overriddenNilary)) {
// always check the first override for paren purposes
def overridesNilary: Boolean = {
val toCheck = if (currentRun.isScala3) computeOverridden(immediate = true) else overridden
toCheck != NoSymbol && toCheck.alternatives.exists(overriddenNilary)
}
if (vparamSymss.isEmpty && overridesNilary) {
def exempt() = meth.overrides.exists(sym => sym.isJavaDefined || isUniversalMember(sym))
val msg = "method without a parameter list overrides a method with a single empty one"
def error(): Unit = if (!exempt()) {
Expand Down Expand Up @@ -1763,20 +1769,34 @@ trait Namers extends MethodSynthesis {

// Pretend we're an erroneous symbol, for now, so that we match while finding the overridden symbol,
// but are not considered during implicit search.
private def safeNextOverriddenSymbol(sym: Symbol, schema: Type = ErrorType): Symbol = {
// `immediate` for immediate override only, not narrowest override
private def safeNextOverriddenSymbol(sym: Symbol, schema: Type = ErrorType, immediate: Boolean = false): Symbol = {
val savedInfo = sym.rawInfo
val savedFlags = sym.rawflags
try {
sym setInfo schema
sym.nextOverriddenSymbol
// pick the overridden symbol with narrowest type; dotty uses intersection
if (!immediate && currentRun.isScala3) {
def typeOf(s: Symbol): Type = {
val t = if (s.isMethod) s.asMethod.returnType else s.tpe
t.asSeenFrom(sym.owner.thisType, s.owner)
}
sym.allOverriddenSymbols match {
case Nil => NoSymbol
case overridden :: candidates =>
candidates.foldLeft(overridden)((acc, o) => if (typeOf(o) <:< typeOf(acc)) o else acc)
}
}
else
sym.nextOverriddenSymbol
} finally {
sym setInfo savedInfo // setInfo resets the LOCKED flag, so restore saved flags as well
sym.rawflags = savedFlags
}
}

private def safeNextOverriddenSymbolLazySchema(sym: Symbol, schema: () => Type): Symbol =
safeNextOverriddenSymbol(sym, new LazyType { override def complete(sym: Symbol): Unit = sym setInfo schema() })
private def safeNextOverriddenSymbolLazySchema(sym: Symbol, schema: () => Type, immediate: Boolean): Symbol =
safeNextOverriddenSymbol(sym, new LazyType { override def complete(sym: Symbol): Unit = sym setInfo schema() }, immediate)


//@M! an abstract type definition (abstract type member/type parameter)
Expand Down
30 changes: 30 additions & 0 deletions test/files/pos/t12671.scala
@@ -0,0 +1,30 @@

// scalac: -Xsource:3

import scala.collection.{mutable, IterableOnce}
import scala.collection.immutable.{AbstractSet, Set, SetOps}

final case class Foo[-T](components: IndexedSeq[Int])

sealed trait FooTrie[T]
extends AbstractSet[Foo[T]]
with SetOps[Foo[T], Set, FooTrie[T]] {

override def fromSpecific(
coll: IterableOnce[Foo[T]]
): FooTrie[T] = {
coll.iterator.foldLeft(empty)(_ incl _) // error here
}

override def newSpecificBuilder
: mutable.Builder[Foo[T], FooTrie[T]] = ???

override def incl(elem: Foo[T]): FooTrie[T] = ???

override def empty = FooTrie.empty[T]
//override def empty: FooTrie[T] = FooTrie.empty[T]
}

object FooTrie {
def empty[T]: FooTrie[T] = ???
}