Skip to content

Commit 9fc286b

Browse files
authored
Merge pull request #12938 from dotty-staging/refactorings
Various refactorings
2 parents 9f088d7 + 350cfa2 commit 9fc286b

File tree

12 files changed

+395
-326
lines changed

12 files changed

+395
-326
lines changed

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

+6-3
Original file line numberDiff line numberDiff line change
@@ -699,10 +699,12 @@ object Trees {
699699
s"TypeTree${if (hasType) s"[$typeOpt]" else ""}"
700700
}
701701

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

707709
/** ref.type */
708710
case class SingletonTypeTree[-T >: Untyped] private[ast] (ref: Tree[T])(implicit @constructorOnly src: SourceFile)
@@ -1079,6 +1081,7 @@ object Trees {
10791081
type JavaSeqLiteral = Trees.JavaSeqLiteral[T]
10801082
type Inlined = Trees.Inlined[T]
10811083
type TypeTree = Trees.TypeTree[T]
1084+
type InferredTypeTree = Trees.InferredTypeTree[T]
10821085
type SingletonTypeTree = Trees.SingletonTypeTree[T]
10831086
type RefinedTypeTree = Trees.RefinedTypeTree[T]
10841087
type AppliedTypeTree = Trees.AppliedTypeTree[T]

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

+6-4
Original file line numberDiff line numberDiff line change
@@ -981,11 +981,13 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
981981
}
982982

983983
/** cast tree to `tp`, assuming no exception is raised, i.e the operation is pure */
984-
def cast(tp: Type)(using Context): Tree = {
985-
assert(tp.isValueType, i"bad cast: $tree.asInstanceOf[$tp]")
984+
def cast(tp: Type)(using Context): Tree = cast(TypeTree(tp))
985+
986+
/** cast tree to `tp`, assuming no exception is raised, i.e the operation is pure */
987+
def cast(tpt: TypeTree)(using Context): Tree =
988+
assert(tpt.tpe.isValueType, i"bad cast: $tree.asInstanceOf[$tpt]")
986989
tree.select(if (ctx.erasedTypes) defn.Any_asInstanceOf else defn.Any_typeCast)
987-
.appliedToType(tp)
988-
}
990+
.appliedToTypeTree(tpt)
989991

