1 / 59

Type Inference with Run-time Logs

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

manasa
Télécharger la présentation

Type Inference with Run-time Logs

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Type Inferencewith Run-time Logs Ravi Chugh, Ranjit Jhala, Sorin Lerner University of California, San Diego

  2. 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 …

  3. ... 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

  4. The Challenge: Inference Goal: practical type system polymorphism def id(x) {return x}; 1 + id(2); “hello” ^ id(“world”); id : ∀X. X → X polymorphism

  5. 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}

  6. 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

  7. 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

  8. The Challenge: Inference Goal: practical type system bounded quantification subtyping polymorphism other features... dynamic

  9. The Challenge: Inference Goal: practical type system bounded quantification subtyping polymorphism other features... dynamic System ML ✓

  10. The Challenge: Inference Goal: practical type system bounded quantification subtyping polymorphism other features... dynamic System F ✗

  11. 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

  12. Route: Inference w/ Run-time Logs omit higher-order functions System E≤ System E bounded quantification subtyping System E− + polymorphism

  13. First Stop: System E− omit higher-order functions System E≤ System E bounded quantification subtyping System E− + polymorphism

  14. 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

  15. 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...

  16. Int → Int ∀B,C. B*C → B*C ∀A. A → A ... but ∀A. A → A is the principal type

  17. 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

  18. ∀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

  19. ∀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

  20. 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

  21. 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

  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 } 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

  23. 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

  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 24

  25. 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

  26. 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

  27. 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

  28. 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

  29. Next Stop: System E System E≤ System E System E bounded quantification subtyping System E− + polymorphism

  30. 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

  31. ∀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:

  32. “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

  33. “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

  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 } ✗* 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

  35. 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

  36. 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

  37. Last Stop: System E≤ System E≤ System E≤ System E bounded quantification subtyping System E− + polymorphism

  38. Bounded Quantification def f[ X1<:τ1, ... , Xn<:τn ] (x:τ) { e }

  39. Bounded Quantification def f[ X1<:τ1 , ... , Xn<:τn ] (x:τ) { e } Type parameters now have bounds 39

  40. ∀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

  41. {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 }

  42. ∀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

  43. 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

  44. 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

  45. ✗* 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

  46. ✓ 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

  47. ✓ 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

  48. 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

  49. 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

  50. Summary System E≤ System E bounded quantification subtyping System E− + polymorphism

More Related