# Interface between T-lang and T-opsem The goal of this meeting is to get a better understanding of what the relationship between T-opsem and T-lang is expected to look like. This document works through a couple examples of some ways in which these teams will interact. ### Example A: T-lang wants to add a feature that interacts with unsafe code A user posts an RFC with the goal of making `Copy` compatible with interior mutability. Specifically, they suggest the language add `impl<T: Copy> Copy for UnsafeCell<T> {}`. The RFC is not primarily a T-opsem decision, as there are a number of non-opsem design concerns. However, such a change of course does interact with unsafe code and the opsem, and so T-opsem does need to be involved in the decision making process. At the latest when the FCP kicks off (but ideally earlier), T-opsem files any comments/concerns they have. In this case, T-opsem is likely to point out (if no one else has yet) that this is a breaking soundness change for unsafe code that exposed a `&UnsafeCell<u8>` to safe code. Along with such a concern, the team is expected to provide: - Some concrete examples to explain what the problem is. - These examples should be as realistic as possible - in this case, a `Mutex<T>::as_ref` with signature `fn(&Mutex<T>) -> &UnsafeCell<T>` might be particularly compelling. - When possible, examples from the ecosystem are useful. - Some discussion of known adjustments or known alternatives that alleviate (or don't) the problem. - For example, how about allowing `unsafe impl Copy` instead? - Alternatives may not always exist; it is possible to know that one option has unacceptable side-effects without having a better alternative in mind - besides "do nothing." - Some indication of how severe the team considers this problem. - There is a fairly wide spectrum here: "Here's an interesting example, although it probably doesn't matter all too much" on one end. "We aren't comfortable merging this because it breaks too much code" on the other. - A shortage of realistic examples does not necessarily imply the concern is minor. Being able to identify serious theoretical concerns that do not easily map to particularly realistic examples is one of the useful features on an opsem team. For this example, we'll imagine that the RFC is edited to instead suggest allowing `unsafe impl Copy`. T-opsem files no further concerns and the FCP goes through. Come stabilization FCP time (but again, ideally earlier) T-opsem gets another chance to file concerns. Concerns around the design of the feature can be filed if new information has come to light since the RFC (or something else has changed). There are other possibilities though. For example, T-opsem might file a concern if Miri does not yet implement support for a feature as that means users will have a more difficult time checking their work. ### Example B: T-opsem wants to stabilize some UB Users are complaining that their futures are too big. T-opsem wants to let rustc do more aggressive optimizations before generator lowering. However, justifying these optimizations requires an opsem for pre-generator lowering Mir. T-opsem comes up with a design, but this design introduces some new UB into user code. The team considers this sufficiently significant or impactful that T-lang should know about it and get a chance to weigh in. This is accomplished by including T-lang in a suitable FCP. As part of the FCP, T-opsem is expected to provide: - Motivating examples explaining why this UB is necessary. - Example: We want to reduce the size of this future: ```rust async { let x = 5; foo().await; let z = x; } ``` - Some explanation of the "formal" version of the rules and some explanation of the version of the rules that is suitable as "user facing documentation." - Example: Truncated here for brevity, but tl;dr: Violating the `Pin` requirements on `async` block generated `Future`s is always immediate UB. - Some descriptions of how significant the gains and drawbacks to this are: - Example: Future sizes are a real problem and this unlocks optimizations to fix eg exponential blowup. - Example: We don't think a lot of user code is violating this, couldn't find any in the ecosystem. - Some discussion of alternatives: - Example: We could just stabilize generator lowering. But then we get no optimizations :( ### Example C: T-opsem wants to raise a blocking concern A user posts an RFC that suggests an `async unpin {}` block which produces a `Unpin` future, along with some static analysis to check that this is sound. T-opsem is not involved in the RFC initially. However, at some point, someone from the team raises the concern that our current semantics for pre-generator lowering Mir rely on the `Pin`-ness of async blocks, and are incompatible with allowing them to be `Unpin`. T-opsem is added to whatever FCP is happening/will happen. - This process is made somewhat easier by both T-opsem team leads also being members of the lang advisors team, meaning they can file rfcbot concerns on lang FCPs directly. At this point, things ought to continue essentially as described in Example A. The path to getting here was slightly different, but we are now once more in a "T-lang wants to stabilize a feature that affects the opsem" situation. ### Appendix Jakob: > Consider an interaction which looks like this: > > 1. T-lang asks T-opsem "is transformation from A to B legal" > 2. T-opsem says yes or no without much further discussion > 3. T-lang uses this information to inform... some decision > > I do not think this is a desirable interaction and I think we should actively attempt to avoid it. My concern is that it is extremely difficult for anyone who does not have 100% of the context to know how representative an example is. There are a *lot* of misleading examples and there is a real risk of miscommunication as a result of them. > > I did not include this in the doc, but maybe I should. More generally, I think the goal should be that with every example, there is also some explanation of what the broader category of code that the example is intended to represent is. Felix: > I think examples of anti-patterns of communication would be welcome > It may be a little to late to ask for edits to the doc to include them, but we can certainly bring it up in the meeting tomorrow. [Context](https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/how.20to.20interface.20with.20T-lang.20lang-team.23196/near/338713156) # Q&A ## Quick note on governance (Want to briefly bring up [the governance RFC][3392] and Council representation, to ensure T-lang subteams are included.) [3392]: https://github.com/rust-lang/rfcs/pull/3392 ## Josh: preferred pattern for the appendix? I'm guessing the preferred pattern would be to replace step 2 with: T-opsem says "what are you trying to do here?", and there's a discussion to figure out a better question and then answer it. Jakob: Yes Josh: Would like to have a "how to interface with the opsem team" document that gives good examples. https://lang-team.rust-lang.org/how_to.html ## nikomatsakis: things opsem expects from lang team Two things I am wondering about. (1) We should enumerate the things opsem expects from lang-team. This doc was oriented more at the reverse, but it seems like there were some "expectations" latent... e.g., T-opsem should be tagged on RFC, FCPs, and questions that involve unsafe code. Is that the main one? nikomatsakis: Per appendix, give broad context about the motivation for a particular question. ## Setting Expectations pnkfelix: (Its probably too early to make commitments here, but I might as well bring it up) Some WG's/subteams have found that they cannot commit to owning certain kinds of authority. (I'm thinking e.g. of T-rustdoc and how T-compiler eventually took back the responsbility of approving backports.) Do we know what authorities are being delegated to T-opsem, and do you have any concerns at this point about whether you *shouldn't* have certain authoritative responsibilies? jakob: opsem likely to be bandwidth limited. Lang initiatives and things that people care about could get stalled as a result. Goal should be that we have a healthy relationship and struggling with how to balance people wanting to move forward. niko: a prior example: we knew when we stabilized `async fn` that Stacked-Borrows did not have a way to handle interior mutability, and yet we went ahead with it. This arguably introduced "opsem tech debt", in terms of creating a problem without a known solution. https://github.com/rust-lang/rust/issues/63818 jakob: distinction is between "seems like it has a solution but we don't know exactly what it is" vs "might not have a solution at all", and I think async was always going to have a solution. In Stacked Borrows, always the possibility of "turning things off" and weakening the constraints. pnkfelix: I guess that's always a *fallback*...like saying you can write inline assembly... jakob: not the same. What we do now is treat references to "not unpin" types as pointers. We lose alias analysis + optimizations as a result. jakob: An example of where we don't have a fallback is permissive provenance. Depends on angelic nondeterminism, and this might just be fundamentally problematic to have in the language. ## nikomatsakis: lang team constraints The doc talked about the idea of opsem team wanting to stabilize some UB, but it didn't cover compiler or lang wanting to stabilize an *optimization* (I'm thinking of future sizes...) and hence possibly *create* UB. How do you think that might work out? pnkfelix: (I had interpreted these as being the same thing; i.e. "stabilizing UB" is an odd phrase, but I took it to mean "we are stating that pattern X *does* yield UB and thus justifies optimization Y. ". We may want to dig into the distinction Niko is drawing here.) nikomatsakis: would be a good example to include -- when we decide to move forward and we want T-opsem to feel good about that. I think there might be room for us to deoptimize in the future if it came down to that. Ben Kimock: not so sure about that! Reintroducing exponential blowup could make people's code really break. pnkfelix: MIR opt level 0 (just exposed to miri) is an interesting question. ## nikomatsakis: layout guarantees and other such things Which team owns the answer to questions like "is `Option<&mut Const>` guaranteed to be a pointer"? It feels squarely opsem, but also like the primary question is one of "is this how users want to think about it", and I think that's more lang? c.f. > T-lang: The team is a subteam of T-lang. It has the same relationship to T-lang as T-types has. This means decisions about "details" will be made by the team alone, but decisions around the big picture "direction" will require consultation with T-lang. There is a pending RFC on a topic like this, how should we play that one out? jakob: my thinking was that an example like this is easily placed in T-lang's camp. There are some variations where T-opsem may have a role, but not this specific one. niko: There are other examples, like niches in unions, that have been difficult jakob: fair. there may remain questions. figuring out what sets of layouts is legal for a given type is a T-opsem concern. Figuring out what subset of those layouts is then guaranteed for a given type is the T-lang concern. Heuristic: * OK for compiler to do the optimization? T-opsem. * OK for users to *rely* on it doing the optimization? T-lang. ## Does anyone "own" MIR? tmandry: MIR seems to be an important way of encoding and talking about opsem, but it isn't really a part of the language. I think of it as more of a compiler implementation detail. Is that right? Does anyone "own" it or is it more of a shared space between T-compiler, T-opsem, and maybe T-types? pnkfelix: we're in the process of trying to create a stable version of MIR (https://github.com/rust-lang/project-stable-mir), where compiler has its own independent version of MIR that can be converted to that. Don't know how T-opsem will want to interact. For today it will be compiler internal MIR. nikomatsakis: I think stable mir should (eventually) be the interface to miri. joshtriplett: I think stable mir is meant to be a baseline, but miri would want the latest and greatest that rustc supports? jakob: t-opsem will certainly want some IR that it uses to define things, doesn't seem to me that it fundamentally has to be the same as compiler's mir or stable mir. Both SMIR and Rustc's MIR need to remain some level of compatible with things T-opsem understands. nikomatsakis: I am hoping we will reach a common (formal) definition of MIR, perhaps owned by T-opsem, but lang/types have to define mapping from Rust surface syntax to MIR. pnkfelix: Not all of it feels like type, e.g., there are weird cases around match desugaring where it becomes significant whether compiler defines L-to-R evaluation semantics or not. Can be critically important for certain kinds of matches. That's not a types thing, is it? nikomatsakis: That feels like lang. Jakob: There are some specific aspects of lowering where T-opsem needs to be involved, and match lowering is one of them. There are some fairly complex questions around it and what happens when you have unsafe-cell and match guards. A couple other examples, e.g., where retags are added. Likely to be fairly isolated examples. pnkfelix: You do think T-opsem -- not just about output from lowering -- also about lowering process itself. You want T-opsem to have influence on this? Jakob: yes nikomatsakis: Another related example is placement of storage dead. We could conceivably say that when you move from a path (or other conditions), we lower that to have a StorageDead in the MIR to allow for better re-use. This is a lang call but I would want opsem to be involved because it's subtle. ## Documentation requirements tmandry: Should there be/are there any requirements to document opsem changes to UCG or the reference? jakob: yes. needs to be some requirements somewhere. One of the big jobs that opsem has (in some reasonable time frame) is figuring out what that looks like. nikomatsakis: my biggest question feels like where/how will we document what we decide. ## nikomatsakis: lower vs upper bounds When we talk about opsem rules, there is often a struggle between defining rules that are overly permissive. For example, we currently treat references to generators as raw pointers without any alias optimization -- does this mean that people can write unsafe code that relies on that? I'd rather they didn't, but it's hard to say precisely what is legal then. jakob: We already have instances of this, in terms of MIR optimizations that do stuff with noalias. jakob: We're going to need to be intentional about how we approach this. It blends into the Documentation Requirements noted above. ## Josh: opsem interface to lang, on a technical level Should we modify rfcbot and similar to treat T-opsem (and T-types) the same as lang-advisors? nikomatsakis (and others): yes. pnkfelix: meaning? nikomatsakis: able to raise concerns.