590 likes | 697 Views
Type Inference with Run-time Logs. Ravi Chugh , Ranjit Jhala, Sorin Lerner University of California, San Diego. Motivation. Dynamically-typed languages Rapid prototyping Facilitate inter-language development Statically-typed languages Prevent certain run-time errors
E N D
Type Inferencewith Run-time Logs Ravi Chugh, Ranjit Jhala, Sorin Lerner University of California, San Diego
Motivation • Dynamically-typed languages • Rapid prototyping • Facilitate inter-language development • Statically-typed languages • Prevent certain run-time errors • Enable optimized execution • Provide checked documentation • Gradual type systems offer a spectrum • Provides hope for evolving existing programs • But …
... We Need Inference! • Goal: migrate programs from dynamic langs • Programmer annotation burden is currently 0 • A migration strategy must require ~0 work • Even modifying 1-10% LOC in a large codebasecan be prohibitive
The Challenge: Inference Goal: practical type system polymorphism def id(x) {return x}; 1 + id(2); “hello” ^ id(“world”); id : ∀X. X → X polymorphism
The Challenge: Inference Goal: practical type system subtyping def succA(x) { return (1 + x.a)}; succA({a=1}); succA({a=1;b=“hi”}); subtyping polymorphism {a:Int;b:Str}<:{a:Int}
The Challenge: Inference Goal: practical type system bounded quantification def incA(x) { x.a := 1 + x.a; return x}; incA({a=1}).a; incA({a=1;b=“hi”}).b; bounded quantification subtyping polymorphism incA:∀X<:{a:Int}.X→X
The Challenge: Inference Goal: practical type system dynamic n := if b then 0 else “bad”; m := if b then n + 1 else 0; n : dynamic bounded quantification subtyping polymorphism dynamic
The Challenge: Inference Goal: practical type system bounded quantification subtyping polymorphism other features... dynamic
The Challenge: Inference Goal: practical type system bounded quantification subtyping polymorphism other features... dynamic System ML ✓
The Challenge: Inference Goal: practical type system bounded quantification subtyping polymorphism other features... dynamic System F ✗
The Idea • Use run-time executions to help inference • Program may have many valid types • But particular execution might rule out some • Dynamic language programmers test a lot • Use existing test suites to help migration
Route: Inference w/ Run-time Logs omit higher-order functions System E≤ System E bounded quantification subtyping System E− + polymorphism
First Stop: System E− omit higher-order functions System E≤ System E bounded quantification subtyping System E− + polymorphism
E− Type System Expression and function types τ ::= | Int | Bool | ... | {fi:τi} | X σ ::= ∀Xi. τ1→ τ2 Typing rules prevent field-not-found and primitive operation errors
def id (a) { a } def id[A] (a:A) { a } : ∀A. A → A def id[] (a:Int) { a } : Int → Int def id[B,C] (x:B*C) { a } : ∀B,C. B*C → B*C Infinitely many valid types for id...
Int → Int ∀B,C. B*C → B*C ∀A. A → A ... but ∀A. A → A is the principal type
Int → Int ∀B,C. B*C → B*C ∀A. A → A ∀B. B*B → B*B Int*Bool → Int*Bool Int*Int → Int*Int ... but ∀A. A → A is the principal type Allows id to be used in most different ways
∀F. {f:F} → F {f:Int} → Int ∀F,G. {f:F;g:G} → F ∀G. {f:Int;g:G} → Int def readF (o) { o.f } def readF[F] (o:{f:F}) { o.f } : ∀F. {f:F} → F This is the best type
∀F.{f:F}→{f:F} ∀F,G.{f:F;g:G}→{f:F;g:G} allows hasF({f=1}) allows hasF({f=1;g=2}).g def hasF (x) { let _ = x.f in x } Two valid types: Neither is better than the other
E− Static Type Inference • E− lacks principal types • Cannot assign type just from definition • Need to consider calling contexts • Our approach is iterative • Impose minimal constraints on argument • If a calling context requires an additional field to be tracked, backtrack and redo the function
Iterative Inference – Example 1 def hasF(x) { let _ = x.f in x } def main1() { hasF({f=1}) } ✓ Constraints on x Solution X = {f:F}x X <: {f:F} “this record type came from the program variable x” hasF:∀F.{f:F} → {f:F}x 21
Iterative Inference – Example 2 ✗* def hasF(x) { let _ = x.f in x } def main2() { let z = {f=1;g=2} in hasF(z).g } hasF(z):{f:Int}x so can’t project on g, unless... Constraints on x Solution X = {f:F}x X <: {f:F} X <: {g:G} hasF:∀F.{f:F} → {f:F}x 22
Iterative Inference – Example 2 def hasF(x) { let _ = x.f in x } def main2() { let z = {f=1;g=2} in hasF(z).g } ✓ Constraints on x Solution X={f:F;g:G}x X <: {f:F} X <: {g:G} hasF:∀F,G.{f:F;g:G} → {f:F;g:G}x 23
Iterative Inference – Example 3 def hasF(x) { let _ = x.f in x } def main3() { let z = {f=1;g=2} in hasF(z).g; hasF({f=1}) } Constraints on x Solution X={f:F;g:G}x X <: {f:F} X <: {g:G} hasF:∀F,G.{f:F;g:G} → {f:F;g:G}x 24
Iterative Inference – Example 3 def hasF(x) { let _ = x.f in x } def main3() { let z = {f=1;g=2} in hasF(z).g; hasF({f=1}) } ✓ ✗ Constraints on x Solution X={f:F;g:G}x X <: {f:F} X <: {g:G} hasF:∀F,G.{f:F;g:G} → {f:F;g:G}x 25
E− Type Inference with Run-time Logs • Evaluation can log caller-induced constraints • Wrap all values with sets of type variables • When a value passed to arg x, add Xx tag • When a value with tag Xx projected on field f, record Xx <: {f : Xx.f} • Modified inference finds all caller-induced constraints in run-time log 26
def hasF(x) { let _ = x.f in x } def main2() { let z = {f=1;g=2} in hasF(z).g } Evaluation main2() ⇒ let z = {f=1;g=2} in hasF(z).g ⇒ let z = [{f=1;g=2},{}] in hasF(z).g ⇒ hasF([{f=1;g=2},{Xx}]).g ⇒ (let _ = [{f=1;g=2},{Xx}].f in [{f=1;g=2},{Xx}]).g ⇒ (let _ = [1,{Xx.f}] in [{f=1;g=2},{Xx}]).g ⇒ [{f=1;g=2},{Xx}].g ⇒ [2,{Xx.g}] Run-time log Xx <: {f : Xx.f} Caller-inducedconstraint Xx <: {g : Xx.g} 27
System E− Summary • Fully static inference needs to iterate, but can optimize with run-time information • If all expressions executed, then • log contains all caller-induced constraints • no need for iteration
Next Stop: System E System E≤ System E System E bounded quantification subtyping System E− + polymorphism
if b then 1 else 2 : Int if b then 1 else true : Top if b then {f=1; g=“”} else {f=2; h=true} : {f:Int} if b then {f=“”} else {f=true} : {f:Top} If-expressions Type is a supertype of branch types
∀A.{n:Int}*Top→Top ∀A.{n:Int}*{n:Top}→{n:Top} {n:Int}*{n:Int}→{n:Int} ∀B.{n:Int;b:B}*{n:Int;b:B}→{n:Int;b:B} ∀B.{n:Int;b:B}*{b:B}→{b:B} def choose (y,z) { if y.n > 0 then y else z } Several incomparable types:
“this type came from theif-expression 1” Iterative Inference – Example def choose(y,z) { if1 y.n > 0 then y else z } def main4() { let t = choose({n=1},{n=2}) in succ t.n } Constraints Y <: {n:N} N = Int choose: {n:Int}*Top → Top1 Iteration 0 32 32
“this value came from the n field of the record returned by if-expression 1” Iterative Inference – Example def choose(y,z) { if1 y.n > 0 then y else z } def main4() { let t = choose({n=1},{n=2}) in succ t.n } ✗* t:Top1 Constraints Y <: {n:N} N = Int Z <: {n:M} choose: {n:Int}*Top → Top1 Iteration 0 choose: {n:Int}*{n:Top} → {n:Top1.n}1 Iteration 1 33 33
Iterative Inference – Example def choose(y,z) { if1 y.n > 0 then y else z } def main4() { let t = choose({n=1},{n=2}) in succ t.n } ✗* t.n:Top1.n Constraints Y <: {n:N} N = Int Z <: {n:M} N = M choose: {n:Int}*Top → Top1 Iteration 0 choose: {n:Int}*{n:Top} → {n:Top1.n}1 Iteration 1 choose: {n:Int}*{n:Int} → {n:Int}1 Iteration 2 34 34
Iterative Inference – Example def choose(y,z) { if1 y.n > 0 then y else z } def main4() { let t = choose({n=1},{n=2}) in succ t.n } ✓ Constraints Y <: {n:N} N = Int Z <: {n:M} N = M choose: {n:Int}*Top → Top1 Iteration 0 choose: {n:Int}*{n:Top} → {n:Top1.n}1 Iteration 1 choose: {n:Int}*{n:Int} → {n:Int}1 Iteration 2 35 35
System E Summary • Lacks principal types • Has iterative inference • Subscripts for types returned by if-exps • Subscripts for Top in case it reaches a primop • Can remove iteration with run-time info
Last Stop: System E≤ System E≤ System E≤ System E bounded quantification subtyping System E− + polymorphism
Bounded Quantification def f[ X1<:τ1, ... , Xn<:τn ] (x:τ) { e }
Bounded Quantification def f[ X1<:τ1 , ... , Xn<:τn ] (x:τ) { e } Type parameters now have bounds 39
∀F, X<:{f:F}.X→X ∀F.{f:F}→{f:F} ∀F,G.{f:F;g:G}→{f:F;g:G} def hasF (x) { let _ = x.f in x } Now there is a best type for hasF Each call site can instantiate X differently 40
{n:Int}*Top→Top {n:Int}*{n:Top}→{n:Top} {n:Int}*{n:Int}→{n:Int} ∀B.{n:Int;b:B}*{n:Int;b:B}→{n:Int;b:B} ∀B.{n:Int;b:B}*{b:B}→{b:B} ∀Y<:{n:Int}. Y*Y→Y def choose (y,z) { if1 y.n > 0 then y else z }
∀Y<:{n:Int}. Y*Y→Y {n:Int}*Top→Top {n:Int}*{n:Top}→{n:Top} • These four types are incomparable ∀B.{n:Int;b:B}*{b:B}→{b:B} def choose (y,z) { if1 y.n > 0 then y else z } 42
choose: ∀Y<:{n:Int}. Y * Y → Y 0 def choose(y,z) { if1 y.n > 0 then y else z } def main5() { let _ = choose({n=1;b=tru},{n=2;b=fls}) in let w = choose({n=1;b=tru},{b=fls}) in not w.b } Constraints Y<:{n:N} N=Int Y=Z 43 43
def choose(y,z) { if1 y.n > 0 then y else z } def main5() { let _ = choose({n=1;b=tru},{n=2;b=fls}) in let w = choose({n=1;b=tru},{b=fls}) in not w.b } ✓ ✗* Constraints Y<:{n:N} N=Int Y=Z choose: ∀Y<:{n:Int}. Y * Y → Y 0 44 44
✗* choose: {n:Int}*Top → Top1 1 def choose(y,z) { if1 y.n > 0 then y else z } def main5() { let _ = choose({n=1;b=tru},{n=2;b=fls}) in let w = choose({n=1;b=tru},{b=fls}) in not w.b } Constraints Y<:{n:N} N=Int Y≠Z choose: ∀Y<:{n:Int}. Y * Y → Y 0 45 45
✓ ✓ choose: {n:Int;b:Top}*{b:Top} → {b:Top1.b}1 2 def choose(y,z) { if1 y.n > 0 then y else z } def main5() { let _ = choose({n=1;b=tru},{n=2;b=fls}) in let w = choose({n=1;b=tru},{b=fls}) in not w.b } ✗* w:Top1 Constraints Y<:{n:N} N=Int Y≠Z Y<:{b:B} Z<:{b:C} choose: ∀Y<:{n:Int}. Y * Y → Y 0 choose: {n:Int}*Top → Top1 1 46 46
✓ ✓ choose: ∀B.{n:Int;b:B}*{b:B}→{b:B} 3 def choose(y,z) { if1 y.n > 0 then y else z } def main5() { let _ = choose({n=1;b=tru},{n=2;b=fls}) in let w = choose({n=1;b=tru},{b=fls}) in not w.b } ✗* w.b:Top1.b Constraints Y<:{n:N} N=Int Y≠Z Y<:{b:B} Z<:{b:C} B=C choose: ∀Y<:{n:Int}. Y * Y → Y 0 choose: {n:Int}*Top → Top1 1 choose: {n:Int;b:Top}*{b:Top} → {b:Top1.b}1 2 47 47
def choose(y,z) { if1 y.n > 0 then y else z } def main5() { let _ = choose({n=1;b=tru},{n=2;b=fls}) in let w = choose({n=1;b=tru},{b=fls}) in not w.b } ✓ Constraints Y<:{n:N} N=Int Y≠Z Y<:{b:B} Z<:{b:C} B=C choose: ∀Y<:{n:Int}. Y * Y → Y 0 choose: {n:Int}*Top → Top1 1 choose: {n:Int;b:Top}*{b:Top} → {b:Top1.b}1 2 choose: ∀B.{n:Int;b:B}*{b:B}→{b:B} 3 48 48
System E≤ Summary • Lacks principal types • When to share variables: Y=Z or Y≠Z? • New source of iteration for static inference • Can partially eliminate with run-time info • When Y≠Z, same as System E
Summary System E≤ System E bounded quantification subtyping System E− + polymorphism