Type-Safe Feature-Oriented Product Lines
A feature-oriented product line is a family of programs that share a common set of features. A feature implements a stakeholder's requirement, represents a design decision and configuration option and, when added to a program, involves the introducti…
Authors: Sven Apel, Christian Kaestner, Armin Groesslinger
T ype-Safe Feature-Oriented Product Lines Sven Apel † , Christian Kästner ‡ , Armin Größlinger † , and Christian Lengauer † † Department of Informatics and Mathematics, Univ ersity of Passau { apel,groesslinger,lengauer } @uni-passau.de ‡ School of Computer Science, Univ ersity of Magdebur g kaestner@iti.cs.uni-magdeburg.de T echnical Report, Number MIP-0909 Department of Informatics and Mathematics Univ ersity of Passau, German y June 2009 T ype-Safe F eature-Oriented Pr oduct Lines Sven Apel † , Christian Kästner ‡ , Armin Größlinger † , and Christian Lengauer † † Department of Informatics and Mathematics, Univ ersity of Passau { apel,groesslinger,lengauer } @uni-passau.de ‡ School of Computer Science, Univ ersity of Magdebur g kaestner@iti.cs.uni-magdeburg.de Abstract. A featur e-oriented pr oduct line is a family of programs that share a common set of features. A featur e implements a stakeholder’ s requirement, rep- resents a design decision and configuration option and, when added to a program, in volv es the introduction of new structures, such as classes and methods, and the refinement of existing ones, such as extending methods. With feature-oriented decomposition, programs can be generated, solely on the basis of a user’ s selec- tion of features, by the composition of the corresponding feature code. A key challenge of feature-oriented product line engineering is how to guarantee the correctness of an entir e feature-oriented product line, i.e., of all of the member programs generated from dif ferent combinations of features. As the number of valid feature combinations gro ws progressi vely with the number of features, it is not feasible to check all indi vidual programs. The only feasible approach is to hav e a type system check the entire code base of the feature-oriented product line. W e hav e developed such a type system on the basis of a formal model of a feature-oriented Ja va-lik e language. W e demonstrate that the type system ensures that every valid program of a feature-oriented product line is well-typed and that the type system is complete. 1 Introduction F eatur e-oriented pr ogramming ( FOP ) aims at the modularization of programs in terms of features [55,13]. A featur e implements a stakeholder’ s requirement and is typically an increment in program functionality [55,13]. Contemporary feature-oriented pro- gramming languages and tools such as AHEAD [13], Xak [2], CaesarJ [48], Class- box/J [14], FeatureHouse [9], and FeatureC++ [10] provide a variety of mechanisms that support the specification, modularization, and composition of features. A ke y idea is that a feature is implemented by a distinct code unit, called a featur e module . When added to a base program, it introduces new structures, such as classes and methods, and refines existing ones, such as e xtending methods [43,11]. A program that is decomposed into features is called henceforth a featur e-oriented pr ogr am . 1 1 T ypically , feature-oriented decomposition is orthogonal to class-based or functional decomposition [63,49,61]. A multitude of modularization and composition mecha- nisms [17,26,24,45,46,56,65] have been de veloped in order to allo w programmers to decom- pose a program along multiple dimensions [63]. Feature-oriented languages and tools pro vide a significant subset of these mechanisms [11]. Beside the decomposition of programs into features, the concept of a feature is useful for distinguishing dif ferent, related programs thus forming a softwar e product line [35,21]. T ypically , programs of a common domain share a set of features b ut also differ in other features. For example, suppose an email client for mobile de vices that supports the protocols IMAP and POP3 and another client that supports POP3, MIME, and SSL encryption. W ith a decomposition of the two programs into the features I M A P, P O P 3 , M I M E , and S S L , both programs can share the code of the feature P O P 3 . Since mobile devices ha ve only limited resources, unnecessary features should be removed. W ith feature-oriented decomposition, programs can be generated solely on the ba- sis of a user’ s selection of features by the composition of the corresponding feature modules. Of course, not all combinations of features are legal and result in correct programs [12]. A featur e model describes which features can be composed in which combinations, i.e., which programs are valid [35,21]. It consists of an (ordered) set of features and a set of constraints on feature combinations [21,12]. For example, our email client may have dif ferent rendering engines for HTML text, e.g., the Mozilla en- gine or the Saf ari engine, but only one at a time. A set of feature modules along wit a feature model is called a featur e-oriented pr oduct line [12]. An important question is how the correctness of feature-oriented programs, in par- ticular , and product lines, in general, can be guaranteed. A first problem is that con- temporary feature-oriented languages and tools usually in volve a code generation step during composition in which the code is transformed into a lo wer-le vel representation. In previous w ork, we have addressed this problem by modeling feature-oriented mech- anisms directly in the formal syntax and semantics of a core language, called F eature F eatherweight J ava ( FFJ ). The type system of FFJ ensures that the composition of feature modules is type-safe [8]. In this paper , we address a second problem: How can the correctness of an entir e feature-oriented product line be guaranteed? A nai ve approach would be to type-check all valid programs of a product line using a type checker like the one of FFJ [8]. How- ev er , this approach does not scale; already for 34 implemented optional features, a vari- ant can be generated for every person on the planet. Noticing this problem, Czarnecki and Pietroszek [22] and Thak er et al. [64] suggested the de velopment of a type sys- tem that checks the entire code base of the feature-oriented product line, instead of all individual feature-oriented programs. In this scenario, a type checker must analyze all feature modules of a product line on the basis of the feature model. W e will show that, with this information, the type checker can ensure that every v alid program variant that can be generated is type-safe. Specifically , we make the follo wing contributions: – W e provide a condensed version of FFJ, which is in many respects more ele gant and concise than its predecessor [8]. – W e de velop a formal type system that uses information about features and con- straints on feature combinations in order to type-check a product line without gen- erating ev ery program. – W e prove correctness by pro ving that every program generated from a well-formed product line is well-formed, as long as the feature selection satisfies the constraints of the product line. Furthermore, we prove completeness by proving that the well- 2 typedness of all programs of a product line guarantees that the product line is well- typed as a whole. – W e offer an implementation of FFJ, including the proposed type system, which can be downloaded for e valuation and for experiments with further feature-oriented language and typing mechanisms. Or work dif fers in many respects from pre vious and related w ork (see Section 5 for a comprehensiv e discussion). Most notably , Thaker et al. ha ve implemented a type system for feature-oriented product lines and conducted sev eral case studies [64]. W e take their work further with a formalization and a correctness and completeness proof. Furthermore, our work differs in many respects from pre vious work on modeling and type-checking feature-oriented and related programming mechanisms. Most no- tably , we model the feature-related mechanisms directly in FFJ’ s syntax and semantics, without any transformation to a lo wer-le vel representation, and we stay very close to the syntax of contemporary feature-oriented languages and tools (see Section 5). W e begin with a brief introduction to FFJ. 2 F eature-Oriented Pr ograms in FFJ In this section, we introduce the language FFJ. Originally , FFJ was designed for feature- oriented programs [8,7]. W e extend FFJ in Section 3 to support feature-oriented product lines, i.e., to support the representation of multiple alternativ e program v ariants at a time. 2.1 An Overview of FFJ FFJ is a lightweight feature-oriented language that has been inspired by F eatherweight J ava ( FJ ) [32]. As with FJ, we have aimed at minimality in the design of FFJ. FFJ provides basic constructs like classes, fields, methods, and inheritance and only a few new constructs capturing the core mechanisms of feature-oriented programming. But, so far , FFJ’ s type system has not supported the de velopment of feature-oriented product lines. That is, the feature modules written in FFJ are interpreted as a single program. W e will change this in Section 3. An FFJ program consists of a set of classes and refinements. A r efinement extends a class that has been introduced previously . Each class and refinement is associated with a feature. W e say that a feature intr oduces a class or applies a refinement to a class. T echnically , the mapping between classes/refinements and the features they belong to can be established in dif ferent ways, e.g., by e xtending the language with modules rep- resenting features [48,14,23] or by grouping classes and refinements that belong to a feature in packages or directories [13,10]. Like in FJ, each class declares a superclass, which may be the class Object . Refine- ments are defined using the keyword refines . The semantics of a refinement applied to a class is that the refinement’ s members are added to and merged with the member of the refined class. This way , a refinement can add ne w fields and methods to the class and override e xisting methods (declared by ov err ides ). 3 On the left side in Figure 1, we sho w an excerpt of the code of a basic email client, called E M A I L C L I E N T , (top) and a feature, called S S L , (bottom) in FFJ. The feature S S L adds the class SSL (Lines 7–10) to the email client’ s code base and refines the class T rans in order to encrypt outgoing messages (Lines 11–15). T o this ef fect, the refinement of T rans adds a new field ke y (Line 12) and overrides the method send of class T rans (Lines 13-15). Feature E M A I L C L I E N T 1 class Msg extends Object { 2 String serialize() { ... } 3 } 4 class T rans extends Object { 5 Bool send(Msg m) { ... } 6 } Feature S S L 7 class SSL extends Object { 8 T rans trans; 9 Bool send(Msg m) { ... } 10 } 11 refines class T rans { 12 K ey ke y; 13 overrides Bool send(Msg m) { 14 return new SSL( this ).send(m); 15 } 16 } refinement chain feature refinement class inherits refines SSL EmailClient Object Trans Msg Trans SSL Fig. 1. A feature-oriented email client supporting SSL encryption. T ypically , a programmer applies multiple refinements to a class by composing a sequence of features. This is called a refinement chain . A refinement that is applied immediately before another refinement in the chain is called its pr edecessor . The order of the refinements in a refinement chain is determined by their composition order . On the right side in Figure 1, we depict the refinement and inheritance relationships of our email example. Fields are unique within the scope of a class and its inheritance hierarchy and re- finement chain. That is, a refinement or subclass is not allowed to add a field that has already been defined in a predecessor in the refinement chain or in a superclass. For example, a further refinement of T rans would not be allowed to add a field k ey , since ke y has been introduced by a refinement of feature S S L already . With methods, this is different. A refinement or subclass may add new methods (overloading is prohibited) and override existing methods. In order to distinguish the two cases, FFJ expects the programmer to declare whether a method overrides an existing method (using the mod- ifier o verrides ). F or example, the refinement of T rans in feature S S L overrides the method send introduced by feature M A I L ; for subclasses, this is similar . The distinction between method introduction and ov erriding allo ws the type system to check (1) whether an introduced method inadvertently replaces or occludes an ex- isting method with the same name and (2) whether, for every ov erriding method, there is a proper method to be overridden. Apart from the modifier overrides , a method in FFJ is similar to a method in FJ. That is, a method body is an expression (prefix ed with 4 return ) and not a sequence of statements. This is due to the functional nature of FFJ and FJ. Furthermore, o verloading of methods (introducing methods with equal names and different ar gument types) is not allo wed in FFJ (and FJ). As sho wn in Figure 1, refinement chains gro w from left to right and inheritance hierarchies from top to bottom. When looking up a method body , FFJ trav erses the combined inheritance and refinement hierarchy of an object and selects the right-most and bottom-most body of a method declaration or method refinement that is compati- ble. This kind of lookup is necessary since we model features dir ectly in FFJ, instead of generating and ev aluating FJ code [40]. First, the FFJ calculus looks for a method declaration in the refinement chain of the object’ s class, starting with the last refinement back to the class declaration itself. The first body of a matching method declaration is returned. If the method is not found in the class’ refinement chain or in its o wn dec- laration, the methods in the superclass (and then the superclass’ superclass, etc.) are searched, each again from the most specific refinement of the class declaration itself. The field lookup works similarly , except that the entire inheritance and refinement hier- archy is searched and the fields are accumulated in a list. In Figure 2, we illustrate the processes of method body and field lookup schematically . Ref (n−1,1) Ref (n−1,k−1) Ref (n,1) Ref (n,m) Class n Ref (n,m−1) Ref (n,1) Ref (n,p) Ref Class 1 Ref (n,p−1) (n−1,k) Class n−1 Object Fig. 2. Order of method body and field lookup in FFJ. 2.2 Syntax of FFJ Before we go into detail, let us e xplain some notational con ventions. W e abbreviate lists in the obvious ways: – C is shorthand for C 1 , . . . , C n – C f is shorthand for C 1 f 1 , . . . , C n f n – C f; is shorthand for C 1 f 1 ; . . . ; C n f n ; – t : C is shorthand for t 1 : C 1 , . . . , t n : C n – C <: D is shorthand for C 1 <: D 1 . . . C n <: D n – . . . Note that, depending on the context, blanks, commas, or semicolons separate the el- ements of a list. The context will make clear which separator is meant. The symbol • 5 denotes the empty list and lists of field declarations, method declarations, and parameter names must not contain duplicates. W e use the metav ariables A – E for class names, f – h for field names, and m for method names. Feature names are denoted by Greek letters. In Figure 3, we depict the syntax of FFJ in e xtended Backus-Naur-F orm. An FFJ program consists of a set of class and refinement declarations. A class declaration L declares a class with the name C that inherits from a superclass D and consists of a list C f ; of fields and a list M of method declarations. 2 A refinement declaration R consists of a list C f ; of fields and a list M of method declarations. L ::= class declarations: class C e xtends D { C f; M } R ::= r efinement declarations: refines class C { C f; M } M ::= method declarations: [ov errides] C m(C x) { retur n t; } t ::= terms: x variable t.f field access t.m(t) method in vocation new C(t) object cr eation (C) t cast v ::= values: new C(v) object cr eation Fig. 3. Syntax of FFJ in e xtended BNF . A method m expects a list C x of arguments and declares a body that returns only a single expression t of type C . Using the modifier ov errides , a method declares that it intends to override another method with the same name and signature. Where we want to distinguish methods that ov erride others and methods that do not o verride others, we call the former method intr oductions and the latter method refinements Finally , there are fiv e forms of terms: the v ariable, field access, method inv ocation, object creation, and type cast, which are taken from FJ without change. The only values are object creations whose arguments are v alues as well. 2.3 FFJ’ s Class T able Declarations of classes and refinements can be looked up via a class table CT . The com- piler fills the class table during the parser pass. In contrast to FJ, class and refinement declarations are identified not only by their names but, additionally , by the names of the enclosing features. For example, in order to retriev e the declaration of class T r ans , introduced by feature M A I L , in our example of Figure 1, we write CT ( M A I L .T r ans ) ; in order to retrie ve the refinement of class T r ans applied by feature S S L, we write CT ( S S L .T r ans ) . W e call Φ .C the qualified type of class C in feature Φ . In FFJ, class and refinement declarations are unique with respect to their qualified types. This prop- erty is ensured because of the following sanity conditions: a feature is not allo wed 2 The concept of a class constructor is unnecessary in FFJ and FJ [54]. Its omittance simplifies the syntax, semantics, and type rules significantly without loss of generality . 6 – to introduce a class or refinement twice inside a single feature module and – to refine a class that the feature has just introduced. These are common sanity conditions in feature-oriented languages and tools [13,10,9]. As for FJ, we impose further sanity conditions on the class table and the inheritance relation: – CT (Φ .C ) = class C. . . or refines class C. . . for every qualified type Φ .C ∈ dom ( CT ) ; Feature Base plays the same role for features as Object plays for classes; it is a symbol denoting the empty feature at which lookups terminate. – Base .Object / ∈ dom ( CT ) ; – for ev ery class name C appearing an ywhere in CT , we have Φ .C ∈ dom ( CT ) for at least one feature Φ ; and – the inheritance relation contains no cycles (incl. self-c ycles). 2.4 Refinement in FFJ Information about the refinement chain of a class can be retriev ed using the refinement table R T . The compiler fills the refinement table during the parser pass. R T ( C ) yields a list of all features that either introduce or refine class C . The leftmost element of the result list is the feature that introduces the class C and, then, from left to right, the features are listed that refine class C in the order of their composition. In our example of Figure 1, R T ( T rans ) yields the list E M A I L C L I E N T , S S L . There is only a single sanity condition for the refinement table: – R T ( C ) = Φ for every type C ∈ dom ( CT ) , with Φ being the features that intro- duce and refine class C . In Figure 4, we show two functions for the navigation of the refinement chain that rely on R T . Function last returns, for a class name C , a qualified type Ψ n . C , in which Ψ n refers to the feature that applies the final refinement to class C ; if a class is not refined at all, Ψ n refers to the feature that introduces class C . Function pr e d returns, for a qualified type Φ .C , another qualified type Ψ n . C , in which Ψ n refers to the feature that introduces or refines class C and that is the immediate predecessor of Φ in the refinement chain; if there is no predecessor , Base .Object is returned. Navigating along the r efinement chain R T ( C ) = Ψ last ( C ) = Ψ n . C R T ( C ) = Ψ , Φ , Ω Ψ 6 = • pr ed (Φ .C ) = Ψ n . C R T ( C ) = Φ , Ω pr ed (Φ .C ) = Base . Object Fig. 4. Refinement in FFJ. 7 2.5 Subtyping in FFJ In Figure 5, we sho w the subtype relation of FFJ. The subtype relation <: is defined by one rule each for reflexivity and transitivity and one rule for relating the type of a class to the type of its immediate superclass. It is not necessary to define subtyping ov er qualified types because only classes (not refinements) declare superclasses and there is only a single declaration per class. Subtyping C < : D C < : C C < : D D < : E C < : E CT (Φ .C ) = class C extends D { . . . } C < : D Fig. 5. Subtyping in FFJ. 2.6 A uxiliary Definitions of FFJ In Figure 6, we show the auxiliary definitions of FFJ. Function fields searches the re- finement chain from right to left and accumulates the fields into a list (using the comma as concatenation operator). If there is no further predecessor in the refinement chain, i.e., we hav e reached a class declaration, then the refinement chain of the superclass is searched (see Figure 2). If Base .Object is reached, the empty list is returned (denoted by • ). Function mbody looks up the most specific and most refined body of a method m . A body consists of the formal parameters x of a method and the actual term t representing the content. The search is like in fields . First, the refinement chain is searched from right to left and, then, the superclasses’ refinement chains are searched, as illustrated in Figure 2. Note that [overrides] means that a giv en method declaration may (or may not) hav e the modifier . This way , we are able to define uniform rules for method introduction and method refinement. Function mtyp e yields the signature B → B 0 of a declaration of method m . The lookup is like in mb o dy . Predicate intr o duc e is used to check whether a class has been introduced by mul- tiple features and whether a field or method has been introduced multiple times in a class. Precisely , it states, in the case of classes, whether C has not been introduced by any feature other than Φ and whether a method m or a field f has not been introduced by Φ .C or in any of its predecessors or superclasses. T o e valuate it, we check, in the case of classes, whether CT (Ψ .C ) yields a class declaration or not, for an y feature Ψ different from Φ , in the case of methods, whether mtyp e yields a signature or not and, in the case of fields, whether f is defined in the list of fields returned by fields . Predicate r efine states whether , for a given refinement, a proper class has been declared previously in the refinement chain. The predicate override states whether a method m has been introduced before in some predecessor of Φ .C and whether the previous declaration of m has the giv en signature. 8 F ield lookup fields (Φ .C ) = C f fields (Base .Object ) = • CT (Φ .C ) = class C extends D { C f ; M } fields (Φ .C ) = fields ( last ( D )) , C f CT (Φ .C ) = refines class C { C f ; M } fields (Φ .C ) = fields ( pr ed (Φ .C )) , C f Method body lookup mbody ( m , Φ .C ) = ( x , t ) [ov errides] B m(B x) { retur n t; } ∈ M CT (Φ .C ) = class C extends D { C f ; M } mbody ( m , Φ .C ) = ( x , t ) m is not defined in M CT (Φ .C ) = class C extends D { C f ; M } mbody ( m , Φ .C ) = mbody ( m , last ( D )) [ov errides] B m(B x) { retur n t; } ∈ M CT (Φ .C ) = refines class C { C f ; M } mbody ( m , Φ .C ) = ( x , t ) m is not defined in M CT (Φ .C ) = refines class C { C f ; M } mbody ( m , Φ .C ) = mbody ( m , pr ed (Φ .C )) Method type lookup mtype ( m , Φ .C ) = C → C B 0 m(B x) { return t; } ∈ M CT (Φ .C ) = class C extends D { C f ; M } mtype ( m , Φ .C ) = B → B 0 m is not defined in M CT (Φ .C ) = class C extends D { C f ; M } mtype ( m , Φ .C ) = mtype ( m , last ( D )) B 0 m(B x) { return t; } ∈ M CT (Φ .C ) = refines class C { C f ; M } mtype ( m , Φ .C ) = B → B 0 m is not defined in M CT (Φ .C ) = refines class C { C f ; M } mtype ( m , Φ .C ) = mtype ( m , pr ed (Φ .C )) V alid class intr oduction intr oduc e (Φ .C ) @ Ψ : ( CT (Ψ .C ) = class C . . . ∧ Φ 6 = Ψ) intr oduc e (Φ .C ) V alid field intr oduction intr oduce ( f , Φ .C ) fields (Φ .C ) = E h f / ∈ h intr oduce ( f , Φ .C ) V alid method intr oduction intr oduce ( m , Φ .C ) ( m , Φ .C ) / ∈ dom ( mtyp e ) intr oduce ( m , Φ .C ) V alid class r efinement r efine (Φ .C ) R T ( C ) = Ψ , Φ , Ω CT (Ψ 1 . C ) = class C . . . r efine (Φ .C ) V alid method overriding override ( m , Φ .C , C → C 0 ) mtype ( m , Φ .C ) = B → B 0 C = B C 0 = B 0 override ( m , Φ .C , C → C 0 ) Fig. 6. Auxiliary definitions of FFJ. 9 2.7 Evaluation of FFJ Pr ograms Each FFJ program consists of a class table and a term. 3 The term is e valuated using the ev aluation rules sho wn in Figure 7. The ev aluation terminates when a v alue, i.e., a term of the form new C(v) , is reached. Note that we use a dir ect semantics of class refine- ment [40]. That is, the field and method lookup mechanisms incorporate all refinements when a class is searched for fields and methods. An alternative, which is discussed in Section 5, w ould be a flattening semantics , i.e., to merge a class in a preprocessing step with all of its refinements into a single declaration. fields ( last ( C )) = C f (new C(v)).f i − → v i ( E - P R O J N E W ) mbody ( m , last ( C )) = ( x , t 0 ) (new C(v)).m(u) − → [ x 7→ u , this 7→ new C(v) ] t 0 ( E - I N V K N E W ) C < : D (D)(new C(v)) − → new C(v) ( E - C A S T N E W ) t 0 − → t 0 0 t 0 .f − → t 0 0 .f ( E - F I E L D ) t 0 − → t 0 0 t 0 .m(t) − → t 0 0 .m(t) ( E - I N V K R E C V ) t i − → t 0 i v 0 .m(v, t i , t) − → v 0 .m(v, t 0 i , t) ( E - I N V K A R G ) t i − → t 0 i new C(v, t i , t) − → new C(v, t 0 i , t) ( E - N E W A R G ) t 0 − → t 0 0 (C)t 0 .f − → (C)t 0 0 .f ( E - C A S T ) Fig. 7. Evaluation of FFJ programs. Using the subtype relation < : and the auxiliary functions fields and mbody , the e val- uation of FFJ is fairly simple. The first three rules are most interesting (the remaining rules are just congruence rules). Rule E - P R O J N E W describes the projection of a field from an instantiated class. A projected field f i ev aluates to a value v i that has been passed as ar gument to the instantiation. Function fields is used to look up the fields of the gi ven class. It receiv es last ( C ) as argument since we want to search the entire refinement chain of class C from right to left (cf. Figure 2). Rule E - P R O J I N V K ev aluates a method inv ocation by replacing the in vocation with the method’ s body . The formal parameters of the method are substituted in the body for 3 The refinement table is not relev ant for evaluation. 10 the arguments of the inv ocation; the v alue on which the method is inv oked is substituted for this . The function mbody is called with the last refinement of the class C in order to search the refinement chain from right to left and return the most specific method body (cf. Figure 2). Rule E - C A S T N E W e valuates an upcast by simply removing the cast. Of course, the premise must be that the cast is really an upcast and not a do wncast or an incorrect cast. 2.8 T ype Checking FFJ Programs The type relation of FFJ consists of the type rules for terms and the well-formedness rules for classes, refinements, and methods, shown in Figures 8 and 9. T erm typing Γ ` t : C x : C ∈ Γ Γ ` x : C ( T- V A R ) Γ ` t 0 : C 0 fields ( last ( C 0 )) = C f Γ ` t 0 .f i : C i ( T- F I E L D ) Γ ` t 0 : C 0 Γ ` t : C mtype ( m , last ( C 0 )) = D → C C <: D Γ ` t 0 .m(t) : C ( T- I N V K ) Γ ` t : C fields ( last ( C )) = D f C <: D Γ ` new C(t) : C ( T- N E W ) Γ ` t 0 : D D < : C Γ ` (C)t 0 : C ( T- U C A S T ) Γ ` t 0 : D C < : D C 6 = D Γ ` (C)t 0 : C ( T- D C A S T ) Γ ` t 0 : D C 6 < : D D 6 < : C stupid warning Γ ` (C)t 0 : C (T- S C A S T ) Fig. 8. T erm typing in FFJ. T erm T yping Rules. A term typing judgment is a triple consisting of a typing context Γ , a term t , and a type C (see Figure 8). Rule T- V A R checks whether a free variable is contained in the typing context. Rule T - F I E L D checks whether a field access t 0 .f is well-typed. Specifically , it checks whether f is declared in the type of t 0 and whether the type f equals the type of the entire term. Rule T - I N V K checks whether a method in vocation t 0 .m ( t ) is well-typed. T o this end, it checks whether the arguments t of the in vocation are subtypes of the types of the 11 Method typing M OK a Φ .C x : B, this : C ` t 0 : E 0 E 0 < : B 0 CT (Φ .C ) = class C extends D { C f; M } intr oduce ( m , last ( D )) B 0 m(B x) { return t 0 ; } OK a Φ .C x : B, this : C ` t 0 : E 0 E 0 < : B 0 CT (Φ .C ) = class C extends D { C f; M } override ( m , last ( D ) , B → B 0 ) ov errides B 0 m(B x) { return t 0 ; } OK a Φ .C x : B, this : C ` t 0 : E 0 E 0 < : B 0 CT (Φ .C ) = refines class C { C f; M } intr oduce ( m , pred (Φ .C )) B 0 m(B x) { return t 0 ; } OK a Φ .C x : B, this : C ` t 0 : E 0 E 0 < : B 0 CT (Φ .C ) = refines class C { C f; M } override ( m , pred (Φ .C ) , B → B 0 ) ov errides B 0 m(B x) { return t 0 ; } OK a Φ .C Class typing L OK a Φ ∀ f ∈ f : intr o duc e ( f , last ( D )) intr o duc e (Φ .C ) M OK a Φ .C class C e xtends D { C f; M } OK a Φ Refinement typing R OK a Φ ∀ f ∈ f : intr o duc e ( f , pred (Φ .C )) refine (Φ .C ) M OK a Φ .C refines class C { C f; M } OK a Φ Fig. 9. W ell-formedness rules of FFJ. 12 formal parameters of m and whether the return type of m equals the type of the entire term. Rule T - N E W checks whether an object creation new C ( t ) is well-typed in that it checks whether the arguments t of the instantiation of C are subtypes of the types D of the fields of C and whether C equals the type of the entire term. The rules T - U C A S T , T - D C A S T , and T - S C A S T check whether casts are well-typed. In each rule, it is checked whether the type C the term t 0 is cast to is a subtype, supertype, or unrelated type of the type of t 0 and whether C equals the type of the entire term. 4 W ell-Formedness Rules. In Figure 9, we sho w FFJ’ s well-formedness rules of classes, refinements, and methods. The typing judgments of classes and refinements are binary relations between a class or refinement declaration and a feature, written L OK a Φ and R OK a Φ . The rule of classes checks whether all methods are well-formed in the context of the class’ qualified type. Moreov er , it checks whether none of the fields of the class declaration is introduced multiple times in the combined inheritance and refinement hierarchy and whether there is no feature other than Φ that introduces a class C (using intr o duc e ). The well-formedness rule of refinements is analogous, e xcept that the rule checks whether a corresponding class has been introduced before (using r efine ). The typing judgment of methods is a binary relation between a method declaration and the qualified type that declares the method, written M OK a Φ .C . There are four different rules for methods (from top to bottom in Figure 9) 1. that do not ov erride another method and that are declared by classes, 2. that ov erride another method and that are declared by classes, 3. that do not ov erride another method and that are declared by refinements, 4. that ov erride another method and that are declared by refinements. All four rules check whether the type E 0 of the method body is a subtype of the declared return type B 0 of the method declaration. For methods that are being introduced, it is checked whether no method with an identical name has been introduced in a superclass (Rule 1) or in a predecessor in the refinement chain (Rule 3). For methods that override other methods, it is checked whether a method with identical name and signature exists in the superclass (Rule 2) or in a predecessor in the refinement chain (Rule 4). W ell-T yped FFJ Pr ograms. Finally , an FFJ program, consisting of a term, a class table, and a refinement table, is well-typed if – the term is well-typed (checked using FFJ’ s term typing rules), – all classes and refinements stored in the class table are well-typed (checked using FFJ’ s well-formedness rules), and – the class and refinement tables are well-formed (ensured by the corresponding san- ity conditions). 4 Rule T- S C A S T is needed only for the small step semantics of FFJ (and FJ) in order to be able to formulate and prov e the type preserv ation property . FFJ (and FJ) programs whose type deriv ation contains this rule (i.e., the premise stupid warning appears in the deriv ation) are not further considered (cf. [32]). 13 T ype Soundness of FFJ. The type system of FFJ is sound. W e can prov e this using the standard theorems of preservation and progress [66]: T H E O R E M 2 . 1 ( Preservation ) If Γ ` t : C and t − → t 0 , then Γ ` t 0 : C 0 for some C 0 <: C . T H E O R E M 2 . 2 ( Pro gr ess ) Suppose t is a well-typed term. 1. If t includes new C 0 (t).f i as a subterm, then fields ( last ( C 0 )) = C f for some C and f . 2. If t includes new C 0 (t).m(u) as a subterm, then mbody ( m , last ( C 0 )) = ( x , t 0 ) and | x | = | u | for some x and t 0 . W e provide the proofs of the tw o theorems in Appendix A. 3 F eature-Oriented Pr oduct Lines in FFJ PL In this section, our goal is to define a type system for feature-oriented product lines – a type system that checks whether all valid combinations of features yield well-typed programs. In this scenario, the features in question may be optional or mutually ex- clusiv e so that different combinations are possible that form different feature-oriented programs. Since there may be plenty of valid combinations, type checking all of them individually is usually not feasible. In order to provide a type system for feature-oriented product lines, we need infor- mation about which combinations of features are v alid, i.e., which features are manda- tory , optional, or mutually exclusi ve, and we need to adapt the subtype and type rules of FFJ to check that there are no combinations/variants that lead to ill-typed terms. The type system guarantees that ev ery program deri ved from a well-typed product line is a well-typed FFJ program. FFJ together with the type system for checking feature- oriented product lines is henceforth called FFJ PL . 3.1 An Overview of F eature-Oriented Product Lines A feature-oriented product line is made up of a set of feature modules and a fea- ture model. The feature modules contains the features’ implementation and the feature model describes ho w the feature modules can be combined. In contrast to the feature- oriented programs of Section 2, typically , some features are optional and some are mu- tually exclusi ve (Also other relations such as disjunction, negation, and implication are possible [12]; they are broken down to mandatory , optional, and mutually exclusi ve features, as we will explain.). Generally , in a derivation step , a user selects a valid sub- set of features from which, subsequently , a feature-oriented program is deri ved. In our case, deri vation means assembling the corresponding feature modules for a gi ven set of features. In Figure 10, we illustrate the process of pr ogram derivation . T ypically , a wide v ariety of programs can be deriv ed from a product line [21,19]. The challenge is to define a type system that guarantees, on the basis of the feature modules and the feature model, that all valid programs are well-typed. Once a program is deriv ed from such a product line, we can be sure that it is well-typed and we can ev aluate it using the standard ev aluation rules of FFJ (see Section 2.7). 14 selection user’s feature B A E C program program program program A B D E A B E B A C E D ... A B C D F F E B B A ... E ... ... feature model F C B A D E feature modules feature−oriented product line feature−oriented programs derivation Fig. 10. The process of deriving programs from a product line. 3.2 Managing V ariability – Featur e Models The aim of dev eloping a product line is to manage the variability of a set of programs dev eloped for a particular domain and to facilitate the r euse of feature implementations among the programs of the domain. A feature model captures the variability by (explic- itly or implicitly) defining an ordered set of all features of a product line and their le gal feature combinations. A well-defined feature order is essential for field and method lookup (see Section 3.6). Different approaches to product line engineering use different representations of feature models to define legal feature combinations. The simplest approach is to enu- merate all legal feature combinations. In practice, commonly different fla vors of tree structures are used, sometimes in combination with additional propositional constraints, to define legal combinations [21,12], as illustrated in Figure 10. For our purpose, the actual representation of legal feature combinations is not rele- vant. In FFJ PL , we use the feature model only to check whether feature and/or specific program elements are present in certain circumstances. A design decision of FFJ PL is to abstract from the concrete representation of the underlying feature model and rather to provide an interface to the feature model. This has to benefits: (1) we do not need to struggle with all the details of the formalization of feature models, which is well understood by researchers [12,22,64,23] and outside the scope of this paper, and (2) we are able to support different kinds of feature model representations, e.g., a tree struc- tures, grammars, or propositional formulas [12]. The interface to the feature model is simply a set of functions and predicates that we use to ask questions like “may (or may not) feature A be present together with feature B ” or “is program element m present in ev ery variant in which also feature A is present”, i.e., “is program element m always r eachable from feature A ”. 3.3 Challenges of T ype Checking Let us explain the challenges of type checking by extending our email example, as shown in Figure 11. Suppose our basic email client is refined to process incoming text messages (feature T E X T , Lines 1–8). Optionally , it is enabled to process HTML 15 messages, using either Mozilla’ s rendering engine (feature M O Z I L L A , Lines 9–12) or Safari’ s rendering engine (feature S A FA R I , Lines 13–16). T o this end, the features M O Z I L L A and S A FA R I override the method render of class Display (Line 11 and 15) in order to in vok e the respectiv e rendering engines (field renderer , Lines 10 and 14) instead of the text printing function (Line 7). Feature T E X T 1 refines class T rans { 2 Unit receive(Msg msg) { 3 return / ∗ do something... ∗ / new Displa y().render(msg); 4 } 5 } 6 class Displa y { 7 Unit render(Msg msg) { / ∗ display message in text format ∗ / } 8 } Feature M O Z I L L A 9 refines class Displa y { 10 MozillaRenderer renderer; 11 overrides Unit render(Msg m) { / ∗ r ender HTML message using the Mozilla engine ∗ / } 12 } Feature S A FA R I 13 refines class Displa y { 14 SafariRenderer renderer; 15 overrides Unit render(Msg m) { / ∗ r ender HTML message using the Safari engine ∗ / } 16 } Fig. 11. A feature-oriented email client using Mozilla’ s and Safari’ s rendering engines. The first thing to observe is that the features M O Z I L L A and S A FA R I rely on class Displa y and its method render introduced by feature T E X T . In order to guarantee that ev ery deri ved program is well-formed, the type system checks whether Display and render are always r eachable from the features M O Z I L L A and S A FA R I , i.e., whether , in e very program variant that contains M O Z I L L A and S A FA R I , also feature T E X T is present. The second thing to observ e is that the features M O Z I L L A and S A FA R I both add a field renderer to Display (Lines 10 and 14), both of which have dif ferent types. In FFJ, a program with both feature modules would not be a well-typed program because the field renderer is introduced twice. Ho wev er , Figure 11 is not intended to repre- sent a single feature-oriented program but a feature-oriented product line; the features M O Z I L L A and S A FA R I are mutually exclusi ve, as defined in the product line’ s feature model (stated earlier), and the type system has to take this fact into account. Let us summarize the key challenges of type checking product lines: – A global class table contains classes and refinements of all features of a product line, even if some features are optional or mutually exclusi ve so that they are present only in some deri ved programs. That is, a single class can be introduced by multiple features as long as the features are mutually e xclusiv e. This is also the case for multiple introductions of methods and fields, which may e ven ha ve dif ferent types. 16 – The presence of types, fields, and methods depends on the presence of the fea- tures that introduce them. A reference from the elements of a feature to a type, a field projection, or a method in vocation is v alid if the referenced element is al ways reachable from the referring feature, i.e., in ev ery v ariant that contains the referring feature. – Like references, an extension of a program element, such as a class or method refinement, is v alid only if the extended program element is always reachable from the feature that applies the refinement. – Refinements of classes and methods do not necessarily form linear refinement chains. There may be alternati ve refinements of a single class or method that ex- clude one another , as explained belo w . 3.4 Collecting Information on F eature Modules For type checking, the FFJ PL compiler collects v arious information on the feature mod- ules of the product line. Before the actual type checking is performed, the compiler fills three tables with information: the class table ( CT ), the introduction table ( IT ), and the refinement table ( R T ). The class table CT of FFJ PL is like the one of FFJ and has to satisfy the same sanity conditions except that (1) there may be multiple declarations of a class (or field or method), as long as the y are defined in are mutually exclusiv e features, and (2) there may be cycles in the inheritance hierarchy , but no cycles for each set of classes which are reachable from any gi ven feature. The introduction table IT maps a type to a list Φ of (mutually exclusi ve) features that introduce the type. The features returned by IT are listed in the order prescribed by the feature model. In our example of Figure 11, a call of IT ( Displa y ) would return a list consisting only of the single feature T E X T . Likewise, the introduction table maps field and method names, in combination with their declaring classes, to features. For example, a call of IT ( Displa y . renderer ) would return the list M O Z I L L A , S A FA R I . The sanity conditions for the introduction table are straightforward: – IT ( C ) = Φ for every type C ∈ dom ( CT ) , with Φ being the features that introduce class C . – IT ( C .f ) = Φ for e very field f contained in some class C ∈ dom ( CT ) , with Φ being the features that introduce field f . – IT ( C .m ) = Φ for e very method m contained in some class C ∈ dom ( CT ) , with Φ being the features that introduce method m . Much like in FFJ, in FFJ PL there is a refinement table R T . A call of R T ( C ) yields a list of all features that either introduce or refine class C , which is different from the introduction table that returns only the features that introduce class C . As with IT , the features returned by R T are listed in the order prescribed by the feature model. The sanity condition for FFJ PL ’ s refinement table is identical to the one of FFJ, namely: – R T ( C ) = Φ for every type C ∈ dom ( CT ) , with Φ being the features that intro- duce and refine class C . 17 3.5 Featur e Model Interface As said before, in FFJ PL , we abstract from the concrete representation of the feature model and define instead an interface consisting of proper functions and predicates. There are two kinds of questions we want to ask about the feature model, which we explain ne xt. First, we would like to know which features are never present together, which fea- tures are sometimes present together , and which features are always present together . T o this end, we define tw o predicates, never and sometimes , and a function always . Pred- icate never (Ω , Φ) indicates that feature Φ is never reachable in the context Ω , i.e., there is no v alid program variant in which the features Ω and feature Φ are present together . Predicate sometimes (Ω , Φ) indicates that feature Φ is sometimes present when the features Ω are present, i.e., there are v ariants in which the features Ω and feature Φ are present together and there are variants in which they are not present together . Function always (Ω , Φ) is used to ev aluate whether feature Φ is always present in the context Ω (either alone or within a group of alternative features). There are three cases: if feature Φ is always present in the context, always returns the feature again ( always (Ω , Φ) = Φ ); if feature Φ is not always present, b ut would be together with a certain group of mutu- ally exclusiv e features Ψ (i.e., one of the group is alw ays present), always returns all features of this group ( always (Ω , Φ) = Φ , Ψ ). If a feature is not present at all, neither alone nor together with other mutually exclusi ve features, always returns the empty list ( always (Ω , Φ) = • ). The above predicates and function pro vide all information we need to kno w about the features’ relationships. They are used especially for field and method lookup. Second, we would like to know whether a specific program element is always present when a giv en set of features is present. This is necessary to ensure that ref- erences to program elements are al ways v alid (i.e., not dangling). W e need two sources of information for that. First, we need to know all features that introduce the program element in question (determined using the introduction table) and, second, we need to know which combinations of features are legal (determined using the feature model). For the field renderer of our example, the introduction table would yield the features M O Z I L L A and S A FA R I and, from the feature model, it follows that M O Z I L L A and S A - FA R I are mutually exclusi ve, i.e., never ( M O Z I L L A , S A FA R I ) . But it can happen that none of the two features is present, which can in validate a reference to the field. The type system needs to know about this situation. T o this end, we introduce a predicate validr ef that expresses that a program element is always reachable from a set of features. For example, validr ef (Ω , C ) holds if type C is always reachable from the context Ω , validr ef (Ω , C .f ) holds if field f of class C is always reachable from the conte xt Ω , and validr ef (Ω , C.m ) holds if method m of class C is always reachable from the context Ω . Applying validr ef to a list of program elements means that the conjunction of the predicates for every list element is taken. Finally , when we write validr ef (Ω , C ) a Ψ , we mean that program element C is always reachable from a context Ω in a subset Ψ of features of the product line. In our prototype, we have implemented the above functions and predicates using a SA T solver that reasons about propositional formulas representing constraints on legal 18 feature combinations (see Section 4), as proposed by Batory [12] and Czarnecki and Pietroszek [22]. 3.6 Refinement in FFJ PL In Figure 12, we sho w the functions last and pr e d for the navigation along the refine- ment chain. The two functions are identical to the ones of FFJ (cf. Figure 4). Ho wev er , in FFJ PL , there may be alternati ve declarations of a class and, in the refinement chain, refinement declarations may e ven precede class declarations, as long as the declaring features are mutually exclusi ve. Let us illustrate refinement in FFJ PL by means of the example shown in Figure 13. Class C is introduced in the features Φ 1 and Φ 3 . Feature Φ 2 refines class C introduced by feature Φ 1 and feature Φ 4 refines class C introduced by feature Φ 3 . Feature Φ 1 and Φ 2 are ne ver present when feature Φ 3 or Φ 4 are present and vice v ersa. A call of R T ( C ) would return the list Φ 1 , . . . , Φ 4 , a call of last ( C ) would return the qualified type Φ 4 . C , and a call of pr e d (Φ 4 . C ) would return the qualified type Φ 3 . C and so on. Navigating along the r efinement chain R T ( C ) = Ψ last ( C ) = Ψ n . C R T ( C ) = Ψ , Φ , Ω Ψ 6 = • pr ed (Φ .C ) = Ψ n . C R T ( C ) = Φ , Ω pr ed (Φ .C ) = Base . Object Fig. 12. Refinement in FFJ PL . C C C C 2 3 4 1 Φ Φ Φ Φ mutually exclusive Fig. 13. Multiple alternative refinements. 3.7 Subtyping in FFJ PL The subtype relation is more complicated in FFJ PL than in FFJ. The reason is that a class may ha ve multiple declarations in different features, each declaring possibly different superclasses, as illustrated in Figure 14. That is, when checking whether a class is a subtype of another class, we need to check whether the subtype relation holds in all alternati ve inheritance paths that may be reached from a given context. For example, F ooBar is a subtype of BarF oo because BarFoo is a superclass of F ooBar in every 19 program v ariant (since always (Φ 1 , Φ 2 ) = Φ 2 , Φ 3 ); b ut F ooBar is not a subtype of F oo and Bar because, in both cases, a program v ariant exists in which F ooBar is not a (indirect) subclass of the class in question. Φ 1 Φ 2 Φ 3 Φ 2 Φ 3 Φ 1 present together with and are mutually exclusive and one of them is always E e; B b; B m(B b); A a; A m(D d); D d; FooBar Foo Bar BarFoo D d; BarFoo Fig. 14. Multiple inheritance chains in the presence of alternative features. In Figure 15, we show the subtype relation of FFJ PL . The subtype relation C < : E a Ω is read as follows: in the context Ω , type C is a subtype of type E , i.e., type C is a subtype of type E in e very v ariant in which also the features Ω are present. The first rule in Figure 15 covers reflexi vity and terminates the recursion ov er the inheritance hierarchy . The second rule states that class C is a subtype of class E if at least one declaration of C is always present (tested with validr ef ) and if e very of C ’ s declarations that may be present together with Ω (tested with sometimes ) declares some type D as its supertype and D is a subtype of E in the context Ω . That is, E must be a direct or indirect supertype of D in all v ariants in which the features Ω are present. Additionally , supertype D must be alw ays reachable from the context ( Ω , Ψ ). When traversing the inheritance hierarchy , in each step, the context is e xtended by the feature that introduces the current class in question, e.g., Ω is extended with Ψ . Interestingly , the second rule subsumes the two FFJ rules for transitivity and direct superclass declaration because some declarations of C may declare E directly as its superclass and some declarations may declare another superclass D that is, in turn, a subtype of E , and the rule must be applicable to both cases simultaneously . Subtyping C < : E a Ω C < : C a Ω validr ef (Ω , C ) ∀ Ψ ∈ IT ( C ) : sometimes (Ω , Ψ) ⇒ CT (Ψ .C ) = class C extends D { . . . } validr ef ((Ω , Ψ) , D ) D < : E a Ω , Ψ C < : E a Ω Fig. 15. Subtyping in FFJ PL . 20 Applied to our example of Figure 14, we hav e F ooBar < : F ooBar a Φ 1 because of the reflexi vity rule. W e also ha ve FooBar < : BarF oo a Φ 1 because FooBar is reachable from feature Φ 1 and every feature that introduces F ooBar , namely Φ 1 , con- tains a corresponding class declaration that declares BarFoo as F ooBar ’ s superclass, and BarFoo is alw ays reachable from Φ 1 . Howe ver , we have F ooBar 6 < : Foo a Φ 1 and F ooBar 6 < : Bar a Φ 1 because F ooBar ’ s immediate superclass BarFoo is not always a subtype of F oo respecti vely of Bar . 3.8 A uxiliary Definitions of FFJ PL Extending FFJ tow ard FFJ PL makes it necessary to add and modify some auxiliary functions. The most complex changes concern the field and method lookup mecha- nisms. Field Lookup. The auxiliary function fields collects the fields of a class including the fields of its superclasses and refinements. Since alternative class or refinement decla- rations may introduce alternativ e fields (or the same field with identical or alternativ e types), fields may return different fields for dif ferent feature selections. Since we want to type-check all valid variants, field returns multiple field lists (i.e., a list of lists) that cov er all possible feature selections. Each inner list contains field declarations collected in an alternativ e path of the combined inheritance and refinement hierarchy . For le gibility , we separate the inner lists using the delimiter ‘ ◦ ’. For e xample, look- ing up the fields of class F ooBar in the conte xt of feature Φ 1 (Figure 14) yields the list A a , D d , E e ◦ B b , D d , E e because the features Φ 2 and Φ 3 are mutually ex- clusiv e and one of them is present in each variant in which also Φ 1 is present. For readability , we use the metav ariables F and G when referring to inner field lists. W e abbreviate a list of lists of fields F 1 ◦ . . . ◦ F n by F . Analogously , F is shorthand for F 11 ◦ . . . ◦ F n 1 ◦ . . . ◦ F 1 m ◦ . . . ◦ F nm . Function fields recei ves a qualified type Φ .C and a context of selected features Ω . If we want all possible field lists, the context is empty . If we want only field lists for a subset of feature selections, e.g., only the fields that can be referenced from a term in a specific feature module, we can use the conte xt to specify one or more features of which we know that the y must be selected. The basic idea of FFJ PL ’ s field lookup is to tra verse the combined inheritance and refinement hierarch y much like in FFJ. There are four situations that are handled differ - ently: 1. The field lookup returns the empty list when it reaches Base .Object . 2. The field lookup ignores all fields that are introduced by features that are nev er present in a giv en context. 3. The field lookup collects all fields that are introduced by features that are alw ays present in a giv en context. References to these fields are alw ays valid. 4. The field lookup collects all fields that are introduced by features that may be present in a gi ven context but that are not alw ays present. In this case, a special 21 marker @ is added to the fields in question because we cannot guarantee that a ref- erence to this field is safe in the given context. 5 It is up to the type system to decide, based on the marker , whether this situation may provoke an error (e.g., the type sys- tem ignores the marker when looking for duplicate fields b ut reports an error when type checking object creations). 5. A special situation occurs when the field lookup identifies a group of alternati ve features. In such a group each feature is optional and excludes every other feature of the group and at least one feature of the group is always present in a given context. Once the field lookup identifies a group of alternati ve features, we split the result list, each list containing the fields of a feature of the group and the fields of the original list. F ield lookup fields (Ω , Φ .C ) = C f fields (Ω , Φ .Object ) = • (FL-1) never (Ω , Φ) fields (Ω , Φ .C ) = fields (Ω , pr e d (Φ .C )) (FL-2) sometimes (Ω , Φ) always (Ω , Φ) = Φ CT (Φ .C ) = class C extends D { C f ; M } fields (Ω , Φ .C ) = app end ( fields (Ω , last ( D )) , C f ) (FL-3.1) sometimes (Ω , Φ) always (Ω , Φ) = Φ CT (Φ .C ) = refines class C { C f ; M } fields (Ω , Φ .C ) = app end ( fields (Ω , pr e d (Φ .C )) , C f ) (FL-3.2) sometimes (Ω , Φ) always (Ω , Φ) = • CT (Φ .C ) = class C extends D { C f ; M } fields (Ω , Φ .C ) = app end ( fields (Ω , last ( D )) , C f @) (FL-4.1) sometimes (Ω , Φ) always (Ω , Φ) = • CT (Φ .C ) = refines class C { C f ; M } fields (Ω , Φ .C ) = app end ( fields (Ω , pr e d (Φ .C )) , C f @) (FL-4.2) sometimes (Ω , Φ) always (Ω , Φ) = Ψ fields (Ω , Φ .C ) = fields ((Ω , Ψ 1 ) , Φ .C ) ◦ . . . ◦ fields ((Ω , Ψ n ) , Φ .C ) (FL-5) Fig. 16. Field lookup in FFJ PL . In order to distinguish the different cases, we use the predicates and functions de- fined in Section 3.5 (especially never , sometimes , and always ). The definition of func- 5 Note that the mark er @ is generated during type checking, so we do not include it in the syntax of FFJ. 22 tion fields , sho wn in Figure 16, follows the intuition described above: Once Base .Object is reached, the recursion terminates (FL-1). When a feature is ne ver reachable in the giv en context, fields ignores this feature and resumes with the pre vious one (FL-2). When a feature is mandatory (i.e., alw ays present in a gi ven context), the fields in ques- tion are added to each alternative result list, which were created in Rule FL-5 (FL-3.1 and FL-3.2). 6 When a feature is optional, the fields in question, annotated with the marker @ , are added to each alternativ e result list (FL-4.1 and FL-4.2). When a feature is part of an alternati ve group of features, we cannot immediately decide ho w to pro- ceed. W e split the result list in multiple lists (by means of multiple recursi ve inv ocations of fields ), in which we add one of the alternati ve features to each context passed to an in vocation of fields (FL-5). Method type lookup mtyp e (Ω , m , Φ .C ) = B → B 0 mtyp e (Ω , m , Base .Object ) = • (ML-1) B 0 m(B x) { . . . } ∈ M sometimes (Ω , Φ) CT (Φ .C ) = class C extends D { C f ; M } mtyp e (Ω , m , Φ .C ) = mtyp e (Ω , m , pr ed (Φ .C )) , mtyp e (Ω , m , last ( D )) , B → B 0 (ML-2) B 0 m(B x) { . . . } ∈ M sometimes (Ω , Φ) CT (Φ .C ) = refines class C { C f ; M } mtyp e (Ω , m , Φ .C ) = mtyp e (Ω , m , pr ed (Φ .C )) , B → B 0 (ML-3) ( m is not defined in M ∨ never (Ω , Φ)) CT (Φ .C ) = class C extends D { C f ; M } mtyp e (Ω , m , Φ .C ) = mtyp e (Ω , m , pr ed (Φ .C )) , mtyp e (Ω , m , last ( D )) (ML-4) ( m is not defined in M ∨ never (Ω , Φ)) CT (Φ .C ) = refines class C { C f ; M } mtyp e (Ω , m , Φ .C ) = mtyp e (Ω , m , pr ed (Φ .C )) (ML-5) Fig. 17. Method Lookup in FFJ PL . Method T ype Lookup. Like in field lookup, in method lookup, we have to take al- ternativ e definitions of methods into account. But the lookup mechanism is simpler than in fields because the order of signatures found in the combined inheritance and refinement hierarchy is irrelev ant for type checking. Hence, function mtyp e yields a simple list B → B 0 of signatures for a given method name m . For example, calling mtyp e (Φ 1 , m , Φ 1 . C ) in the context of Figure 14 yields the list D → A , B → B . 6 Function app end adds to each inner list of a list of field lists a given field. Its implementation is straightforward and omitted for bre vity . 23 In Figure 17, we show the definition of function mtyp e . For Base . Object , the empty list is returned (ML-1). If a class that is sometimes reachable introduces a method in question (ML-2), its signature is added to the result list and all possible predecessors in the refinement chain (using pr e d ) and all possible subclasses are searched (using last ). Like wise, if a refinement that is sometimes reachable introduces a method with the name searched (ML-3), its signature is added to the result list and all possible prede- cessors in the refinement chain are searched (using pr e d ). If a class or refinement does not declare a corresponding method (ML-4 and ML-5) or the a class is nev er reachable, the search proceeds with the possible superclasses or predecessors. The current definition of function mtyp e returns possibly many duplicate signatures. A straightforward optimization would be to remove duplicates before using the result list, which we omitted for simplicity . V alid class intr oduction intr oduc e (Ω , Φ .C ) @ Ψ : CT (Ψ .C ) = class C extends D { C f ; M } Ψ 6 = Φ sometimes (Ω , Ψ) intr oduc e (Ω , Φ .C ) V alid field intr oduction intr oduc e (Ω , f , Φ .C ) ∀ E h ∈ fields (Ω , Φ .C ) : f / ∈ h intr oduc e (Ω , f , Φ .C ) V alid method intr oduction intr oduc e (Ω , m , Φ .C ) mtyp e (Ω , m , Φ .C ) = • intr oduc e (Ω , m , Φ .C ) V alid class r efinement r efine (Ω , Φ .C ) R T ( C ) = Ψ , Φ , Π validr ef (Ω , C ) a Ψ r efine (Ω , Φ .C ) V alid method overriding override (Ω , m , Φ .C , C → C 0 ) R T ( C ) = Ψ , Φ , Π validr ef (Ω , C .m ) a Ψ , Φ ∀ B → B 0 ∈ mtyp e (Ω , m , Φ .C ) : C = B ∧ C 0 = B 0 override (Ω , m , Φ .C , C → C 0 ) Fig. 18. V alid introduction, refinement, and overriding in FFJ PL . 24 V alid Introduction, Refinement, and Overriding. In Figure 18, we show predicates for checking the validity of introduction, refinement, and overriding in FFJ PL . Predicate intr o duc e indicates whether a class with the qualified type Φ .C has not been introduced by any other feature Ψ that may be present in the conte xt Ω . Like wise, intr o duc e holds if a method m or a field f has not been introduced by a qualified type Φ .C (including possible predecessors and superclasses) that may be present in the gi ven context Ω . T o this end, it checks either whether mtyp e yields the empty list or whether f is not contained in ev ery inner list returned by fields . For a giv en refinement, predicate r efine indicates whether a proper class, which is always reachable in the gi ven context, has been declared previously in the refinement chain. W e write validr ef (Ω , C ) a Ψ in order to state that a declaration of class C has been introduced in the set Ψ of features, which is only a subset of the features of the product line, namely the features that precede the feature that introduces class C . Predicate override indicates whether a declaration of method m has been introduced (and is always reachable) in some feature introduced by before the feature that refines m and whether e very possible declaration of m in any predecessor of a Φ .C has the same signature. 3.9 T ype Relation of FFJ PL The type relation of FFJ PL consists of type rules for terms and well-formedness rules for classes, refinements, and methods, shown in Figure 19 and Figure 20. T erm T yping Rules. A term typing judgment in FFJ PL is a quadruple, consisting of a typing context Γ , a term t , a list of types C , and a feature Φ that contains the term (see Figure 19). A term can hav e multiple types in a product line because there may be multiple declarations of classes, fields, and methods. The list C contains all possible types a term can hav e. Rule T - V A R PL is standard and does not refer to the feature model. It yields a list consisting only of the type of the variable in question. Rule T - F I E L D PL checks whether a field access t 0 .f is well-typed in e very possi- ble variant in which also Φ is present. Based on the possible types E of the term t 0 the field f is accessed from, the rule checks whether f is always reachable from Φ (using validr ef ). Note that this is a key mechanism of FFJ PL ’ s type system. It en- sures that a field, being accessed, is definitely present in e very v alid program variant in which the field access occurs – without generating all these v ariants. Furthermore, all possible fields of all possible types E are assembled in a nested list F , C f , G in which C f denotes a declaration of the field f ; the call of fields (Φ , last ( E )) is shorthand for fields (Φ , last ( E 1 )) . . . fields (Φ , last ( E n )) , in which the individual result lists are concatenated. Finally , the list of all possible types C 11 , . . . , C n 1 , . . . , C 1 m , . . . , C nm of field f becomes the list of types of the ov erall field access. Note that the result list may contain duplicates, which could be eliminated for optimization purposes. Rule T - I N V K PL checks whether a method in vocation t 0 .m ( t ) is well-typed in e very possible variant in which also Φ is present. Based on the possible types E of the term t 0 the method m is inv oked on, the rule checks whether m is alw ays reachable from Φ 25 T erm typing Γ ` t : C a Φ x : C ∈ Γ Γ ` x : C a Φ (T- V A R PL ) ∀ E ∈ E : validr ef (Φ , E.f ) Γ ` t 0 : E a Φ fields (Φ , last ( E )) = F , C f , G Γ ` t 0 .f : C 11 , . . . , C n 1 , . . . , C 1 m , . . . , C nm a Φ (T- F I E L D PL ) ∀ E ∈ E : validr ef (Φ , E.m ) ∀ C ∈ C , ∀ D ∈ D ∈ D : C < : D a Φ Γ ` t 0 : E a Φ Γ ` t : C a Φ mtyp e (Φ , m , last ( E )) = D → B Γ ` t 0 .m(t) : B 11 , . . . , B n 1 , . . . , B 1 m , . . . , B nm a Φ (T- I N V K PL ) validr ef (Φ , C ) ∀ D g ∈ F , ∀ C ∈ C : C < : D a Φ Γ ` t : C a Φ fields (Φ , last ( C )) = F @ / ∈ F Γ ` new C(t) : C a Φ (T- N E W PL ) validr ef (Φ , C ) Γ ` t 0 : E a Φ ∀ E ∈ E : ( E < : C a Φ ∨ C < : E a Φ) Γ ` (C)t 0 : C a Φ (T- U D C A S T PL ) validr ef (Φ , C ) stupid warning Γ ` t 0 : E a Φ ∃ E ∈ E : ( C 6 < : E a Φ ∧ E 6 < : C a Φ) Γ ` (C)t 0 : C a Φ (T- S C A S T PL ) Fig. 19. T erm typing in FFJ PL . 26 Method typing M OK a Φ .C x : B, this : C ` t 0 : E a Φ ∀ E ∈ E : E < : B 0 a Φ validr ef (Φ , B ) intr o duce (Φ , m , last ( D )) CT (Φ .C ) = class C extends D { C f; M } B 0 m(B x) { return t 0 ; } OK a Φ .C x : B, this : C ` t 0 : E a Φ ∀ E ∈ E : E < : B 0 a Φ validr ef (Φ , B ) override (Φ , m , last ( D ) , B → B 0 ) CT (Φ .C ) = class C extends D { C f; M } ov errides B 0 m(B x) { return t 0 ; } OK a Φ .C x : B, this : C ` t 0 : E a Φ ∀ E ∈ E : E < : B 0 a Φ validr ef (Φ , B ) intr o duce (Φ , m , pr e d (Φ .C )) CT (Φ .C ) = refines class C { C f; M } B 0 m(B x) { return t 0 ; } OK a Φ .C x : B, this : C ` t 0 : E a Φ ∀ E ∈ E : E < : B 0 a Φ validr ef (Φ , B ) override (Φ , m , pr e d (Φ .C ) , B → B 0 ) CT (Φ .C ) = refines class C { C f; M } ov errides B 0 m(B x) { return t 0 ; } OK a Φ .C Class typing L OK a Φ validr ef (Φ , D ) validr ef (Φ , C ) ∀ f ∈ f : intr o duc e (Φ , f , last ( D )) intr o duc e (Φ , Φ .C ) M OK a Φ .C class C e xtends D { C f; M } OK a Φ Refinement typing R OK a Φ validr ef (Φ , C ) ∀ f ∈ f : intr o duc e (Φ , f , pr e d (Φ .C )) r efine (Φ , Φ .C ) M OK a Φ .C refines class C { C f; M } OK a Φ Fig. 20. W ell-formedness rules of FFJ PL . 27 (using validr ef ). As with field access, this check is essential. It ensures that in generated programs only methods are inv oked that are also present. Furthermore, all possible signatures of m of all possible types E are assembled in the nested list D → B and it is checked that all possible lists C of argument types of the method inv ocation are subtypes of all possible lists D of parameter types of the method (this implies that the lengths of the tw o lists must be equal). A method in vocation has multiple types assembled in a list that contains all result types of method m determined by mtyp e . As with field access, duplicates should be eliminated for optimization purposes. Rule T - N E W PL checks whether an object creation ne w C ( t ) is well-typed in e very possible variant in which also Φ is present. Specifically , it checks whether there is a declaration of class C always reachable from Φ . Furthermore, all possible field combi- nations of C are assembled in the nested list F , and it is checked whether all possible combinations of argument types passed to the object creation are subtypes of the types of all possible field combinations (this implies that the number of arguments types must equal the number of field types). The fields of the result list must not be annotated with the marker @ since optional fields may not be present in every v ariant and references may become in valid (see field lookup). 7 An object creation has only a single type C . Rules T- U D C A S T PL and T- S C A S T PL check whether casts are well-typed in e very possible variant in which also Φ is present. This is done by checking whether the type C the term t 0 is cast to is always reachable from Φ and whether this type is a subtype, supertype, or unrelated type of all possible types E the term t 0 can have. W e ha ve only a single rule T - U D C A S T PL for up- and downcasts because the list E of possible types may contain super- and subtypes of C simultaneously . If there is a type in the list which leads to a stupid case, we flag a stupid warning . A cast yields a list containing only a single type C . W ell-Formedness Rules. In Figure 20, we show the well-formedness rules of classes, refinements, and methods. Like in FFJ, the typing judgment of classes and refinements is a binary relation be- tween a class or refinement declaration and a feature. The rule of classes checks whether all methods are well-formed in the context of the class’ qualified type. Moreover , it checks whether the class declaration is unique in the scope of the enclosing feature Φ , i.e., whether no other feature, that may be present together with feature Φ , introduces a class with an identical name (using intr o duc e ). Furthermore, it checks whether the superclass and all field types are always reachable from Φ (using validr ef ). Finally , it checks whether none of the fields of the class declaration hav e been introduced before (using intr o duc e ). The well-formedness rule of refinements is analogous, except that the rule checks that there is at least one class declaration reachable that is refined and that has been introduced before the refinement (using r efine ). The typing judgment of methods is a binary relation between a method declaration and the qualified type that declares the method. Like in FFJ, there are four dif ferent rules for methods (from top to bottom in Figure 20) 1. that do not ov erride another method and that are declared by classes, 7 The treatment of @ is semiformal but simplifies the rule. 28 2. that ov erride another method and that are declared by classes, 3. that do not ov erride another method and that are declared by refinements, 4. that ov erride another method and that are declared by refinements. All four rules check whether all possible types E of the method body are subtypes of the declared return type B 0 of the method and whether the argument types B are always reachable from the enclosing feature Φ (using validr ef ). For methods that are introduced, it is checked, using intr o duc e , whether no method with identical name has been introduced in any possible superclass (Rule 1) or in any possible predecessor in the refinement chain (Rule 3). For methods that ov erride other methods, it is checked, using override , whether a method with identical name and signature exists in any possible superclass (Rule 2) or in any possible predecessor in the refinement chain (Rule 4). W ell-T yped FFJ PL Product Lines. An FFJ PL product line, consisting of a term, a class table, an introduction table, and a refinement table, is well-typed if – the term is well-typed (checked using FFJ PL ’ s term typing rules), – all classes and refinements stored in the class table are well-formed (check ed using FFJ PL ’ s well-formedness rules), and – the class, introduction, and refinement tables are well-formed (ensured by the cor- responding sanity conditions). 3.10 T ype Safety of FFJ PL T ype checking in FFJ PL is based on information contained in the class table, introduc- tion table, refinement table, and feature model. The first three are filled by the compiler that has parsed the code base of the product line. The feature model is supplied directly by the user (or tool). The compiler determines which class and refinement declarations belong to which features. The classes and refinements of the class table are checked us- ing their well-formedness rules which, in turn, use the well-formedness rules for meth- ods and the term typing rules for method bodies. Sev eral rules use the introduction and refinement tables in order to map types, fields, and methods to features and the fea- ture model to navig ate along refinement chains and to check the presence of program elements. What does type safety mean in the context of a product line? The product line itself is never e valuated; rather , dif ferent programs are deri ved that are then e valuated. Hence, the property we are interested in is that all programs that can be derived from a well- typed product line are in turn well-typed. Furthermore, we would like to be sure that all FFJ PL product lines, from which only well-typed FFJ programs can be deri ved, are well-typed. W e formulate the two properties as the two theorems Corr ectness of FFJ PL and Completeness of FFJ PL . 29 Correctness T H E O R E M 3 . 1 ( Correctness of F F J PL ) Giv en a well-typed FFJ PL product line pl (in- cluding with a well-typed term t , well-formed class, introduction, and refinement tables CT , IT , and R T , and a feature model FM ), e very program that can be deri ved with a valid feature selection fs is a well-typed FFJ program (cf. Figure 10). pl = ( t , CT , IT , R T , FM ) pl is well-typed fs is valid in FM derive ( pl , fs ) is well-typed Function derive collects the feature modules from a product line according to a user’ s selection fs , i.e., non-selected feature modules are remov ed from the deriv ed program. After this deriv ation step, the class table contains only classes and refinements stemming from the selected feature modules. W e define a valid feature selection to be a list of features whose combination does not contradict the constraints implied by the feature model. The proof idea is to show that the type deri vation tree of an FFJ PL product line is a superimposition of multiple so-called type derivation slices . As usual, the type deriv ation proceeds from the root (i.e., an initial type rule that checks the term and all classes and refinements of the class table) to the leaves (type rules that do not have a premise) of the type deri vation tree. Each time a term has multiple types, e.g., a method has dif ferent alternativ e return types, which is caused by multiple mutually e xclusiv e method declarations, the type derivation splits into multiple branches. With branc h we refer only to positions in which the type deri vation tree is split into multiple subtrees in order to type check multiple mutually exclusi ve term definitions. Each subtree from the root of the type deriv ation tree along the branches to ward a leaf is a type deri v ation slice. Each slice corresponds to the type deriv ation of a feature-oriented program. Let us illustrate the concept of a type deri vation slice by a simplified example. Sup- pose the application of an arbitrary type rule to a term t somewhere in the type deri va- tion. T erm t has multiple types C due to dif ferent alternati ve definitions of t ’ s subterms. For simplicity , we assume here that t has only a single subterm t 0 , like in the case of a field access ( t = t 0 . f ), in which the overall term t has multiple types depending on t 0 ’ s and f ’ s types; the rule can be easily e xtended to multiple subterms by adding a predicate per subterm. The type rule ensures the well-typedness of all possible v ariants of t on the basis of the v ariants of t ’ s subterm t 0 . Furthermore, the type rule checks whether a predicate pr e dic ate (e.g., C <: D ) holds for each variant of the subterm with its possible types E , written pr e dic ate ( t 0 : E i ) . The possible types C of the ov erall term follo w in some way from the possible types E of its subterm. Predicate validr ef is used to check whether all referenced elements and types are present in all v alid variants, including different combinations of optional features. For the general case, this can be written as follows: pr e dic ate ( t 0 : E 1 ) pr e dic ate ( t 0 : E 2 ) . . . pr e dic ate ( t 0 : E n ) t 0 : E always ( . . . ) Γ ` t : C a Φ ( T- * PL ) The different uses of pr e dic ate in the premise of an FFJ PL type rule correspond to the branches in the type deriv ation that denote alternative definitions of subterms. 30 Hence, the premise of the FFJ PL type rule is the conjunction of the dif ferent premises that cov er the different alternati ve definitions of the subterms of a term. The proof strategy is as follows. Assuming that the FFJ PL type system ensures that each slice is a valid FFJ type deriv ation (see Lemma B.1 in Appendix B.1) and that each v alid feature selection corresponds to a single slice (since alternativ e features have been removed; see Lemma B.2 in Appendix B.1), each feature-oriented program that corresponds to a valid feature selection is guaranteed to be well-typed. Note that mul- tiple valid feature selections may correspond to the same slice because of the presence of optional features. It follows that, for ev ery valid feature selection, we deriv e a well- formed FFJ program – since its type deriv ation is valid – whose ev aluation satisfies the properties of progress and preservation (see Appendix A). In Appendix B, we describe the proof of Theorem 3.1 in more detail. Completeness T H E O R E M 3 . 2 ( Completeness of F F J PL ) Gi ven an FFJ PL product line pl (including a well-typed term t , well-formed class, introduction, and refinement tables CT , IT , and R T , and a feature model FM ), and given that all valid feature selections fs yield well-typed FFJ programs, according to Theorem 3.1, pl is a well-typed product line according to the rules of FFJ PL . pl = ( t , CT , IT , R T , FM ) ∀ fs : ( fs is valid in FM ⇒ derive ( pl , fs ) is well-typed ) pl is well-typed The proof idea is to examine three basic cases and to generalize subsequently: (1) pl has only mandatory features; (2) pl has only mandatory features except a single optional feature; (3) pl has only mandatory features except two mutually exclusiv e features. All other cases can be formulated as combinations of these three basic cases. T o this end, we divide the possible relations between features into three disjoint sets: (1) a feature is reachable from another feature in all variants, (2) a feature is reachable from another feature in some , b ut not in all , v ariants, (3) tw o features are mutually exclusiv e. From these three possible relations, we can prove the three basic cases in isolation and, subsequently , construct a general case that can be phrased as a combination of the three basic cases. The description of the general case and the reduction finish the proof of Theorem 3.2. In Appendix B, we describe the proof of Theorem 3.2 in detail. 4 Implementation & Discussion W e have implemented FFJ and FFJ PL in Hask ell, including the program e valuation and type checking of product lines. The FFJ PL compiler expects a set of feature modules and a feature model both of which, together , represent the product line. A feature mod- ule is represented by a directory . The files found inside a feature module’ s directory are assigned to / belong to the enclosing feature. The FFJ PL compiler stores this informa- tion for type checking. Each file may contain multiple classes and class refinements. In Figure 21, we sho w a snapshot of our test en vironment, which is based on Eclipse 31 and a Haskell plugin 8 . W e use Eclipse to interpret or compile our FFJ and FFJ PL type systems and interpreters. Specifically , the figure sho ws the directory structure of our email system. The file EmailClient.features contains the user’ s feature selection and the feature model of the product line. Fig. 21. Snapshot of the test environment of the Hask ell implementation. The feature model of a product line is represented by a propositional formula, fol- lowing the approach of Batory [12] and Czarnecki and Pietroszek [22]. Propositional formulas are an effecti ve way of representing the relationships between features (e.g., of specifying which feature implies the presence and absence of other features and of ma- chine checking whether a feature selection is valid). For example, we have implemented predicate sometimes as follo ws: sometimes ( FM , Ω , Ψ) = satisfiable ( FM ∧ Ω 1 ∧ . . . ∧ Ω n ∧ Ψ) The feature model is an propositional formula; feature are v ariables; and satisfiable is a satisfiability solver . Like wise, we hav e implemented predicate always on the basis of logical reasoning on propositional formulas: always ( FM , Ω , Ψ) = ¬ ( satisfiable ( ¬ ( FM ⇒ ((Ω 1 ∧ . . . ∧ Ω n ) ⇒ Ψ)))) For a more detailed e xplanation of how propositional formulas relate to feature models and feature selections, we refer the interest to the work of Batory [12]. In Figure 22, we show the textual specification of the feature model of our email system, which can be passed directly to the FFJ PL compiler . 8 http://eclipsefp.sourceforge.net/haskell/ 32 1 features: 2 EmailClient IMAP POP3 MIME SSL T ext Mozilla Safari 3 4 model: 5 EmailClient implies (IMAP or POP3); 6 IMAP implies EmailClient; 7 POP3 implies EmailClient; 8 MIME implies EmailClient; 9 SSL implies EmailClient; 10 T ext implies (IMAP or POP3); 11 Mozilla implies (IMAP or POP3); 12 Safari implies (IMAP or POP3); 13 Mozilla implies ( not Safari); 14 Safari implies ( not Mozilla); Fig. 22. Feature model of an email client product line. The first section ( featur es: ) of the file representing the feature model defines an ordered set of names of the features of the product line and the second section ( model: ) defines constraints on the features’ presence in the derived programs. In our example, each email client supports either the protocols IMAP , POP3, or both. Furthermore, every feature requires the presence of the base feature E M A I L C L I E N T . Feature T E X T requires either the presence of I M A P or P O P 3 or both – the same for M O Z I L L A and S A FA R I . Finally , feature M O Z I L L A requires the absence of feature S A FA R I and vice versa. On the basis of the feature modules and the feature model, FFJ PL ’ s type system checks the entire product line and identifies valid program v ariants that still contain type errors. A SA T solver is used to check whether elements are ne ver , sometimes, or always reachable. If an error is found, the product line is rejected as ill-formed. If not, a feature-oriented program guaranteed to be well-formed can deri ved on the basis of a user’ s feature selection. This program can be e valuated using the standard ev aluation rules of FFJ, which we hav e also implemented in Haskell. In contrast to previous work on type checking feature-oriented product lines [64,23], our type system provides detailed error messages. This is possible due to the fine- grained checks at the le vel of individual term typing and well-formedness rules. For example, if a field access succeeds only in some program v ariants, this fact can be reported to the user and the error message can point to the erroneous field access. Pre- viously proposed type systems compose all code of all features of a product line and extract a single propositional formula, which is check ed for satisfiability . If the formula is not satisfiable (i.e., a type error has occurred), it is not possible to identify the location that has caused the error (at least not without further information). See Section 5, for a detailed discussion of related approaches. W e made sev eral tests and experiments with our Haskell implementation. Ho wever , real-world tests were not feasible because of two reasons. First, in pre vious work it has been already demonstrated that feature-oriented product lines require proper type sys- tems and that type checking entire real-world product lines is feasible and useful [64]. Second, like FJ, FFJ is a core language into which all Jav a programs can be compiled and which, by its relati ve simplicity , is suited for the formal definition and proof of language properties – in our case, a type system and its correctness and completeness. 33 But, a core language is ne ver suited for the dev elopment of real-w orld programs. This is why our examples and test programs are of similar size and complexity as the FJ ex- amples of Pierce [54]. T ype checking our test programs required acceptable amounts of time (in the order of magnitude of milliseconds per product line). W e do not claim to be able to handle full-sized feature-oriented product lines by hand-coding them in FFJ PL . Rather , this would require an expansion of the type system to full Jav a (including sup- port for features as provided by AHEAD [13] or FeatureHouse [9]) – an enticing goal, but one for the future (especially , as Ja va’ s informal language specification [28] has 688 pages). Our work lays a foundation for implementing type systems in that it provides evidence that core feature-oriented mechanisms are type sound and type systems of feature-oriented product lines can be implemented correctly and completely . Still, we would like to make some predictions on the scalability of our approach. The nov elty of our type system is that it incorporates alternati ve features and, consequently , alternativ e definitions of classes, fields, and methods. This leads to a type deriv ation tree with possibly multiple branches denoting alternati ve term types. Hence, performing a type deriv ation of product line with many alternativ e features may consume a significant amount of computation time and memory . It seems that this overhead is the price for allowing alternati ve implementation of program parts. Nev ertheless, our approach minimizes the overhead caused by alternative features compared to the naive approach. In the naive approach, all possible programs are de- riv ed and type checked subsequently . In our approach, we type check the entire code base of the product line and branch the type deriv ation only at terms that really have multiple, alternati ve types, and not at the le vel of entire program variants, as done in the nai ve approach. Our experience with feature-oriented product lines sho ws that, usu- ally , there are not many alternati ve features in a product line, but mostly optional fea- tures [42,3,64,37,59,11,9,5,6,57,60]. For e xample, in the Berkeley DB product line (JE edition; 80 000 lines of code) there are 99 feature modules, but only two pairs of them alternativ e [9,37]; in the Graph Product Line there are 26 feature modules, of which only three pairs are alternative [42,9]. A further observation is that most alternativ e fea- tures that we encountered do not alter types. That is, there are multiple definitions of fields and methods but with equal types. For example, GPL and Berkeley DB contain alternativ e definitions of a fe w methods but only with identical signatures. T ype check- ing these product lines with our approach, the type deriv ation would ha ve almost no branches. In the naiv e approach, still many program variants exist due to optional fea- tures. Hence, our approach is preferable. For example, in a product line with n features and c ∗ n variants (with c being a constant), in our approach, the type system would hav e to check n feature modules (with some fe w branches in the type deriv ation and solving few simple SA T problems; see belo w) and, in the naiv e approach, the type sys- tem would have to check, at least, 2 ∗ n feature modules b ut, commonly , 2 ∗ n ∗ m with m < n . For product lines with a higher de gree of variability , e.g., with n 2 or e ven 2 n variants the benefit of our approach becomes e ven more significant. W e believ e that this benefit can make a dif ference in real world product line engineering. A further point is that almost all typing and well-formedness rules contain calls to the built-in SA T solver . This results in possibly many in vocations of the SA T solver at type checking time. Determining the satisfiability of a propositional formula is in 34 general an N P -complete problem. Howe ver , it has been shown that the structures of propositional formulas occurring in software product lines are simple enough to scale satisfiability solving to thousands of features [47]. Furthermore, in our experiments, we hav e observed that many calls to the SA T solver are redundant, which is easy to see when thinking about type checking feature-oriented product lines where the presence of single types or members is checked in many type rules. W e hav e implemented a caching mechanism to decrease the number of calls to the SA T solver to a minimum. Finally , the implementation in Haskell helped us a lot with the ev aluation of the correctness of our type rules. It can serve other researchers to reproduce and e valuate our work and to e xperiment with further (feature-oriented) language mechanisms. The implementations of FFJ and FFJ PL , along with test programs, can be do wnloaded from the W eb . 9 5 Related W ork W e divide our discussions of related work into two parts: the implementation, formal models, and type systems (1) of feature-oriented programs and (2) of feature oriented product lines. 5.1 Featur e-Oriented Programs FFJ has been inspired by se veral feature-oriented languages and tools, most notably AHEAD/Jak [13], FeatureC++ [10], FeatureHouse [9], and Prehofer’ s feature-oriented Jav a extension [55]. Their ke y aim is to separate the implementation of software arti- facts, e.g., classes and methods, from the definition of features. That is, classes and re- finements are not annotated or declared to belong to a feature. There is no statement in the program text that defines explicitly a connection between code and features. Instead, the mapping of software artifacts to features is established via so-called containment hi- erarchies, which are basically directories containing software artifacts. The advantage of this approach is that a feature’ s implementation can include, beside classes in the form of Jav a files, also other supporting documents, e.g., documentation in the form of HTML files, grammar specifications in the form of Jav aCC files, or b uild scripts and deployment descriptors in the form of XML files [13]. T o this end, feature composition merges not only classes with their refinements but also other artifacts, such as HTML or XML files, with their respectiv e refinements [2,9]. Another class of programming languages that provide mechanisms for the definition and extension of classes and class hierarchies includes, e.g., ContextL [29], Scala [52], and Classbox/J [14]. The difference to feature-oriented languages is that they provide explicit language constructs for aggregating the classes that belong to a feature, e.g., family classes, classboxes, or layers. This implies that non-code software artifacts can- not be included in a feature [11]. Howe ver , FFJ still models a subset of these languages, in particular , class refinement. Similarly , related work on a formalization of the ke y concepts underlying feature- oriented programming has not disassociated the concept of a feature from the le vel of 9 http://www.fosd.de/ffj 35 code. Especially , calculi for mixins [26,16,1,34], traits [41], family polymorphism and virtual classes [33,25,30,18], path-dependent types [52,51], open classes [20], depen- dent classes [27], and nested inheritance [50] either support only the refinement of sin- gle classes or e xpect the classes that form a semantically coherent unit (i.e., that belong to a feature) to be located in a physical module that is defined in the host programming language. For example, a virtual class is by definition an inner class of the enclosing object, and a classbox is a package that aggregates a set of related classes. Thus, FFJ differs from previous approaches in that it relies on contextual information that has been collected by the compiler , e.g., the features’ composition order or the mapping of code to features. A dif ferent line of research aims at the language-independent reasoning about fea- tures [13,44,9,39]. The calculus gDeep is most closely related to FFJ since it provides a type system for feature-oriented languages that is language-independent [4]. The idea is that the recursiv e process of mer ging software artifacts, when composing hierarchically structured features, is very similar for different host languages, e.g., for Jav a, C#, and XML. The calculus describes formally how feature composition is performed and what type constraints hav e to be satisfied. In contrast, FFJ does not aspire to be language- independent, although the key concepts can certainly be used with different languages. The advantage of FFJ is that its type system can be used to check whether terms of the host language (Jav a or FJ) violate the principles of feature orientation, e.g., whether methods refer to classes that hav e been added by other features. Due to its language independence, gDeep does not hav e enough information to perform such checks. 5.2 Featur e-Oriented Product Lines Our work on type checking feature-oriented product lines was motiv ated by the work of Thaker et al. [64]. They suggested the de velopment of a type system for feature- oriented product lines that does not check all indi vidual programs b ut the individual feature implementations. They hav e implemented an (incomplete) type system and, in a number of case studies on real product lines, they found numerous hidden errors using their type rules. Nevertheless, the implementation of their type system is ad-hoc in the sense that it is described only informally , and they do not provide a correctness and completeness proof. Our type system has been inspired by their work and we were able to provide a formalization and a proof of type safety . In a parallel line of work, Delaw are et al. have dev eloped a formal model of a feature-oriented language, called Lightweight F eatur e J ava ( LFJ ), and a type system for feature-oriented product lines [23]. Their work was also influenced by the practical work of Thaker et al. So, it is not surprising that it is closest to ours. Ho wev er , there are numerous differences. First, their formal model of a feature-oriented language is based on Lightweight Java ( LJ ) [62] and not on F eatherweight J ava ( FJ ). While LJ is more expressi ve, it is also more complex. W e decided for the simpler variant FJ, omitting, e.g., constructors and mutable state. Second, Delaware et al. do not model feature- oriented mechanisms, such as class or method refinements, directly in the semantics and type rules of the language. Instead, they introduce a transformation step in which LFJ code is “compiled down” to LJ code, i.e., they flatten refinement chains to single classes. Proceeding like wise, we would hav e to generate first an FJ program from an 36 FFJ product line and type check the FJ program (that consists of some or all possible features of the product line) subsequently . W e refrained from such a transformation step in order to model the semantics of feature-oriented mechanisms directly in terms of dedicated field and method lookup mechanisms as well as special well-formed rules for method and class refinements. Lagorio et al. hav e shown that a flattening semantics and a direct semantics are equiv alent [40]. An adv antage of a “direct” semantics is that it allo ws a type checking and error reporting at a finer grain. In LFJ, all feature modules are composed and a single propositional formula is generated and tested for satisfiability; if the formula is not satisfiable, it is difficult to identify precisely the point of failure. In FFJ PL , the individual type rules consult the feature model and can point directly to the point of failure. A further advantage of our approach is that it lea ves open when feature composi- tion is performed. Currently , feature composition is modeled in FFJ/FFJ PL as a static process done before compilation but, with our approach, it becomes possible to model dynamic feature composition at run time [58,53] by making the class and feature tables and the feature model dynamic, i.e., allowing them to change during a computation. W ith LFJ this is not possible. Hutchins has shown that feature composition can be per - formed by an interpreter and partial ev aluation can be used to pre-ev aluate the parts of a composition that are static [31]. Howe ver , Delaware et al. ha ve de veloped a machine- checked model of their type system formalized with the theorem prover Coq [15]. Our proof is hand-written, b ut we hav e a Haskell implementation of the FFJ and FFJ PL calculi that we hav e tested thoroughly . Even previously to the work of Thaker et al., Czarnecki et al. presented an automatic verification procedure for ensuring that no ill-structured UML model template instances will be generated from a valid feature selection [22]. That is, they type check product lines that consist not of Jav a programs but of UML models. They use OCL (object constraint language) constraints to express and implement a type system for model composition. In this sense, their aim is v ery similar to that of FFJ PL , b ut limited to model artif acts – although they ha ve proposed to generalize their work to programming languages. Kästner et al. hav e implemented a tool, called CIDE, that allows a developer to decompose a software system into features via annotations [38]. In contrast to other feature-oriented languages and tools, the link between code and features is established via annotations. If a user selects a set of features, all code that is annotated with features (using background colors) that are not present in the selection is removed. Kästner et al. ha ve developed a formal calculus and a set of type rules that ensure that only well- typed programs can be generated from a valid feature selection [36]. For example, if a method declaration is removed, the remaining code must not contain calls to this method. CIDE’ s type rules are related to the type rules of FFJ PL but, so far , mutually exclusi ve features are not supported in CIDE. In some sense, FFJ PL and CIDE represent two sides of the same coin: the former aims at the composition of feature modules, the latter at the annotation of feature-related code. 37 6 Conclusion A feature-oriented product line imposes sev ere challenges on type checking. The naive approach of checking all individual programs of a product line is not feasible because of the combinatorial explosion of program variants. Hence, the only practical option is to check the entire code base of a product line, including all features, and, based on the information of which feature combinations are valid, to ensure that it is not possible to deriv e a valid program v ariant that contains type errors. W e hav e developed such a type system based on a formal model of a feature- oriented Jav a-like language, called Feature Featherweight Java (FFJ). A distinguishing property of our work is that we have modeled the semantics and type rules for core feature-oriented mechanisms directly , without compiling feature-oriented code down to a lo wer-le vel representation such as object-oriented Ja va code. The direct semantics al- lows us to reason about core feature-oriented mechanisms in terms of themselves and not of generated lower -lev el code. A further advantage is the fine-grained error report- ing and that the time of feature composition may vary between compile time and run time. W e hav e demonstrated and prov ed that, based on a valid feature selection, our type system ensures that ev ery program of a feature-oriented product line is well-formed and that our type system is complete. Our implementation of FFJ, including the type system for product lines, indicates the feasibility of our approach and can serve as a testbed for experimenting with further feature-oriented mechanisms. Acknowledgment This work is being funded in part by the German Research Foundation (DFG), project number AP 206/2-1. References 1. D. Ancona, G. Lagorio, and E. Zucca. Jam—Designing a Ja va Extension with Mixins. A CM T ransactions on Pr ogramming Languag es and Systems (TOPLAS) , 25(5):641–712, 2003. 2. F . Anfurrutia, O. Díaz, and S. T rujillo. On Refining XML Artifacts. In Proceedings of the International Conference on W eb Engineering (ICWE) , volume 4607 of LNCS , pages 473– 478. Springer-V erlag, 2007. 3. S. Apel and K. Böhm. T ow ards the Dev elopment of Ubiquitous Middleware Product Lines. In Softwar e Engineering and Middlewar e , volume 3437 of LNCS , pages 137–153. Springer- V erlag, 2004. 4. S. Apel and D. Hutchins. An Ov erview of the gDeep Calculus. T echnical Report MIP-0712, Department of Informatics and Mathematics, Univ ersity of Passau, 2007. 5. S. Apel, F . Janda, S. Trujillo, and C. Kästner . Model Superimposition in Software Product Lines. In Proceedings of the International Confer ence on Model T ransformation (ICMT) , volume 5563 of LNCS , pages 4–19. Springer -V erlag, 2009. 6. S. Apel, C. Kästner , A. Größlinger , and C. Leng auer . Feature (De)composition in Functional Programming. In Proceedings of the International Confer ence on Software Composition (SC) , volume 5634 of LNCS , pages 9–26. Springer -V erlag, 2009. 38 7. S. Apel, C. Kästner , and C. Lengauer . An Overview of Feature Featherweight Java. T echnical Report MIP-0802, Department of Informatics and Mathematics, Uni versity of P assau, 2008. 8. S. Apel, C. Kästner, and C. Lengauer . Feature Featherweight Jav a: A Calculus for Feature- Oriented Programming and Stepwise Refinement. In Proceedings of the International Con- fer ence on Generative Pr ogramming and Component Engineering (GPCE) , pages 101–112. A CM Press, 2008. 9. S. Apel, C. Kästner, and C. Lengauer . FeatureHouse: Language-Independent, Automated Software Composition. In Pr oceedings of the International Confer ence on Software Engi- neering (ICSE) , pages 221–231. IEEE CS Press, 2009. 10. S. Apel, T . Leich, M. Rosenmüller, and G. Saake. FeatureC++: On the Symbiosis of Feature- Oriented and Aspect-Oriented Programming. In Pr oceedings of the International Confer ence on Generative Pr ogramming and Component Engineering (GPCE) , volume 3676 of LNCS , pages 125–140. Springer-V erlag, 2005. 11. S. Apel, T . Leich, and G. Saak e. Aspectual Feature Modules. IEEE T ransactions on Softwar e Engineering (TSE) , 34(2):162–180, 2008. 12. D. Batory . Feature Models, Grammars, and Propositional F ormulas. In Proceedings of the International Software Pr oduct Line Conference (SPLC) , volume 3714 of LNCS , pages 7–20. Springer-V erlag, 2005. 13. D. Batory , J. Sarvela, and A. Rauschmayer . Scaling Step-W ise Refinement. IEEE T ransac- tions on Softwar e Engineering (TSE) , 30(6):355–371, 2004. 14. A. Bergel, S. Ducasse, and O. Nierstrasz. Classbox/J: Controlling the Scope of Change in Jav a. In Proceedings of the International Confer ence on Object-Oriented Pr ogr amming, Systems, Languages, and Applications (OOPSLA) , pages 177–189. A CM Press, 2005. 15. Y . Bertot and P . Casteran. Interactive Theor em Pr oving and Pr ogram Development – Coq’Art: The Calculus of Inductive Constructions . T exts in Theoretical Computer Science. An EA TCS Series. Springer-V erlag, 2004. 16. V . Bono, A. P atel, and V . Shmatikov . A Core Calculus of Classes and Mixins. In Pr oceedings of the European Confer ence on Object-Oriented Pro gramming (ECOOP) , volume 1628 of LNCS , pages 43–66. Springer-V erlag, 1999. 17. G. Bracha and W . Cook. Mixin-Based Inheritance. In Pr oceedings of the Eur opean Confer- ence on Object-Oriented Pro gramming (ECOOP) and International Conference on Object- Oriented Pro gramming Systems, Languag es, and Applications (OOPSLA) , pages 303–311. A CM Press, 1990. 18. D. Clarke, S. Drossopoulou, J. Noble, and T . Wrigstad. T ribe: A Simple V irtual Class Cal- culus. In Proceedings of the International Confer ence on Aspect-Oriented Software Devel- opment (A OSD) , pages 121–134. A CM Press, 2007. 19. P . Clements and L. Northrop. Software Pr oduct Lines: Practices and P atterns . Addison- W esley , 2002. 20. C. Clifton, T . Millstein, G. Leavens, and C. Chambers. MultiJav a: Design Rationale, Com- piler Implementation, and Applications. A CM T ransactions on Pr ogramming Languages and Systems (TOPLAS) , 28(3):517–575, 2006. 21. K. Czarnecki and U. Eisenecker . Generative Pro gramming: Methods, T ools, and Applica- tions . Addison-W esley , 2000. 22. K. Czarnecki and K. Pietroszek. V erifying Feature-Based Model T emplates Against W ell- Formedness OCL Constraints. In Pr oceedings of the International Conference on Generative Pr ogramming and Component Engineering (GPCE) , pages 211–220. A CM Press, 2006. 23. B. Delaware, W . Cook, and D. Batory . A Machine-Checked Model of Safe Composition. In Pr oceedings of the International W orkshop on F oundations of Aspect-Oriented Languages (FO AL) , pages 31–35. ACM Press, 2009. 39 24. S. Ducasse, O. Nierstrasz, N. Schärli, R. W uyts, and A. Black. T raits: A Mechanism for Fine- Grained Reuse. ACM T ransactions on Pr ogramming Languages and Systems (TOPLAS) , 28(2):331–388, 2006. 25. E. Ernst, K. Ostermann, and W . Cook. A V irtual Class Calculus. In Pr oceedings of the International Symposium on Principles of Pr ogramming Languages (POPL) , pages 270– 282. A CM Press, 2006. 26. M. Flatt, S. Krishnamurthi, and M. Felleisen. Classes and Mixins. In Proceedings of the International Symposium on Principles of Pr ogramming Languages (POPL) , pages 171– 183. A CM Press, 1998. 27. V . Gasiunas, M. Mezini, and K. Ostermann. Dependent Classes. In Pr oceedings of the International Confer ence on Object-Oriented Pr ogramming , Systems, Languages, and Ap- plications (OOPSLA) , pages 133–152. A CM Press, 2007. 28. J. Gosling, B. Joy , G. Steele, and G. Bracha. The J ava Language Specification . The Java Series. Addison-W esley , 3rd edition, 2005. 29. R. Hirschfeld, P . Costanza, and O. Nierstrasz. Context-Oriented Programming. Journal of Object T echnology (JO T) , 7(3):125–151, 2008. 30. D. Hutchins. Eliminating Distinctions of Class: Using Prototypes to Model V irtual Classes. In Pr oceedings of the International Confer ence on Object-Oriented Progr amming, Systems, Languages, and Applications (OOPSLA) , pages 1–19. A CM Press, 2006. 31. D. Hutchins. Pur e Subtype Systems: A T ype Theory F or Extensible Softwar e . PhD thesis, School of Informatics, Univ ersity of Edinbur gh, 2008. 32. A. Ig arashi, B. Pierce, and P . W adler . Featherweight Java: A Minimal Core Calculus for Ja va and GJ. ACM T ransactions on Pr ogramming Languages and Systems (TOPLAS) , 23(3):396– 450, 2001. 33. A. Igarashi, C. Saito, and M. V iroli. Lightweight Family Polymorphism. In Pr oceedings of the Asian Symposium on Pro gramming Languag es and Systems (APLAS) , v olume 3780 of LNCS , pages 161–177. Springer-V erlag, 2005. 34. T . Kamina and T . T amai. McJava – A Design and Implementation of Jav a with Mixin-T ypes. In Pr oceedings of the Asian Symposium on Pr ogramming Languag es and Systems (APLAS) , volume 3302 of LNCS , pages 398–414. Springer -V erlag, 2004. 35. K. Kang, S. Cohen, J. Hess, W . No vak, and A. Peterson. Feature-Oriented Domain Analysis (FOD A) Feasibility Study. T echnical Report CMU/SEI-90-TR-21, Software Engineering Institute, Carnegie Mellon Uni versity , 1990. 36. C. Kästner and S. Apel. T ype-Checking Softw are Product Lines – A Formal Approach. In Proceedings of the International Confer ence on Automated Software Engineering (ASE) , pages 258–267. IEEE CS Press, 2008. 37. C. Kästner , S. Apel, and D. Batory . A Case Study Implementing Features using AspectJ. In Pr oceedings of the International Softwar e Pr oduct Line Conference (SPLC) , pages 222–232. IEEE CS Press, 2007. 38. C. Kästner , S. Apel, and M. K uhlemann. Granularity in Software Product Lines. In Pr oceed- ings of the International Confer ence on Software Engineering (ICSE) , pages 311–320. A CM Press, 2008. 39. C. Kästner , S. Apel, S. T rujillo, M. K uhlemann, and D. Batory . Guaranteeing Syntactic Cor - rectness for all Product Line V ariants: A Language-Independent Approach. In Proceedings of the International Confer ence on Objects, Models, Components, P atterns (T OOLS EUROPE) , volume 33 of LNBI , pages 174–194. Springer -V erlag, 2009. 40. G. Lagorio, M. Servetto, and E. Zucca. Featherweight Jigsaw – A Minimal Core Calculus for Modular Composition of Classes. In Pr oceedings of the Eur opean Conference on Object- Oriented Pr ogramming (ECOOP) , LNCS. Springer -V erlag, 2009. 41. L. Liquori and A. Spiwack. FeatherT rait: A Modest Extension of Featherweight Jav a. ACM T ransactions on Pr ogramming Languag es and Systems (TOPLAS) , 30(2):1–32, 2008. 40 42. R. Lopez-Herrejon and D. Batory . A Standard Problem for Evaluating Product-Line Method- ologies. In Proceedings of the International Confer ence on Generative and Component- Based Softwar e Engineering (GCSE) , v olume 2186 of LNCS , pages 10–24. Springer-V erlag, 2001. 43. R. Lopez-Herrejon, D. Batory , and W . Cook. Evaluating Support for Features in Ad- vanced Modularization T echnologies. In Proceedings of the Eur opean Confer ence on Object- Oriented Pr ogramming (ECOOP) , volume 3586 of LNCS , pages 169–194. Springer-V erlag, 2005. 44. R. Lopez-Herrejon, D. Batory , and C. Lengauer . A Disciplined Approach to Aspect Com- position. In Pr oceedings of the International Symposium P artial Evaluation and Semantics- Based Pr ogram Manipulation (PEPM) , pages 68–77. A CM Press, 2006. 45. O. Madsen and B. Moller-Pedersen. V irtual Classes: A Po werful Mechanism in Object- Oriented Programming. In Proceedings of the International Confer ence on Object-Oriented Pr ogramming , Systems, Languages, and Applications (OOPSLA) , pages 397–406. ACM Press, 1989. 46. H. Masuhara and G. Kiczales. Modeling Crosscutting in Aspect-Oriented Mechanisms. In Pr oceedings of the European Confer ence on Object-Oriented Pro gramming (ECOOP) , vol- ume 2743 of LNCS , pages 2–28. Springer-V erlag, 2003. 47. M. Mendonca, A. W asowski, and K. Czarnecki. SA T -based Analysis of Feature Models is Easy. In Proceedings of the International Softwar e Product Line Confer ence (SPLC) . Software Engineering Institute, Carnegie Mellon Uni versity , 2009. 48. M. Mezini and K. Ostermann. V ariability Management with Feature-Oriented Programming and Aspects. In Pr oceedings of the International Symposium on F oundations of Softwar e Engineering (FSE) , pages 127–136. A CM Press, 2004. 49. G. Murphy , A. Lai, R. W alker , and M. Robillard. Separating Features in Source Code: An Exploratory Study. In Pr oceedings of the International Confer ence on Software Engineering (ICSE) , pages 275–284. IEEE CS Press, 2001. 50. N. Nystrom, S. Chong, and A. Myers. Scalable Extensibility via Nested Inheritance. In Pr o- ceedings of the International Conference on Object-Oriented Pro gramming, Systems, Lan- guages, and Applications (OOPSLA) , pages 99–115. A CM Press, 2004. 51. M. Odersky , V . Cremet, C. Röckl, and M. Zenger . A Nominal Theory of Objects with Depen- dent T ypes. In Pr oceedings of the Eur opean Conference on Object-Oriented Pr ogramming (ECOOP) , volume 2743 of LNCS , pages 201–224. Springer -V erlag, 2003. 52. M. Odersky and M. Zenger . Scalable Component Abstractions. In Pr oceedings of the Inter- national Confer ence on Object-Oriented Pro gramming, Systems, Languages, and Applica- tions (OOPSLA) , pages 41–57. A CM Press, 2005. 53. K. Ostermann. Dynamically Composable Collaborations with Delegation Layers. In Pr o- ceedings of the Eur opean Confer ence on Object-Oriented Pro gramming (ECOOP) , volume 2374 of LNCS , pages 89–110. Springer-V erlag, 2002. 54. B. Pierce. T ypes and Pr ogramming Languages . MIT Press, 2002. 55. C. Prehofer. Feature-Oriented Programming: A Fresh Look at Objects. In Pr oceedings of the Eur opean Conference on Object-Oriented Pr ogramming (ECOOP) , volume 1241 of LNCS , pages 419–443. Springer-V erlag, 1997. 56. T . Reenskaug, E. Andersen, A. Berre, A. Hurlen, A. Landmark, O. Lehne, E. Nordhagen, E. Ness-Ulseth, G. Oftedal, A. Skaar , and P . Stenslet. OORASS: Seamless Support for the Creation and Maintenance of Object-Oriented Systems. Journal of Object-Oriented Pr o- gramming (JOOP) , 5(6):27–41, 1992. 57. M. Rosenmüller , C. Kästner , N. Siegmund, S. Sunkle, S. Apel, T . Leich, and G. Saake. SQL á la Carte – T o ward T ailor-made Data Management. In Datenbanksysteme in Business, T ech- nologie und W eb – F achtagung des GI-F achber eichs Datenbanken und Informationssysteme , volume P-144 of GI-Edition – LNI , pages 117–136. Gesellschaft für Informatik, 2009. 41 58. M. Rosenmüller, N. Siegmund, G. Saake, and S. Apel. Code Generation to Support Static and Dynamic Composition of Software Product Lines. In Pr oceedings of the International Confer ence on Gener ative Pr ogramming and Component Engineering (GPCE) , pages 3–12. A CM Press, 2008. 59. M. Rosenmüller , N. Siegmund, H. Schirmeier , J. Sincero, S. Apel, T . Leich, O. Spinczyk, and G. Saake. F AME-DBMS: T alor-made Data Management Solutions for Embedded Sys- tems. In Pr oceedings of the EDBT W orkshop on Softwar e Engineering for T ailor-made Data Management (SETMDM) , pages 1–6. A CM Press, 2008. 60. N. Siegmund, C. Kästner , M. Rosenmüller, F . Heidenreich, S. Apel, and G. Saake. Bridging the Gap between V ariability in Client Application and Database Schema. In Datenbanksys- teme in Business, T echnologie und W eb – F achtagung des GI-F achber eichs Datenbanken und Informationssysteme , volume P-144 of GI-Edition – LNI , pages 297–306. Gesellschaft für Informatik, 2009. 61. Y . Smaragdakis and D. Batory . Mixin Layers: An Object-Oriented Implementation T ech- nique for Refinements and Collaboration-Based Designs. ACM T ransactions on Software Engineering and Methodology (T OSEM) , 11(2):215–255, 2002. 62. R. Strniša, P . Sewell, and M. Parkinson. The Jav a Module System: Core Design and Se- mantic Definition. In Pr oceedings of the International Conference on Object-Oriented Pro- gramming, Systems, Languages, and Applications (OOPSLA) , pages 499–514. A CM Press, 2007. 63. P . T arr , H. Ossher, W . Harrison, and S. Sutton, Jr . N Degrees of Separation: Multi- Dimensional Separation of Concerns. In Proceedings of the International Confer ence on Softwar e Engineering (ICSE) , pages 107–119. IEEE CS Press, 1999. 64. S. Thaker , D. Batory , D. Kitchin, and W . Cook. Safe Composition of Product Lines. In Pr oceedings of the International Conference on Generative Pr ogramming and Component Engineering (GPCE) , pages 95–104. A CM Press, 2007. 65. M. V anHilst and D. Notkin. Using Role Components in Implement Collaboration-based Designs. In Proceedings of the International Confer ence on Object-Oriented Pr ogramming, Systems, Languages, and Applications (OOPSLA) , pages 359–369. A CM Press, 1996. 66. A. K. Wright and M. Felleisen. A Syntactic Approach to T ype Soundness. Information and Computation , 115(1):38–94, 1994. A T ype Soundness Proof of FFJ Before giving the main proof, we state and proof some required lemmas. L E M M A A . 1 If mtype ( m , last ( D )) = C → C 0 , then mtype ( m , last ( C )) = C → C 0 for all C <: D . Pr oof Straightforward induction on the deriv ation of C <: D . There are two cases: First, if method m is not defined in the declaration or in any refinement of class C , then mtype ( m , last ( C )) should be the same as mtype ( m , last ( E )) where CT (Φ .C ) = class C e xtends E { . . . } for some Φ . This follows from the definition of mtyp e that searches E ’ s refinement chain from right to left if m is not declared in C ’ s refinement chain. Second, if m is defined in the declaration or in any refinement of class C , then mtype ( m , last ( C )) should also be the same as mtype ( m , last ( E )) with CT (Φ .C ) = class C e xtends E { . . . } for some Φ . This case is co vered by the well-formedness rules for methods that use the predicate override to ensure that m is properly overrid- den, i.e., the signatures of the ov erridden and the o verriding declaration of m are equal, and that m is not introduced twice, i.e., overloading is not allo wed in FFJ. u t 42 L E M M A A . 2 ( T erm substitution pr eserves typing ) If Γ , x : B ` t : D and Γ , s : A , where A <: B , then Γ ` [ x 7→ s ] t : C for some C <: D . Pr oof By induction on the derivation of Γ , x : B ` t : D . C A S E ( T - V A R ) t = x x : D ∈ Γ If x 6∈ x , then the result is trivial since [ x 7→ s ] x = x . 10 On the other hand, if x = x i and D = B i , then, since [ x 7→ s ] x = s i , letting C = A i finishes the case. C A S E ( T - F I E L D ) t = t 0 .f i Γ , x : B ` t 0 : D 0 fields ( last ( D 0 )) = C f D = C i By the induction hypothesis, there is some C 0 such that Γ ` [ x 7→ s ] t 0 : C 0 and C 0 <: D 0 . It is easy to check that fields ( last ( C 0 )) = ( fields ( last ( D 0 )) , D g ) for some D g . Therefore, by T - F I E L D , Γ ` ([ x 7→ s ] t 0 ) .f i : C i . The fact that the refinements of a class may add new fields does not cause problems. D g contains all fields that C 0 , including all of its refinements, add to D 0 . C A S E ( T - I N V K ) t = t 0 .m(t) Γ , x : B ` t 0 : D 0 mtype ( m , last ( D 0 )) = E → D Γ , x : B ` t : D D <: E By the induction hypothesis, there are some C 0 and C such that: Γ ` [ x 7→ s ] t 0 : C 0 C 0 <: D 0 Γ ` [ x 7→ s ] t : C C <: D . By Lemma A.1, we have mtype ( m , last ( C 0 )) = E → D . Moreover , C <: E by the transitivity of <: . Therefore, by T - I N V K , Γ ` [ x 7→ s ] t 0 .m( [ x 7→ s ] t) : D . The key is that subclasses and refinements may o verride methods but the well-formedness rules of methods ensure that the method’ s type is not altered, i.e., there is no overloading in FFJ. C A S E ( T - N E W ) t = ne w D(t) fields ( last ( D )) = D f Γ , x : B ` t : C C <: D By the induction hypothesis, Γ ` [ x 7→ s ] t : E for some E with E <: C . W e have E <: D by the transiti vity of <: . Therefore, by rule T - N E W , Γ ` new D( [ x 7→ s ] t) : D . Al- though refinements of class D may add new fields, rule T - N E W ensures that the argu- ments of the object creation match the o verall fields of D , including all refinements, in number and types. That is, the number of arguments ( t ) equals the number of fields ( f ) which function fields returns. C A S E ( T - U C A S T ) t = (D)t 0 Γ , x : B ` t 0 : C C <: D By the induction hypothesis, there is some E such that Γ ` [ x 7→ s ] t 0 : E and E <: C . W e hav e E <: D by the transitivity of <: , which yields Γ ` (D)( [ x 7→ s ] t 0 ) : D by T - U C A S T . C A S E ( T - D C A S T ) t = (D)t 0 Γ , x : B ` t 0 : C D <: C D 6 = C 10 Note that [ x 7→ s ] x is an abbreviation for [ x 1 7→ s 1 , . . . , x n 7→ s n ] x . It means that all occur- rences of the variables x 1 , . . . , x n in the term x are substituted with the corresponsing terms s 1 , . . . , s n . 43 By the induction hypothesis, there is some E such that Γ ` [ x 7→ s ] t 0 : E and E <: C . If E <: D or D <: E , then Γ ` (D)( [ x 7→ s ] t 0 ) : D by T- U C A S T or T - D C A S T , respec- tiv ely . If both D 6 <: E and E 6 <: D , then Γ ` (D)( [ x 7→ s ] t 0 ) : D (with a stupid warning ) by T- S C A S T . C A S E ( T - S C A S T ) t = (D)t 0 Γ , x : B ` t 0 : C D 6 <: C C 6 <: D By the induction hypothesis, there is some E such that Γ ` [ x 7→ s ] t 0 : E and E <: C . This means that E 6 <: D because, in FFJ, each class has just one superclass and, if both E <: C and E <: D , then either C <: D or D <: C , which contradicts the induction hy- pothesis. So Γ ` (D)( [ x 7→ s ] t 0 ) : D (with a stupid warning ), by T- S C A S T . u t L E M M A A . 3 ( W eakening ) If Γ ` t : C , then Γ , x : D ` t : C Pr oof Straightforward induction. The proof for FFJ is similar to the proof for FJ. u t L E M M A A . 4 If mtype ( m , last ( C 0 )) = D → D , and mbody ( m , last ( C 0 )) = ( x , t ) , then for some D 0 and some C <: D we hav e C 0 <: D 0 and x : D , this : D 0 ` t : C . Pr oof By induction on the deriv ation of mbody ( m , last ( C 0 )) . The base case (in which m is defined in the most specific refinement of C 0 ) is easy since m is defined in CT ( last ( C 0 )) and the well-formedness of the class table implies that we must have deriv ed x : D , this : C 0 ` t : C by the well-formedness rules of methods. The induction step is also straightforward: if m is not defined in CT ( last ( C 0 )) , then mb o dy searches the refinement chain from right to left; if m has not been found, the superclass’ refine- ment chain is searched. There are two subcases: first, m is defined in the declaration or in any refinement of C 0 ; this case is similar to the base case. Second, m is defined in a superclass D 0 of C 0 or in one of D 0 ’ s refinements; in this case, the well-formedness of the class table implies that we must have deri ved x : D , this : D 0 ` t : C by the well- formedness rules of methods, which finishes the case. u t Note that this lemma holds because method refinements do not change the types of the arguments and the result of a method, ov erloading is not allowed, and this points always to the class that is introduced or refined. T H E O R E M A . 1 ( Preservation ) If Γ ` t : C and t − → t 0 , then Γ ` t 0 : C 0 for some C 0 <: C . Pr oof By induction on a derivation of t − → t 0 , with a case analysis on the final rule. C A S E ( E - P R O J N E W ) t = new C 0 (v).f i t 0 = v i fields ( last ( C 0 )) = D f From the shape of t , we see that the final rule in the deriv ation of Γ ` t : C must be T - F I E L D , with premise Γ ` new C 0 (v) : D 0 , for some D 0 , and that C = D i . Similarly , the last rule in the deriv ation of Γ ` new C 0 (v) : D 0 must be T- N E W , with premises Γ ` v : C and C <: D , and with D 0 = C 0 . In particular , Γ ` v i : C i , which finishes the case, since C i <: D i . C A S E ( E - I N V K N E W ) t = (new C 0 (v)).m(u) t 0 = [ x 7→ u , this 7→ new C 0 (v) ] t 0 mbody ( m , last ( C 0 )) = ( x , t 0 ) 44 The final rules in the deriv ation of Γ ` t : C must be T - I N V K and T - N E W , with premises Γ ` new C 0 (v) : C 0 , Γ ` u : C , C <: D , and mtype ( m , last ( C 0 )) = D → C . By Lemma A.4, we hav e x : D , this : D 0 ` t : B for some D 0 and B , with C 0 <: D 0 and B <: C . By Lemma A.3, Γ , x : D , this : D 0 ` t 0 : B . Then, by Lemma A.2, we hav e Γ [ x 7→ u , this 7→ new C 0 (v) ] t 0 : E for some E <: B . By the transitivity of <: , we ob- tain E <: C . Letting C 0 = E completes the case. C A S E ( E - C A S T N E W ) t = (D)(new C 0 (v)) C 0 <: D t 0 = new C 0 (v) The proof of Γ ` (D)(new C 0 (v)) : C must end with T - U C A S T since ending with T- S C A S T or T - D C A S T would contradict the assumption of C 0 <: D . The premises of T - U C A S T gi ve us Γ ` new C 0 (v) : C 0 and D = C , finishing the case. The cases for the congruence rules are easy . W e sho w just the case E - C A S T . C A S E ( E - C A S T ) t = (D)t 0 t 0 = (D)t 0 0 t 0 − → t 0 0 There are three subcases according to the last typing rule used. S U B C A S E ( T- U C A S T ) Γ ` t 0 : C 0 C 0 <: D D = C By the induction hypothesis, Γ ` t 0 0 : C 0 0 for some C 0 0 <: C 0 . By transitivity of <: , C 0 0 <: C . Therefore, by T - U C A S T , Γ ` (C)t 0 0 : C (with no additional stupid warning ). S U B C A S E ( T- D C A S T ) Γ ` t 0 : C 0 D <: C 0 D = C By the induction hypothesis, Γ ` t 0 0 : C 0 0 for some C 0 0 <: C 0 . If C 0 0 <: C or C <: C 0 0 , then Γ ` (C)t 0 0 : C by T - U C A S T or T - D C A S T (without any additional stupid warning ). On the other hand, if both C 0 0 6 <: C or C 6 <: C 0 0 , then Γ ` (C)t 0 0 : C with a stupid warning by T- S C A S T . S U B C A S E ( T- S C A S T ) Γ ` t 0 : C 0 D 6 <: C 0 C 0 6 <: D D = C By the induction hypothesis, Γ ` t 0 0 : C 0 0 for some C 0 0 <: C 0 . Then, also C 0 0 6 <: C and C 6 <: C 0 0 . Therefore Γ ` (C)t 0 0 : C with a stupid warning . If C 0 0 6 <: C , then C 6 <: C 0 0 since C 6 <: C 0 and, therefore, Γ ` (C)t 0 0 : C with stupid war ning . If C 0 0 <: C , then Γ ` (C)t 0 0 : C by T - U C A S T (with no additional stupid warning ). This subcase is analogous to the case T- S C A S T of the proof of Lemma A.2. u t T H E O R E M A . 2 ( Pro gr ess ) Suppose t is a well-typed term. 1. If t includes new C 0 (t).f i as a subterm, then fields ( last ( C 0 )) = C f for some C and f . 2. If t includes new C 0 (t).m(u) as a subterm, then mbody ( m , last ( C 0 )) = ( x , t 0 ) and | x | = | u | for some x and t 0 . Pr oof If t has new C 0 (t).f i as a subterm, then, by well-typedness of the subterm, it is easy to check that fields ( last ( C 0 )) is well-defined and f i appears in it. The fact that refinements may add fields (that ha ve not been defined already) does not inv alidate this conclusion. Note that for every field of a class, including its superclasses and all its refinements, there must be a proper argument. Similarly , if t has ne w C 0 (t).m(u) as a subterm, then it is also easy to sho w that mbody ( m , last ( C 0 )) = ( x , t 0 ) and | x | = | u | from the fact that mtype ( m , last ( C 0 )) = C → D where | x | = | C | . This conclusion holds for FFJ since a method refinement must hav e the same signature than the method refined and ov erloading is not allowed. u t 45 T H E O R E M A . 3 ( T ype soundness of F F J ) If ∅ ` t : C and t − → ∗ t 0 with t 0 a normal form, then t 0 is either a value v with ∅ ` v : D and D <: C , or a term containing (D)(new C(t)) in which C <: D . Pr oof Immediate from Theorem A.1 and A.2. Nothing changes in the proof of Theo- rem A.3 for FFJ compared to FJ. u t B T ype Soundness Proof of FFJ PL In this section, we provide proof sketches of the theorems Corr ectness of FFJ PL and Completeness of FFJ PL . A further formalization would be desirable, but we have stopped at this point. As is often the case with formal systems, there is a trade-off between for- mal precision and legibility . W e decided that a semi-formal dev elopment of the proof strategies are the best fit for our purposes. B.1 Correctness T H E O R E M B . 1 ( Correctness of F F J PL ) Gi ven a well-typed FFJ PL product line pl (in- cluding with a well-typed term t , well-formed class, introduction, and refinement tables CT , IT , and R T , and a feature model FM ), e very program that can be deri ved with a valid feature selection fs is a well-typed FFJ program (cf. Figure 10). pl = ( t , CT , IT , R T , FM ) pl is well-typed fs is valid in FM derive ( pl , fs ) is well-typed The proof strategy is as follows: assuming that the FFJ PL type system ensures that each slice is a valid FFJ type deriv ation (Lemma B.1) and that each valid feature se- lection corresponds to a single slice (Lemma B.1), it follows that the corresponding feature-oriented program is well-formed. Before we prov e Theorem B.1 we de velop two required lemmas that cov er the two assumptions of our proof strategy . L E M M A B . 1 Giv en a well-formed FFJ PL product line, ev ery slice of the product line’ s type deriv ation corresponds to a (set of) valid type deri vation(s) in FFJ. Pr oof (Proof sketch) Giv en a well-formed FFJ PL product line, the corresponding type deriv ation consists of possibly multiple slices. The basic case is easy: there is only a simple deri vation without branches due to mutually exclusi ve features (optional features may be present). In this case, each term has only a single type, which is the one that would also be determined by FFJ. Fur- thermore, FFJ PL guarantees that referenced types, methods, and fields are present in all valid v ariants, using the predicate validr ef . Let us illustrate this with the rule T- F I E L D PL ; the other rules are analogous: ∀ E ∈ E : validr ef (Φ , E.f ) Γ ` t 0 : E a Φ fields (Φ , last ( E )) = F , C f , G Γ ` t 0 .f : C 11 , . . . , C n 1 , . . . , C 1 m , . . . , C nm a Φ ( T- F I E L D PL ) 46 In the basic case there are no branches in the type deriv ation and thus the term t 0 has only a single type E 1 . F or the same reason, fields returns only a simple list of fields that contains the declaration of field f . Finally , T - F I E L D PL checks whether the declaration of f is present in all valid v ariants (using validr ef ). Hence, in the basic case, an FFJ PL deriv ation that ends at the rule T - F I E L D PL is equiv alent to a set of corresponding FFJ deri vations, which do not contain alternati ve and optional features and thus t 0 has a single type, fields returns a simple list of fields that contains the declaration of f , and the declaration of f is present. The reason that an FFJ PL deriv ation without mutually exclusi ve features (i.e., a single slice) corresponds to multiple FFJ deriv ations is that the FFJ PL deriv ation may contain optional features whose different combinations correspond to the different FFJ deriv ations. Using predicate validr ef , all type rules of FFJ PL ensure that all possible combinations of optional features are well- typed. In the case that there are multiple slices in the FFJ PL deriv ation, a term t 0 may ha ve multiple types E . The type rules of FFJ PL make sure that every possible shape of a giv en term is well-typed. Each possible type of the term leads to a branch in the deriv ation tree. The premise of T - F I E L D PL checks whether all possible shapes of a gi ven term are well-typed by taking the conjunction of all branches of the deriv ation. Hence, if T - F I E L D PL is successful, each individual branch holds, i.e., each slice corresponds to a well-typed FFJ program. Ensuring that, in the presence of optional features, all rele vant subterms are well-typed (i.e., all referenced elements are present in all v alid v ariants), a well-typed slice covers a set of well-typed FFJ deriv ations that correspond to different combinations of optional features, like in the basic case. For e xample, in a field projection t 0 .f , the subterm t 0 has multiple types E . For all these types, fields yields all possible combinations of fields declared by the variants of the types. It is checked whether , for each type of the subterm t 0 , each combination of fields contains a proper declaration of field f . The different types of f become the possible types of the overall field projection term. Like in the basic case, it is checked whether e very possible type of t 0 is present in all valid v ariants (using validr ef ), so that each slice corresponds a valid FFJ deri vation, i.e., a whole set of deriv ations covering different combinations of optional features. u t L E M M A B . 2 Giv en a well-formed FFJ PL product line, each v alid feature selection cor - responds to a single slice in the corresponding type deriv ation. Pr oof (Proof sketch) By definition, a valid feature selection does not contain mutually exclusi ve features. Considering only a single valid feature selection, each term has only a single type. But the type deriv ation of the overall product line contains branches corre- sponding to alternati ve types of the terms. A successiv e removal of mutually exclusiv e features removes these branches until only a single branch remains. Consequently , a valid feature selection corresponds to a single slice. u t Pr oof (Proof sketch of Theor em B.1 (Corr ectness of FFJ PL )) The fact that the FFJ PL type system ensures that each slice is a v alid FFJ type deri vation (Lemma B.1) and that each valid feature selection corresponds to a single slice (Lemma B.2), implies that each feature-oriented program that corresponds to a valid feature selection is well-formed. u t 47 B.2 Completeness T H E O R E M B . 2 ( Completeness of F F J PL ) Giv en an FFJ PL product line pl (including a well-typed term t , well-formed class, introduction, and refinement tables CT , IT , and R T , and a feature model FM ), and given that all valid feature selections fs yield well-typed FFJ programs, according to Theorem B.1, pl is a well-typed product line according to the rules of FFJ PL . pl = ( t , CT , IT , R T , FM ) ∀ fs : ( fs is valid in FM ⇒ derive ( pl , fs ) is well-typed ) pl is well-typed Pr oof (Proof sketch of Theor em B.2 (Completeness of FFJ PL )) There are three basic cases: (1) pl has only mandatory features; (2) pl has only mandatory features except a single optional feature; (3) pl has only mandatory features except two mutually exclu- siv e features. Proving Theorem B.2 for the first basic case is trivial. Since only manda- tory features exist, only a single FFJ program can be deriv ed from the product line. If the FFJ program is well-typed, the product line is well-typed, too, because all elements are always reachable and each term has only a single type. In fact, the type rules of FFJ PL and FFJ become equiv alent in this case. In the second basic case, two FFJ programs can be deri ved from the product line, one including and one excluding the optional feature. The difference between the two programs is the content of the optional feature. The feature can add ne w classes, refine existing classes by new methods and fields, and refine existing methods by overriding. If the two programs are well-typed, then the overall product line is well-typed as well since the reachability checks succeed in e very type rule of FFJ PL . Otherwise, at least one of the two programs would not be well-typed since, in this case, the reachability checks are the only dif ference between FFJ PL ’ s and FFJ’ s type rules (as in the first case, each term has only a single type since there are no mutually exclusi ve features). The fact that the two FJ programs are well-typed implies that all elements are reachable in the type deriv ations of two FFJ programs. Thus, the reachability checks of the FFJ PL deriv ation succeed in every case, i.e., the product line in question is well-typed. In the third basic case, two FFJ programs can be deri ved from the product line, one including the first alternati ve and the other including the second alternative of the feature in question. The difference between the two programs is, on the one hand, the program elements one feature introduces that are not present in the other and, on the other hand, the alternati ve definitions of similar elements, like two alternative defini- tions of a single class. The first kind of dif ference is already cov ered by the second basic case. Alternati ve definitions of a program element (second kind of dif ference) that are well-typed in the context of their enclosing FFJ programs, are well-typed in FFJ PL because they lead to two new branches in the deri v ation tree which are handled separately and the conjunction of their premises must hold. Since the corresponding FFJ type rule for the element succeeds in both FFJ programs, their conjunction in the FFJ PL type rule always holds, i.e., the product line in question is well-typed. Finally , we it remains to show that all other cases, i.e., all other combinations of mandatory , optional, and alternati ve features, can be reduced to combinations of the three basic cases, which prov es Theorem B.2. T o this end, we divide the possible rela- tions between features into three disjoint sets: (1) a feature is reachable from another 48 feature in all variants, (2) a feature is reachable from another feature in some , but not in all , variants, (3) two features are mutually exclusiv e. From these three possible rela- tions we construct a general case that can be reduced to a combination of the three basic cases. Assume a feature Φ that is mandatory with respect to a set of features Π , that is optional with respect to a set of features Ω , and that is alternati ve to a set ∆ of features. W e use arrows to illustrate to which of the three basic cases a pairwise relation between Φ and each element of a list is reduced: Φ 2 / / 1 3 A A A A A A A A Ω Π ∆ Such an arro w diagram can be created for e very feature of a product line. The reason is that the three kinds of relations are orthogonal and there are no further relations relev ant for type checking. Hence, the general case covers all possible relations between features and combinations of features. The description of the general case and the reduction finish the proof of Theorem B.2, i.e., FFJ PL ’ s type system is complete. u t 49
Original Paper
Loading high-quality paper...
Comments & Academic Discussion
Loading comments...
Leave a Comment