• No results found

Co-Contextual Featherweight Java with Generics Typing Rules

II. Co-contextual Type Checkers for Functional Languages

9. Co-contextual Featherweight Java

10.4. Co-Contextual Featherweight Java with Generics Typing Rules

else {

T = C <T >

for(i ← 0 until X.length) { if ( X(i) == tF)

return T (i) } }

}

A field f is accessed by a class, or a class instance. Consequently, we distinguish two cases; 1) a field is accessed by a class which has generic parameters, e.g., in case of method declarations, or 2) a field is accessed by an object of a class. In the first case, we only return the type variable of the declared field f , which will be the type of the required field f . In the second case, we ensure that the required field has a type, which is an instantiation of its corresponding declared type variable. An object has instances of the generic parameters. To deduce the specific type of a field, we have to know the position of the generic class parameter that corresponds to the type variable of the declared field f . This is realized via the function getTypeF, which gets the position of the type variable corresponding to f in X, then it applies this position to the sequence of instances T of an object of a class C. The constraint S is solved when we know the actual type of T0.

Let us illustrate the remove operation given the field access new Pair<Int, Int>(1, 2).fst from the example in Section 10.1. In our co-contextual setting, the dual of accessing the field fst is introducing a new requirement for the field fst. The resulting type of the constructor call is Pair<Int, Int>, which is the receiver class of the required field, i.e., Pair<Int, Int>.fst : U. U is a fresh unification variable because we do not know the actual type of fst, until we encounter a declaration corresponding to it. Given the declaration (Pair<X/ Object, Y/ Object>. fst : X), we can apply remove on the field requirement for fst, which generates the constraint S. The actual type of fst is not ground. Therefore, S is U = getTypeF(P air<X / Object, Y / Object>, P air<Int, Int>, X). The result from applying the function getTypeF is U = Int and the condition Pair<Int, Int>.name = Pair holds. As a result, the type of the accessed field fst is Int.

10.4. Co-Contextual Featherweight Java with Generics

Typing Rules

In this section, we derive the typing rules for co-contextual FGJ. The co-contextual typing rules are constraint-based and do not have contexts and a class table. Instead, they generate requirements. The translation is systematic from contextual to co-contextual FGJ and we use dualism to enable this translation. The complete set of type rules for co-contextual FGJ is shown in Figure 10.9.

The ruleGTC-Fieldis used to type check field accesses. The type checker starts with

the receiver expression e. The contextual rule for field access GT-Fielduses the function

bound to get the bound of Te. The dual to looking up the bound of a type is introducing

a bounded requirement. However, this function applied to a nonvariable type returns the type itself and not a look up in ∆. In the co-contextual setting, we do not know whether

GTC-Field

e : Te | Se| Re | CRe| BRe BR|S0 = mergeBR(BRe, (Te U )opt)

CR|Sf = mergeCR(CRe, (U.fi : Uf, ∅)) U, Uf are fresh

e.fi : Uf | Se∪ Sf ∪ S 0

| Re | CR | BR

GTC-Invk

e : Te | Se| Re | CRe| BRe e : T | S | R | CR | BR

CRm= (U.m : <Uy/ Up> U → U0, ∅) U, U0, U , Up, Uy are fresh Sp= {V <:{Uy7→V }Up} Ss= {S <:{Uy7→V }U } ` V ok | S0 | CR0| BR0 Su= {U ={Uy7→V }U0} ∪ Se BR|Sb = mergeBR(BRe, BR, BR0, (Te U )opt) CR|S cr = mergeCR(CRe, CRm, CR, CR 0 ) R|S r = mergeR(Re, R) e.m<V>(e) : U | Su∪ S ∪ Sp∪ Ss∪ Sr∪ Scr∪ S 0 ∪ Sb | R | CR | BR GTC-New ` N ok | S0 | CR0 | BR0 e : T | S | R | CR | BR U is fresh CRf = (C.init(U ), ∅) Ss = {T <: U } R|Sr = mergeR(R) CR|S cr = mergeCR(CRf, CR, CR 0 ) BR|S = mergeBR(BR, BR, BR0) new N (e) : N | S0∪ S ∪ Ss∪ Sr∪ Scr∪ S | R | CR | BR GTC-Method e : Te| Se| Re| CRe | BRe ` T ok | St| CRt| BRt ` T0 ok | Sz| CRz | BRz ` P ok | Sp | CRp | Sp Uc, Ud, U are fresh Sx= {T = Re(x) | x ∈ dom(Re)}

Sc= {Uc= Re(this) | this ∈ dom(Re)}

Re− this − x = ∅ BR|S0 = mergeBR(BRe, BRt, BRz, BRp)

removeBR(Y / P , BR) = BR0|s0 So= (T0<: U )opt

CR|S

cr = mergeCR(CRe, (Uc.extends: Ud, ∅), (Ud.m : T → U, ∅)opt)

CR0|S r = mergeCR(CR, CRp, CRz, CRt) S = Se∪ St∪ Sz∪ Sp∪ So∪ Sc∪ S0∪ Scr∪ Sx∪ {Te<: T0} <Y / P> T0 m(T x) {return e} OK | S | Uc| CR | BR 0 GTC-Class ` N ok | Sn| CRn| BRn ` N ok | S0| CR0 | BR0 ` T ok | St| CRt| BRt K = C(U g, T0 f ){super(g); this.f = f } M OK | S | U | CR | BR BR|S = mergeBR(BR, BRn, BR 0 , BRt) removeBR(X / N , BR) = ∅|Sr Sb = Sn∪ S0∪ St∪ Sr∪ {U = C<X>} CR|S cr = mergeCR((N.init(U ), ∅), CR, CRn, CR 0 , CRt) class C<X / N>/ N {T f ; K M } OK | Sb∪ S ∪ S ∪ Seq∪ Scr | CR

