{"action":"created","comment":{"url":"https://api.github.com/repos/rust-lang/rfcs/pulls/comments/23137780","id":23137780,"diff_hunk":"@@ -0,0 +1,339 @@\n+- Start Date: 2015-01-18\n+- RFC PR: (leave this empty)\n+- Rust Issue: (leave this empty)\n+\n+# Summary\n+This RFC proposes an incremental compilation strategy for `rustc` that allows for translation, codegen, and parts of static analysis to be done in an incremental fashion, without precluding the option of later expanding incrementality to parsing, macro expansion, and resolution.\n+\n+In the C world, source code is split up into source files and header files, where source files are the unit of (re)compilation and header files contain 'interface' information that is needed to compile source files independently of each other. This RFC proposes an algorithm that could be described as (figuratively) splitting a Rust codebase into a set of source files, computing the minimal 'header/interface' information for each virtual source file and then using this information to determine if the object code for a given virtual source file (cached from a previous compiler invocation) can safely be re-used without having to recompile the source file.\n+(Note: This is just a metaphor, no actual header or source files are generated)\n+\n+# Motivation\n+At the moment `rustc` takes a long time to compile anything but trivial programs.\n+\n+# Detailed design\n+For making compilation incremental, the compiler must be changed in two ways:\n+\n+1. The compilation process must produce artifacts that can be (re)compiled and cached independently of each other.\n+2. The compiler must track dependencies between the items in a program, so it can infer which artifacts to recompile and which to re-use from a previous compilation.\n+\n+## Independent Compilation Artifacts\n+In order for the compiler to be able to re-use compilation artifacts from previous runs, these artifacts must be as independent of each other as possible. I propose to make the unit of (re)compilation individual functions and globals, that is, everything that ends up as a symbol in the output binary. At least the LLVM IR and object code of each function and global is cached, so the cache has two entries for every symbol, one in the form of object code, one in the form of LLVM IR.\n+\n+Why object code? Because it is the final output of the compiler and can be passed immediately to the linker without having to re-run the costliest compiler passes: trans and codegen.\n+\n+Why LLVM IR? Because if a fully optimized crate build is needed, it allows to generate an LLVM IR version of the crate without re-running parts of type-checking and trans. The individual IR fragments can be concatenated and LLVM can do its optimization passes on the whole crate then. \n+\n+It might also make sense to cache other things in between compiler runs (e.g. important lookup tables) but the two above are certainly the most important ones. I will refer to the cached data for some symbol as `compilation artifact` in the remainder of the document.\n+\n+## Dependency Tracking\n+First, lets define some terms that are useful for talking about the topic:\n+\n+- A `program item` is any kind of function-, type-, or global variable definition. This includes functions, types, and statics defined locally within other functions and associated type assignments in `impl` blocks. It does not include modules, local variables, statements or expressions.\n+- A `program item interface` is that part of a `program item` that may be relevant for compiling *other* `program items`. Types are 'all interface' while for functions it's only the signature and for statics it's their type.\n+- A `program item body` is the full definition of a `program item`, i.e. the full functions definition, including the function body, respectively the whole global variable definition, including the initializer.\n+\n+`Program items` can *depend* on each other. Let's define what that means more formally:\n+\n+- A program item `A` *depends* on a program item `B` iff a change to `B` means that `A` needs to be re-compiled.\n+\n+With these terms in place we can state that compiling a single `program item` will thus depend \n+\n+1. on its own `program item body`, and\n+2. the `program item interfaces` of any `program items` (types, traits, functions, methods, globals) it **transitively** references\n+\n+The dependency structure of a `program item` can be modeled as a directed graph:\n+\n+* For the `program item body` there is a node in the graph. Note that `program items` that don't have a body (structs, enums, traits, ...) are only represented by their 'interface node' in the graph.\n+* For each `program item interface` there is one node in the graph.\n+* If a `program item interface` `A` directly references another `program item` `B` then there is an edge from `A`'s graph node to `B`'s interface node. That is, whenever the name of a type, function, or global occurs, there is an edge to the interface node of that type, function, or global.\n+* If a `program item body` `A` references another `program item` `B` then there is an edge from `A`'s graph node to `B`'s interface node.\n+* There's always an edge from a `program items` body node to its interface node.\n+\n+Let's illustrate this with an example:\n+\n+```rust\n+struct Kid {\n+  name: &'static str\n+}\n+\n+struct Tiger {\n+  name: &'static str\n+}\n+\n+struct Dinosaur {\n+  name: &'static str,\n+  stomach_contents: Gastropod\n+}\n+\n+struct Gastropod {\n+  name: &'static str,\n+  height_in_stories: u32\n+}\n+\n+fn transmogrify(kid: Kid) -> Tiger {\n+    let intermediate_dinosaur = trans_internal(kid);\n+    Tiger { name: intermediate_dinosaur.name }\n+}\n+\n+fn trans_internal(kid: Kid) -> Dinosaur {\n+    Dinosaur {\n+        name: kid.name,\n+        stomach_contents: Gastropod {\n+            name: \"Larry\".to_string(),\n+            height_in_stories: 500\n+        }\n+    }\n+}\n+\n+fn main() {\n+    let calvin = Kid { name: \"Calvin\" };\n+    let hobbes = Tiger { name: \"Hobbes\" };\n+\n+    let calvin = transmogrify(calvin);\n+\n+    assert!(calvin < hobbes);\n+}\n+```\n+\n+The dependency graph of the above program looks like this:\n+\n+```                \n+              Gastropod <--- Dinosaur  +--> Kid  Tiger  \n+                 ^               ^     |     ^     ^     \n+                 |               |     |     |     |     \n+                 |               |     |     |     |     \n+                 |               |     |     |     |     \n+                 |   trans_internal ---+   transmogrify   main\n+INTERFACES       |       ^    ^                ^     ^       ^\n+-----------      |       |    |                |     |       |\n+BODIES           |       |    +---------+      |     +----+  |\n+                 |       |              |      |          |  | \n+               trans_internal'        transmogrify'       main'\n+\n+```\n+\n+Note that, for readability, I've omitted some redundant edges in the above graph. An edge between two nodes can be omitted if there is another path between those nodes (as in `trans_internal' --> Kid`). More formally, two dependency graphs are equivalent for our purposes if their transitive closures are equal.\n+\n+This dependency graph can be queried for finding all `program item interfaces` that a `program item` `A` depends on: Start at the node corresponding to `A` and collect all transitively reachable nodes. In the above example this means that `main()` needs to be recompiled if `Kid`, `Tiger` or the interface of `transmogrify` change (because these are reachable from `main`'s body node) but not if `Dinosaur`, `Gastropod` or any of the other function's body changes (because their nodes are not reachable from `main`'s body).\n+\n+## Generic Program Items\n+So far the dependency graph has only described non-generic `program items`. For generic definitions the situation is a bit more complicated since their dependencies are only fully defined once all type parameters are substituted for concrete arguments. Consider the following example:\n+\n+```rust\n+trait Transmogrifiable<T> {\n+    fn transmogrify(self) -> T;\n+}\n+\n+impl Transmogrifiable<Tiger> for Kid {\n+    fn transmogrify(self) -> Tiger { ... }\n+}\n+\n+impl Transmogrifiable<Gastropod> for Dinosaur {\n+    fn transmogrify(self) -> Gastropod { ... }\n+}\n+\n+fn transmogrify<TFrom, TTo>(val: TFrom) -> TTo \n+    where TFrom: Transmogrifiable<TTo> \n+{\n+    val.transmogrify()        \n+}\n+```\n+\n+In this example `transmogrify<Kid, Tiger>` will have a different dependency graph than `transmogrify<Dinosaur, Gastropod>`. In other words, the monomorphized implementation of `transmogrify<Kid, Tiger>` is not affected if the definition of `Dinosaur` or `Gastropod` changes and the dependency graph should reflect this.\n+\n+One way to model this behavior is by not creating dependency graph nodes for generic `program items` but `node templates`, which---like generic items---have type parameters and yield a concrete, monomorphic dependency graph node, if all type parameters are substituted for concrete arguments. When the need arises to check if a particular monomorphized function implementation from the cache can be re-used, the dependency graph for the function can be constructed on demand from the given `node template` and parameter substitutions.\n+\n+For consistency it also makes sense to treat non-generic `program items` as generic `program items` that happen to have zero type parameters. We thus obtain a direct correspondence between `program items` and `node templates` on the one hand and monomorphized `program item` instances and dependency graph nodes on the other.\n+\n+## Program Item and Compilation Artifact Identifiers\n+`Program items` and their corresponding `node templates` must be identifiable in a way that is stable across multiple invocations of the compiler. The `DefId` type that is currently used for cross-crate item identification does not fit this requirement unfortunately, since it contains the rather unstable AST `NodeId`. Adding a single AST node currently can invalidate all `NodeId`s in the codebase due to the sequential Node ID assignment strategy.\n+\n+One straightforward way of creating stable identifiers would be to use the `program item`'s path within the AST, while generating local integer IDs for anonymous blocks:\n+\n+```rust\n+// ID: \"M1\"\n+mod M1 { \n+\n+    // ID: \"M1::f1\"\n+    fn f1() {\n+\n+        // ID: \"M1::f1::S1\"\n+        struct S1;\n+\n+        {\n+            // ID: \"M1::f1::0::S2\"\n+            struct S2;\n+\n+        }\n+\n+        {\n+            // ID: \"M1::f1::1::E1\"\n+            enum E1;\n+        }\n+    }\n+\n+    impl SomeTrait for SomeType {\n+        fn something() { // ID <SomeType as M1::SomeTrait>::something\n+            ...\n+        }\n+    }\n+}\n+```\n+\n+The dependency tracking system as described above contains `node templates` for `program item` definitions on a syntactic level, that is, for each `struct`, `enum`, `type`, `trait`, there is one `node template`, for each `fn`, `static`, and `const` there are two (one for the interface, one for the body). However, as seen in the section on generics, the codebase can refer to monomorphized instances of program items that cannot be identified by a single identifier as described above. A reference like `Option<String>` is a composite of multiple `program item` IDs, a tree of program item IDs in the general case:\n+\n+```rust\n+\n+Option<Result<u32,GenericError<String>>>\n+\n+            Option\n+              |\n+            Result\n+            /   \\\n+         u32     GenericError\n+                     |\n+                  String \n+```\n+\n+Incidentally, it is also such a composite ID that is needed for identifying `compilation artifacts` within the cache, since the cache only stores monomorphized instances of generic functions. For want of a better name I'll call it `mono-id` as in \"identifier for a monomorphized type, function, trait, ...\". Non-generic `program items` are just a special case of generic ones and need no special treatment.\n+\n+## Determining If A Cache Entry Is Still Valid\n+This is the central question we want answered from the dependency tracking system: Given the need to include the object code of some (monomorphized) `program item` in the output binary, can we just reuse the object code already stored in the cache? This question can be answered by constructing the monomorphized dependency graph from the given `mono-id` and check each node for source code changes between cached and current version. In pseudo-rust:\n+\n+```rust\n+\n+trait MonoId {\n+    fn definition_id(&self) -> ProgramItemId;\n+    fn type_arguments(&self) -> &Map<TypeParam, MonoId>;  \n+}\n+\n+trait NodeTemplate {\n+    // A fingerprint of the relevant parts of the resolved AST\n+    fn description(&self) -> Fingerprint;\n+    fn instantiate(&self, type_arguments: &Map<TypeParam, MonoId>) -> &Node;\n+}\n+\n+trait Node {\n+    fn dependencies(&self) -> &[MonoId];\n+}\n+\n+fn has_interface_changed(\n+    id: MonoId,\n+    interface_node_templates: &Map<ProgramItemId, NodeTemplate>,\n+    cache: &Cache)\n+ -> bool {\n+    \n+    let node_template = interface_node_templates.get(id.definition_id);\n+\n+    if node_template.description() != \n+       cache.load_interface_description(id.definition_id()) {\n+        return true;\n+    }\n+\n+    let node = node_template.instantiate(id.type_arguments());\n+    for dependency_id in node.dependencies() {\n+        if has_interface_changed(dependency_id, node_templates, cache) {\n+            return true;\n+        }\n+    }\n+\n+    return false;\n+}\n+\n+fn is_cached_implementation_still_valid(\n+    id: MonoId,\n+    interface_node_templates: &Map<ProgramItemId, NodeTemplate>,\n+    body_node_templates: &Map<ProgramItemId, NodeTemplate>,\n+    cache: &Cache)\n+ -> bool {\n+    \n+    let node_template = body_node_templates.get(id.definition_id());\n+    \n+    if node_template.description() != \n+       cache.load_body_description(id.definition_id()) {\n+        return false;\n+    }\n+\n+    let node = node_template.instantiate(id.type_arguments());\n+\n+    for dependency_id in node.dependencies() {\n+        if has_interface_changed(dependency_id, node_templates, cache) {\n+            return false;\n+        }\n+    }\n+\n+    return true;\n+}\n+\n+// Handling of cycles in dependency graph has been omitted for clarity.\n+```\n+\n+\n+## Incremental Compilation Algorithm\n+With these things in place, we can construct an algorithm for building a crate incrementally. \n+\n+```\n+(1) Parse, Expand, Resolve whole crate\n+(2) Run type inference on all program items\n+(3) Build NodeTemplate map \n+(4) Add all monomorphic program items to set of needed compilation\n+    artifacts\n+(5) Ensure that all needed compilation artifacts are in the cache:\n+    (1) Take mono-id from list of needed compilation artifacts\n+    (3) Construct dependency graph to determine whether cache entry\n+        can be reused\n+    (4) If not, compile item\n+    (5) Add item fingerprint and compilation artifacts to cache\n+    (6) If new monomorphized item instances are discovered during\n+        compilation, add them to list of needed compilation artifacts\n+(6) Clear unreferenced entries from the cache\n+(7) Create output binary\n+    (a) Link cached object files into output binary, or\n+    (b) Concatenate cached IR fragments, do optimization and codegen,\n+        then link\n+```\n+\n+\n+## Miscellaneous Aspects\n+\n+### More fine-grained dependency tracking\n+It would be possible to make dependency tracking aware of the kind of reference one item makes to another. If an item `A` mentions another item `B` only via some reference type (e.g. `&T`), then item `A` only needs to be updated if `B` is removed or `B` changes its 'sized-ness'. This is comparable to how forward declarations in C are handled. In the dependency graph this would mean that there are different kinds of edges that trigger for different kinds of changes to items.\n+\n+### Global Switches Influencing Codegen\n+There are many compiler flags that change the way the generated code looks like, e.g. optimization and debuginfo levels. A simple strategy to deal with this would be to store the set of compiler flags used for building the cache and clearing the cache completely if another set of flags is used. Another option is to keep multiple caches, each for a different set of compiler flags (e.g. keeping both on disk, a 'debug build cache' and a 'release build cache').\n+\n+### Source Location Encoding\n+The current `codemap` implementation in `rustc` stores everything in one global 'address space'. This is unsuited for incremental compilation since old versions of source files need to be removed from the codemap and new source files need to be added. This leads to a similar problem as the sequentially assigned AST node IDs.\n+\n+It would be better to store span information in byte-offsets relative to a given source file. Then modifying the codemap is possible without invalidating existing source locations stored somewhere in the cache.\n+\n+### Physical Cache Structure\n+The cache could be kept somewhere within the output directory. Object code could be maintained in an `ar` archive so the linker can directly access it. Something similar might be possible for LLVM bitcode.\n+\n+### Automatic inter-function optimizations\n+It should not be too hard to let the compiler keep track of which parts of the program change infrequently and then let it speculatively build object files with more than one function in them. For these aggregate object files inter-function LLVM optimizations could then be enabled, yielding faster object code at little additional cost. Other strategies for controlling cache granularity can be implemented in a similar fashion.\n+\n+### Parallelization\n+If some care is taken in implementing the above concepts it should be rather easy to do translation and codegen in parallel for all items, since by design we already have (or can deterministically compute) all the information we need.\n+\n+# Drawbacks\n+\n+Implementing this will need a lot of work on the compiler architecture. But that's something that will be needed sooner or later anyway.\n+\n+# Alternatives\n+\n+I'd definitely like to hear about them.\n+\n+# Unresolved questions\n+\n+## Dependency Graph Construction before Type Inference\n+I'm not sure if it would be possible to construct valid dependency graphs *before* type-inference or if that would miss some dependency edges. Or more generally, how much per-item work can be pushed until after caching strikes.","path":"text/0000-incremental-compilation.md","position":331,"original_position":331,"commit_id":"3f55ba6754d2bb0b46960bb6f25f265c2536c220","original_commit_id":"3f55ba6754d2bb0b46960bb6f25f265c2536c220","user":{"login":"nick29581","id":762626,"avatar_url":"https://avatars.githubusercontent.com/u/762626?v=3","gravatar_id":"","url":"https://api.github.com/users/nick29581","html_url":"https://github.com/nick29581","followers_url":"https://api.github.com/users/nick29581/followers","following_url":"https://api.github.com/users/nick29581/following{/other_user}","gists_url":"https://api.github.com/users/nick29581/gists{/gist_id}","starred_url":"https://api.github.com/users/nick29581/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nick29581/subscriptions","organizations_url":"https://api.github.com/users/nick29581/orgs","repos_url":"https://api.github.com/users/nick29581/repos","events_url":"https://api.github.com/users/nick29581/events{/privacy}","received_events_url":"https://api.github.com/users/nick29581/received_events","type":"User","site_admin":false},"body":"@epdtry found that he needed type information for constructing the dependency graph, although I don't recall why, exactly.","created_at":"2015-01-18T22:31:15Z","updated_at":"2015-01-18T22:31:15Z","html_url":"https://github.com/rust-lang/rfcs/pull/594#discussion_r23137780","pull_request_url":"https://api.github.com/repos/rust-lang/rfcs/pulls/594","_links":{"self":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/comments/23137780"},"html":{"href":"https://github.com/rust-lang/rfcs/pull/594#discussion_r23137780"},"pull_request":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/594"}}},"pull_request":{"url":"https://api.github.com/repos/rust-lang/rfcs/pulls/594","id":27575112,"html_url":"https://github.com/rust-lang/rfcs/pull/594","diff_url":"https://github.com/rust-lang/rfcs/pull/594.diff","patch_url":"https://github.com/rust-lang/rfcs/pull/594.patch","issue_url":"https://api.github.com/repos/rust-lang/rfcs/issues/594","number":594,"state":"open","locked":false,"title":"RFC: Support Incremental Compilation","user":{"login":"michaelwoerister","id":1825894,"avatar_url":"https://avatars.githubusercontent.com/u/1825894?v=3","gravatar_id":"","url":"https://api.github.com/users/michaelwoerister","html_url":"https://github.com/michaelwoerister","followers_url":"https://api.github.com/users/michaelwoerister/followers","following_url":"https://api.github.com/users/michaelwoerister/following{/other_user}","gists_url":"https://api.github.com/users/michaelwoerister/gists{/gist_id}","starred_url":"https://api.github.com/users/michaelwoerister/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/michaelwoerister/subscriptions","organizations_url":"https://api.github.com/users/michaelwoerister/orgs","repos_url":"https://api.github.com/users/michaelwoerister/repos","events_url":"https://api.github.com/users/michaelwoerister/events{/privacy}","received_events_url":"https://api.github.com/users/michaelwoerister/received_events","type":"User","site_admin":false},"body":"This RFC proposes an incremental compilation strategy for `rustc` that allows for translation, codegen, and parts of static analysis to be done in an incremental fashion, without precluding the option of later expanding incrementality to parsing, macro expansion, and resolution.\r\n\r\nThis RFC is purely about the architecture and implementation of the Rust compiler. It does not propose any changes to the language. I also don't expect it to be acted on any time before 1.0 is out of the door, but I wanted to get this out into the open, so that it can discussed as part of the *RustC Architecture Improvement Initiative* (that's right, RAII) that I invented just now and that will begin to discuss how the Rust compiler can get as good as possible once the language has become a more stable target.\r\n\r\n[Rendered](https://github.com/michaelwoerister/rfcs/blob/master/text/0000-incremental-compilation.md)","created_at":"2015-01-18T18:52:33Z","updated_at":"2015-01-18T22:31:15Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d7363252b1cbfaeeaef3a780902830c6be37eb8b","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/rust-lang/rfcs/pulls/594/commits","review_comments_url":"https://api.github.com/repos/rust-lang/rfcs/pulls/594/comments","review_comment_url":"https://api.github.com/repos/rust-lang/rfcs/pulls/comments/{number}","comments_url":"https://api.github.com/repos/rust-lang/rfcs/issues/594/comments","statuses_url":"https://api.github.com/repos/rust-lang/rfcs/statuses/3f55ba6754d2bb0b46960bb6f25f265c2536c220","head":{"label":"michaelwoerister:master","ref":"master","sha":"3f55ba6754d2bb0b46960bb6f25f265c2536c220","user":{"login":"michaelwoerister","id":1825894,"avatar_url":"https://avatars.githubusercontent.com/u/1825894?v=3","gravatar_id":"","url":"https://api.github.com/users/michaelwoerister","html_url":"https://github.com/michaelwoerister","followers_url":"https://api.github.com/users/michaelwoerister/followers","following_url":"https://api.github.com/users/michaelwoerister/following{/other_user}","gists_url":"https://api.github.com/users/michaelwoerister/gists{/gist_id}","starred_url":"https://api.github.com/users/michaelwoerister/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/michaelwoerister/subscriptions","organizations_url":"https://api.github.com/users/michaelwoerister/orgs","repos_url":"https://api.github.com/users/michaelwoerister/repos","events_url":"https://api.github.com/users/michaelwoerister/events{/privacy}","received_events_url":"https://api.github.com/users/michaelwoerister/received_events","type":"User","site_admin":false},"repo":{"id":29435940,"name":"rfcs","full_name":"michaelwoerister/rfcs","owner":{"login":"michaelwoerister","id":1825894,"avatar_url":"https://avatars.githubusercontent.com/u/1825894?v=3","gravatar_id":"","url":"https://api.github.com/users/michaelwoerister","html_url":"https://github.com/michaelwoerister","followers_url":"https://api.github.com/users/michaelwoerister/followers","following_url":"https://api.github.com/users/michaelwoerister/following{/other_user}","gists_url":"https://api.github.com/users/michaelwoerister/gists{/gist_id}","starred_url":"https://api.github.com/users/michaelwoerister/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/michaelwoerister/subscriptions","organizations_url":"https://api.github.com/users/michaelwoerister/orgs","repos_url":"https://api.github.com/users/michaelwoerister/repos","events_url":"https://api.github.com/users/michaelwoerister/events{/privacy}","received_events_url":"https://api.github.com/users/michaelwoerister/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/michaelwoerister/rfcs","description":"RFCs for changes to Rust","fork":true,"url":"https://api.github.com/repos/michaelwoerister/rfcs","forks_url":"https://api.github.com/repos/michaelwoerister/rfcs/forks","keys_url":"https://api.github.com/repos/michaelwoerister/rfcs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/michaelwoerister/rfcs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/michaelwoerister/rfcs/teams","hooks_url":"https://api.github.com/repos/michaelwoerister/rfcs/hooks","issue_events_url":"https://api.github.com/repos/michaelwoerister/rfcs/issues/events{/number}","events_url":"https://api.github.com/repos/michaelwoerister/rfcs/events","assignees_url":"https://api.github.com/repos/michaelwoerister/rfcs/assignees{/user}","branches_url":"https://api.github.com/repos/michaelwoerister/rfcs/branches{/branch}","tags_url":"https://api.github.com/repos/michaelwoerister/rfcs/tags","blobs_url":"https://api.github.com/repos/michaelwoerister/rfcs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/michaelwoerister/rfcs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/michaelwoerister/rfcs/git/refs{/sha}","trees_url":"https://api.github.com/repos/michaelwoerister/rfcs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/michaelwoerister/rfcs/statuses/{sha}","languages_url":"https://api.github.com/repos/michaelwoerister/rfcs/languages","stargazers_url":"https://api.github.com/repos/michaelwoerister/rfcs/stargazers","contributors_url":"https://api.github.com/repos/michaelwoerister/rfcs/contributors","subscribers_url":"https://api.github.com/repos/michaelwoerister/rfcs/subscribers","subscription_url":"https://api.github.com/repos/michaelwoerister/rfcs/subscription","commits_url":"https://api.github.com/repos/michaelwoerister/rfcs/commits{/sha}","git_commits_url":"https://api.github.com/repos/michaelwoerister/rfcs/git/commits{/sha}","comments_url":"https://api.github.com/repos/michaelwoerister/rfcs/comments{/number}","issue_comment_url":"https://api.github.com/repos/michaelwoerister/rfcs/issues/comments/{number}","contents_url":"https://api.github.com/repos/michaelwoerister/rfcs/contents/{+path}","compare_url":"https://api.github.com/repos/michaelwoerister/rfcs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/michaelwoerister/rfcs/merges","archive_url":"https://api.github.com/repos/michaelwoerister/rfcs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/michaelwoerister/rfcs/downloads","issues_url":"https://api.github.com/repos/michaelwoerister/rfcs/issues{/number}","pulls_url":"https://api.github.com/repos/michaelwoerister/rfcs/pulls{/number}","milestones_url":"https://api.github.com/repos/michaelwoerister/rfcs/milestones{/number}","notifications_url":"https://api.github.com/repos/michaelwoerister/rfcs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/michaelwoerister/rfcs/labels{/name}","releases_url":"https://api.github.com/repos/michaelwoerister/rfcs/releases{/id}","created_at":"2015-01-18T18:29:30Z","updated_at":"2015-01-18T09:31:24Z","pushed_at":"2015-01-18T18:41:11Z","git_url":"git://github.com/michaelwoerister/rfcs.git","ssh_url":"git@github.com:michaelwoerister/rfcs.git","clone_url":"https://github.com/michaelwoerister/rfcs.git","svn_url":"https://github.com/michaelwoerister/rfcs","homepage":"","size":6398,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"rust-lang:master","ref":"master","sha":"e0999be5aade97bb794d9f886bac31cfcb0f8a8d","user":{"login":"rust-lang","id":5430905,"avatar_url":"https://avatars.githubusercontent.com/u/5430905?v=3","gravatar_id":"","url":"https://api.github.com/users/rust-lang","html_url":"https://github.com/rust-lang","followers_url":"https://api.github.com/users/rust-lang/followers","following_url":"https://api.github.com/users/rust-lang/following{/other_user}","gists_url":"https://api.github.com/users/rust-lang/gists{/gist_id}","starred_url":"https://api.github.com/users/rust-lang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rust-lang/subscriptions","organizations_url":"https://api.github.com/users/rust-lang/orgs","repos_url":"https://api.github.com/users/rust-lang/repos","events_url":"https://api.github.com/users/rust-lang/events{/privacy}","received_events_url":"https://api.github.com/users/rust-lang/received_events","type":"Organization","site_admin":false},"repo":{"id":17527989,"name":"rfcs","full_name":"rust-lang/rfcs","owner":{"login":"rust-lang","id":5430905,"avatar_url":"https://avatars.githubusercontent.com/u/5430905?v=3","gravatar_id":"","url":"https://api.github.com/users/rust-lang","html_url":"https://github.com/rust-lang","followers_url":"https://api.github.com/users/rust-lang/followers","following_url":"https://api.github.com/users/rust-lang/following{/other_user}","gists_url":"https://api.github.com/users/rust-lang/gists{/gist_id}","starred_url":"https://api.github.com/users/rust-lang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rust-lang/subscriptions","organizations_url":"https://api.github.com/users/rust-lang/orgs","repos_url":"https://api.github.com/users/rust-lang/repos","events_url":"https://api.github.com/users/rust-lang/events{/privacy}","received_events_url":"https://api.github.com/users/rust-lang/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rust-lang/rfcs","description":"RFCs for changes to Rust","fork":false,"url":"https://api.github.com/repos/rust-lang/rfcs","forks_url":"https://api.github.com/repos/rust-lang/rfcs/forks","keys_url":"https://api.github.com/repos/rust-lang/rfcs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rust-lang/rfcs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rust-lang/rfcs/teams","hooks_url":"https://api.github.com/repos/rust-lang/rfcs/hooks","issue_events_url":"https://api.github.com/repos/rust-lang/rfcs/issues/events{/number}","events_url":"https://api.github.com/repos/rust-lang/rfcs/events","assignees_url":"https://api.github.com/repos/rust-lang/rfcs/assignees{/user}","branches_url":"https://api.github.com/repos/rust-lang/rfcs/branches{/branch}","tags_url":"https://api.github.com/repos/rust-lang/rfcs/tags","blobs_url":"https://api.github.com/repos/rust-lang/rfcs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rust-lang/rfcs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rust-lang/rfcs/git/refs{/sha}","trees_url":"https://api.github.com/repos/rust-lang/rfcs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rust-lang/rfcs/statuses/{sha}","languages_url":"https://api.github.com/repos/rust-lang/rfcs/languages","stargazers_url":"https://api.github.com/repos/rust-lang/rfcs/stargazers","contributors_url":"https://api.github.com/repos/rust-lang/rfcs/contributors","subscribers_url":"https://api.github.com/repos/rust-lang/rfcs/subscribers","subscription_url":"https://api.github.com/repos/rust-lang/rfcs/subscription","commits_url":"https://api.github.com/repos/rust-lang/rfcs/commits{/sha}","git_commits_url":"https://api.github.com/repos/rust-lang/rfcs/git/commits{/sha}","comments_url":"https://api.github.com/repos/rust-lang/rfcs/comments{/number}","issue_comment_url":"https://api.github.com/repos/rust-lang/rfcs/issues/comments/{number}","contents_url":"https://api.github.com/repos/rust-lang/rfcs/contents/{+path}","compare_url":"https://api.github.com/repos/rust-lang/rfcs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rust-lang/rfcs/merges","archive_url":"https://api.github.com/repos/rust-lang/rfcs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rust-lang/rfcs/downloads","issues_url":"https://api.github.com/repos/rust-lang/rfcs/issues{/number}","pulls_url":"https://api.github.com/repos/rust-lang/rfcs/pulls{/number}","milestones_url":"https://api.github.com/repos/rust-lang/rfcs/milestones{/number}","notifications_url":"https://api.github.com/repos/rust-lang/rfcs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rust-lang/rfcs/labels{/name}","releases_url":"https://api.github.com/repos/rust-lang/rfcs/releases{/id}","created_at":"2014-03-07T21:29:00Z","updated_at":"2015-01-18T09:31:24Z","pushed_at":"2015-01-13T00:31:18Z","git_url":"git://github.com/rust-lang/rfcs.git","ssh_url":"git@github.com:rust-lang/rfcs.git","clone_url":"https://github.com/rust-lang/rfcs.git","svn_url":"https://github.com/rust-lang/rfcs","homepage":"","size":6398,"stargazers_count":231,"watchers_count":231,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":146,"mirror_url":null,"open_issues_count":228,"forks":146,"open_issues":228,"watchers":231,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/594"},"html":{"href":"https://github.com/rust-lang/rfcs/pull/594"},"issue":{"href":"https://api.github.com/repos/rust-lang/rfcs/issues/594"},"comments":{"href":"https://api.github.com/repos/rust-lang/rfcs/issues/594/comments"},"review_comments":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/594/comments"},"review_comment":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/594/commits"},"statuses":{"href":"https://api.github.com/repos/rust-lang/rfcs/statuses/3f55ba6754d2bb0b46960bb6f25f265c2536c220"}}}}