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

Add optional seenfromClass to ThisTypeSubstitution #663

Open
wants to merge 2 commits into
base: idea242.x
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.intellij.util.containers.{ContainerUtil, SmartHashSet}
import com.intellij.util.{AstLoadingFilter, SmartList}
import it.unimi.dsi.fastutil.Hash
import it.unimi.dsi.fastutil.objects.{Object2ObjectMap, Object2ObjectMaps, Object2ObjectOpenCustomHashMap}
import org.jetbrains.annotations.Nullable
import org.jetbrains.plugins.scala.caches.{ModTracker, cachedInUserData, cachedWithRecursionGuard}
import org.jetbrains.plugins.scala.extensions._
import org.jetbrains.plugins.scala.lang.psi.api.base.ScFieldId
Expand Down Expand Up @@ -133,31 +134,33 @@ object MixinNodes {
case newTd: ScNewTemplateDefinition => MixinNodes.linearization(newTd)
case _ => MixinNodes.linearization(thisClass).drop(1)
}
val thisTypeSubst = thisClass match {
case td: ScTemplateDefinition => ScSubstitutor(ScThisType(td))
case _ => ScSubstitutor.empty
val thisType = thisClass match {
case td: ScTemplateDefinition => ScThisType(td)
case _ => null
}
SuperTypesData(superTypes, thisTypeSubst)
SuperTypesData(superTypes, thisType)
}

def apply(cp: ScCompoundType, compoundThisType: Option[ScType]): SuperTypesData = {
val superTypes = MixinNodes.linearization(cp)
val thisTypeSubst = ScSubstitutor(compoundThisType.getOrElse(cp))
SuperTypesData(superTypes, thisTypeSubst)
val thisType = compoundThisType.getOrElse(cp)
SuperTypesData(superTypes, thisType)
}

