Efficient Type-Checking for Amortised
Heap-Space Analysis
Martin Hofmann and Dulma Rodriguez
Department of Computer Science, University of Munich
Oettingenstr. 67, D-80538 München, Germany
{martin.hofmann|dulma.rodriguez}@ifi.lmu.de
Abstract. In 2006 Hofmann and Jost presented a type system for a
compile-time analysis of heap-space requirements of Java style objectoriented programs with explicit deallocation, which is based on an amortised complexity analysis: the data is arbitrarily assigned a potential
related to its size and layout; allocations must be ”payed for” from this
potential. The potential of each input then furnishes an upper bound on
the heap space usage for the computation on this input.
In this paper we describe efficient automated type-checking for a finite,
annotated version of this system. We prove soundness and completeness
of the type checking algorithm and show its efficiency.
Moreover, we present an improvement of the system, which defines subtyping and sharing directly on refined types, thus, enabling the correct
typing of more interesting examples.
Keywords: Heap-space analysis, algorithmic type-checking.
1
Introduction
The prediction of resource consumption in programs has gained interest in the
last years. It is important for a number of areas, in particular embedded systems and mobile computing. A variety of approaches to resource analysis have
been proposed based in particular on recurrence solving [AAG+ 07,Gro01], abstract interpretation [GL98,NCQR05], sized types [HP99], and amortised analysis [HJ03,HJ06,Cam08].
The amortised approach which the present paper belongs to is particularly
useful in situations where heap-allocated data structures whose size is proportional to parts of the input must be costed. Typical examples are various sorting
algorithms where trees, lists, or heaps appear as intermediate data structures.
In such cases amortised analysis can infer very good bounds based on intuitive
programmer annotations in the form of types and the solution of linear inequations.
In [HJ06] amortised analysis has been applied to a Java-like class-based
object-oriented language without garbage collection, but with explicit deallocation in the style of C’s free(). Such programs may be evaluated by maintaining
a set of free memory units, the freelist. Upon object creation a number of heap
units required to store the object is taken from the freelist provided it contains
enough units; each deallocated heap unit is returned to the freelist. An attempt
to create a new object with an insufficient freelist causes unsuccessful abortion
of the program.
The goal is to predict a bound to the initial size that the freelist must have so
that a given program may be executed without causing unsuccessful abortion due
to penury of memory. This has been achieved using a modified amortized analysis [Tar85,Oka98], distinguished from previous work by being reference based.
Essentially each object is ascribed an abstracted portion of the freelist, referred to as potential. The potential is just a number, denoting the size of freelist
portion associated with the object. Any object creation must be paid for from
the potential in scope. The initial potential thus furnishes an upper bound on
the total heap consumption.
While type inference and automated type checking have already been developed for a functional language within the EmBounded Project ([HDF+ 05],
[HBH+ 07]), most of the properties of the type system for the Java-like language
(called Resource Aware JAva – RAJA) are still unknown.
In this paper we introduce algorithmic typing for a slightly improved version
of the original RAJA. We prove soundness and completeness of algorithmic typing with respect to the declarative typing from [HJ06]. Semantic soundness of
RAJA is a direct extension of the soundness proof in [HJ06]. It can be found at
the second author’s homepage.
Contents. The rest of this section motivates the system RAJA with an example. Section 2 describes the system RAJA. In Section 3 we define the typechecking algorithm and show its soundness and completeness w.r.t. the declarative system. Afterwards, we show the efficiency of the algorithm. Finally, we
discuss future and related work in the conclusions.
Acknowledgement We acknowledge support by the EU integrated project
MOBIUS IST 15905. We thank Andreas Abel, Lennart Beringer and Steffen
Jost for valuable comments.
1.1
Views and potential
We aim at assigning objects a potential so that any object creation must be
paid for from the potential in scope and the potential of the initial configuration
furnishes an upper bound on the total heap consumption. For an interesting
analysis we should be able to assign objects of the same class different potentials.
Thus, we need more refined types than classes to assign potential to. To this end,
we introduce the views. A view on an object shall determine its contribution to
the potential. A refined type then consists of a class C and a view r and is
written C r .
The meaning of views is given by three maps ♦() defining potentials, A defining views of attributes, and M defining refined method types. More precisely,
♦() : Class × View → Q+ assigns each class its potential according to the employed view. A : Class × View × Field → View × View determines the refined
2
types of the fields. A different view may apply according to whether a field is
read from (get-view) or written to (set-view). M : Class × View × Method →
P(Views of Arguments → Effect × View of Result) assigns refined types and effects to methods. The effect is a pair of numbers representing the potential
consumed before and released after method invocation.
1.2
Example: Copying singly-linked lists
Suppose we have defined a class of singly-linked lists in an object-oriented
style which harnesses dynamic dispatch to obtain the functionality of patternmatching.
abstract class List { abstract List copy(); }
class Nil extends List { List copy() { return this; }}
class Cons extends List { int elem; List next;
List copy() { Cons res = new Cons(); res.elem = this.elem;
res.next = this.next.copy(); return res; }}
It is clear that the memory consumption of a call x.copy() will equal the length
of the list x. To calculate this formally we construct a view rich which assigns to
List itself the potential 0, to Nil the potential 0 and to Cons the potential 1.
Another view is needed to describe the result of copy() for otherwise we could
repeatedly copy lists without paying for it. Thus, we introduce another view
poor that assigns potential 0 to all classes. The complete specification of the two
views is shown here:
♦(·)
List
Nil
Cons
rich poor
0
0
0
0
1
0
Consrich Conspoor
A (· , next) rich
poor
poor
Aset(· , next) rich
get
0/0
M {Listrich , Consrich , Nilrich }, copy = ()−−
−→poor
The call x.copy() is well-typed and of type Listpoor if x has refined type
Listrich , Nilrich or Consrich . It is ill-typed if x has refined type, e.g., Listpoor .
Its effect 0/0 will not decrement the freelist beyond the amount implicit in
the potential of x (which equals in this case the length of the list pointed to by
x) and will not return anything to the freelist beyond the amount implicit in the
potential of the result (which equals zero due to its refined type Listpoor ). Thus,
the typing amounts to saying that the memory consumption of this call is equal
to the length of x.
Let us explain why the potential of x indeed equals its length. Suppose for
the sake of the example that x points to a list of length 2. The potential is worked
out as the sum over all access paths emanating from x and not leading to null
or being undefined. In this case, these access paths are p1 = x, p2 = x.next,
p3 = x.next.next. Each of these has a dynamic type: Cons for p1 and p2 ; Nil
for p3 . Each of them also has a view worked out by chaining the view of x along
the get-views. Here it is view a in each case. For each access paths we now look
3
up the potential annotation of its dynamic type under its view.
It equals 1 in
case of p1 and p2 given ♦ Consrich = 1 and 0 for p3 by ♦ Nilrich = 0, yielding a
sum of 2. This example and some others are explained in more detail in [HJ06].
2
Extending FJEU to RAJA
Our formal model of Java, FJEU [HJ06], is an extension of Featherweight Java
(FJ) [IPW99] with attribute update, conditional and explicit deallocation. It is
thus similar to Flatt et al. Classic Java [FKF98].
We now extend FJEU to an annotated version, RAJA, (Resource Aware
JAva) as announced in the Introduction. A RAJA program is an annotation of
an FJEU class table C in the form of a sextuple
R = (C , V , ♦(·), Aget(· , ·) , Aset(· , ·) , M(· , ·)) specified as follows:
1. V is a possibly infinite set of views. For each class C ∈ dom(C ) and for each
view r ∈ V the pair C r is called a RAJA class. We refer to RAJA classes
also as (refined) types.
2. ♦(·) assigns to each RAJA class C r a number ♦(C r ) ∈ D, where D = Q+ ∪ ∞.
This number will be used to define the potential of a heap configuration under
a given static RAJA typing.
3. Aget(· , ·) and Aset(· , ·) assign to each RAJA class C r and attribute a ∈ A(C)
two views q = Aget(C r, a) and s = Aset(C r, a). The intention is that if D = C.a
is the FJEU type of attribute a in C then the RAJA type Dq will be the
type of an access to a, whereas the (intendedly stronger) type Ds must be
used when updating a. The stronger typing is needed since an update will
possibly affect several aliases.
4. M(· , ·) assigns to each RAJA class C r and method m ∈ M(C) having method
type E1 , . . . , Ej → E0 a j-ary polymorphic RAJA method type M(C r, m).
A j-ary polymorphic RAJA method type is a (possibly empty or infinite) set
of j-ary monomorphic RAJA method types. A j-ary monomorphic RAJA
method type consists of j + 1 views and two numbers p, q ∈ D, written
p/q
r1 , . . . , rj −
→r0 .
The idea is that if m (of FJEU-type E1 , . . . , Ej → E0 ) has (among others)
p/q
the monomorphic RAJA method type r1 , . . . , rj −
→r0 then it may be called
rj
r1
with arguments v1 : E1 , . . . , vj : Ej , whose associated potential will be
consumed, as well as an additional potential of p. Upon successful completion
the return value will be of type E0r0 hence carry an according potential. In
addition to this a potential of another q units will be returned.
r p/q r0
We sometimes write E1r1 , . . . , Ej j −
→E0 to denote an FJEU method type
combined with a corresponding monomorphic RAJA method type.
We will now define when such a RAJA-annotation of an FJEU class table is
indeed valid; in particular this will require that each method body is typable
with each of the monomorphic RAJA method types given in the annotation.
4
Definition 1 (Subtyping of RAJA types). We define a preorder <: on
RAJA types C r , Ds where C <: D in C and r, s ∈ V , as the largest relation
(C r <: Ds ) such that:
C r <: Ds ⇐⇒ for each E, F <: D with E <: F :
♦(E r ) ≥ ♦(F s )
Aget(E r,a)
∀a ∈ A(F ) . (F.a)
∀a ∈ A(F ) . (F.a)A
s
set
(F ,a)
s
(2.1)
Aget(F s,a)
<: (F.a)
<: (F.a)A
r
set
(2.2)
r
(E ,a)
α
(2.3)
β
∀m ∈ M(F ) . ∀β ∈ M(F , m) . ∃α ∈ M(E , m) . (F.m) <: (F.m)
(2.4)
where we extend <: to monomorphic RAJA method types as follows:
Definition 2 (Subtyping of RAJA methods). If D.m = E1 , . . . , Ej → E0 ,
p/q
t/u
α
β
α = r1 , . . . , rj −
→r0 and β = s1 , . . . , sj −→s0 then (D.m) <: (D.m) is defined
ri
si
s0
r0
as p ≤ t and q ≥ u and E0 <: E0 and Ei <: Ei for i = 1, . . . , j.
If Γ and Θ are contexts we write Γ <: Θ if ∀x ∈ Θ . Γx <: Θx .
The following definition is important for correctly using variables more than
once. If a variable is to be used more than once, e.g., as an argument to a
method, then the different occurrences must be given different types which are
chosen such that the individual potentials assigned to each occurrence add up
to the total potential available.
Definition 3 (Sharing Relation). We define the sharing relation between
a single RAJA type C r and a multiset of RAJA types Ds1 , . . . , Dsn written
.(C r |Ds1 , . . . , Dsn ) as the largest relation ., such that if .(C r |Ds1 , . . . , Dsn )
then for all E, F <: D with E <: F :
X
♦(E r ) ≥
(2.5)
♦(F si )
i
∀i . E r <: F si
get s1
get sn
get r
∀a ∈ dom(A(F )) . . (F.a)A (E ,a) (F.a)A (F ,a) , . . . , (F.a)A (F ,a)
(2.6)
(2.7)
Subtyping and sharing coincide when the multiset consists of only one element.
Lemma 1. C r <: C s ⇐⇒ .(C r |C s )
Typing RAJA We now give the formal definition of the RAJA-typing judgement. See Figure 1. The type system allows us to derive assertions of the form
n
Γ n′ e : C r where e is an expression or program phrase, C is a Java class, r is a
view (so C r is a refined type). Γ maps variables occurring in e to refined types;
we often write Γx instead of Γ (x). Finally n, n′ are nonnegative numbers. The
5
meaning of such a judgement is as follows. If e terminates successfully in some
environment η and heap σ with unbounded memory resources available then it
will also terminate successfully with a bounded freelist of size at least n plus the
potential ascribed to η, σ with respect to the typings in Γ .
Furthermore, the freelist size upon termination will be at least n′ plus the
potential of the result with respect to the view r.
The typing rules are standard. The most interesting ones are (♦Share) and
(♦W aste). First we notice that they are not syntax directed, thus, they need
to be eliminated when it comes to implement the system in the next section.
The purpose of the (♦Share) rule is to ensure that a variable can be used twice
without duplication of potential.
The judgement ⊢ m : α ok where m is the name of a method in a RAJA class
C r , and α is a RAJA type for this method means that α is a valid RAJA type
for m if the method body of m can be typed with the arguments, return type
and effects as specified in α. There is another interesting point here: in order to
use the potential of the variable this, we put it in the context with a modified
type. For example, if we have this : C r and ♦(C r ) = 1, if we can find two views
such that .(C r |C q , C s ) with ♦(C q ) = 0 and ♦(C s ) = 1, then we put this in the
context with type this : C q and we can use the potential 1 of C s when typing
the method body. The rule gives no information about how to find the views q
and s, but in our implementation these views can be found automatically.
Definition 4 (Well-typed RAJA-program). A RAJA-program
R = (C , V , ♦(·), Aget(· , ·) , Aset(· , ·) , M(·, ·)) is well-typed if for all C ∈ C and
r ∈ V the following conditions are satisfied:
1. S(C) = D ⇒ C r <: Dr
set r
get r
2. ∀a ∈ A(C) . (C.a)A (C ,a) <: (C.a)A (C ,a)
3. ∀m ∈ M(C) . ∀α ∈ M(C r, m) . ⊢ m : α ok
2.1
Algorithmic Views and Complete RAJA Programs
In this section we extend a given set of views of a RAJA-program by algorithmic
views. They enable us to have a better structure in the program, which simplifies
algorithmic type-checking.
Definition 5 (Algorithmic views). Let s1 , s2 ∈ V be two arbitrary views of a
RAJA-program R = (C , V , ♦(·), Aget(· , ·) , Aset(· , ·) , M(·, ·)). We extend the given
set of views V by algorithmic views
α ::= s1 ∨ s2 | s1 ∧ s2 | s1 + s2
n
s −̇ n
si ∈ V
n∈D
s ∈ V ,n ∈ D
by extending the given maps ♦(·), Aget(· , ·), Aset(· , ·), M(·, ·) by the values given
in Fig 2.
6
RAJA Typing
∅
Γ
♦(C r ) + 1
0
0
0
e : Cr
(♦N ew)
x : Cr
new C : C r
C <: E
x : Er
n
n′
(♦Cast)
(C)x : C r
s = Aget(C r, a)
x : Cr
0
0
0
0
∅
D = C.a
free(x) : E r
(♦N ull)
x : Cr
null : C r
(♦Access)
x.a : Ds
Γ1
(♦F ree)
0
♦(C r ) + 1
e1 : Ds
C.a = D
0
0
x.a<-y : C r
Γ2 , x : Ds
n
n′′
Γ1 , Γ2
n′
n′′
e2 : C r
` q1
q n/n′ q0 ´
r
E1 , . . . , Ej j −
−→E0 ∈ M(C , m)
n
n′
q
x∈Γ
Γ
n
n′
Γ
n
n′
Γ, x : Ds
n
n′
e2 : C r
Γ, y1 : Dq1 , . . . , yn : Dqn
n
n′
n + u′ ≥ n ′ + u
RAJA Method Typing
(♦Invocation)
(♦Conditional)
n
n′
e : Cr
e[x/y1 , . . . , x/yn ] : C r
Θ
Γ
m ∈ M(C)
Γ
if x instanceof E then e1 else e2 : C r
.(Ds |Dq1 , . . . , Dqn )
n≥u
x.m(y1 , . . . , yj ) : E0q0
e1 : C r
n
n′
u
u′
e : Ds
e:
Γ <: Θ
Cr
(♦U pdate)
(♦Let)
let x = e1 in e2 : C r
x : C r , y1 : E1q1 , . . . , yj : Ej j
(♦V ar)
x : Cr
Aset(C r, a) = s
x : C r , y : Ds
n
n′
0
0
Ds <: C r
(♦Share)
(♦W aste)
⊢ m : α ok
′
r
r0
n/n
r
α = E1r1 , . . . , Ej j −
−→E0 ∈ M(C , m)
r
this : C q , x1 : E1r1 , . . . , xj : Ej j
n + ♦(C s )
n′
Mbody (C, m) : E0r0
⊢ m : α ok
Fig. 1. Typing RAJA
7
.(C r |C q , C s )
(♦M Body)
Let C ∈ C , a ∈ A(C) and m ∈ M(C). We set:
♦(C s1 ∧s2 )
s1 ∨s2
)´
♦(C
`
♦ C s1 +s2
♦(C n )
“
”
♦ C s −̇ n
max( ♦(C s1 ), ♦(C s2 ))
min( ♦(C s1 ), ♦(C s2 ))
♦(C s1 ) + ♦(C s2 )
n
♦(C s ) −̇ n if ♦(C s ) ≥ n
=
0
otherwise
=
=
=
=
Aget(C s1 ∗s2, a) = Aget(C s1, a) ∗ Aget(C s2, a) where ∗ ∈ {∧, ∨, +}.
n
Aget(C
“ , a) ” = 0
Aget C s −̇ n, a = Aget(C s, a)
Aset(C s1 ∧s2, a)
Aset`(C s1 ∨s2, a)´
Aset C s1 +s2, a
Aset“
(C n, a) ”
=
=
=
=
Aset(C s1, a) ∨ Aset(C s2, a)
Aset(C s1, a) ∧ Aset(C s2, a)
Aset(C s1, a) ∨ Aset(C s2, a)
0
Aset C s −̇ n, a
= Aset(C s, a)
M(C s1 ∧s2, m)
s1 ∨s2
M(C
` s +s, m)´
M C 1 2, m
n
M(C
“ , m) ”
=
=
=
=
M C s −̇ n, m
M(C s1, m) ∩ M(C s2, m)
M(C s1, m) ∪ M(C s2, m)
M(C s1, m) ∩ M(C s2, m)
∅
= M(C s, m)
Fig. 2. Definition of ♦(·), Aget(· , ·) , Aset(· , ·) , M(· , ·) of algorithmic views
Definition 6 (Complete RAJA-program). A RAJA-program
R = (C , V , ♦(·), Aget(· , ·) , Aset(· , ·) , M(·, ·)) is complete if the following conditions are satisfied:
1.
2.
3.
4.
5.
6.
s1 ∧ s2 ∈ V , for all s1 , s2 ∈ V .
s1 ∨ s2 ∈ V , for all s1 , s2 ∈ V .
s1 + s2 ∈ V , for all s1 , s2 ∈ V .
n ∈ V , for all n ∈ D.
s −̇ n ∈ V , for all s ∈ V , n ∈ D.
The annotation table of R satisfies the equations from Def. 5.
The idea behind this definition is that given a RAJA program R we complete
it with algorithmic views. C s1 ∨s2 is the least upper bound of C s1 and C s2 and
C s1 ∧s2 is the greatest upper bound of C s1 and C s2 . C s1 +s2 helps in converting
sharing into subtyping, i.e. .(C s |C s1 , C s2 ) is equivalent to C s <: C s1 +s2 . This
way we can deal only with subtyping instead of sharing, which is simpler and
more intuitive. Finally, the views n are neutral views of potential n. They are
intended to be used together with the views s − n, which are nothing but the
view s, with n units of potential stripped-off. This way, we get .(C s |C s−n , C n ).
8
These algorithmic views are useful for implementing ⊢ m : α ok. If we need to
use n units of potential of the type C s of this in the method body of a given
method, we give this the type C s−n and use the potential of C n in the method.
In the following we assume given a complete well-typed RAJA program R.
We proceed to prove all this results. First we notice that a well-typed RAJA
program remains well-typed after completion.
Lemma 2. Let C, D ∈ C and s1 , s2 , r1 , r2 ∈ V and let C si <: Dri for i ∈
{1, 2}. Then C s1 ∗ s2 <: Dr1 ∗ r2 for ∗ ∈ {+, ∧, ∨}.
Proof. Simultaneously by coinduction.
Lemma 3 (Well-typedness of complete RAJA programs). A well-typed
RAJA program remains well-typed after completion.
Proof. Items 1. and 2. of definition 4 follow by Lemma 2. Item 3. is clear since
the RAJA method types of the algorithmic views are already present in the
original system.
Of course, we are free to use the algorithmic views from the beginning and in
particular in the provided class and method typings. They may be seen as a
shorthand for a longer table which includes them explicitly. We stress, though,
that efficient type checking for incomplete programs is not possible with the
techniques from this paper. This for example because subtyping in incomplete
programs may fail to have least upper bounds which are crucial for typechecking
case distinctions. We do not consider typechecking of incomplete programs to
be of any practical relevance.
The following lemma summarises the desirable order- and proof-theoretic
properties of algorithmic views:
Lemma 4. Let C, D ∈ C and s, t, s1 , s2 , . . . , sn ∈ V . Denote C ∨ D the least
upper bound in FJEU of C and D.
1.
2.
3.
4.
5.
(C ∨ D)s1 ∨s2 is the least upper bound of C s1 and Ds2 .
C s1 ∧s2 is the g.l.b. of C si .
.(C s1 +s2 |C s1 , C s2 ).
s
s1 +...+sn
.
.(C s |C s1 , . . . , C sn ) ⇐⇒
C <: C
n
s
s s −̇ n
, C . Moreover, .(C s |C s1 , C s2 ) and ♦(C s2 ) ≥
If n ≤ ♦(C ) then . C C
n imply C s −̇ n <: C s1 .
3
Algorithmic Typing of RAJA Programs
In this section we present an algorithm for typechecking RAJA programs, which
returns a type for a given expression. It is diffiult though to calculate a type
for null and free (x) expressions, because they can have any type. We could add
a RAJA type ⊥ to our theory which is a subtype of any type and has infinite
potential, and give this type to null and free (x) expressions. We choose instead
9
to annotate these expressions with a type in order to keep our type system
simpler.
In the implementation of the (♦Conditional) rule, the algorithm returns the
least upper bound of the types from the if and else branches.
Moreover, algorithmic type-checking must consist of syntax directed rules,
thus, the rules (♦Share) and (♦W aste) must be integrated in other rules. Recall
that the effects of the rule (♦W aste) are twofold: on the one hand, it deals with
subtyping, and on the other hand, it allows to change the effects. Instead of
using (♦W aste), we integrate subtyping in the rules.
The purpose of the (♦Share) rule is to ensure that a variable can be used
more than once without unsound incrementation of potential. The main challenge for implementing it is that it contains no information about how to find
the views q1 to qn for the different occurrences. The current implementation
does not include inference of these views. Instead, every variable occurrence has
been annotated with the corresponding view. The task of the type checker is
then to check the correctness of the given sharing, or, more exactly, since, as we
saw in last section, using algorithmic views a sharing task can be reduced into
a subtyping task, the algorithm checks only subtyping. The inference of these
intermediate views remains under investigation.
Finally, new C expressions are annotated with a view as well, in order to
infer RAJA types for them. In summary, we define a type-checking algorithm
which infers a RAJA type for a given annotated RAJA expression.
n
We define the judgement ∆Ψ n′ e ⇉ C γ inductively by the rules in Figure
3, where ∆ and e are inputs and Ψ , C γ , n and n′ are outputs. ∆ is an FJEU
context, i.e. a map from variable names to FJEU types. Ψ is a map from variable
names to algorithmic views. C γ is an algorithmic RAJA type, which is an FJEU
class refined with an algorithmic view.
The notation ∆Ψ means that for every variable x ∈ ∆, if ∆x = C and Ψx = α
α1
α
Ψ1 +Ψ2
1
for meaning that if ∆Ψ
then ∆Ψ
x =C
x = C . We also use the notation ∆
Ψ1 ∧Ψ2
α1 +α2
Ψ1 +Ψ2
α2
Ψ2
is similar. We
. The meaning of ∆
=C
and ∆x = C then ∆x
write x : C r ,+ y : Ds for meaning a case distinction. The usual case is x 6= y and
then it means nothing but x : C r , y : Ds . On the other hand, if x = y, then C = D
too, and the notation means x : C r+s . Ψ∅ assigns 0 to all variables. It is used for
returning neutral views for variables that are not used in the given expression.
Let ea denote an annotated RAJA expression. Moreover, if e is an annotated
RAJA expression, let |e| be the same expression without type annotations.
In summary, we define the function typecheck(∆, ea ) by:
a
typecheck(∆, e ) =
n
(Ψ, C γ , n, n′ ) if ∆Ψ n′ ea ⇉ C γ
fail
otherwise
Next, we define the algorithmic judgement ⊢a m : α ok based on algorithmic
typing. (Fig. 3). The typechecking algorithm returns a greater context and a
smaller type than the declared ones. This has to be checked. Moreover, it calculates the space consumption u of the method body. If u ≤ n then n items are
enough and we do not need any potential from this. Otherwise, we calculate how
10
∆Ψ ∅ , x : C r
new C r ⇉ C r
0
♦(D s ) + 1
∆Ψ ∅ , x : D s
D <: E (or E <: D)
0
0
∆Ψ∅ , x : E q
ea ⇉ C r
( ⊢ N ew)
♦(C r ) + 1
0
∆Ψ ∅
n
n′
∆Ψ
Algorithmic RAJA Typing
free (xs )C r ⇉ C r
( ⊢ Cast)
∆Ψ∅
(D)xq ⇉ Dq
Aget (C r , a) = s
Aset (C r , a) = s
C.a = D
0
0
∆Ψ ∅ , x : C r
xr .a
0
0
∆Ψ
n
n′
′
e1 ⇉ Dγ1
( ⊢ F ree)
( ⊢ Access)
( ⊢ U pdate)
xr .a ← y p ⇉ C r
p
p′
t
,+ yj : Fj j
′′
∆Ψ , x : D α
∀i . Fiti <: Eiqi
∆Ψ ′ +Ψ ′′
max( n, n + m − n )
max( m′ , m′ + n′ − m)
n
n′
∆Ψ
∆Ψ
∆Ψ ′ ∧Ψ ′′
′
e1 ⇉ C1γ1
′′
m
m′
u
min( n′ + u − n, m′ + u − m)
( ⊢ Invocation)
t
xs .m (y1t1 , . . . , yjj ) ⇉ E0q0
m
m′
e2 ⇉ C γ2
Dγ1 <: Dα
′
x∈∆
( ⊢ N ull)
r
nullC ⇉ C r
F p <: Ds
`
q p/p′ q0 ´
M (C r , m) ∋ E1q1 , . . . , Ej j −
−→E0
∆Ψ∅ , x : C r ,+ y1 : F1t1 ,+ . . .
( ⊢ V ar)
xr ⇉ C r
⇉ Ds
C.a = D
∆ Ψ ∅ , x : C r ,+ y : F p
0
0
0
0
( ⊢ Let)
let x = e1 in e2 ⇉ C γ2
e2 ⇉ C2γ2
C = (C1 ∨ C2 )
u = max( m, n)
if x instanceof E then e1 else e2 ⇉ C γ1 ∨γ2
⊢a m : α ok
Algorithmic RAJA Method Typing
′
r
r0
n/n
r
α = E1r1 , . . . , Ej j −
−→E0 ∈ M(C , m)
β
this : C β , x1 : E1β1 , . . . , xj : Ej j
Eiri <: Eiβi
p = u −̇ n
u
u′
Mbody (C, m) ⇉ Dγ
u′ ≥ n′ + u − (n + p)
⊢a m : α ok
♦(C r ) ≥ p
Fig. 3. Algorithmic RAJA Typing
11
Dγ <: E0r0
C r −̇ p <: C β
much potential we need from this, i.e. p = u −̇ n, and we of course have to check
whether the potential of this is at least p. Finally, the amount of free-list units
u′ released by the expression should be at least n′ + u − (n + p).
In the following we show that the algorithmic typing system we just defined is
correct w.r.t. the declarative typing system of RAJA. Let Γ be a RAJA context
and let ∆ = |Γ | be the corresponding FJEU context.
Lemma 5 (Soundness of algorithmic RAJA typing).
n
n
If ∆Ψ n′ e ⇉ C γ then ∆Ψ n′ |e| : C γ .
Proof. By induction on algorithmic typing derivations, using the (♦W aste) rule
and Lemma 4.
Lemma 6 (Soundness of algorithmic RAJA method typing). Given a
RAJA type C r , a method m ∈ M(C) and a RAJA method type α ∈ M(C r, m), if
⊢a m : α ok then ⊢ m : α ok.
′
r
β1
n/n
r0
β
Proof. Let α = E1r1 , . . . , Ej j −
−→E0 . We have this : C , x1 : E1 , . . . , xj :
β
u
Ej j u′ Mbody (C, m) ⇉ Dγ , u′ ≥ n′ + u − (n + p), p = u −̇ n, ♦(C r ) ≥ p,
. C r C r −̇ p , C p and Dγ <: E0r0 , C r−p <: C β , Eiri <: Eiβi . We get by soundness of algorithmic typing (Lemma 5):
β
this : C β , x1 : E1β1 , . . . , xj : Ej j
u
u′
Mbody (C, m) : Dγ
r
this : C q , x1 : E1r1 , . . . , xj : Ej j
p
n + ♦(C )
n′
u′ ≥ n′ + u − (n + p)
Mbody (C, m) : E0r0
The completeness proof is a bit more complicated than the soundness proof.
The reason for this is that we have eliminated the rules (♦Share) and (♦W aste)
from the algorithmic system and we have to show that typing derivations that
use these rules are still admissible in the algorithmic system.
The following lemma states the admissibility of sharing in the algorithmic
system. Clearly, the witness α is either (α1 + . . . + αn ) or (α1 ∧ . . . ∧ αn ).
n
Lemma 7 (Share). Let ∆Ψ , y1 : Dα1 , . . . , yn : Dαn n′ e ⇉ C r , Ds <: Dq1 +...+qn ,
n
Dqi <: Dαi for all i. Then ∆Ψ , x : Dα n′ e[x/y1 , . . . , x/yn ] ⇉ C r for some α
with Ds <: Dα .
Proof. By induction on algorithmic typing derivations.
Lemma 8 (Completeness of algorithmic RAJA typing).
n
If Γ n′ e : C r then there is an annotated version ea of the expression e with
Ψ u′
∆ u ea ⇉ E γ for some u ≤ n and u′ ≥ n′ + u − n so that Γ <: ∆Ψ and
E γ <: C r .
Proof. By induction on typing derivations.
12
n
Case (♦Let)
We have Γ1 , Γ2 n′′ let x = e1 in e2 : C r . Notice that
Γ1 ∩ Γ2 = ∅, otherwise the rule (♦Share) should have been applied. We have
u
α ū′′
γ1
2
1
and ∆Ψ
e2 ⇉ E γ2
by induction hypothesis: ∆Ψ
u′ e1 ⇉ F
u
2 ,x:D
1
r
Ψi
s
α
γ2
γ1
with F <: D <: D and E <: C and Γi <: ∆ for i = 1, 2 and
u ≤ n, u′ ≥ n′ + u − n, ū ≤ n′ and u′′ ≥ n′′ + ū − n′ . By weakenΨ∅ u
Ψ
α ū′′
γ1
1
2
ing we also have: ∆Ψ
and ∆1 ∅ , ∆Ψ
e2 ⇉
u′ e1 ⇉ F
u
1 , ∆2
2 ,x : D
Ψ
+Ψ
Ψi
i
∅
γ2
= ∆i , we can finish by ( ⊢ Let) and obtain
E , and since ∆i
Ψ2 w
1
′
,
∆
∆Ψ
let
x
=
e
in
e2 ⇉ E γ2 with w = max( u, u + ū − u′ ) and
1
w
2
1
′
′′
′′
′
w = max( u , u + u − ū).
For the required inequalities we first consider the case where ū ≤ u′ . Then
w = u ≤ n and w′ = u′ −ū+u′′ ≥ n′ +u−n−ū+n′′ +ū−n′ ≥ n′′ +u−n = n′′ +
w−n. Otherwise, when u′ ≤ ū, we have w = u+ ū−u′ ≤ u+n′ −n′ −u+n = n
and n′′ + w − n = n′′ + u + ū − u′ − n ≤ n′′ + u + ū − n′ − u + n − n =
n′′ + ū − n′ ≤ u′′ = w′ .
n
Case (♦Conditional) We have Γ n′ if x instanceof E then e1 else e2 : C r .
u
ū
By induction hypothesis we get ∆Ψ1 u′ e1 ⇉ Dγ1 and ∆Ψ2 u′′ e2 ⇉ E γ2
r
Ψi
r
γ2
γ1
and D <: C and E <: C and Γ <: ∆ . Let F = (D ∨ E). Then we
can apply ( ⊢ Conditional) and notice that F γ1 ∨γ2 <: C r follows by Lemma
4. Finally, it es easy to see that Γ <: ∆Ψ1 ∧Ψ2 .
For the inequalities we write w = max( u, ū) and
w′ = min( u′ + w − u, u′′ + w − ū). We now clearly have w ≤ u. For w′ ≥
n′ + u − n we compute as follows: u′ + w − u ≥ n′ + u − n + w − u = n′ + w − n
and likewise u′′ + w − ū ≥ n′ + ū − n + w − ū = n′ + w − n.
Case (♦Share) Follows by Lemma 7.
m
m′
n + m′ ≥ n′ + m Γ <: Θ Ds <: C r
(♦W aste)
n
Γ n′ e : C r
Let |Θ| = ∆¯ and |Γ | = ∆. We can apply the induction hypothesis and obtain
u
∆¯Ψ u′ e ⇉ E γ with E γ <: Ds and Θ <: ∆¯Ψ and u ≤ m, u′ ≥ m′ + u − m ≥
′
n + u − n since n + m′ ≥ n + m as required.
¯ by the definition of
Γ <: ∆Ψ follows from Γ <: Θ <: ∆¯Ψ , and ∆ <: ∆,
subtyping.
Case
Θ
e : Ds
n≥m
Lemma 9 (Completeness of algorithmic RAJA method typing). Given
a RAJA type C r , a method m ∈ M(C) and a RAJA method type α ∈ M(C r, m),
if ⊢ m : α ok then ⊢a m : α ok.
r
′
n/n
r0
r
q
s
Proof. Let α = E1r1 , . . . , Ej j −
−→E0 and .(C |C , C ). We have
r
this : C q , x1 : E1r1 , . . . , xj : Ej j
n + ♦(C s )
n′
Mbody (C, m) : E0r0
By Lemma 8 we obtain β, β1 , . . . , βj with C q <: C β and Eiri <: Eiβi for all
u
β
i = 1, . . . , j and this : C β , x1 : E1β1 , . . . , xj : Ej j u′ Mbody (C, m) ⇉ Dγ for some
13
u′ ≥ n′ + u − n − ♦(C s ) and u ≤ n + ♦(C s ) and Dγ <: E0r0 . Let p = u −̇ n = u − n,
then p ≤ ♦(C s ) ≤ ♦(C r ). We still need to show C r−p <: C β . By Lemma 4 we get
C r−p <: C q and we are done by transitivity. Finally, u′ ≥ n′ + u − n − ♦(C s ) ≥
n′ + u − n − p ≥ n′ + u − (n + p).
Lemma 10 (Efficiency of algorithmic RAJA typing).
n
Γ Ψ n′ e ⇉ C r is decidable in polynomial time.
Proof (sketch). The syntax-directed backwards application of the algorithmic
typing rules produces a linear number of subtyping and sharing constraints.
Furthermore, the algorithmic view expressions occurring in these constraints are
themselves of linear size. It then suffices to restrict attention to the views that
occur as subexpressions of the ones appearing in the constraints. Their number is therefore polynomial in the size of the program. A complete table of the
subtyping and sharing judgements for this relevant subset can then be computed iteratively in polynomial time. In practice, a goal-directed implemention
performs even better.
Lemma 11. Given a RAJA class C, a view r, a method m ∈ M(C) and a
RAJA method type α ∈ M(C r, m), ⊢ m : α ok is decidable.
Proof. By Lemma 6 and Lemma 9 together we have that ⊢ m : α ok is equivalent
to ⊢a m : α ok, and ⊢a m : α ok is decidable because this : C β , x1 : E1β1 , . . . , xj :
n + ♦(C s )
β
Mbody (C, m) ⇉ E0r0 is decidable by Lemma 10.
Ej j
u
Theorem 1 (Decidability of RAJA typing). Given a RAJA -Program R,
its well-typedness is decidable.
Proof. We show that the three items of the definition of well-typedness are decidable. Conditions 1. and 2. involve subtyping which is clearly decidable. Item
3 is decidable by Lemma 11.
4
Conclusions
We have provided a type checking algorithm for RAJA programs and proved
its correctness and efficiency in the sense of polynomial-time computability. In
order to do this, we introduced algorithmic views which render the subtyping
lattice more well behaved and could also be a useful addition to the declarative
system which is exposed to the programmer. In this way, we were able to get rid
of most type annotations in method bodies although we still have to indicate
the types of multiple occurrences of a variable, i.e., how the potential belonging
to the variable is to be split among the different occurrences.
The algorithmic typechecking and the implementation allow us to investigate
larger examples which might prompt further extensions to RAJA. In particular, we would like to investigate the typability of the Iterator pattern and more
challengingly patterns involving callbacks like Observer. From a pragmatic viewpoint, polymorphic quantification over views could be a useful extension, too.
Of course, full-blown type inference is also on our agenda, thus potentially
rendering RAJA into a push-button analysis.
14
References
[AAG+ 07] Elvira Albert, Puri Arenas, Samir Genaim, German Puebla, and Damiano
Zanardini. COSTA: Design and implementation of a cost and termination
analyzer for java bytecode. In Frank S. de Boer, Marcello M. Bonsangue,
Susanne Graf, and Willem P. de Roever, editors, FMCO, volume 5382 of
Lecture Notes in Computer Science, pages 113–132. Springer, 2007.
[Cam08] Brian Campbell. Type-based amortized stack memory prediction. PhD thesis,
University of Edinburgh, 2008.
[FKF98] Matthew Flatt, Shriram Krishnamurthi, and Matthias Felleisen. Classes and
mixins. In The 25th ACM SIGPLAN-SIGACT Symposium on Principles of
Programming Languages (POPL ’98), pages 171–183, New York, January
1998. Association for Computing Machinery.
[GL98]
Gustavo Gómez and Yanhong A. Liu. Automatic accurate cost-bound analysis for high-level languages. In Frank Mueller and Azer Bestavros, editors,
Languages, Compilers, and Tools for Embedded Systems, ACM SIGPLAN
Workshop LCTES’98, Montreal, Canada. Springer, 1998. LNCS 1474.
[Gro01]
Bernd Grobauer. Topics in Semantics-based Program Manipulation. PhD
thesis, BRICS Aarhus, 2001.
[HBH+ 07] Christoph A. Herrmann, Armelle Bonenfant, Kevin Hammond, Steffen Jost,
Hans-Wolfgang Loidl, and Robert Pointon. Automatic amortised worst-case
execution time analysis. In 7th Int’l Workshop on Worst-Case Execution
Time (WCET) Analysis, Proceedings, pages 13–18, 2007.
[HDF+ 05] Kevin Hammond, Roy Dyckhoff, Christian Ferdinand, Reinhold Heckmann,
Martin Hofmann, Steffen Jost, Hans-Wolfgang Loidl, Greg Michaelson,
Robert F. Pointon, Norman Scaife, Jocelyn Srot, and Andy Wallace. The
embounded project (project start paper). In Marko C. J. D. van Eekelen,
editor, Trends in Functional Programming, volume 6 of Trends in Functional
Programming, pages 195–210. Intellect, 2005.
[HJ03]
Martin Hofmann and Steffen Jost. Static prediction of heap space usage for
first-order functional programs. In POPL: 30th ACM SIGACT-SIGPLAN
Symposium on Principles of Programming Languages, 2003.
[HJ06]
Martin Hofmann and Steffen Jost. Type-based amortised heap-space analysis (for an object-oriented language). In Peter Sestoft, editor, Proceedings
of the 15th European Symposium on Programming (ESOP), Programming
Languages and Systems, volume 3924 of LNCS, pages 22–37. Springer, 2006.
[HP99]
John Hughes and Lars Pareto. Recursion and dynamic data-structures in
bounded space:, June 21 1999.
[IPW99] Atshushi Igarashi, Benjamin Pierce, and Philip Wadler. Featherweight Java:
A minimal core calculus for Java and GJ. In Loren Meissner, editor, Proceedings of the 1999 ACM SIGPLAN Conference on Object-Oriented Programming, Systems, Languages & Applications (OOPSLA‘99), volume 34(10),
pages 132–146, N. Y., 1999.
[NCQR05] Huu Hai Nguyen, Wei Ngan Chin, Shengchao Qin, and Martin C. Rinard.
Memory usage inference for object-oriented programs. January 2005.
[Oka98] Chris Okasaki. Purely Functional Data Structures. Cambridge University
Press, 1998.
[Tar85]
Robert E. Tarjan. Amortized computational complexity. SIAM Journal on
Algebraic and Discrete Methods, 6(2):306–318, April 1985.
15
© Copyright 2026 Paperzz