Type Soundness for DOT ( D ependent O bject T ypes) Tiark Rompf Nada Amin OOPSLA November 3, 2016 1
DOT Syntax S , T , U ::= types : x ::= variables : ⊤ top y concrete var. ⊥ bot. z abstract var. T ∧ T inter. t ::= terms : T ∨ T union x variable L : S .. U type mem. { z ⇒ d } new object m ( x : S ) : U meth. mem. t . m ( t ) meth. app. x . L sel. d ::= init. : { z ⇒ T } rec. self L = T type mem. Γ ::= contexts : m ( x : T ) = t meth. mem. ∅ | Γ , x : T var. bind. v ::= values : ρ ::= stores : y store loc. ∅ | ρ, y : d val. bind. 2
DOT Types S , T , U ::= subtyping lattice : ⊤ top ⊥ bottom T ∧ T intersection T ∨ T union structural member types : L : S .. U type member m ( x : S ) : U ( U may depend on x ) method member path-dependent types : x . L type selection recursive type : { z ⇒ T } ( T may depend on z ) recursive self type 3
Semantic Intuition for Path-Dependent Types Γ ⊢ x : ( L : S .. U ) (Sel) Γ ⊢ S < : x . L < : U 4
Type Members, Path-Dependent Types trait Keys { type Key def key(data: String): Key } object hashKeys extends Keys { type Key = Int def key(s: String) = s.hashCode } def mapKeys(k: Keys, ss: List[String]): List[k.Key] = ss.map(k.key) 5
Translucency Γ ⊢ x : ( L : S .. U ) (Sel) Γ ⊢ S < : x . L < : U val abstracted: Keys = hashKeys val transparent: Keys { type Key = Int } = haskKeys val upperBounded: Keys { type Key <: Int } = hashKeys val lowerBounded: Keys { type Key >: Int } = hashKeys (1: lowerBounded.Key) (upperBounded.key("a"): Int) 6
DOT as a type-theoretic foundation ◮ few yet powerful concepts, with uniform means of abstraction and combination e.g. quantification only over term, yet supports polymorphism ◮ “user-extensible” subtyping ◮ mixture of nominal and structural ◮ nominality is “scoped” e.g. no global class table ◮ no imposed notion of code sharing such as prototype vs class inheritance, mixins, ... 7
Impact on Practice, particularly Scala/Dotty ◮ suggesting simplifications, e.g. a core type system based on DOT ◮ lifting ad-hoc restrictions, e.g. recursive structural types are more powerful in DOT than in Scala ◮ characterizing soundness issues, e.g. type selection on Null or ⊥ paths 8
Impact on Practice, particularly Scala/Dotty ◮ suggesting simplifications, e.g. a core type system based on DOT ◮ lifting ad-hoc restrictions, e.g. recursive structural types are more powerful in DOT than in Scala ◮ characterizing soundness issues, e.g. type selection on Null or ⊥ paths Java and Scala’s Type Systems are Unsound: The Existential Crisis of Null Pointers N. Amin & R. Tate, OOPSLA’16 See Ross’s talk tomorrow at 11:45 in Matterhorn 1. :) 9
The Quest for Soundness 10
Review of Negative Results (Amin et al. OOPSLA ’14) ◮ (invertible) transitivity Γ ⊢ S < : T , T < : U ( < : -trans) Γ ⊢ S < : U ◮ narrowing Γ a , ( x : U ) , Γ b ⊢ T < : T ′ Γ a ⊢ S < : U ( < : -narrow) Γ a , ( x : S ) , Γ b ⊢ T < : T ′ ◮ lattice collapse through bad bounds Γ ⊢ x : ( L : ⊤ .. ⊥ ) (Sel) Γ ⊢ ⊤ < : x . L < : ⊥ 11
Main Breakthrough distinction between concrete and abstract variables (proof device) 12
Recursive Subtyping Γ , z : T 1 ⊢ T 1 < : T 2 (BindX) Γ ⊢ { z ⇒ T 1 } < : { z ⇒ T 2 } Γ , z : T 1 ⊢ T 1 < : T 2 z / ∈ fv ( T 2 ) (Bind1) Γ ⊢ { z ⇒ T 1 } < : T 2 enables comparisons like scalalib.List & { type Elem = Apple } <: { z => type Elem <: Fruit; def head: z.Elem } 13
Mutual Dependencies in Lemmas! ◮ z 1 : T 1 , z 2 : T 2 ⊢ { z 3 ⇒ T 3 } < : { z 3 ⇒ T ′ 3 } . . . z 1 : T 1 , z 2 : T 2 , z 3 : T 3 ⊢ z 2 . L < : U ◮ No one breakthrough or principle. ◮ Hard work refactoring the bottom-up proof to break cycles. ◮ Inversion lemmas, substitution lemmas, equivalence lemmas all got entangled. ◮ Unpacking self types as part of subtyping. ◮ Growing context leads to more proof obligations that may need to unpack, and grow context again. 14
Contractiveness Restrictions in Model in typing for subtyping (now : ! instead of : ) ◮ no self packing ◮ context “popping” (left-to-right behavior) Γ [ x ] ( z 1 : T 1 , z 2 : T 2 , z 3 : T 3 ) [ z 2 ] ≡ ( z 1 : T 1 , z 2 : T 2 ) ◮ does not seem to matter much for most purposes, maybe affects semantics of strange loops, e.g. { z ⇒ z . L ∧ ( L : S .. U ) } ◮ seems similar to restrictions on recursive structural types for decidability (as opposed to soundness) 15
Formal Semantics 16
DOT Subtyping Γ ⊢ S < : U Lattice structure Γ ⊢ ⊥ < : T Γ ⊢ T < : ⊤ (Bot) (Top) Γ ⊢ T 1 < : T Γ ⊢ T < : T 1 (And11) (Or21) Γ ⊢ T 1 ∧ T 2 < : T Γ ⊢ T < : T 1 ∨ T 2 Γ ⊢ T 2 < : T Γ ⊢ T < : T 2 (And12) (Or22) Γ ⊢ T 1 ∧ T 2 < : T Γ ⊢ T < : T 1 ∨ T 2 Γ ⊢ T < : T 1 , T < : T 2 Γ ⊢ T 1 < : T , T 2 < : T (And2) (Or1) Γ ⊢ T < : T 1 ∧ T 2 Γ ⊢ T 1 ∨ T 2 < : T Properties Γ ⊢ T 1 < : T 2 , T 2 < : T 3 Γ ⊢ T < : T (Refl) (Trans) Γ ⊢ T 1 < : T 3 17
DOT Subtyping Γ ⊢ S < : U Method and type members Γ ⊢ S 2 < : S 1 Γ , x : S 2 ⊢ U 1 < : U 2 (Fun) Γ ⊢ m ( x : S 1 ) : U 1 < : m ( x : S 2 ) : U 2 Γ ⊢ S 2 < : S 1 , U 1 < : U 2 (Typ) Γ ⊢ L : S 1 .. U 1 < : L : S 2 .. U 2 18
DOT Subtyping Γ ⊢ S < : U Type selections Γ [ x ] ⊢ x : ! ( L : T .. ⊤ ) Γ [ x ] ⊢ x : ! ( L : ⊥ .. T ) (Sel2) (Sel1) Γ ⊢ T < : x . L Γ ⊢ x . L < : T Recursive self types Γ , z : T 1 ⊢ T 1 < : T 2 (BindX) Γ ⊢ { z ⇒ T 1 } < : { z ⇒ T 2 } Γ , z : T 1 ⊢ T 1 < : T 2 z / ∈ fv ( T 2 ) (Bind1) Γ ⊢ { z ⇒ T 1 } < : T 2 19
DOT Typing Γ ⊢ t : (!) T Γ ⊢ t : (!) T 1 , T 1 < : T 2 Γ( x ) = T (Var) (Sub) Γ ⊢ x : (!) T Γ ⊢ t : (!) T 2 Γ ⊢ x : T Γ ⊢ x : (!) { z ⇒ T } (VarPack) (VarUnpack) Γ ⊢ x : { z ⇒ [ x �→ z ] T } Γ ⊢ x : (!) [ z �→ x ] T 20
DOT Typing Γ ⊢ t : T Γ ⊢ t : ( m ( x : T 1 ) : T 2 ) , t 2 : T 1 x / ∈ fv ( T 2 ) (TApp) Γ ⊢ t . m ( t 2 ) : T 2 Γ ⊢ t : ( m ( x : T 1 ) : T 2 ) , y : T 1 (TAppVar) Γ ⊢ t . m ( y ) : [ x �→ y ] T 2 (labels disjoint) Γ , x : T 1 ∧ . . . ∧ T n ⊢ d i : T i ∀ i , 1 ≤ i ≤ n (TNew) Γ ⊢ { x ⇒ d 1 . . . d n } : { x ⇒ T 1 ∧ . . . ∧ T n } 21
DOT Member Initialization Γ ⊢ d : T Γ ⊢ T < : T (DTyp) Γ ⊢ ( L = T ) : ( L : T .. T ) Γ , x : T 1 ⊢ t : T 2 (DFun) Γ ⊢ ( m ( x ) = t ) : ( m ( x : T 1 ) : T 2 ) 22
→ t ′ ρ ′ DOT Small-Step Operational Semantics ρ t − { z ⇒ d } − → ρ, ( y : [ z �→ y ] d ) ρ y with y fresh − → [ x �→ v 2 ] t if ρ ( y 1 ) ∋ m ( x ) = t ρ y 1 . m ( v 2 ) ρ → t ′ ρ ′ ρ e [ t ] − → e [ t ′ ] ρ ′ if ρ t − where e ::= [ ] | [ ] . m ( t ) | v . m ([ ]) 23
DOT Store Typing ρ Γ ⊢ S < : U Subtyping... ρ ( y ) ∋ ( L = U ) ρ ∅ ⊢ T < : U (SSel2) ρ Γ ⊢ T < : y . L ρ ( y ) ∋ ( L = U ) ρ ∅ ⊢ U < : T (SSel1) ρ Γ ⊢ y . L < : T Typing... ρ Γ ⊢ t : (!) T ( y , d ) ∈ ρ ∀ i , 1 ≤ i ≤ n (labels disjoint) ρ ∅ , ( x : T 1 ∧ . . . ∧ T n ) ⊢ [ y �→ x ] d i : T i (TLoc) ρ Γ ⊢ y : [ x �→ y ]( T 1 ∧ . . . ∧ T n ) 24
Some Unsound Variations on Typing Objects 1. Add subsumption to member initialization. Γ ⊢ d : T Γ ⊢ T < : U (DSub) Γ ⊢ d : U { x ⇒ L = ⊤} : { x ⇒ L : ⊤ .. ⊥} 2. Change type member initialization from { L = T } to { L : S .. U } . Γ ⊢ S < : U (DTyp) { L : S .. U } : { L : S .. U } { x ⇒ L : ⊤ .. ⊥} : { x ⇒ L : ⊤ .. ⊥} 25
Unsound Variation 1 Adding subsumption to definition type assignment... Γ ⊢ d : T Γ ⊢ T < : U ( Def-Sub ) Γ ⊢ d : U ... would fail to flag this bad typing: { x ⇒ X = ⊤} : { x ⇒ X : ⊤ .. ⊥} ... because the following typing derivation tree would be accepted: x : ( X : ⊤ .. ⊥ ) ⊢ ⊤ < : x . X < : ⊥ (Trans) x : ( X : ⊤ .. ⊥ ) ⊢ ⊤ < : ⊥ (DTyp) (Typ) x : ( X : ⊤ .. ⊥ ) ⊢ ( X = ⊤ ) : ( X : ⊤ .. ⊤ ) x : ( X : ⊤ .. ⊥ ) ⊢ ( X : ⊤ .. ⊤ ) < : ( X : ⊤ .. ⊥ ) (Def-Sub) x : ( X : ⊤ .. ⊥ ) ⊢ ( X = ⊤ ) : ( X : ⊤ .. ⊥ ) (TObj) ∅ ⊢ { x ⇒ X = ⊤} : { x ⇒ X : ⊤ .. ⊥} 26
Unsound Variation 2 Changing type definition from { L = T } to { L : S .. U } ... Γ ⊢ S < : U (DTyp’) Γ ⊢ { L : S .. U } : { L : S .. U } ... would fail to flag this bad typing: { x ⇒ X : ⊤ .. ⊥} : { x ⇒ X : ⊤ .. ⊥} ... because the following typing derivation tree would be accepted: x : ( X : ⊤ .. ⊥ ) ⊢ ⊤ < : x . X < : ⊥ (Trans) x : ( X : ⊤ .. ⊥ ) ⊢ ⊤ < : ⊥ (DTyp’) x : ( X : ⊤ .. ⊥ ) ⊢ ( X : ⊤ .. ⊥ ) : ( X : ⊤ .. ⊥ ) (TObj) ∅ ⊢ { x ⇒ X : ⊤ .. ⊥} : { x ⇒ X : ⊤ .. ⊥} 27
Recommend
More recommend