-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Implement multiple assignments (SIP-59) #19597
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
base: main
Are you sure you want to change the base?
Changes from all commits
e4682ce
af187a5
99bcde3
d0e46b5
716a323
1664620
ed92d95
6b4b3f0
a9d11fd
f414f67
431cffd
4566328
1b98ba4
71fbbd0
9a2d8a6
2841e53
3b83559
c2a3705
6cf4e15
e16dcde
833d329
65c428b
5dab0f6
51e863f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,156 @@ | ||||||||||
package dotty.tools | ||||||||||
package dotc | ||||||||||
package typer | ||||||||||
|
||||||||||
import dotty.tools.dotc.ast.Trees.ApplyKind | ||||||||||
import dotty.tools.dotc.ast.tpd | ||||||||||
import dotty.tools.dotc.ast.untpd | ||||||||||
import dotty.tools.dotc.ast.TreeInfo | ||||||||||
import dotty.tools.dotc.core.Contexts.Context | ||||||||||
import dotty.tools.dotc.core.Names.Name | ||||||||||
import dotty.tools.dotc.core.NameKinds.TempResultName | ||||||||||
|
||||||||||
import core.Symbols.defn | ||||||||||
|
||||||||||
/** A function computing the assignment of a lvalue. | ||||||||||
* | ||||||||||
* @param lhs The target of the assignment. | ||||||||||
* @param perform: A closure that accepts `lhs` and an untyped tree `rhs`, and returns a tree | ||||||||||
* representing the assignment of `rhs` to `lhs`. | ||||||||||
*/ | ||||||||||
private[typer] final class PartialAssignment[+T <: LValue](val lhs: T)( | ||||||||||
perform: (T, untpd.Tree) => untpd.Tree | ||||||||||
): | ||||||||||
|
||||||||||
/** Returns a tree computing the assignment of `rhs` to `lhs`. */ | ||||||||||
def apply(rhs: untpd.Tree): untpd.Tree = | ||||||||||
perform(lhs, rhs) | ||||||||||
|
||||||||||
end PartialAssignment | ||||||||||
|
||||||||||
/** The expression of a pure value or a synthetic val definition binding a value whose evaluation | ||||||||||
* must be hoisted. | ||||||||||
* | ||||||||||
* Use this type to represent a part of a lvalue that must be evaluated before the lvalue gets | ||||||||||
* used for updating a value. | ||||||||||
*/ | ||||||||||
private[typer] final class PossiblyHoistedValue private (representation: tpd.Tree): | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider putting all those helper classes inside an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than having a single |
||||||||||
|
||||||||||
/** Returns a tree representing the value of `self`. */ | ||||||||||
def value(using Context): tpd.Tree = | ||||||||||
definition match | ||||||||||
case Some(d) => tpd.Ident(d.namedType).withSpan(representation.span) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this equivalent to
Suggested change
? |
||||||||||
case _ => representation | ||||||||||
|
||||||||||
/** Returns the synthetic val defining `self` if it is hoisted. */ | ||||||||||
def definition: Option[tpd.ValDef] = | ||||||||||
representation match | ||||||||||
case d: tpd.ValDef => Some(d) | ||||||||||
case _ => None | ||||||||||
|
||||||||||
/** Returns a tree representing the value of `self` along with its hoisted definition, if any. */ | ||||||||||
def valueAndDefinition(using Context): (tpd.Tree, Option[tpd.ValDef]) = | ||||||||||
definition match | ||||||||||
Comment on lines
+52
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason not to implement that method as
Suggested change
? |
||||||||||
case Some(d) => (tpd.Ident(d.namedType).withSpan(representation.span), Some(d)) | ||||||||||
case _ => (representation, None) | ||||||||||
|
||||||||||
object PossiblyHoistedValue: | ||||||||||
|
||||||||||
/** Creates a value representing the `e`'s evaluation. */ | ||||||||||
def apply(e: tpd.Tree, isSingleAssignment: Boolean)(using Context): PossiblyHoistedValue = | ||||||||||
if isSingleAssignment || (tpd.exprPurity(e) >= TreeInfo.Pure) then | ||||||||||
new PossiblyHoistedValue(e) | ||||||||||
else | ||||||||||
new PossiblyHoistedValue(tpd.SyntheticValDef(TempResultName.fresh(), e).withSpan(e.span)) | ||||||||||
|
||||||||||
/** The left-hand side of an assignment. */ | ||||||||||
private[typer] sealed abstract class LValue: | ||||||||||
|
||||||||||
/** Returns the local `val` definitions composing this lvalue. */ | ||||||||||
def locals: List[tpd.ValDef] | ||||||||||
|
||||||||||
/** Returns a tree computing the assignment of `rhs` to this lvalue. */ | ||||||||||
def formAssignment(rhs: untpd.Tree)(using Context): untpd.Tree | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
might better follow typical naming conventions of the codebase? |
||||||||||
|
||||||||||
end LValue | ||||||||||
|
||||||||||
/** A simple expression, typically valid on left-hand side of an `Assign` tree. | ||||||||||
* | ||||||||||
* Use this class to represent an assignment that translates to an `Assign` tree or to wrap an | ||||||||||
* error whose diagnostic can be delayed until the right-hand side is known. | ||||||||||
* | ||||||||||
* @param expression The expression of the lvalue. | ||||||||||
*/ | ||||||||||
private[typer] final case class SimpleLValue(expression: tpd.Tree) extends LValue: | ||||||||||
|
||||||||||
def locals: List[tpd.ValDef] = | ||||||||||
List() | ||||||||||
|
||||||||||
def formAssignment(rhs: untpd.Tree)(using Context): untpd.Tree = | ||||||||||
val s = untpd.Assign(untpd.TypedSplice(expression), rhs) | ||||||||||
untpd.TypedSplice(s.withType(defn.UnitType)) | ||||||||||
Comment on lines
+89
to
+91
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems suspicious. IIRC, a Why not directly returning |
||||||||||
|
||||||||||
end SimpleLValue | ||||||||||
|
||||||||||
/** A lvalue represeted by the partial application a function. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
* | ||||||||||
* @param function The partially applied function. | ||||||||||
* @param arguments The arguments of the partial application. | ||||||||||
*/ | ||||||||||
private[typer] final case class ApplyLValue( | ||||||||||
function: ApplyLValue.Callee, | ||||||||||
arguments: List[PossiblyHoistedValue] | ||||||||||
) extends LValue: | ||||||||||
|
||||||||||
val locals: List[tpd.ValDef] = | ||||||||||
function.locals ++ (arguments.flatMap { (v) => v.definition }) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
? |
||||||||||
|
||||||||||
def formAssignment(rhs: untpd.Tree)(using Context): untpd.Tree = | ||||||||||
val s = function.expanded | ||||||||||
val t = arguments.map((a) => untpd.TypedSplice(a.value)) :+ rhs | ||||||||||
untpd.Apply(s, t) | ||||||||||
|
||||||||||
object ApplyLValue: | ||||||||||
|
||||||||||
/** The callee of a lvalue represented by a partial application. */ | ||||||||||
sealed abstract class Callee: | ||||||||||
|
||||||||||
/** Returns the tree representing this callee. */ | ||||||||||
def expanded(using Context): untpd.Tree | ||||||||||
|
||||||||||
/** Returns the local `val` definitions composing this lvalue. */ | ||||||||||
def locals: List[tpd.ValDef] | ||||||||||
|
||||||||||
object Callee: | ||||||||||
|
||||||||||
/** Creates an instance from a function represented as a typed tree. */ | ||||||||||
def apply( | ||||||||||
receiver: tpd.Tree, isSingleAssignment: Boolean | ||||||||||
)(using Context): Typed = | ||||||||||
Typed(PossiblyHoistedValue(receiver, isSingleAssignment), None) | ||||||||||
|
||||||||||
/** Creates an instance denoting a selection on a receiver represented as a typed tree. */ | ||||||||||
def apply( | ||||||||||
receiver: tpd.Tree, member: Name, isSingleAssignment: Boolean | ||||||||||
)(using Context): Typed = | ||||||||||
Typed(PossiblyHoistedValue(receiver, isSingleAssignment), Some(member)) | ||||||||||
|
||||||||||
/** A function representing a lvalue. */ | ||||||||||
final case class Typed(receiver: PossiblyHoistedValue, member: Option[Name]) extends Callee: | ||||||||||
|
||||||||||
def expanded(using Context): untpd.Tree = | ||||||||||
val s = untpd.TypedSplice(receiver.value) | ||||||||||
member.map((m) => untpd.Select(s, m)).getOrElse(s) | ||||||||||
|
||||||||||
def locals: List[tpd.ValDef] = | ||||||||||
receiver.definition.toList | ||||||||||
|
||||||||||
/** The untyped expression of a function representing a lvalue along with its captures. */ | ||||||||||
final case class Untyped(value: untpd.Tree, locals: List[tpd.ValDef]) extends Callee: | ||||||||||
|
||||||||||
def expanded(using Context): untpd.Tree = | ||||||||||
value | ||||||||||
|
||||||||||
end Callee | ||||||||||
|
||||||||||
end ApplyLValue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this change required? If yes, why?