990992
/** cast `tree` to `tp` (or its box/unbox/cast equivalent when after
991993
* erasure and value and non-value types are mixed),

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

+35-10
Original file line numberDiff line numberDiff line change
@@ -159,14 +159,29 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
159159
argStr ~ " " ~ arrow(isGiven) ~ " " ~ argText(args.last)
160160
}
161161

162-
def toTextDependentFunction(appType: MethodType): Text =
163-
"("
164-
~ keywordText("erased ").provided(appType.isErasedMethod)
165-
~ paramsText(appType)
166-
~ ") "
167-
~ arrow(appType.isImplicitMethod)
168-
~ " "
169-
~ toText(appType.resultType)
162+
def toTextMethodAsFunction(info: Type): Text = info match
163+
case info: MethodType =>
164+
changePrec(GlobalPrec) {
165+
"("
166+
~ keywordText("erased ").provided(info.isErasedMethod)
167+
~ ( if info.isParamDependent || info.isResultDependent
168+
then paramsText(info)
169+
else argsText(info.paramInfos)
170+
)
171+
~ ") "
172+
~ arrow(info.isImplicitMethod)
173+
~ " "
174+
~ toTextMethodAsFunction(info.resultType)
175+
}
176+
case info: PolyType =>
177+
changePrec(GlobalPrec) {
178+
"["
179+
~ paramsText(info)
180+
~ "] => "
181+
~ toTextMethodAsFunction(info.resultType)
182+
}
183+
case _ =>
184+
toText(info)
170185

171186
def isInfixType(tp: Type): Boolean = tp match
172187
case AppliedType(tycon, args) =>
@@ -230,8 +245,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
230245
if !printDebug && appliedText(tp.asInstanceOf[HKLambda].resType).isEmpty =>
231246
// don't eta contract if the application would be printed specially
232247
toText(tycon)
233-
case tp: RefinedType if defn.isFunctionType(tp) && !printDebug =>
234-
toTextDependentFunction(tp.refinedInfo.asInstanceOf[MethodType])
248+
case tp: RefinedType
249+
if (defn.isFunctionType(tp) || (tp.parent.typeSymbol eq defn.PolyFunctionClass))
250+
&& !printDebug =>
251+
toTextMethodAsFunction(tp.refinedInfo)
235252
case tp: TypeRef =>
236253
if (tp.symbol.isAnonymousClass && !showUniqueIds)
237254
toText(tp.info)
@@ -245,6 +262,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
245262
case ErasedValueType(tycon, underlying) =>
246263
"ErasedValueType(" ~ toText(tycon) ~ ", " ~ toText(underlying) ~ ")"
247264
case tp: ClassInfo =>
265+
if tp.cls.derivesFrom(defn.PolyFunctionClass) then
266+
tp.member(nme.apply).info match
267+
case info: PolyType => return toTextMethodAsFunction(info)
268+
case _ =>
248269
toTextParents(tp.parents) ~~ "{...}"
249270
case JavaArrayType(elemtp) =>
250271
toText(elemtp) ~ "[]"
@@ -501,6 +522,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
501522
"<derived typetree watching " ~ tpt.watched.showSummary() ~ ">"
502523
case TypeTree() =>
503524
typeText(toText(tree.typeOpt))
525+
~ Str("(inf)").provided(tree.isInstanceOf[InferredTypeTree] && printDebug)
504526
case SingletonTypeTree(ref) =>
505527
toTextLocal(ref) ~ "." ~ keywordStr("type")
506528
case RefinedTypeTree(tpt, refines) =>
@@ -510,6 +532,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
510532
changePrec(OrTypePrec) { toText(args(0)) ~ " | " ~ atPrec(OrTypePrec + 1) { toText(args(1)) } }
511533
else if (tpt.symbol == defn.andType && args.length == 2)
512534
changePrec(AndTypePrec) { toText(args(0)) ~ " & " ~ atPrec(AndTypePrec + 1) { toText(args(1)) } }
535+
else if defn.isFunctionClass(tpt.symbol)
536+
&& tpt.isInstanceOf[TypeTree] && tree.hasType && !printDebug
537+
then changePrec(GlobalPrec) { toText(tree.typeOpt) }
513538
else args match
514539
case arg :: _ if arg.isTerm =>
515540
toTextLocal(tpt) ~ "(" ~ Text(args.map(argText), ", ") ~ ")"

compiler/src/dotty/tools/dotc/reporting/messages.scala

+1-2
Original file line numberDiff line numberDiff line change
@@ -149,15 +149,14 @@ import transform.SymUtils._
149149
}
150150

151151
class AnonymousFunctionMissingParamType(param: untpd.ValDef,
152-
args: List[untpd.Tree],
153152
tree: untpd.Function,
154153
pt: Type)
155154
(using Context)
156155
extends TypeMsg(AnonymousFunctionMissingParamTypeID) {
157156
def msg = {
158157
val ofFun =
159158
if param.name.is(WildcardParamName)
160-
|| (MethodType.syntheticParamNames(args.length + 1) contains param.name)
159+
|| (MethodType.syntheticParamNames(tree.args.length + 1) contains param.name)
161160
then i" of expanded function:\n$tree"
162161
else ""
163162

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

+12-14
Original file line numberDiff line numberDiff line change
@@ -194,20 +194,18 @@ abstract class Dependencies(root: ast.tpd.Tree, @constructorOnly rootContext: Co
194194
if isExpr(sym) && isLocal(sym) then markCalled(sym, enclosure)
195195
case tree: This =>
196196
narrowTo(tree.symbol.asClass)
197-
case tree: DefDef =>
198-
if sym.owner.isTerm then
199-
logicOwner(sym) = sym.enclosingPackageClass
200-
// this will make methods in supercall constructors of top-level classes owned
201-
// by the enclosing package, which means they will be static.
202-
// On the other hand, all other methods will be indirectly owned by their
203-
// top-level class. This avoids possible deadlocks when a static method
204-
// has to access its enclosing object from the outside.
205-
else if sym.isConstructor then
206-
if sym.isPrimaryConstructor && isLocal(sym.owner) && !sym.owner.is(Trait) then
207-
// add a call edge from the constructor of a local non-trait class to
208-
// the class itself. This is done so that the constructor inherits
209-
// the free variables of the class.
210-
symSet(called, sym) += sym.owner
197+
case tree: MemberDef if isExpr(sym) && sym.owner.isTerm =>
198+
logicOwner(sym) = sym.enclosingPackageClass
199+
// this will make methods in supercall constructors of top-level classes owned
200+
// by the enclosing package, which means they will be static.
201+
// On the other hand, all other methods will be indirectly owned by their
202+
// top-level class. This avoids possible deadlocks when a static method
203+
// has to access its enclosing object from the outside.
204+
case tree: DefDef if sym.isPrimaryConstructor && isLocal(sym.owner) && !sym.owner.is(Trait) =>
205+
// add a call edge from the constructor of a local non-trait class to
206+
// the class itself. This is done so that the constructor inherits
207+
// the free variables of the class.
208+
symSet(called, sym) += sym.owner
211209
case tree: TypeDef =>
212210
if sym.owner.isTerm then logicOwner(sym) = sym.topLevelClass.owner
213211
case _ =>

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

+45
Original file line numberDiff line numberDiff line change
@@ -287,5 +287,50 @@ object SymUtils:
287287
self.addAnnotation(
288288
Annotation(defn.TargetNameAnnot,
289289
Literal(Constant(nameFn(original.targetName).toString)).withSpan(original.span)))
290+
291+
/** The return type as seen from the body of this definition. It is
292+
* computed from the symbol's type by replacing param refs by param symbols.
293+
*/
294+
def localReturnType(using Context): Type =
295+
if self.isConstructor then defn.UnitType
296+
else
297+
def instantiateRT(info: Type, psymss: List[List[Symbol]]): Type = info match
298+
case info: PolyType =>
299+
instantiateRT(info.instantiate(psymss.head.map(_.typeRef)), psymss.tail)
300+
case info: MethodType =>
301+
instantiateRT(info.instantiate(psymss.head.map(_.termRef)), psymss.tail)
302+
case info =>
303+
info.widenExpr
304+
instantiateRT(self.info, self.paramSymss)
305+
306+
/** The expected type of a return to `self` at the place indicated by the context.
307+
* This is the local return type instantiated by the symbols of any context function
308+
* closures that enclose the site of the return
309+
*/
310+
def returnProto(using Context): Type =
311+
312+
/** If `pt` is a context function type, its return type. If the CFT
313+
* is dependent, instantiate with the parameters of the associated
314+
* anonymous function.
315+
* @param paramss the parameters of the anonymous functions
316+
* enclosing the return expression
317+
*/
318+
def instantiateCFT(pt: Type, paramss: => List[List[Symbol]]): Type =
319+
val ift = defn.asContextFunctionType(pt)
320+
if ift.exists then
321+
ift.nonPrivateMember(nme.apply).info match
322+
case appType: MethodType =>
323+
instantiateCFT(appType.instantiate(paramss.head.map(_.termRef)), paramss.tail)
324+
else pt
325+
326+
def iftParamss = ctx.owner.ownersIterator
327+
.filter(_.is(Method, butNot = Accessor))
328+
.takeWhile(_.isAnonymousFunction)
329+
.toList
330+
.reverse
331+
.map(_.paramSymss.head)
332+
333+
instantiateCFT(self.localReturnType, iftParamss)
334+
end returnProto
290335
end extension
291336
end SymUtils

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ object Inferencing {
333333
@tailrec def boundVars(tree: Tree, acc: List[TypeVar]): List[TypeVar] = tree match {
334334
case Apply(fn, _) => boundVars(fn, acc)
335335
case TypeApply(fn, targs) =>
336-
val tvars = targs.filter(_.isInstanceOf[TypeVarBinder[?]]).tpes.collect {
336+
val tvars = targs.filter(_.isInstanceOf[InferredTypeTree]).tpes.collect {
337337
case tvar: TypeVar
338338
if !tvar.isInstantiated &&
339339
ctx.typerState.ownedVars.contains(tvar) &&

0 commit comments

Comments
 (0)