Skip to content

Various refactorings #12938

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

Merged
merged 12 commits into from
Jul 5, 2021
Merged
9 changes: 6 additions & 3 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -699,10 +699,12 @@ object Trees {
s"TypeTree${if (hasType) s"[$typeOpt]" else ""}"
}

/** A type tree that defines a new type variable. Its type is always a TypeVar.
* Every TypeVar is created as the type of one TypeVarBinder.
/** A type tree whose type is inferred. These trees appear in two contexts
* - as an argument of a TypeApply. In that case its type is always a TypeVar
* - as a (result-)type of an inferred ValDef or DefDef.
* Every TypeVar is created as the type of one InferredTypeTree.
*/
class TypeVarBinder[-T >: Untyped](implicit @constructorOnly src: SourceFile) extends TypeTree[T]
class InferredTypeTree[-T >: Untyped](implicit @constructorOnly src: SourceFile) extends TypeTree[T]

/** ref.type */
case class SingletonTypeTree[-T >: Untyped] private[ast] (ref: Tree[T])(implicit @constructorOnly src: SourceFile)
Expand Down Expand Up @@ -1079,6 +1081,7 @@ object Trees {
type JavaSeqLiteral = Trees.JavaSeqLiteral[T]
type Inlined = Trees.Inlined[T]
type TypeTree = Trees.TypeTree[T]
type InferredTypeTree = Trees.InferredTypeTree[T]
type SingletonTypeTree = Trees.SingletonTypeTree[T]
type RefinedTypeTree = Trees.RefinedTypeTree[T]
type AppliedTypeTree = Trees.AppliedTypeTree[T]
Expand Down
10 changes: 6 additions & 4 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -981,11 +981,13 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
}

/** cast tree to `tp`, assuming no exception is raised, i.e the operation is pure */
def cast(tp: Type)(using Context): Tree = {
assert(tp.isValueType, i"bad cast: $tree.asInstanceOf[$tp]")
def cast(tp: Type)(using Context): Tree = cast(TypeTree(tp))

/** cast tree to `tp`, assuming no exception is raised, i.e the operation is pure */
def cast(tpt: TypeTree)(using Context): Tree =
assert(tpt.tpe.isValueType, i"bad cast: $tree.asInstanceOf[$tpt]")
tree.select(if (ctx.erasedTypes) defn.Any_asInstanceOf else defn.Any_typeCast)
.appliedToType(tp)
}
.appliedToTypeTree(tpt)