private def apply(superTypes: Seq[ScType], thisTypeSubst: ScSubstitutor): SuperTypesData = {
private def apply(superTypes: Seq[ScType], @Nullable thisType: ScType): SuperTypesData = {
val substitutorsBuilder = SeqMap.newBuilder[PsiClass, ScSubstitutor]
val refinementsBuilder = List.newBuilder[ScCompoundType]

for (superType <- superTypes) {
superType.extractClassType match {
case Some((superClass, s)) =>
val seenFromClass = if (thisType == null) null else superClass
val dependentSubst = superType match {
case p @ ScProjectionType(proj, _) => ScSubstitutor(proj).followed(p.actualSubst)
case ParameterizedType(p @ ScProjectionType(proj, _), _) => ScSubstitutor(proj).followed(p.actualSubst)
case p @ ScProjectionType(proj, _) => ScSubstitutor(proj, seenFromClass).followed(p.actualSubst)
case ParameterizedType(p @ ScProjectionType(proj, _), _) => ScSubstitutor(proj, seenFromClass).followed(p.actualSubst)
case _ => ScSubstitutor.empty
}
val thisTypeSubst = if (thisType == null) ScSubstitutor.empty else ScSubstitutor(thisType, seenFromClass)
val newSubst = combine(s, superClass).followed(thisTypeSubst).followed(dependentSubst)
substitutorsBuilder += superClass -> newSubst
case _ =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,15 @@ private class BaseTypesIterator(tp: ScType) extends Iterator[ScType] {
}
else None
case ScThisType(clazz) =>
clazz.getTypeWithProjections().toOption
// Given:
// trait Father[A] { trait Son }
// trait Charles extends Father[Int] {
// trait William extends Father[String] with Son
// }
// then William.this.type.baseType(trait Son)
// should return Charles.this.Son not Charles#Son
// (what `clazz.getTypeWithProjections()` returns)
clazz.`type`().toOption
case tpt: TypeParameterType =>
Some(tpt.upperType)
case ScExistentialArgument(_, Nil, _, upper) =>
Expand All @@ -143,4 +151,4 @@ private class BaseTypesIterator(tp: ScType) extends Iterator[ScType] {
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ final class ScProjectionType private(val projected: ScType,
processor.candidates match {
case Array(candidate) => candidate.element match {
case candidateElement: PsiNamedElement =>
val thisSubstitutor = ScSubstitutor(projected)
val thisSubstitutor = ScSubstitutor(projected, element.findContextOfType(classOf[PsiClass]).orNull)
val defaultSubstitutor =
projected match {
case _: ScThisType => candidate.substitutor
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.jetbrains.plugins.scala.lang.psi.types.recursiveUpdate

import com.intellij.openapi.diagnostic.Logger
import com.intellij.psi.PsiClass
import org.jetbrains.plugins.scala.extensions.ArrayExt
import org.jetbrains.plugins.scala.lang.psi.api.statements.params.{ScParameter, TypeParamId}
import org.jetbrains.plugins.scala.lang.psi.types.Compatibility.Expression
Expand Down Expand Up @@ -116,6 +117,10 @@ final class ScSubstitutor private(_substitutions: Array[Update], //Array is us
ScSubstitutor(tp).followed(this)
}

def followUpdateThisType(tp: ScType, seenFromClass: PsiClass): ScSubstitutor = {
ScSubstitutor(tp, seenFromClass).followed(this)
}

def withBindings(from: Iterable[TypeParameter], target: Iterable[TypeParameter]): ScSubstitutor = {
assertFullSubstitutor()

Expand Down Expand Up @@ -187,7 +192,10 @@ object ScSubstitutor {
}

def apply(updateThisType: ScType): ScSubstitutor =
ScSubstitutor(ThisTypeSubstitution(updateThisType))
ScSubstitutor(ThisTypeSubstitution(updateThisType, null))

def apply(updateThisType: ScType, seenFromClass: PsiClass): ScSubstitutor =
ScSubstitutor(ThisTypeSubstitution(updateThisType, seenFromClass))

def paramToExprType(parameters: Seq[Parameter], expressions: Seq[Expression], useExpected: Boolean = true): ScSubstitutor =
ScSubstitutor(ParamsToExprs(parameters, expressions, useExpected))
Expand Down Expand Up @@ -218,4 +226,4 @@ object ScSubstitutor {
}

def undefineTypeParams[T](tps: Seq[TypeParameter]): ScSubstitutor = bind(tps)(UndefinedType(_))
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
package org.jetbrains.plugins.scala.lang.psi.types.recursiveUpdate

import com.intellij.psi.{PsiClass, PsiElement, PsiTypeParameter}
import org.jetbrains.plugins.scala.extensions.PsiMemberExt
import org.jetbrains.plugins.scala.lang.psi.ScalaPsiUtil.isInheritorDeep
import org.jetbrains.plugins.scala.lang.psi.api.base.patterns.ScBindingPattern
import org.jetbrains.plugins.scala.lang.psi.api.statements.ScTypeAliasDeclaration
import org.jetbrains.plugins.scala.lang.psi.api.statements.params.{ScParameter, ScTypeParam}
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.ScTypedDefinition
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.typedef.{ScTemplateDefinition, ScTypeDefinition}
import org.jetbrains.plugins.scala.lang.psi.types.api.designator.{ScProjectionType, ScThisType}
import org.jetbrains.plugins.scala.lang.psi.types.api.{ParameterizedType, TypeParameterType}
import org.jetbrains.plugins.scala.lang.psi.types.{LeafType, ScCompoundType, ScType}
import com.intellij.psi._
import org.jetbrains.annotations.Nullable
import org.jetbrains.plugins.scala.extensions._
import org.jetbrains.plugins.scala.lang.psi.ScalaPsiUtil._
import org.jetbrains.plugins.scala.lang.psi.api.base.patterns._
import org.jetbrains.plugins.scala.lang.psi.api.statements._, params._
import org.jetbrains.plugins.scala.lang.psi.api.toplevel._, typedef._
import org.jetbrains.plugins.scala.lang.psi.types._, api._, designator._, nonvalue._

import scala.annotation.tailrec

private case class ThisTypeSubstitution(target: ScType) extends LeafSubstitution {
private case class ThisTypeSubstitution(target: ScType, @Nullable seenFromClass: PsiClass) extends LeafSubstitution {

override def toString: String = s"`this` -> $target"
override def toString: String = seenFromClass match {
case null => s"`this` -> $target"
case _ => s"`this` -> $target asSeenFrom $seenFromClass"
}

override protected val subst: PartialFunction[LeafType, ScType] = {
case th: ScThisType if !hasRecursiveThisType(target, th.element) => doUpdateThisType(th, target)
case th: ScThisType if !hasRecursiveThisType(target, th.element) => doUpdateThisTypeFromClass(th, target, seenFromClass)
}

@tailrec
Expand All @@ -32,6 +32,16 @@ private case class ThisTypeSubstitution(target: ScType) extends LeafSubstitution
}
}

private def doUpdateThisTypeFromClass(thisTp: ScThisType, target: ScType, @Nullable clazz: PsiClass): ScType =
if (clazz == null || clazz == thisTp.element || clazz.containingClass == null)
doUpdateThisType(thisTp, target)
else {
BaseTypes.iterator(target).find(_.extractClass.contains(clazz)).flatMap(containingClassType) match {
case Some(targetContext) => doUpdateThisTypeFromClass(thisTp, targetContext, clazz.containingClass)
case _ => thisTp
}
}

private def hasRecursiveThisType(tp: ScType, clazz: ScTemplateDefinition): Boolean =
tp.subtypeExists {
case tpe: ScThisType => isSameOrInheritor(clazz, tpe)
Expand All @@ -52,11 +62,12 @@ private case class ThisTypeSubstitution(target: ScType) extends LeafSubstitution
private def isSameOrInheritor(clazz: PsiClass, thisTp: ScThisType): Boolean =
clazz == thisTp.element || isInheritorDeep(clazz, thisTp.element)

private def hasSameOrInheritor(compound: ScCompoundType, thisTp: ScThisType) = {
private def hasSameOrInheritor(compound: ScCompoundType, thisTp: ScThisType): Boolean = {
compound.components
.exists {
_.extractDesignated(expandAliases = true)
extractAll(_)
.exists {
case tp: ScCompoundType => hasSameOrInheritor(tp, thisTp)
case tp: ScTypeParam =>
(for {
upper <- tp.upperBound.toOption
Expand All @@ -70,7 +81,7 @@ private case class ThisTypeSubstitution(target: ScType) extends LeafSubstitution

@tailrec
private def isMoreNarrow(target: ScType, thisTp: ScThisType, visited: Set[PsiElement]): Boolean = {
target.extractDesignated(expandAliases = true) match {
extractAll(target) match {
case Some(pat: ScBindingPattern) =>
if (visited.contains(pat)) false
else isMoreNarrow(pat.`type`().getOrAny, thisTp, visited + pat)
Expand Down Expand Up @@ -100,11 +111,36 @@ private case class ThisTypeSubstitution(target: ScType) extends LeafSubstitution
case Some(td: ScTypeAliasDeclaration) => isMoreNarrow(td.upperBound.getOrAny, thisTp, visited)
case Some(cl: PsiClass) => isSameOrInheritor(cl, thisTp)
case Some(named: ScTypedDefinition) => isMoreNarrow(named.`type`().getOrAny, thisTp, visited)
case _ =>
target match {
case compound: ScCompoundType => hasSameOrInheritor(compound, thisTp)
case _ => false
}
case Some(compound: ScCompoundType) => hasSameOrInheritor(compound, thisTp)
case _ => false
}
}

private def extractAll(tp: ScType) = {
// Like tp.extractDesignated(expandAliases = true)
// But also return non-designator returns
// Such as ScCompoundType, after dealiasing and dereferencing.

def elem1(tp: DesignatorOwner) = tp match { case tp: ScProjectionType => tp.actualElement case _ => tp.element }
def subst(tp: DesignatorOwner) = tp match { case tp: ScProjectionType => tp.actualSubst case _ => ScSubstitutor.empty }

def rec(tp: ScType, seen: Set[ScTypeAlias]): Either[ScType, PsiNamedElement] = tp match {
case tp: NonValueType => rec(tp.inferValueType, seen)
case tp: DesignatorOwner => elem1(tp) match {
case ta: ScTypeAliasDefinition if !seen(ta) => ta.aliasedType.map(subst(tp)).fold(_ => Left(tp), rec(_, seen + ta))
case tp => Right(tp)
}
case tp: ParameterizedType => tp match {
case AliasType(ta: ScTypeAliasDefinition, _, Right(ub)) if !seen(ta) => rec(ub, seen + ta)
case _ => rec(tp.designator, seen)
}
case tp: StdType => tp.syntheticClass.toRight(tp)
case tp: ScExistentialType => rec(tp.quantified, seen)
case tp: TypeParameterType => Right(tp.psiTypeParameter)
case tp: ScCompoundType => Left(tp)
case _ => Left(tp)
}

rec(tp, Set.empty).fold(Some(_), Some(_))
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.jetbrains.plugins.scala.lang.resolve

import com.intellij.openapi.util.Key
import com.intellij.psi.ResolveState
import com.intellij.psi.{ PsiClass, ResolveState }
import org.jetbrains.plugins.scala.extensions.ObjectExt
import org.jetbrains.plugins.scala.lang.psi.ScExportsHolder
import org.jetbrains.plugins.scala.lang.psi.api.statements.ScExtension
Expand Down Expand Up @@ -96,6 +96,9 @@ trait ResolveStateOps extends Any {
def substitutorWithThisType: ScSubstitutor =
fromType.fold(substitutor)(substitutor.followUpdateThisType)

def substitutorWithThisType(seenFromClass: PsiClass): ScSubstitutor =
fromType.fold(substitutor)(substitutor.followUpdateThisType(_, seenFromClass))

def fromType: Option[ScType] =
option(FROM_TYPE_KEY)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class MethodResolveProcessor(override val ref: PsiElement,
val accessible = isNamedParameter || isAccessible(namedElement, ref)
if (accessibility && !accessible) return true

val s = state.substitutorWithThisType
val s = state.substitutorWithThisType(namedElement.findContextOfType(classOf[PsiClass]).orNull)
val resultBuilder: PsiNamedElement => ScalaResolveResult = e =>
new ScalaResolveResult(
e,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Bug2Test extends ResolveTestBase {
def testSCL2765(): Unit = {doTest()}
def testDependent(): Unit = {doTest()}
def testDependentEquality(): Unit = {doTest()}
def testDependentEquality2(): Unit = {doTest()}
def testSCL2904(): Unit = {doTest()}
def testSCL2827A(): Unit = {doTest()}
def testSCL2827B(): Unit = {doTest()}
Expand All @@ -55,4 +56,4 @@ class Bug2Test extends ResolveTestBase {
def testSCL3485(): Unit = {doTest()}
def testSCL3539(): Unit = {doTest()}
def testSCL3546(): Unit = {doTest()}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,22 @@ class BugTest extends ResolveTestBase {
def testThisTypeSelfType(): Unit = {doTest()}

def testImplicitsOverloading(): Unit = {doTest()}
}

def testSCL21585B(): Unit = doTest()
def testSCL21585C(): Unit = doTest()
def testSCL21585C2(): Unit = doTest()
def testSCL21585Cm(): Unit = doTest()
def testSCL21585D(): Unit = doTest()
def testSCL21585E(): Unit = doTest()
def testSCL21585E2(): Unit = doTest()
def testSCL21585E3(): Unit = doTest()
def testSCL21585E4(): Unit = doTest()
def testSCL21585E5(): Unit = doTest()
def testSCL21585F(): Unit = doTest()
def testSCL21585G(): Unit = doTest()
def testSCL21585H(): Unit = doTest()
def testSCL21585I(): Unit = doTest()

def testSCL21947A(): Unit = doTest()
def testSCL21947B(): Unit = doTest()
}
22 changes: 22 additions & 0 deletions scala/scala-impl/testdata/resolve2/bug/SCL21585B.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
trait M {
type A
}

trait Base {
type T
}
trait B extends Base {
type T

def t: T
}


class Repro {

type M_A[M1 <: M] = M1#A

def test(value: (B{ type T = Repro }) with M_A[M { type A = B }]) = value.t./*resolved: true*/ok

def ok = ""
}
49 changes: 49 additions & 0 deletions scala/scala-impl/testdata/resolve2/bug/SCL21585C.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@

object Repro {
trait HasBuilder {
type Builder
}

trait TList {
type Builder
}
trait TNil extends TList {
type Builder
}

trait TCons[H <: HasBuilder, T <: TList] extends TList {
type Builder = H#Builder with T#Builder
}

class Action1 extends HasBuilder {
type Builder = Action1Builder
}
trait Action1Builder {
type TargetType
def target(__DEBUG__ : Any = ""): TargetType = ???
}

trait HasTargetType[T] {
type TargetType = T
}
type ActionParam = TCons[Action1, TNil]
// def action1NOK1: ActionParam#Builder { type TargetType = TargetType1 } = ???
def action1NOK2: HasTargetType[TargetType1] with ActionParam#Builder = ???

// def action1OK1: TCons[Action1, TNil]#Builder { type TargetType = TargetType1 } = ???
// def action1OK2: Action1#Builder { type TargetType = TargetType1 } = ???
// def action1OK3: HasTargetType[TargetType1] with TCons[Action1, TNil]#Builder = ???
// def action1OK4: HasTargetType[TargetType1] with Action1#Builder = ???

class TargetType1 {
def ok = true
}

// action1OK1.target.ok
// action1OK2.target.ok
// action1OK3.target.ok
// action1OK4.target.ok
// good code red, "cannot resolve symbol ok"
// action1NOK1.target.ok
action1NOK2.target()./*resolved:true*/ok
}
Loading