• No results found

A FORMAL DESCRIPTION OF SPECIALIZATION 69 are primitive types. The call is rewritten towards the specialized variant using

In document Compiling Scala for Performance (Page 69-73)

Opportunistic Specialization

4.3. A FORMAL DESCRIPTION OF SPECIALIZATION 69 are primitive types. The call is rewritten towards the specialized variant using

a specialization of all type parameters. Partial specializations are also possible, when only class or method type parameters are instantiated at primitive types.

For a new object instance when all type parameters are primitive, TS rewrites it to its corresponding specialized subclass.

To sum everything up, we need to define what a BabyScala program is and extend the various definitions to operate on programs.

A BabyScala program is a collection of class definitions C and a term t. A well-formed program P = (C, t) satisfies ` c is ok for all classes in C, and

`Dt : T where D is the corresponding class table for C.

We extend the previous translations to programs in the natural way.

norm r

class C[X]extends I{val f : T; md}z::=

class C[X]extends I{val f : T; normJmdK}

TSqdef m[Y](x : T): T=ey ::=def m[Y](x : T): T=TSJeK TSr

class C[X]extends I{val f : T; md}z::=class C[X]extends I{val f : T; TSJmdK} specJ(c, t)K ::= (TSJs pecJnormJcKKK, TSJtK)

The last line shows the order in which the various steps are performed: method expansion, class specialization and lastly term specialization.

4.3.5 Specialization preserves typing

In this section we give a formal proof that the transformation introduced previ-ously preserves typing. More formally, given a well-typed BabyScala program, we prove that spec does not introduce any badly-typed definitions nor terms.

Theorem 1. Given a BabyScala program P and a class table D, and that`D P : ok., spec(P): ok.

Before we delve into the proofs proper, we need a couple of lemmas on how type specialization interacts with fields, mtype and substitution.

Lemma 1(Type Substitution). Given a specialization s not defined at any type vari-able in X,|[T/X]U|s= [|T|s/X]|U|s.

Proof. See Appendix A.

Lemma 2(Field Specialization). Given a specialization s, a well-formed type C[T], and fields(C[T]) =U f , we have that

fields C[T] s

= |U|s f .

Proof. See Appendix A.

Lemma 3(Term Specialization). Given a valid class table D, a specialization s and a term e such thatΓ`e : T, we have

|Γ|s `JeKs:|T|s Proof. See Appendix A.

First we prove that normalization introduces only well-typed methods.

Theorem 2 (Normalization preserves typing). Given a well-formed class C[X] normJCK is still well formed.

Proof. We prove that normJ MK preserves typing.

We have that

M=def m[Y](x : T): T=e, is well-formed and that

normJ MK=def ms(x :|T|s):|T|s=JeKs, for some s= {Y7→P}. We prove that

Γ`def ms(x :|T|s):|T|s =JeKs is well-formed.

According to the type checking rule

C[X]extends D[V]{...m..} ∈ D override(m, D[V],∀Y.T→T) X, Y, x : T, this : C[X] `e : T

`def m[Y](x : T): T=e ok

we need to first prove that override(ms, I,[Y]T → T)holds. In other words, if there is a method with the same name msin C’s superclass, it has the same type as ms. If there is no such method, the proof is trivial.

Assume there is a method ms in class D, the superclass of C. By construc-tion, such a method exists iff there is a method m in D which is specialized using a specialization s0. Let that method be

def m[Y0](x : U): U=e The type of m is then∀Y0.(U→U)and the type of ms0is

U→U s0. Let

s=Y7→P , s0=nY0 7→Po

Because the two specializations give rise to methods with the same name, both s and s0 map to the same primitive types. They differ however in the name of

4.3. A FORMAL DESCRIPTION OF SPECIALIZATION 71 Let s={Xi7→Pi|i∈ [1..n]}. Then

[Y/X]s=Yi7→Pi| ∀i, j∈ [1..n], Yi=Yj =⇒ Pi=Pj

Substitution is defined only if all types Y are type variables, and there are no conflicting mappings.

s1=s2iff s1(x) =s2(x) ∀x∈dom(s1) ∪dom(s2)

Figure 4.14: Substitution and equality on specializations

the type variables, as the overridden method may have named its own type parameters differently.

We need to prove that the type of ms0 is the same as the type of ms, seen from C.

ms : T→T

s

= ∀Y.(T→T) s by spec definition

= [V/X]∀Y0.(U→U)

s0 by mtype definition

= [|V|s0/X] ∀Y0.(U→U)

s0 by Lemma 1

= [|V|s0/X] (U→U) s0 by definition of specialization

V are the types used to instantiate the supertype of C and therefore cannot mention any type variable in Y0. It follows that

V

s0=V and that T→T

s = [V/X] U→U s0

which is exactly what we needed to prove: the type of ms is the same as the type of ms0, when seen from C, therefore overriding is legal.

The last thing we need to prove is that the body of msis well-typed X, x :|T|s, this : C[X] `JeKs :|T|s

This is follows directly from Lemma 3.

Before we move to the next theorem, we need to explain overriding of spe-cialized methods. We did not specify how method names are derived from a specialization s, and we are going to leave that as an implementation detail.

However, we need to specify when two methods ms1 and ms2 have the same name for the purpose of overriding.

Definition 1. Assume ms1 is defined in a class C[X], and ms2 in D[Y] and D[Y]extends C[V]. Then ms2 overrides ms1 iff[V/X]s1=s2.

The definitions of substitution on specializations and equality between spe-cialization is shown in Figure 4.14.

Lemma 4(Substitution on specialization). Given a specialization s, X and Y type variables such that s is not defined anywhere in Y, and[Y/X]s is defined, we have

[Y/X]T

[Y/X]s =|T|s Proof. See Appendix A.

Theorem 3 (specJK preserves typing). Given a well-typed program P = (C, T),

`specJCK ok,∀C∈C.

Proof. We begin by proving that specJ MKC[X]is well-typed. By definition, we have that

specqdef m[Y](x : T) =ey

C[X]::= f wds(M) |s∈ P (X)

f wdsqdef m[Y](x : T) =ey ::= def ms[Y](x :

T

s):|T|s = (this : C[X]).m(xi.as[Ti]).as[|T|s] We prove that the new method definition is well-typed. We do so by using T-METHOD, and we need to prove that overriding is sound, and the body of msis well-typed. We begin with the last goal, as it is simpler:

Γ=X, Y, x :|T|s, this : C[X] ` (this : C[X]).m(xi.as[Ti]).as[|T|s]:|T|s We have

Γ`this : C[X] trivially

mtype(m, C[X]) = ∀Y.T→T by definition of f wds

Γ`xi.as[Ti]: Ti trivially

and the method call is well-typed.

In the following we assume that method type parameters do not change name through overriding. This has no impact on generality but keeps things simple, as there is one less step of alfa-renaming needed when looking at type equality.

We now prove that overriding is correct. If msoverrides an inherited method ms0, then they have the same type.

Let C[X]extend B[V]and B[Z]be the declaration of B. Let m0the original method for m0s. We have that m overrides m0 correctly (the initial program is well-typed, and they have the same name). Let the type of m0be Y.T0→T0.

4.3. A FORMAL DESCRIPTION OF SPECIALIZATION 73

In document Compiling Scala for Performance (Page 69-73)