1 / 24

Adoption and Focus: Practical Linear Types for Imperative Programming

Adoption and Focus: Practical Linear Types for Imperative Programming. Manuel Fahndrich and Robert DeLine. From before. From the previous paper, we have tracked types, and guarded types. Tracked types have the most onerous constraint, that they do not have aliasing.

ozzy
Download Presentation

Adoption and Focus: Practical Linear Types for Imperative Programming

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. Adoption and Focus: Practical Linear Types for Imperative Programming Manuel Fahndrich and Robert DeLine

  2. From before • From the previous paper, we have tracked types, and guarded types. • Tracked types have the most onerous constraint, that they do not have aliasing. • The type uniquely defines the object it represents. • Guarded types simply limit the lifetime of an object to the lifetime of some tracked type. • Guarded types allow for aliasing, within the tracked type they are bounded by. Thus, two references whose types differ only in their guard cannot alias each other. However, if the guard type is the same, they may alias each other.

  3. More flexible linear types • Tracked types correspond to linear types, in that there can be only one object associated with the type • Because of this, Vault is unable to put the tracked (linear) types into a container • Solution: convert tracked types to guarded types, when additional flexibility is required

  4. Type language

  5. Conversion problems • Guarded types need a key to guard them - Ask the programmer for a key at demotion • Guarded types allow aliasing, which can witness behaviors not allowed in tracked types - Abstract the tracked components; the untracked components might have been aliased already, so we don’t need to worry about them

  6. Adoption • Demoting an object corresponds to adoption • The programmer supplies an object of tracked type, tr(K), which inherits the tracked type we are demoting • The guard on the “demoted” type is the key of the adopting object • The types of the tracked components are changed, to indicate they are not presently accessible

  7. Adoption rule

  8. Focus • Converts abstract type to concrete type, for data manipulation • Of course, it has to be tracked again • However, we have all these aliases floating around, and we need to prevent access to the aliases when we have the tracked type available • Use guarded aspect of aliases: revoke capability to access aliases for the duration of access to the tracked type

  9. Focus rule

  10. Example • Dictionary with two indexes to access growable arrays fun dict_lookup(phone:int, ssn:int, d:dictionary) { … let c = newCell(d) in … } fun add_amount(cell:ref<int[]>, elem:int) { … resize(cell, newsize) … } fun add_entry(d:dictionary, phone:int, ssn:int, amount:int) { let cell = dict_lookup(phone, ssn, d) in add_amount(cell, amount) }

  11. Example • Traditional linear typing doesn’t work • We want the growable arrays to be linear, so that we can be sure there are no other dangling references after we grow them • But, the array is contained in a ref cell, that is shared between the two indexes • Traditional linear typing would require the array’s linear type extend to the ref cell, but the ref cell, by design must be aliased

  12. newCell fun newCell(dct : tr(d)) : dB <int[]*> pre {d dictionary} post {d dictionary} { let cell : tr() = new <1> in cell.1 := newarray(10); adopt cell: <int[]*> by dct }

  13. Tracked-update After new, capability set is {d dictionary,  hinti } Applying the t-update rule, we get: {d dictionary,  hint[ ]i }

  14. Adoption Key set before adoption: {d dictionary,  hint[ ]i } Recall code: adopt cell:h int[ ]* i by dct Key set after adoption: {d dictionary} The type of the expression is dBh int[ ]* i

  15. resize fun resize[d](cell:dB <int[]*>, size:int): int pre {d dictionary} post {d dictionary} { let newa = newarray(size) in let fcell = focus cell in let olda = fcell.1 in copy(olda, newa); fcell.1 := newa; free olda }

  16. focusing • Just before the focus, the capabilities are: {d dictionary} ­ {1 int[ ] } • At the start of the focus body, capabilities are {h int[ ]* i } ­ {1 int[ ] } • Any use of an object guarded by d would become a type error in the body of the focus

  17. T-index let olda = fcell.1 in • C = {h int[ ]* i } ­ {1 int[ ] } • However, t-index cannot return a linear type, so we must unpack the capability to convert it into a tracked type • [cap-transform]: {h int[ ]*i} ` {h tr(2)i}­ {2 int [ ]} So capability set is now: {h tr(2)i}­ {2 int [ ]} ­ {1 int[ ] } Apply t-index, to access the tracked array. olda has type tr(2)

  18. T-update copy(olda, newa); fcell.1 := newa; • Copy takes in two tracked arrays, and does the copy. No type effects. • The t-update rule has already been seen. It simply changes the type of the named field. Not a null operation, because it is a tracked type, so we update the keys • Capabilities after: {h tr(1)i}­ {2 int [ ]} ­ {1 int[ ] }

  19. free free olda; • Capabilities: {h tr(1)i}­ {2 int [ ]} ­ {1 int[ ] } • Recall olda has type tr(2) • New capability set: {h tr(1)i} ­ {1 int[ ] }

  20. End focus • Now, we return to end the focus rule • Capabilities are: {h tr(1)i} ­ {1 int[ ] } • But we need \rho to have the same type as it did at the beginning • We unpacked before, so pack: {h tr(1) i} ­ {1 int [ ] } ` { h int [ ]* i } • C3 is empty, so our final capability set is {d dictionary}

  21. resize fun resize[d](cell:dB <int[]*>, size:int): int pre {d dictionary} post {d dictionary} { let newa = newarray(size) in let fcell = focus cell in let olda = fcell.1 in copy(olda, newa); fcell.1 := newa; free olda }

  22. What’s bad • It’s hard (inefficient) to write a compare function, to check if two arrays are identical based on keys • Cannot have a reference to two arrays in the dictionary • Solution: get a reference to one array, copy it, release the original array. Now open a second array, and compare against the copy • This is twice as much work as would be required without linear types

  23. Comparing guarded arrays fun compare(a:dBh int [ ]* i, b: dBh int [ ]* i , size:int) pre {d dictionary} post {d dictionary} { let newa = newarrray(size) in let result = (let fcell = focus a in let a’ = fcell.1 in copy(‘a, newa); (let fcell = focus b in let b’ = fcell.1 in compare_array(newa, b’);)) in free(newa); result } A read-only focus would allow the obvious implementation to work, but would require functions to have an annotation on whether they update any values. It’s unclear if this annotation can be checked automatically, thus it may be error-prone.

  24. Operational view • When an object is adopted, it gets put into the adopted set of the new parent. • When the parent is freed, it’s adopted children are linear once again • The simplest semantic is to recursively free all the children • Could also provide a callback function, which would know how to return the object t

More Related