Nested Refinements: A Logic for Duck Typing Ravi Chugh, Pat Rondon, Ranjit Jhala (UCSD) ::
-
date post
19-Dec-2015 -
Category
Documents
-
view
214 -
download
1
Transcript of Nested Refinements: A Logic for Duck Typing Ravi Chugh, Pat Rondon, Ranjit Jhala (UCSD) ::
Nested Refinements:A Logic for Duck
Typing
Ravi Chugh, Pat Rondon, Ranjit Jhala (UCSD)
::
2
“dictionary” = finite map from “keys” to values
“object” = mutable dictionary
JavaScript has “prototype” inheritance
record
Terminology
fields
properties
delegation
imperative
3
Types for JavaScript: Approach
System !D
System D
lambdas
dictionaries
tag tests
mutation
prototypes
Guha et al. (ECOOP 2010)
Dependent JavaScript (DJS)
(POPL 2012)
(In Submission)
Dependent JavaScript (DJS)
4
System D
lambdas
dictionaries
tag tests (POPL 2012)
{|tag()=“Int”tag()=“Bool”}x ::
{|tag()=“Dict” tag(sel(,“n”))=“Int”tag(sel(,m))=“Int”}
d ::
Functions inside dictionariesChallenge:All types as quantifier-free formulasApproach:
5
Key Idea: Nested Refinements
{|tag()=“Dict”
sel(,f) ::
}
d ::
{|tag()=“Int” }
{|tag()=“Int” }
uninterpreted predicate“x :: U” says“x has-type U”
uninterpreted constantin the logic…
… but syntactic arrowin the type system
1 + d[f](0)
6
• All values described by refinement formulasT ::= {|p}
• “Has-type” predicate for arrows in formulasp ::= … | x :: y:T1T2
• Can express several scripting idioms
• Automatic type checking– Decidable refinement logic
– Subtyping = SMT Validity + Syntactic Subtyping
Key Idea: Nested Refinements
7
function negate (x) { if (typeof x == “number”) { return 0 – x; } else { return !x; }}
y:Top{| = tag(y)}typeof ::
{| :: }typeof :: y: {|true } {| = tag(y) }
y:{|true}{| = tag(y)}typeof ::
x:{|tag() = “number” tag() = “boolean”}
{|tag() = tag(x)}
x:IntOrBool{|tag() = tag(x)}
8
function negate (x) { if (typeof x == “number”) { return 0 – x; } else { return !x; }}
{|Int()}x ::
{|Int()}0 - x ::
✓x:IntOrBool{|tag() = tag(x)}
9
function negate (x) { if (typeof x == “number”) { return 0 – x; } else { return !x; }}
{|Bool()}x ::
{|Bool()}!x ::
✓x:IntOrBool{|tag() = tag(x)}
10
function getCount (x,c) { if (c in x) { return toInt(x[c]); } else { return 0; }}
(x:Dict,c:Str)Int
{| = true has(x,c)}
safe dictionary key lookup
11
function getCount (x,c) { if (c in x) { return toInt(x[c]); } else { return 0; }}
function incCount (x,c) { var i = getCount(x,c); return {x with c = i + 1};}
(x:Dict,c:Str)Int
(x:Dict,c:Str){|EqMod(,x,c) Int(d[c])}
tag(sel(d,c)) = “Int”
12
T ::= {|p}
p ::= … | x :: U
U ::= y:T1T2 | A | List T | Null
Adding Type Constructors
13
function apply (f,x) { return f(x); }
∀A,B. ({|:: {|:: A }{|:: B } }, {|:: A })
{|:: B }
∀A,B. ((AB), A) B
14
function dispatch (d,f) { return d[f](d); }
∀A,B. (d:{| :: A }, {|d[] :: AB }) {| :: B }
d :: A but additional constraints on A
≈ ∀A <: {f: A B}. d :: A
a form of“bounded quantification”
15
function map (f,xs) { if (xs == null) { return null; } else { return f(xs[“hd”]) :: map (f,xs[“tl”]); }}
∀A,B.
({|:: AB }, {|:: List[A] }) {|:: List[B] }
encode recursive data as dictionaries
∀A,B. ((AB), List[A]) List[B]
16
function filter (f,xs) { if (xs == null) { return null; } else if (f(xs[“hd”])) { return xs[“hd”] :: filter(f,xs[“tl”]); } else { return filter(f,xs[“tl”]); }}
∀A,B. ((x:A{|n = true x:: B }), List[A])List[B]
usual definition,but an interesting type*
*Tobin Hochstadt and Felleisen (POPL 2008)
17
System !D
System D
lambdas
dictionaries
tag tests
mutation
prototypes
Object.beget = function(o) { var F = function(o) { return this; }; F.prototype = o; return new F();}
Crockford (JavaScript: The Good Parts)
18
var x = {};
x.f = 1;
var xf = x.f;
Strong Update à la Alias Types• Types are flow-insensitive (as usual)• But heap types are flow-sensitive
– Mappings from locations to types can change
18
{|:: Ref L }x ::
L :: d:{|=empty }
safe given the updated heap type
L :: d’: {|=upd(d,“f”,1)}
19
Prototype Chain Unrolling
L1 :: dChild:
L2 :: dParent:
L3 :: dGrandpa:
{|:: Ref L3 }grandpa ::
{|:: Ref L2 }parent ::
{|:: Ref L1 }child ::
H = Rest of Heap
var granpda = …;
var parent = beget(grandpa);
var child = beget(parent);
T3
T1
T2
(k in child)
has(dChild,k)
has(dParent,k)
has(dGrandpa,k)
HeapHas(H,L4,k)
Recap / Future Work
20
!D
D
lambdas
dictionaries
tag tests
mutation
prototypes
DJSApplications so far:
Inheritance patterns, JS: Good Parts
Future work:Microbenchmarks (e.g. SunSpider)
Libraries (e.g. Array.prototype)
Application to Browser Extensions
ES5/ES6 Features
Class-based Inheritance
More Local Type Inference
21
Thanks!
Dravichugh.com/nested
::
Thanks to Brown PLT for the LambdaJS tools!
22
Extra Slides
23
Constants
tagof :: x:Top{| = tag(x)}
mem :: d:Dictk:Str {|Bool() = True has(d,k)}
get :: d:Dictk:{|Str() has(d,)}{| = sel(d,k)}
set :: d:Dictk:Strx:Top {| = upd(d,k,x)}
rem :: d:Dictk:Str{| = upd(d,k,bot)}
24
• Types
• Formulas
• Logical Values
Macros{|tag()=“Int” }
Int
x:T1T2 {|:: }x:T1T2
tag(x) = “Str”Str(x)
sel(n,“k”)x.k
sel(n,k)x[k]
sel(d,k) != bothas(d,k)
∀k’. k’ != k sel(d,k) != sel(d’,k)
EqMod(d,d’,k)
25
Ontolet onto callbacks f obj = if f = null then new List(obj,callbacks) else let cb = if tagof f = “Str” then obj[f] else f in new List(fun () -> cb obj, callbacks)
∀A. callbacks:List[TopTop]
f:{|Str():: ATop }
obj:{|:: A
(f=null :: AInt)
(Str(f) [f] :: AInt) }
List[TopTop]
onto ::
functional version of Dojo function
26
Onto (2)let onto (callbacks,f,obj) = if f = null then new List(obj,callbacks) else let cb = if tagof f = “Str” then obj[f] else f in new List(fun () -> cb obj, callbacks)
callbacks:List[TopTop]
* f:{g|Str(g)g:: {x|x= obj}Top }
* obj:{o|(f=null o :: {x|x= o}Int)
(Str(f) o[f] :: {x|x= o}Int) }
List[TopTop]
onto ::
functional version of Dojo function
27
Related Work• TODO
28
Traditional vs. Nested Refinements
29
• Reuse refinement type architecture
• Find a decidable refinement logic for– Tag-tests– Dictionaries– Lambdas
• Define nested refinement type architecture
Approach: Refinement Types
✓
✗✓✓*
30
Nested Refinements
T ::= {|p}U ::= x:T1T2
p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x :: U
• Refinement formulas over a decidable logic– uninterpreted functions, McCarthy arrays, linear arithmetic
• Only base values refined by formulasAll values
T ::= {|p} | x:T1T2
p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | …
traditional refinements
31
Nested Refinements
T ::= {|p}U ::= x:T1T2
p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U
• Refinement formulas over a decidable logic– uninterpreted functions, McCarthy arrays, linear arithmetic
• Only base values refined by formulas• “has-type” allows “type terms” in formulas
All values
T ::= {|p} | x:T1T2
p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | …
traditional refinements
32
Nested Refinements
T ::= {|p}U ::= x:T1T2
p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U
• Refinement formulas over a decidable logic– uninterpreted functions, McCarthy arrays, linear arithmetic
• Only base values refined by formulas• “has-type” allows “type terms” in formulas
All values
33
Subtyping
34
Subtyping
T ::= {|p} | x:T1T2
traditional refinements
Implication
SMT Solver
Subtyping
{|true }Top
tag()=“Int” true
{|tag()=“Int” }Int
Int <: Top
35
Subtyping
T ::= {|p} | x:T1T2
traditional refinements
Implication
SMT Solver
Subtyping
{|true }Top
{|tag()=“Int” }Int
Top Int <: Int Int
Int <: Top Int <: Int
tag()=“Int” true
tag()=“Int” tag()=“Int”
36
Subtyping
T ::= {|p} | x:T1T2
traditional refinements
Implication
SMT Solver
Subtyping
{|true }Top
{|tag()=“Int” }Int
Top Int <: Int Int
Int <: Top Int <: Int
tag()=“Int” true
tag()=“Int” tag()=“Int”
Arrow Rule
37
Subtyping
T ::= {|p} | x:T1T2
traditional refinements
Implication
SMT Solver
Subtyping Arrow Rule
Decidable if:• Only values in formulas• Underlying theories decidable
With nested refinements:• No new theories• But implication is imprecise!
38
Subtyping with Nesting
Implication
SMT Solver
Subtyping
Invalid, as these are distinctuninterpreted constants
:: Top Int :: Int Int✗
39
Subtyping with Nesting
Implication
SMT Solver
Subtyping Arrow Rule
When goal is base predicate:p q
When goal is “has-type” predicate:p x :: U
Implication
SMT Solver
Subtyping
p x :: U’
U’ <: U
p q
40
p :: Top Int
UninterpretedReasoning
Subtyping with Nesting
p :: Int Int
Normalize formulas tosubdivide obligations appropriately
Top Int <: Int Int
+ SyntacticReasoning
41
Normalization• TODO
42
foo example
43
let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)
T ::= {|p}U ::= x:T1T2
p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U
f:{|Str():: IntInt }
d:{|Dict()
Int(.n)
Str(f) [f] :: IntInt }
Int
foo ::
44
let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)
T ::= {|p}U ::= x:T1T2
p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U
f:{|Str():: IntInt }
d:{|Dict()
Int(.n)
Str(f) [f] :: IntInt }
Int
foo ::
45
let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)
T ::= {|p}U ::= x:T1T2
p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U
f:{|Str():: IntInt }
d:{|Dict()
Int(.n)
Str(f) [f] :: IntInt }
Int
foo ::
tag() = “Str”
{|tag()=“Int” }
{|tag()=“Int” }
46
let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)
T ::= {|p}U ::= x:T1T2
p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U
f:{|Str():: IntInt }
d:{|Dict()
Int(.n)
Str(f) [f] :: IntInt }
Int
foo ::
47
let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)
T ::= {|p}U ::= x:T1T2
p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U
f:{|Str():: IntInt }
d:{|Dict()
Int(.n)
Str(f) [f] :: IntInt }
Int
foo ::
sel(n,“n”) sel(n,f)
48
let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)
T ::= {|p}U ::= x:T1T2
p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U
f:{|Str():: IntInt }
d:{|Dict()
Int(.n)
Str(f) [f] :: IntInt }
Int
foo ::
49
{|::
}
{|::
} Int
d: {|tag()=“Dict”
tag(sel(,“n”))=“Int”
tag(f)=“Str”
sel(,f) :: }IntInt
f:{|tag()=“Str”:: }IntInt
let foo f d = if tagof f = “Str” then d.n + d[f](0) else d.n + f(0)
T ::= {|p}U ::= x:T1T2
p ::= pq | … | x = y | x < y | … | tag(x) = “Int” | … | sel(x,y) = z | … | x::U
foo ::