The quantifier for<...> ... does not itself “produce” a lifetime, so it’s not really an operation on lifetimes.
Interestingly, lifetimes in RustBelt are a semilattice with intersection and 'static; we don’t have a union operation (“join”, I guess, but I also always mix them up). Intersection turned out to be key to model, e.g., Rc and RefCell; union was not necessary. The Rust compiler works on a subset of lifetimes (those corresponding to sets of points on the CFG of a function); it is very possible that this subset has intersection while the “full” set of lifetimes (which can, e.g., span across multiple threads) does not (need to).
Actually I’d probably say that the compiler works on “symbolic lifetimes” (as in abstract interpretation). The abstraction function cuts away everything outside the current function, only remembering whether the lifetime lasts after the function returned or not. Clearly, this equates lifetimes that are not actually equal, but the analysis is of course sound for the abstraction, so that’s not a problem. Symbolic lifetimes form a full lattice, concrete lifetimes may not. (Or maybe I’m splitting hair here, because really the Rust compiler doesn’t care about “concrete lifetimes” at all. But we’ve needed them in RustBelt, to explain how unsafe code can rely on the borrow checker.)
Are you saying you want your model to closely follow the actual implementation in the compiler? That would be extremely interesting; with RustBelt we’ve decided to build a more abstract model to avoid things getting too messy.