The key idea is that we add to the context a constrained term,
placeTerm
(of type place), representing the current known information
about here
.
placeTerm
's term represents the current place (the value of
here
). placeTerm
is an XTerm
of type place, and its
constraint specifies equalities relating this XTerm to other
XTerms. E.g. we may know that here = p1, here = p2
. So then PlaceTerm
will be p1 given p1=p2
or p2, given p1=p2
.
here
must never occur in the PlaceTerm.
Here is how the value of placeTerm
is defined for various pieces of code where
here
can be used:
FIRST_PLACE
.
placeTerm
is set to this.home
The at(p) e
or at(p) S
introduces place-shifts.
p
of the at
expression at(p) e
or statement at(p) s
to be of
type Object
or of type Place
. In case it is of type Object
, the at
expression/statement will automatically coerce p
to p.home
.
p
is permitted to be any expression, including a method call.
A new placeTerm is created and pushed into the current context. The
placeTerm is the variable v
if p's
type equates self
to v
. Otherwise
the placeTerm
is set to a new UQV v
. The constraint of the placeTerm
is the constraint in the type of p
, with the substitution v/self
applied
if v
was generated.
At an implementation level, XContext
and ClosureDef_c
now carry two
additional items, a placeTerm
and a placeConstraint
. The
typeCheckOverride
method of AtExpr_c
is modified to establish the
value of the placeTerm
and placeConstraint
fields in the ClosureDef_c
associated with the body of the AtExpr_c
.
AtExpr_c
work correctly. The type of the AtExpr
is the return type
of the closure representing its body. Now a closure body may have
multiple return statements inside it. The type of the closure is
taken to be the merge (least upper bound) of the types of the
expressions returned from within the body of the closure. This
information is recorded in the associated codeDef()
, of type
ClosureDef
.
returnType
is visited during type-checking, and is
disambiguated. By this point, typeCheckOverride
will have been
called on the parent AST node (atExpr). Unfortunately, this node is
a copy of the node with which the resolver was initialized!
Hence, we need to ensure that when the resolver runs it has access
to the constrained placeTerm. We ensure this by storing the
information in the ClosureDef
associated with the AtExpr
AST, since
all copies of the AST refer to the same ClosureDef
.
Additionally, we modify the type-checking of X10New_c
to add the
clause self.home=v,c
, where v given c
is the current placeTerm
obtained
from the context.
self.homen==here
" by adding to the type of the X10New_c
expression the constraints self.home==placeTerm, placeConstraint
. This is the right thing to do since placeTerm
may
be a symbolic variable about which the only pieces of information
we have are those in placeConstraint
.
p
is a final variable, the type-checker can infer that
the type of at(p) (new Foo())
is Foo{self.home==p}
. If
p()
is a procedure call with the return type Place{self==x}
then the type of at (p())(new Foo())
will be inferred as
Foo{self.home==_1786, _1786==x}
(for some new symbolic variable
_1786
), i.e. Foo{self.home==x}