Tracking issue for allowing overlapping implementations for marker trait
Tracking issue for rust-lang/rfcs#1268.
Status
- [x] Initial implementation: #41309
- [ ] Documentation
- [ ] Move to stabilize
- [ ] Stabilization PR
Known bugs
History
- initial implementation #41309
- https://github.com/rust-lang/rust/pull/53693
Prior to stabilization
- Is it ok that adding items to a previously empty trait is a breaking change? Should we make declaring something a marker trait more explicit somehow? --> resolved by adding explicit
#[marker]annotation, see https://github.com/rust-lang/rust/pull/53693
Tracking issue for rust-lang/rfcs#1268.
Status
- [x] Initial implementation: #41309
- [ ] Documentation
- [ ] Move to stabilize
- [ ] Stabilization PR
Known bugs
- [ ] cross-crate support lacking?
- [ ] #102360
History
- initial implementation #41309
- https://github.com/rust-lang/rust/pull/53693
Prior to stabilization
- Is it ok that adding items to a previously empty trait is a breaking change? Should we make declaring something a marker trait more explicit somehow? --> resolved by adding explicit
#[marker]annotation, see https://github.com/rust-lang/rust/pull/53693
Other notes
In #96766 we decided NOT to disable the orphan check for marker traits as part of this work (just the overlap check), which was a proposed extension.
Niko Matsakis at 2022-05-10 17:38:18
With respect to the second question of whether we should make a more explicit notion of marker trait, I have been pondering this since the RFC discussion. On the one hand, there are a number of places in Rust today where going from nothing to something is a breaking change: adding a private field to a struct, for example.
The difference here I think is that:
- Given the ability to specify defaults, adding an item to a trait does not necessarily have to be a breaking change otherwise (though methods and fns can cause new ambiguities etc).
- If there are impls that are taking advantage of the ability to overlap, there may just be no way to port older code forward, because it would require overlap.
However, the second point depends a lot on the fate of specialization. If we wind up adopting specialization, and in particular a model that does not require strict subsets, then it would still be possible to adapt existing impls. This makes me a lot less worried. Of course, implementing that form of specialization is tricky, but not (I don't think...) that much trickier than implementing THIS suggestion. The hard part in both cases is the potential for overlap.
Niko Matsakis at 2015-11-18 19:55:21
@nrc This has the RFC implemented label, but I don't think it has been. If it has, is there a feature gate that I'm missing? If it hasn't been implemented, is there anything I can do to help push this along? It's now been a year since the RFC was accepted.
Sage Griffin at 2016-11-15 18:09:04
@sgrif swapped labels
Niko Matsakis at 2016-11-15 19:16:26
Initial implementation just merged in https://github.com/rust-lang/rust/pull/41309.
Corey Farwell at 2017-04-15 04:46:41
Looks like this doesn't work when the overlapping impls are provided by two separate crates, even if both crates have
#![feature(overlapping_marker_traits)]enabled (ideally only one of the two crates would need the feature)Sage Griffin at 2017-04-20 20:53:08
@sgrif interesting, what is the example you have in mind?
Niko Matsakis at 2017-04-21 16:41:33
One drawback mentioned in the RFC is that this feature makes it a breaking change to add default methods to a trait. Forgive me if this issue was already discussed and resolved, but I didn't see it mentioned in the RFC discussion. Prior to hearing this RFC mentioned, I never thought of "marker traits" (i.e. traits without any items) as anything special or distinct from regular traits, and I would not have expected it to be a breaking change to add default methods to them.
Personally, I'd prefer if the user had to opt-in to making a trait a "marker" trait so that they didn't unwittingly cause breakage.
Taylor Cramer at 2017-11-06 18:43:30
One alternative I don't see in the RFC or its discussion thread is doing this only to auto traits, not to all "marker traits". Unless I missed something, all of the motivating examples (including hypotheticals) appear to be auto traits, and afaik you can't add a method to an auto trait anyway so that breaking change issue would just evaporate.
Ixrec at 2017-11-08 00:11:38
Yeah, I'd be totally fine with just doing this on auto traits (whose definition is already unstable).
Taylor Cramer at 2017-11-08 01:37:30
You could explicitly seperate marker and "normal" traits by having marker traits have a different syntax, my suggestion would be:
trait MyMarker;Similar to ZSTs. This makes it clearer when you're turning a marker trait into a non-marker trait.
Eira Fransham at 2018-01-11 15:07:36
@cramertj I would be in favor, I think, of a
#[marker]attribute to signifify this behavior. There is always this line about whether attributes ought to be "semantically meaningful", but I kind of think it's ok.Niko Matsakis at 2018-01-11 22:15:19
I also don't believe this is particularly tied to auto traits.
Niko Matsakis at 2018-01-11 22:15:31
@nikomatsakis There's also precedence for semantically meaningful attributes in derives and in
#[nonexhaustive](or whatever it's called now).Taylor Cramer at 2018-01-11 22:23:43
I needed this feature for a single trait in a personal project, and turning it on means overlapping is allowed for all of my marker traits. I'd personally like a warning issued for overlapping impls which can then be
allow'ed ordeny'ed. This would be similar to haskell's per instance{-# OVERLAPPING #-}.Also, I assume this is a bug in the current implementation, but order seems to matter:
#![feature(overlapping_marker_traits)] pub trait F {} impl<T> F for T where T: Copy {} impl<T> F for T where T: 'static {}Gives the error:
error[E0310]: the parameter type `T` may not live long enough --> src/lib.rs:5:9 | 5 | impl<T> F for T where T: Copy {} | - ^ | | | help: consider adding an explicit lifetime bound `T: 'static`... | note: ...so that the type `T` will meet its required lifetime bounds --> src/lib.rs:5:9 | 5 | impl<T> F for T where T: Copy {}While this (same code, different order) compiles and works as expected:
pub trait F {} impl<T> F for T where T: 'static {} impl<T> F for T where T: Copy {}mtak- at 2018-02-21 00:01:39
Also the error messages when using the above trait aren't great. https://play.rust-lang.org/?gist=707605bff9858839c0d870a5c380ab2e&version=nightly
The errors always indicate that the type needs a
'staticlifetime, when it could also beCopy.This appears to be a problem solely with lifetimes (which is my use case). If
'staticis changed toDebugin the above, a less confusing error is given.mtak- at 2018-02-21 03:34:21
That particular example will be complex to make work as desired in any case, due to how region handling is done in rustc at present (e.g., https://github.com/rust-lang/rust/issues/21974). In general, integrating "or" constraints and region solving is a bit tricky I suppose. I imagine we can do it by making the region checker smarter, which we have to do eventually.
Niko Matsakis at 2018-02-21 19:08:47
No matter what the actual syntax is (either an annotation or
;-vs-{}) there definitely has to be a difference between possibly-overlapping marker traits and nonoverlapping-but-empty-by-circumstance traits otherwise we'll get a host of forward-compatibility-ensuringconst __UNUSED: () = ();items like we already do to prevent constructing empty-by-circumstance structs.Eira Fransham at 2018-02-26 10:55:19
A thought in favour of
#[marker]overtrait Foo;: one could allow defaulted associated items on markertraits, and just prohibit any of theimpls from overriding them.(Inspired by a discussion with the bunny about having something like
#[marker] unsafe trait Zeroable { fn zeroed() -> Self { unsafe { std::mem::zeroed() } } }in winapi.)Edit: Also, using an attribute fits the generally-suggested "do it with a macro or attribute first, then add syntax once we have more experience" procedure.
scottmcm at 2018-02-26 12:01:46
I actually quite like that idea, since it's not an uncommon pattern to have convenience functions in traits and often these will only ever be implemented in the defining crate and it might be a useful tradeoff to allow overlapping implementations at the cost of disallowing consuming crates from overriding the function body. If that was to be allowed, though, I'd prefer the name
#[overlapping]instead of#[marker].Eira Fransham at 2018-02-26 12:10:11
It seems obvious that we should make a
#[marker]annotation.Niko Matsakis at 2018-02-27 17:30:24
I was just thinking about a way to prevent users from implementing the
Unpintrait for types defined by a local macro invocation, and one idea I had was to always generate anUnpinimplementation that was conditional so as to prevent the user from creating their own unconditional impl. However, this would be made unsound if this feature ever stabilized. I don't know that my idea was a good one, or that it's a pattern worth encouraging, but I thought it was interesting enough to share.Taylor Cramer at 2018-07-31 23:18:43
@cramertj Agreed; the discussion seems to have decided that this needs to be opt-in on the trait, not something that just happens. I've started a PR towards doing so: https://github.com/rust-lang/rust/pull/53693
scottmcm at 2018-08-26 22:19:39
cc https://github.com/rust-lang/rust/issues/60784#issuecomment-491901601
Mazdak Farrokhzad at 2019-05-13 17:56:04
cc https://github.com/rust-lang/rust/issues/61651
Mazdak Farrokhzad at 2019-06-08 16:15:34
So is the current status that we have both
#[feature(overlapping_marker_traits)]which applies to any trait with no items, and#[marker]?Based on https://github.com/rust-lang/rust/issues/29864#issuecomment-409398581, it seems like consensus is that
overlapping_marker_traitsshouldn't happen without opt-in from the trait, and indeed with https://github.com/taiki-e/pin-project/pull/18 we now have a concrete example for code that would be unsound if overlapping impls were allowed forUnpin. Is that correct? Does that mean we could remove the code that can permit any item-less trait to have overlapping impls?Ralf Jung at 2019-07-28 17:35:24
The current
#[feature(overlapping_marker_traits)]certainly seems to be able to break the guarantee that pin-project provides: playgroundcc https://github.com/taiki-e/pin-project/issues/105
Taiki Endo at 2019-09-24 10:34:30
I think reverting to having the trait opt-in is better. This also means that you don't have to break backwards compatibility to add items to a trait.
Eira Fransham at 2019-09-26 04:30:04
@Vurich note that the opt-in is the new direction (FCP), so there isn't any reverting needed.
So I think there's a PR opportunity here of removing
overlapping_marker_traits(the new one ismarker_trait_attr), if anyone's interested in making one.scottmcm at 2019-09-26 06:07:52
I'd like to see this stabilized. The next check box on the issue is documentation, which I'm happy to contribute if it moves this feature forward. Could someone point me to where we'd want to document this feature?
Others at 2020-06-04 20:43:31
@Others I think moving forward here would be reasonable. I think a first step would be to do a bit of "archival research", documenting the history and current design (which is now using e.g. a
#[marker]attribute) and looking for any unresolved questions. We've not done a very good job of tracking that on this issue, it seems, though I just made a few edits to the main header.One thing I just remembered is that https://github.com/rust-lang/rust/pull/68004 we were discussing some interaction between marker traits and negative impls (see this comment for more details). In particular there was a sort of unresolved question (I can't find the issue just now) that was asking whether marker traits should be exempt from the orphan rules (i.e., I should be able to implement a marker trait for any type, not only my own types).
I think the answer to this is probably no -- or at least I would not feel comfortable stabilizing those orphan rule extensions until we talk more about negative reasoning.
Niko Matsakis at 2020-06-08 15:02:29
I've relied on
#[marker]extensively for prototyping new features in typic. The instability of#[marker]is a major barrier to adopting typic in zerocopy. I cannot overstate how much I'd like to see this stabilized. I'm happy to chat about the use cases I've encountered!Jack Wrenn at 2020-06-17 18:15:38
In particular there was a sort of unresolved question (I can't find the issue just now) that was asking whether marker traits should be exempt from the orphan rules (i.e., I should be able to implement a marker trait for any type, not only my own types).
I think the answer to this is probably no -- or at least I would not feel comfortable stabilizing those orphan rule extensions until we talk more about negative reasoning.
I agree - I think any kind of 'orphan rule exemption' for
#[marker]should require an explicit opt-in.Imagine you have a 'safe marker trait', like
std::cmp::Eq. If you're willing to commit to having it always have no associated items, it might make sense to make it#[marker]. However, disabling the orphan rules would allow downstream crates to break a (safe) API contract. For example (in a world wherestd::cmp::eqis#[marker]):// crate1 struct NotReflexive; impl std::cmp::PartialEq for NotReflexive { fn eq(&self, _other_: &Self) -> bool { false } } // crate 2 impl std::cmp::Eq for crate1::NotReflexive {} // uh ohHere, we've violated the (safe) API contract for
std::cmp::Eq, and there's nothing thatcrate1can do to prevent this. While this can't lead to unsoundness, I think it's a good idea to be able to prevent this kind of code from compiling (via the orphan rule).Aaron Hill at 2020-06-18 05:16:20
With
Unpin, we even have a safe marker trait where this would lead to unsoundness.Ralf Jung at 2020-06-18 07:18:20
@jswrenn would you be interested in working with me to stabilize the marker trait annotation?
Niko Matsakis at 2020-06-18 15:53:18
@nikomatsakis Currently on my honeymoon, but I'd be thrilled to help after July 1!
Jack Wrenn at 2020-06-26 19:56:56
@nikomatsakis @jswrenn I'd be interested in helping with this. How do we get started?
Ryan Levick at 2020-07-16 11:08:12
Is it possible for marker-trait implementations to ignore the
unconstrained type parametererror? Marker-trait impl can't use type parameters in any way, so I would expect this to compile:#![feature(marker_trait_attr)] #[marker] trait Fun {} impl<F, T, R> Fun for T where F: FnOnce(T) -> R { // In difference with common traits you can't use T or R here // so this can't bring ambiguity "which type param to use for a concrete type" }[playground] However, today it gives an error:
error[E0207]: the type parameter `F` is not constrained by the impl trait, self type, or predicates --> src/lib.rs:4:6 | 4 | impl<F, T, R> Fun for T where F: FnOnce(T) -> R { | ^ unconstrained type parameter error[E0207]: the type parameter `R` is not constrained by the impl trait, self type, or predicates --> src/lib.rs:4:12 | 4 | impl<F, T, R> Fun for T where F: FnOnce(T) -> R { | ^ unconstrained type parameterwaffle at 2020-07-17 07:32:12
@WaffleLapkin I would not want to change something that fundamental without more thought, although I can't give an immediate reason why it is strictly necessary. Without that rule, though, you would have fundamentally "unconstrained" types that result from type check and which we cannot recover -- as things are, we have enough information to fully recover all the information we need for any trait match.
Niko Matsakis at 2020-07-17 20:28:41
@rylev basically we just need to create a stabilization PR with a write-up that includes
- quick summary of the feature
- any changes since the RFC
- some tests that highlight key aspects of the feature
There is some documentation here about writing a report and also preparing a PR.
Niko Matsakis at 2020-07-17 20:30:13
I've tried to collect the known open questions and issues here.
Questions
- Bike-shedding the name. Does this require an RFC? @nikomatsakis thoughts on this?
- Is there any unsoundness around Unpin now that you are required to explicitly opt into marker traits with
#[marker]? I haven't able to completely follow this topic yet, but it seems that @RalfJung was able to convince himself that there is no unsoundness although his latest comment does not indicate as such. I believe however that this concern along with others are resolved by the fact that the mechanism is now opt-in. - How does this interact with negative trait impls? The discussion of this seemed to have good ideas on how to handle this, but was consensus reached? This is tied to how
#[marker]traits interact with the orphan rule. Currently#[marker]traits fully respect the orphan rule.
Bugs
- Does the implementation work cross crate? I'm not fully sure what this means. This might be a left over from before
#[marker]explicit opt-in was introduced. - The order of impl declarations matter.
I think most of these should be answered before we move forward with stabilization.
Ryan Levick at 2020-08-19 13:11:24
I believe however that this concern along with others are resolved by the fact that the mechanism is now opt-in.
Yes, opt-in is key. Likely none of the existing auto traits can be made
#[marker], but certainly notUnpin.Ralf Jung at 2020-08-19 16:47:07
@nikomatsakis do you have thoughts on whether an RFC is required for stabilizing the name
#[marker]? @Centril brought up good concerns on negative trait impls, which was never fully resolved. How can we continue the discussion? Are Niko's ideas what we want to stabilize?Ryan Levick at 2020-09-01 14:26:48
@scottmcm did you have a specific reason to nominate this? :)
Niko Matsakis at 2021-11-16 18:43:31
cc @rust-lang/wg-traits -- I seem to recall some concerns about the implementation potentially being unsound? Does anyone remember what I'm talking about?
Niko Matsakis at 2021-11-23 18:43:50
We discussed this in our @rust-lang/lang meeting today. One thing we were wondering about are the active use cases and stakeholders -- seems like @rust-lang/wg-safe-transmute was one potential consumer, did the current design meet the needs there?
Niko Matsakis at 2021-11-23 18:44:27
Likely none of the existing auto traits can be made #[marker], but certainly not Unpin.
Can you elaborate on this one, @RalfJung? I would have guessed that
Copycould be#[marker], for example. (Albeit I'm not sure how useful that one would be, given the extra magic requirements that exist onCopy.)scottmcm at 2021-11-23 18:49:55
For
Unpinwe have a concrete example where making it#[marker]would introduce soundness issues in a crate (https://github.com/rust-lang/rust/issues/29864#issuecomment-409398581, https://github.com/taiki-e/pin-project/pull/18).I assume the same could happen, in principle, for any of the other existing marker traits, though I am not aware of concrete examples.
Ralf Jung at 2021-11-23 19:19:19
cc @rust-lang/wg-traits -- I seem to recall some concerns about the implementation potentially being unsound? Does anyone remember what I'm talking about?
I think the issue was #88139. The implementation now results in weird ambiguities, but at least it isn't unsound anymore afaik
For
Unpinwe have a concrete example where making it#[marker]would introduce soundness issues in a crate (#29864 (comment), taiki-e/pin-project#18).Afaict the issue here is about impls for marker traits violating the orphan rules, which is generally unsound to my knowledge and not permitted by the current implementation. It might actually be possible to change all auto traits to marker traits again, only requiring opt-in for non-auto traits, as for those, adding an item to the trait definition would otherwise be a breaking change.
edit: nm, the idea is for a macro to add
impl<T: NotImplemented> Unpin for NotUnpin<T>to prevent the type from implementingUnpin. This would not work anymore ifUnpinwas a marker trait. While I don't necessarily think that this pattern is the best solution and am hoping for the stabilization of negative impls for this instead, it is definitely a reason to keep marker traits as opt in.lcnr at 2021-11-23 19:23:45
Afaict the issue here is about impls for marker traits violating the orphan rules, which is generally unsound to my knowledge and not permitted by the current implementation.
The entire point of the feature in this tracking issue is to allow violating the orphan rules by permitting overlapping implementations -- or am I misunderstanding?
Ralf Jung at 2021-11-23 19:48:33
it allows overlapping impls in the same crate, allowing it across crates is unsound without restricting the way marker traits can be used.
consider a crate like this:
#![feature(marker_trait_attr)] #[marker] pub trait Marker {} pub struct B; trait NotMarker { type Assoc; } impl NotMarker for B { type Assoc = usize; } impl<T: Marker> NotMarker for T { type Assoc = Box<String>; }if a different crate were to add an impl leading to
B: Marker, we have an ambiguous associated typelcnr at 2021-11-23 20:30:54
Oh, because rustc actually uses negative reasoning inside a crate, so not all 'monotone' extensions of the impl set are actually allowed... that's nasty.
Ralf Jung at 2021-11-24 01:45:06
I have an action item to leave a comment here but I can't find the notes. In any case, my vague recollection is that:
Despite marker traits permitting one to stretch the coherence rules, they are specifically targeting the overlap rules and not the orphan rules (i.e., they don't let you write impls in crates that you wouldn't have been permitted to write an impl in). If we ensure that marker traits follow the same orphan rules as everyone else, I don't think they pose any particular problem for the negative reasoning that we do elsewhere.
There was also a (related) interaction between marker traits and negative impls that was discussed here. In short, the problem was that if we lifted the orphan rules for marker traits, then one could add an explicit
!Markerimpl in a crate and anMarkerimpl in another crate. As I noted then, the way I think about it is that, after a trait is published, only one crate ever has the right to add any particular impl (positive or negative). Specifically, if an impl applies to a type X, only the crate in which X is declared has the right to add that impl. This fits with the fact that adding blanket impls (which could apply to such types) is a semver-breaking change after a trait is released. This same idea applies to negative impls too: you can only add a negative impl if you would've been the one to add the positive impl.The case that @RalfJung is talking about seems to be the same, but the "negative reasoning" in question is implicit due to the fact that everything is within the same crate.
Niko Matsakis at 2022-02-01 20:23:39
One use for this which I'm particularly excited about but haven't seen explicitly mentioned is that it allows things like this:
#[marker] trait TupleHas<T> {} impl<T1> TupleHas<T1> for (T1, ) {} impl<T1, T2> TupleHas<T1> for (T1, T2) {} impl<T1, T2> TupleHas<T2> for (T1, T2) {} impl<T1, T2, T3> TupleHas<T1> for (T1, T2, T3) {} impl<T1, T2, T3> TupleHas<T2> for (T1, T2, T3) {} impl<T1, T2, T3> TupleHas<T3> for (T1, T2, T3) {} // ...A use case for this would be Bevy's queries, which look like this.
fn score_system(mut query: Query<(&Player, &mut Score)>) { // Type safe access to components for (player, mut score) in query.iter_mut() { // Some score updating logic here } // However, we can also do this: let player: &Player = query.get_component(some_specific_entity); // We're only allowed to call get_component with one of the types in the Query. // (Player or Score in this case). // Currently, this is only enforced with a runtime error. // A QueryHasReadAccess<T> marker trait could allow this to be a type error instead. }I'd love to see this stabilized!
Sam at 2022-05-18 16:28:29
@SamPruden I'd also like to see this stabilize.
It looks like there's at least one open issue that probably blocks it, though: https://github.com/rust-lang/rust/labels/F-marker_trait_attr
scottmcm at 2022-05-22 00:36:14
I'm currently writing a linear algebra library that would really benefit from this feature as well, so I'd love to see this move forward.
My usecases, in case they're insightful:
The first is
VectorLike, a trait implemented when one dimension of a matrix is statically known to be 1. This allows interpreting the matrix like a (row or column) vector and allows using some methods and operators where it would otherwise be ambiguous whether they operate in column-major or row-major order (.iter(), indexing with a scalar, etc.).#[marker] trait VectorLike {} impl<D> VectorLike for (Const<1>, D) {} impl<D> VectorLike for (D, Const<1>) {}The second is
DimEq, a trait implemented when two matrix dimensions are potentially equal at runtime. This is useful in the presence of runtime-dynamic matrix dimensions, indicated by theDyntype – those require a runtime check in addition to theDimEqbound.#[marker] trait DimEq {} impl<const D: usize> DimEq for (Const<D>, Const<D>) {} // guaranteed to be equal impl<const D: usize> DimEq for (Dyn, Const<D>) {} // *can* be equal (ensured by runtime assertion) impl DimEq for (Dyn, Dyn) {} // *can* be equal (ensured by runtime assertion) impl<T, U> DimEq for (T, U) where (U, T): DimEq {} // order shouldn't matter when using this // (important in generic code calling other generic code, otherwise they have to require both or agree on order) // Notably, this isn't implemented for `(Const<A>, Const<B>)` when A and B are different. // catches mistakes at compiletime instead of runtime when possible!Sludge at 2024-01-30 04:42:44
@SludgePhD I wonder if your logic for
impl<T, U> DimEq for (T, U) where (U, T): DimEq {}is right. Notably, it overflows the current typechecker, and the next trait solver intends to resolves cycles with a "yes" answer by separating
is-implandhas-impl. It would work as if you've additionaly writtenimpl<T> DimEq for (T, T) where (T, T): DimEq {} // has impl, so is impl; in other words, always holds.Kolsky at 2024-05-04 13:49:03
I'm nominating this to @rust-lang/lang and @rust-lang/types ---
I'd like to get a temperature check on this feature. Quick summary is that it lets you put
#[marker]on a trait and then permit overlap.From lang, I'm curious as to whether folks still think this is desirable. (I do, though I'm potentially open to bike-shedding on the details -- but this precise scenario comes up all the time.)
From types, I'm curious as to whether folks are aware of impl problems, how this fits with the new solver, etc. Seems like a great candidate to target with a-mir-formality and spec work for testing out a new process.
@rustbot labels +I-lang-nominated +I-types-nominated
Niko Matsakis at 2024-12-26 14:42:30
I think it goes particularly well with https://github.com/rust-lang/rfcs/pull/3678, since then
#[marker]traits could have methods so long as they'refinal fn.That would allow, for example,
#[marker] trait Copy : Clone { #[final] fn copy(&self) -> Self { *self } }scottmcm at 2025-01-08 16:49:31
@rustbot labels -I-lang-nominated
We talked about this during our lang day in December, and we touched on it briefly again today. We're feeling favorably toward allowing overlap on marker traits that are explicitly designated, and are looking forward to seeing concrete next steps on this nominated for us.
Travis Cross at 2025-01-08 17:23:09
This was discussed a bit in the T-types/nominated zulip stream. A proper implementation of this feature needs OR region constraints and is blocked on -Znext-solver everywhere
Boxy at 2025-01-08 17:25:55