1

I'm trying to work with LLVM in Rust using this crate. I'm trying to create a code generator struct to hold the context, module, and builder for me, but when I try to compile I get an error message that says c does not live long enough. How can I get this to compile, and why isn't c living long enough?

Code:

use llvm::*;
use llvm::Attribute::*;
pub struct CodeGen<'l> {
    context: CBox<Context>,
    builder: CSemiBox<'l, Builder>,
    module: CSemiBox<'l, Module>,
}
impl<'l> CodeGen<'l> {
    pub fn new() -> CodeGen<'l> {
        let c = Context::new();
        let b = Builder::new(&c);
        let m = Module::new("test", &c);
        CodeGen {
            context: c,
            builder: b,
            module: m,
        }
    }
}

Full error message:

error: `c` does not live long enough
  --> src/codegen.rs:17:31
   |
17 |         let b = Builder::new(&c);
   |                               ^ does not live long enough
...
24 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'l as defined on the body at 15:32...
  --> src/codegen.rs:15:33
   |
15 |     pub fn new() -> CodeGen<'l> {
   |                                 ^

error: `c` does not live long enough
  --> src/codegen.rs:18:38
   |
18 |         let m = Module::new("test", &c);
   |                                      ^ does not live long enough
...
24 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'l as defined on the body at 15:32...
  --> src/codegen.rs:15:33
   |
15 |     pub fn new() -> CodeGen<'l> {
   |                                 ^

error: aborting due to 2 previous errors
BookOwl
  • 378
  • 3
  • 11

1 Answers1

2

This looks like one of those situations where lifetime elision makes things less clear.

Here's the prototype of Builder::new:

pub fn new(context: &Context) -> CSemiBox<Builder>

Which might make you think that the CSemiBox doesn't have any relation to the lifetime of context. But the definition of CSemiBox has a lifetime parameter:

pub struct CSemiBox<'a, D>

As I understand it, when the output type of a function (in this case Builder::new) has a lifetime parameter, it can be elided if there is only one input lifetime. (The lifetime elision rules are described in the book and in this question.) In this case, the output lifetime is taken to be the same as the input lifetime. That means the prototype from before is actually equivalent to the following:

pub fn new<'a>(context: &'a Context) -> CSemiBox<'a, Builder>

I hope this clarifies what's happening: after Builder::new(&c), the CSemiBox contains a reference to the Context it was created from (b contains a reference to c). You can't put b and c in the same struct because the compiler has to be able to prove that c outlives b. For a more thorough explanation, see Why can't I store a value and a reference to that value in the same struct?

There are two ways I can think of to handle this. (You can't use Rc because you don't control the crate.)

  1. Don't store the Context inside the CodeGen struct. You're limited in how you can structure your code, but that's not necessarily bad.

  2. Since the Context is stored on the heap, you can use unsafe to make the references (appear to) have a 'static lifetime. Something like the following snippet ought to work, which removes the lifetime annotation from CodeGen. If you do this (as any time you use unsafe), you take responsibility for ensuring the safety of the exposed interface. That means, for example, CodeGen can't hand out references to builder and module, because that could leak a 'static reference to context.

    pub struct CodeGen {
        context: CBox<Context>,
        builder: CSemiBox<'static, Builder>,
        module: CSemiBox<'static, Module>,
    }
    impl CodeGen {
        pub fn new() -> CodeGen {
            let c = Context::new();  // returns a CBox<Context>
            let c_static_ref: &'static _ = unsafe {
                let c_ptr = c.as_ptr() as *const _;  // get the underlying heap pointer
                &*c_ptr
            };
            let b = Builder::new(c_static_ref);
            let m = Module::new("test", c_static_ref);
            CodeGen {
                context: c,
                builder: b,
                module: m,
            }
        }
    }
    
Community
  • 1
  • 1
trent
  • 25,033
  • 7
  • 51
  • 90
  • @FrancisGagné AHA! That's the one I wanted to link to but I could only find [this one](http://stackoverflow.com/questions/20698384/lifetime-of-rust-structs-that-reference-each-other) by searching. Thanks – trent Mar 02 '17 at 02:45
  • Thank you for the great answer! – BookOwl Mar 02 '17 at 15:10
  • I think that I will just not use a struct and have the calling code create the context, module, and builder and then pass them in to the code generator function. – BookOwl Mar 02 '17 at 16:20
  • @trentcl do you this is this a duplicate of either http://stackoverflow.com/q/32300132/234590 or http://stackoverflow.com/questions/20698384/lifetime-of-rust-structs-that-reference-each-other? – Shepmaster Mar 09 '17 at 20:47
  • @Shepmaster Actually, I think the key to this answer was realizing that an `'a` had been elided. But I guess I did go into a bit more detail and some of it is redundant – trent Mar 09 '17 at 21:41