/** cast `tree` to `tp` (or its box/unbox/cast equivalent when after
* erasure and value and non-value types are mixed),
Expand Down
45 changes: 35 additions & 10 deletions compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,29 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
argStr ~ " " ~ arrow(isGiven) ~ " " ~ argText(args.last)
}

def toTextDependentFunction(appType: MethodType): Text =
"("
~ keywordText("erased ").provided(appType.isErasedMethod)
~ paramsText(appType)
~ ") "
~ arrow(appType.isImplicitMethod)
~ " "
~ toText(appType.resultType)
def toTextMethodAsFunction(info: Type): Text = info match
case info: MethodType =>
changePrec(GlobalPrec) {
"("
~ keywordText("erased ").provided(info.isErasedMethod)
~ ( if info.isParamDependent || info.isResultDependent
then paramsText(info)
else argsText(info.paramInfos)
)
~ ") "
~ arrow(info.isImplicitMethod)
~ " "
~ toTextMethodAsFunction(info.resultType)
}
case info: PolyType =>
changePrec(GlobalPrec) {
"["
~ paramsText(info)
~ "] => "
~ toTextMethodAsFunction(info.resultType)
}
case _ =>
toText(info)

def isInfixType(tp: Type): Boolean = tp match
case AppliedType(tycon, args) =>
Expand Down Expand Up @@ -230,8 +245,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
if !printDebug && appliedText(tp.asInstanceOf[HKLambda].resType).isEmpty =>
// don't eta contract if the application would be printed specially
toText(tycon)
case tp: RefinedType if defn.isFunctionType(tp) && !printDebug =>
toTextDependentFunction(tp.refinedInfo.asInstanceOf[MethodType])
case tp: RefinedType
if (defn.isFunctionType(tp) || (tp.parent.typeSymbol eq defn.PolyFunctionClass))
&& !printDebug =>
toTextMethodAsFunction(tp.refinedInfo)
case tp: TypeRef =>
if (tp.symbol.isAnonymousClass && !showUniqueIds)
toText(tp.info)
Expand All @@ -245,6 +262,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case ErasedValueType(tycon, underlying) =>
"ErasedValueType(" ~ toText(tycon) ~ ", " ~ toText(underlying) ~ ")"
case tp: ClassInfo =>
if tp.cls.derivesFrom(defn.PolyFunctionClass) then
tp.member(nme.apply).info match
case info: PolyType => return toTextMethodAsFunction(info)
case _ =>
toTextParents(tp.parents) ~~ "{...}"
case JavaArrayType(elemtp) =>
toText(elemtp) ~ "[]"
Expand Down Expand Up @@ -501,6 +522,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
"<derived typetree watching " ~ tpt.watched.showSummary() ~ ">"
case TypeTree() =>
typeText(toText(tree.typeOpt))
~ Str("(inf)").provided(tree.isInstanceOf[InferredTypeTree] && printDebug)
case SingletonTypeTree(ref) =>
toTextLocal(ref) ~ "." ~ keywordStr("type")
case RefinedTypeTree(tpt, refines) =>
Expand All @@ -510,6 +532,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
changePrec(OrTypePrec) { toText(args(0)) ~ " | " ~ atPrec(OrTypePrec + 1) { toText(args(1)) } }
else if (tpt.symbol == defn.andType && args.length == 2)
changePrec(AndTypePrec) { toText(args(0)) ~ " & " ~ atPrec(AndTypePrec + 1) { toText(args(1)) } }
else if defn.isFunctionClass(tpt.symbol)
&& tpt.isInstanceOf[TypeTree] && tree.hasType && !printDebug
then changePrec(GlobalPrec) { toText(tree.typeOpt) }
else args match
case arg :: _ if arg.isTerm =>
toTextLocal(tpt) ~ "(" ~ Text(args.map(argText), ", ") ~ ")"
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,14 @@ import transform.SymUtils._
}

class AnonymousFunctionMissingParamType(param: untpd.ValDef,
args: List[untpd.Tree],
tree: untpd.Function,
pt: Type)
(using Context)
extends TypeMsg(AnonymousFunctionMissingParamTypeID) {
def msg = {
val ofFun =
if param.name.is(WildcardParamName)
|| (MethodType.syntheticParamNames(args.length + 1) contains param.name)
|| (MethodType.syntheticParamNames(tree.args.length + 1) contains param.name)
then i" of expanded function:\n$tree"
else ""

Expand Down
26 changes: 12 additions & 14 deletions compiler/src/dotty/tools/dotc/transform/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -194,20 +194,18 @@ abstract class Dependencies(root: ast.tpd.Tree, @constructorOnly rootContext: Co
if isExpr(sym) && isLocal(sym) then markCalled(sym, enclosure)
case tree: This =>
narrowTo(tree.symbol.asClass)
case tree: DefDef =>
if sym.owner.isTerm then
logicOwner(sym) = sym.enclosingPackageClass
// this will make methods in supercall constructors of top-level classes owned
// by the enclosing package, which means they will be static.
// On the other hand, all other methods will be indirectly owned by their
// top-level class. This avoids possible deadlocks when a static method
// has to access its enclosing object from the outside.
else if sym.isConstructor then
if sym.isPrimaryConstructor && isLocal(sym.owner) && !sym.owner.is(Trait) then
// add a call edge from the constructor of a local non-trait class to
// the class itself. This is done so that the constructor inherits
// the free variables of the class.
symSet(called, sym) += sym.owner
case tree: MemberDef if isExpr(sym) && sym.owner.isTerm =>
logicOwner(sym) = sym.enclosingPackageClass
// this will make methods in supercall constructors of top-level classes owned
// by the enclosing package, which means they will be static.
// On the other hand, all other methods will be indirectly owned by their
// top-level class. This avoids possible deadlocks when a static method
// has to access its enclosing object from the outside.
case tree: DefDef if sym.isPrimaryConstructor && isLocal(sym.owner) && !sym.owner.is(Trait) =>
// add a call edge from the constructor of a local non-trait class to
// the class itself. This is done so that the constructor inherits
// the free variables of the class.
symSet(called, sym) += sym.owner
case tree: TypeDef =>
if sym.owner.isTerm then logicOwner(sym) = sym.topLevelClass.owner
case _ =>
Expand Down
45 changes: 45 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/SymUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -287,5 +287,50 @@ object SymUtils:
self.addAnnotation(
Annotation(defn.TargetNameAnnot,
Literal(Constant(nameFn(original.targetName).toString)).withSpan(original.span)))

/** The return type as seen from the body of this definition. It is
* computed from the symbol's type by replacing param refs by param symbols.
*/
def localReturnType(using Context): Type =
if self.isConstructor then defn.UnitType
else
def instantiateRT(info: Type, psymss: List[List[Symbol]]): Type = info match
case info: PolyType =>
instantiateRT(info.instantiate(psymss.head.map(_.typeRef)), psymss.tail)
case info: MethodType =>
instantiateRT(info.instantiate(psymss.head.map(_.termRef)), psymss.tail)
case info =>
info.widenExpr
instantiateRT(self.info, self.paramSymss)

/** The expected type of a return to `self` at the place indicated by the context.
* This is the local return type instantiated by the symbols of any context function
* closures that enclose the site of the return
*/
def returnProto(using Context): Type =

/** If `pt` is a context function type, its return type. If the CFT
* is dependent, instantiate with the parameters of the associated
* anonymous function.
* @param paramss the parameters of the anonymous functions
* enclosing the return expression
*/
def instantiateCFT(pt: Type, paramss: => List[List[Symbol]]): Type =
val ift = defn.asContextFunctionType(pt)
if ift.exists then
ift.nonPrivateMember(nme.apply).info match
case appType: MethodType =>
instantiateCFT(appType.instantiate(paramss.head.map(_.termRef)), paramss.tail)
else pt

def iftParamss = ctx.owner.ownersIterator
.filter(_.is(Method, butNot = Accessor))
.takeWhile(_.isAnonymousFunction)
.toList
.reverse
.map(_.paramSymss.head)

instantiateCFT(self.localReturnType, iftParamss)
end returnProto
end extension
end SymUtils
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Inferencing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ object Inferencing {
@tailrec def boundVars(tree: Tree, acc: List[TypeVar]): List[TypeVar] = tree match {
case Apply(fn, _) => boundVars(fn, acc)
case TypeApply(fn, targs) =>
val tvars = targs.filter(_.isInstanceOf[TypeVarBinder[?]]).tpes.collect {
val tvars = targs.filter(_.isInstanceOf[InferredTypeTree]).tpes.collect {
case tvar: TypeVar
if !tvar.isInstantiated &&
ctx.typerState.ownedVars.contains(tvar) &&
Expand Down
Loading