10.4. Co-Contextual Featherweight Java with Generics Typing Rules the type Te is a variable or a nonvariable type. Consequently, the type checker cannot decide whether to generate a bounded requirement or not. To solve this, we introduce the optional bounded requirements (Te U )opt, where U a fresh unification variable. This special requirement is considered and has to be removed if Te is a type variable. Otherwise, the requirement is invalidated and an quality constraint is generated indicating that Te = U . The dual to looking up the field type is introducing a field requirement (U.f : Uf, ∅), where Uf is a fresh unification variable, which is a placeholder for the actual

field type.

The ruleGTC-Invkis used to type check method invocation. The type checker starts

with the expression e and the method arguments e. The dual to looking up the bound of the type of expression e is generating an optional bounded requirement, as in the case of field accesses. The operation of looking up the method signature of m in CT is dual to generating a new requirement for the method m (U.m <Uy/ Up> : U → U

0

). This requirement has a fresh type variable U as receiver since the bound of Te is not known. Also, the parameter and return types of the required method are unknown, therefore, we generate fresh unification variables, as placeholders for their actual types. We generate fresh unification variables for the generic parameters of the method (Uy) and their bounds (Up). The relation between them is added to the bounded requirements set. All subtype

relations are recorded in the constraints Sp, Ss.

The ruleGTC-Newis used to type check the object creation in co-contextual FGJ. The

dual of looking up the constructor signature is generating a constructor requirement C.init(U ). The actual constructor signature of the class C is not known because the class table is removed. Therefore, we generate fresh unification variables. The context, bounded and class table requirements are merged yielding resulting sets of requirements and constraints.

The ruleGTC-Methodis used to type check method declarations. We describe only the

required changes done to the corresponding ruleTC-Methodin co-contextual FJ to support

generic types. Initially, we consider the generic parameters of the declared method. Type variables Y are part of the method declaration and we know their actual bounds P . Therefore, the bounded requirements regarding Y are now removed from the bounded requirements set. However, we do not know the bounds of the class Uc. The rest of the bounded requirements will be satisfied when we encounter the class corresponding to Uc. The most significant change is regarding method overriding and how it behaves in FGJ. Overriding a method in FGJ allows the methods to have covariant return types. Method overriding is already supported by the co-contextual FJ, but methods must have the same signature in class Uc and supper class Ud. To this end, we introduced optional method requirement. In co-contextual FJ this method requirement had the same signature with the receiver Ud, which is superclass of Uc, as the declared method m in Uc. This optional requirement looks like (Ud.m : T → U, ∅)opt. Since FGJ allows covariant return types, we have to make sure that the overriding of m is valid. The parameter types of the overriding and overridden methods are the same. Since the return types are covariant, we generate a fresh unification variable U as placeholder for the return type of m in Ud. Then, we generate a new constraint to relate the return type T0 of m in Uc with the return type

U of m in Ud. This constraint is called the optional constraint (T0 <: U )opt. As for the optional requirement, this constraint is optional because we do not know whether there will be a declaration of m in Ud, or not. Therefore, this constraint is taken into consideration if there exists a declaration of m in the superclass Udof Uc. Otherwise, the optional constraint is invalidated and not considered while solving the other constraints.

The rule for class declaration in co-contextual FGJ is the ruleGTC-Class. This rule

is almost the same as the corresponding rule TC-Class in co-contextual FJ. The only

difference are the bounded requirements. Since the class C declares the bounds of its generic type variables, then the requirements on these type variables are satisfied and removed from the requirements set. As a result, the bounded requirements set is empty.