Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjust default object bounds [breaking change] #1156

Merged
merged 3 commits into from
Jun 19, 2015

Conversation

nikomatsakis
Copy link
Contributor

Adjust the object default bound algorithm for cases like &'x Box<Trait> and &'x Arc<Trait>. The existing algorithm would default to &'x Box<Trait+'x>. The proposed change is to default to &'x Box<Trait+'static>. A new attribute, #[legacy(default_object_bounds)], can be used to restore the current behavior to ease the transition.

Note: This is a BREAKING CHANGE. The change has been implemented and its impact has been evaluated. It was found to cause no root regressions on crates.io.

This change was initially discussed as a pre-RFC. In particular there is much discussion there about the strategy towards breaking changes. Based on the reasoning in my last comment, I'm advancing this to "full RFC" status. Note in particular that if we're going to make this change, we have to do it quickly. :)

UPDATE: The RFC has been adjusted to include an opt-out that restores the older behavior. The summary here was also updated appropriately.

Rendered view.

@nikomatsakis nikomatsakis added the T-lang Relevant to the language team, which will review and decide on the RFC. label Jun 9, 2015
@comex
Copy link

comex commented Jun 9, 2015

I won't repeat myself, but 👎. Rust 1.0 should be considered the end of the era of breaking changes and going around fixing things to work in the latest compiler, period.

@tshepang
Copy link
Member

tshepang commented Jun 9, 2015

@comex would you prefer a 2.0.0 this early?

@comex
Copy link

comex commented Jun 9, 2015

I'd prefer that either (a) the change not be made at all, or (b) a version number attribute be used. In regard to the latter, I recognize the argument from the comment linked in the PR summary about Rust 1.16 vs. 1.17, but when changes to standard libraries are considered in addition to the language (especially adding new public methods to types and traits, which is always potentially breaking), I believe the advantage of being able to relatively easily extend the compiler to allow true backwards compatibility (e.g. by hiding those methods, which I've elaborated on elsewhere) outweighs the drawback of version number confusion.

@pythonesque
Copy link
Contributor

I would prefer a version number be used as well, in general.

As far as this change goes: I think it's early enough that it is okay (under the theory that backwards incompatibility only matters if it really breaks something), but I'm also not someone using Rust in production (which the crates.io lint isn't going to catch).

@tbu-
Copy link
Contributor

tbu- commented Jun 11, 2015

I doubt that something like this backward-incompatible change would even be considered if it wasn't suggested by someone from core team. I would prefer not making such non-safety related backward-incompatible changes until 2.0. Something like Python's from __future__ import object_bounds would work though.

@comex
Copy link

comex commented Jun 11, 2015

(Should this and the other RFCs mentioned in the subteam report be tagged final-comment-period?)

@DanielKeep
Copy link

In the last week or so, I've seen two instances of people being tripped up by this. There's this StackOverflow question, and this exchange on #rust.

I'm in 100% agreement that the current rules are unintuitive and baffling, the error message confusing, and the solution completely non-obvious, nor particularly discoverable. It turns out that the current rules are rubbish. :P

Given that, and the lengths that have been gone to in order to locate code this breaks... I think this is an acceptable break under these specific circumstances.

@comex
Copy link

comex commented Jun 11, 2015

I agree the current rule is unintuitive, but a partial alternative solution to your complaint would be improving the error messages for such cases...

@DanielKeep
Copy link

@comex On the other hand, I saw a question today from a user where the solution was explicitly spelled out in the error message. Improving the message would help, but it doesn't hold a candle to changing the rules so the problem itself happens less often.

If we take it as read that opt-in to breaking changes isn't going to happen (which appears to be the way the wind is blowing at the moment), then I'd rather see this particular change happen now than not. It sucks, as is almost certainly going to give Rust a bit of a black eye for a while... but it's that or being stuck with it. I certainly don't know which will have been the correct choice in retrospect, which is why I don't envy the core team on this. :P

@gkoz
Copy link

gkoz commented Jun 11, 2015

👍
Been bitten by this. Had to make a similar breaking change in a library (add 'static bounds) to make it actually match my intentions.

@llogiq
Copy link
Contributor

llogiq commented Jun 11, 2015

@DanielKeep @nikomatsakis I fully agree that this change should be made as soon as possible.

In my somewhat related discussion of breaking API changes, I not only proposed to allow breaking changes for fixing security holes, but also for footguns, if the resulting breakage from the change is less than the breakage resulting from the mis-feature. Following DanielKeep's observations, that seems to be the case here.

Edit: I've written a more thorough treatment of the ideas outlined above at internals

@gnzlbg
Copy link
Contributor

gnzlbg commented Jun 11, 2015

+1

Unfortunately, the error messages produced as a result of this change are not especially clear. This is because they are produced through lifetime inference, which is one of the trickier parts of the compiler when it comes to error messages. The author (nmatsakis) plans to investigate possible changes to the compiler to improve the situation.

Is it possible to provide either

  • a diagnostic that alerts the user when code breaks due to this change, and informs them how to fix it

or

  • a way to automatically port old code

during at least one full release cycle, i.e., from 1.1. to 1.2?

I am fine with minor breaking changes that can be automatically fixed or at least offer a good diagnostic between point releases. Major breaking changes that cannot be automatically identified (or anything worse) should wait till 2.0. This is bending the rules of semantic versioning a bit, but I would consider this a good compromise.

@ArtemGr
Copy link

ArtemGr commented Jun 11, 2015

It was found to cause no root regressions on crates.io.

👍

I agree with https://github.com/nikomatsakis/rfcs/blob/language-semver/text/0000-language-semver.md and with llogiq, the impact is minimal and the change will not disrupt the user experience, it will improve it.

@CloudiDust
Copy link
Contributor

I agree with @tbu- that something like python's "future imports" is a better solution, like:

#![future_feature(adjusted_default_object_bounds)]

Along with a warning if this attribute is missing:

"The current rule for default object bounds is deprecated, please opt-in the new rule by adding ..."

@glaebhoerl
Copy link
Contributor

While I agree that we should go ahead and make the change, I think we should also go the extra mile to make sure that any code that might be affected can keep working, be automatically fixed, or something of the sort. (The version-based opt-in, for example.) Importantly, I think we should do so even if there's no indication from crates.io that it's strictly necessary, as a gesture that we take the backwards compatibility issue seriously.

@llogiq
Copy link
Contributor

llogiq commented Jun 11, 2015

Would it be possible to try and infer some superposition of 'a and 'static and decide later based on whether 'a results in an error?

@nikomatsakis
Copy link
Contributor Author

Hear ye, hear ye. This RFC was moved into Final Comment Period as of Wed, June 10th. (Sorry, forgot to add this notice!)

@nikomatsakis nikomatsakis added the final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. label Jun 11, 2015
@nikomatsakis
Copy link
Contributor Author

@aturon just suggested to me that we should add an opt out marker to restore the older defaults. This marker could hang around indefinitely. This means that even if your code breaks, all you have to do is add this marker to the top of your crate to get back the old defaults for now (and you can remove it at your leisure). This would be very simple to maintain in the compiler. Seems like a good idea.

My main concern about the opt-out marker, though, is that I would want to use an attribute name that we can generalize in the future, and I'm not sure what that should be. I propose #[legacy(...)], where the content is a series of names for legacy rules to be used. In this particular case:

#[legacy(default_object_lifetime_bounds)].

Misuse of this attribute would not be an error, but rather a lint, to allow for future expansion.

@nikomatsakis
Copy link
Contributor Author

Regarding diagnostics, I'd like to investigate how to improve them to be more targeted (and perhaps suggest the #[legacy] attribute).

@nikomatsakis
Copy link
Contributor Author

@glaebhoerl

While I agree that we should go ahead and make the change, I think we should also go the extra mile to make sure that any code that might be affected can keep working, be automatically fixed, or something of the sort.

A #[legacy] annotation seems close to this. I don't think it's possible to "automatically fix" or accept older code, except perhaps via a version annotation. I'm not a big fan of version annotations -- even though I initially proposed one! -- for the reasons I outlined in the discuss thread.

@arthurprs
Copy link

+1 let's not shoot ourselves in the foot by allowing the problem to persist. It's already known that the impact is minimal.

@llogiq
Copy link
Contributor

llogiq commented Jun 11, 2015

I don't think it's possible to "automatically fix"

Why is that?

Let's say you have inference A and B. Let A result in the old, B in the new behaviour. Do the lifetime analysis with B. If an error occurs, back up, retry with A. If an error still occurs, show it, otherwise issue a warning that the code relies on outdated behaviour that may go away in the next version and go on. This may be a naive and slow implementation, but I wouldn't call it impossible.

@nikomatsakis
Copy link
Contributor Author

@llogiq

Let's say you have inference A and B. Let A result in the old, B in the new behaviour. Do the lifetime analysis with B.

I spoke too strongly. I don't think it's possible to fix this in a local, simple fashion. I agree that if we exhaustively searched throughout the entire crate and tweaked defaults, we could do it, but keep in mind that if the type appears in a fn boundary, then we have to go check the callers to decide which usage they expected (and of course callers can disagree). So I should say it's not feasible to accept crates and deduce what default they were expecting. In any case, I also consider that undesirable -- I want simple rules, not some curious hodgepodge. That is, if we tried to accept the older defaults, we'd in fact be creating a scheme with very complex inference behavior that would be very challenging to support. I'd much rather just stick with the current defaults if that were the alternative. :)

@nikomatsakis
Copy link
Contributor Author

I added some text about the #[legacy] attribute.

@Hoverbear
Copy link

I'm worried this could set a dangerous precedent, especially for something that seems to be more a quality of life change than anything else.


This is a breaking change, and hence it behooves us to evaluate the
impact and describe a procedure for making the change as painless as
possible. One nice propery of this change is that it only affects

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "propery" --> "property"

@elszben
Copy link

elszben commented Jun 11, 2015

I only have one comment about the #[legacy(default_object_bounds)] attribute. What will be the name of the next tweak of default object bounds? Can this name be changed to something more descriptive? In my experience naming something legacy is not too robust, it only works if that stuff has only two versions. I don't know enough about this issue and I admit that I did not read all these comments:) Ignore this if my argument is not applicable:)

@gnzlbg
Copy link
Contributor

gnzlbg commented Jun 17, 2015

@nikomatsakis

Ideally, this #[legacy] attribute will only persist for a fixed number of cycles.

I feel you and I wish that we will never need a Rust 2.0. I wish that the solution would allow for a future Rust 1.15 crate to seamlessly depend on a Rust 1.0 crate.

@llogiq

I wholeheartedly agree with your guidelines, except I still oppose the use of legacy flags, for the reasons I explained before – we cannot expect people to be able to change other people's code to add them.

What if one could also pass them to rustc as a parameter?

@llogiq
Copy link
Contributor

llogiq commented Jun 17, 2015

@gnzlbg

What if one could also pass them to rustc as a parameter?

This would get very messy, fast, once you have multiple dependencies that may need different legacy flag combinations.

It would make deployment of any rust-based solution much more complicated than necessary, because _dammit, the information about what language version the code uses belongs in the code_! (Sorry, did I become loud? breathe deeply...now I'm better. Ok,)

I've been arguing for target-version based API / language evolution for some time now. So much that I fear it's getting stale. People still stick to the idea of opt-in or opt-out, as if it was an easy or even workable solution (pretty pretty please read my RFC PR #1147 for an explanation why it's not).

@nikomatsakis

I've already answered your comment, but I feel I need to stress another point:

Ideally, this #[legacy] attribute will only persist for a fixed number of cycles.

Ideally, this #[legacy] attribute will never be used anywhere. Because it means that people must act to keep their code (or the code their code depends on) from breaking. Please, let's set up a fellowship of Rustaceans to take the very idea of #[legacy] to the fiery lava pits deep within Mordor's mountains and throw it into the sizzling hot lava, so it may never corrupt the beautiful Rustic homelands again.

Edit: With the above, I mean #[legacy] as an opt-out, not the general idea of people allowing to specify what they want to use.

@gnzlbg
Copy link
Contributor

gnzlbg commented Jun 17, 2015

@llogiq

This would get very messy, fast, once you have multiple dependencies that may need different legacy flag combinations.

Oh, you are right. I was thinking that I could tell cargo externally that a package requires Rust version 1.0. If I try to compile it with Rust 1.13 cargo could look up the flags it needs to pass to rustc to compile this package. It could potentially be a very long list of flags, and certainly messy for cargo developers, but as a users I wouldn't need to worry about it much.

the information about what language version the code uses belongs in the code!

I gotta sleep over this but right now I would agree that it belongs at the crate level at least in the Cargo.toml. Having it in the source files would make me right now a bit uncomfortable, but I'm open to give this some more thought over the next couple of days.

I've already answered your comment, but I feel I need to stress another point:

Ideally, this #[legacy] attribute will only persist for a fixed number of cycles.

Ideally, this #[legacy] attribute will never be used anywhere. Because it means that people must act to keep their code (or the code their code depends on) from breaking. Please, let's set up a fellowship of Rustaceans to take the very idea of #[legacy] to the fiery lava pits deep within Mordor's mountains and throw it into the sizzling hot lava, so it may never corrupt the beautiful Rustic homelands again.

I think that a fine grained solution is a good starting point for a discussion tho. We can have legacy attributes for each breaking change, legacy attributes for whole rust versions #[legacy(rust-1.1)] that are expanded by rustc to a collection of attributes, and a way to pass these attributes to rustc externally so that you only need to write this in the Cargo.toml file. Such a layered approach might allow some projects to be on nightly, but not on the latest nightly.

@llogiq
Copy link
Contributor

llogiq commented Jun 17, 2015

@gnzlbg I actually included opt-in by flag (be it legacy flag or otherwise) as an alternative/extension in my RFC PR, and I intend to move them into the main proposal once I have thought the consequences through.

I agree that allowing for more fine-grained control is a worthy goal, in addition to setting up a good default by the target version. This means we get sane defaults, break no code and still can have all unstable or legacy features we may want by inserting the corresponding flags.

@db47h
Copy link

db47h commented Jun 17, 2015

As a newcomer to Rust and having been badly bitten by this issue, I'd say yes, merge ASAP.

Now, on the subject on how to make breaking changes on point releases, llogiq's idea of a phased change looks to me to be the best trade off. Possibly make the change available early using the existing compiler features mechanism.

As for opt-out and target API version, keep in mind that in a thriving ecosystem, unmaintained code that just works never stays unmaintained for long: if some piece of code is really useful, there's always someone to fork it, dust it off and add the latest bells and whistles to it. That said, it could come in handy when a major release of Rust comes out with loads of breaking changes. But please, not for point releases, we'd end up in a dependency hell: what if you have code that uses libc targeted at the current Rust version while using at the same time a crate that uses a libc version targeted at previous, incompatible, Rust version? I know it's doable, but no thanks.

When Rust 12.0 comes out, the best place for a target API flag would however be in the Cargo.toml file:

  • target 1.0? compile with Rust 1.x.y
  • no target? compile with latest

And make it so that Rust 12.0 can be installed along with previous versions (like gcc does for example) so that cargo can invoke the appropriate version. Oh, and provide some vetting tool to help transition code from one version to the other.

@llogiq
Copy link
Contributor

llogiq commented Jun 17, 2015

@db47h

[...] in a thriving ecosystem, unmaintained code that just works never stays unmaintained for long [...]

This is expressly not about unmaintained code. It's about enterprise users who may have invested money in vetting one version of a given solution, which you now ask to spend more money to switch to another. It's about users of (not yet existing, but conceivable) proprietary crates, which they are contractually obligated not to change. Finally it's about all users, because we're going to lose an awful lot of them once the next few point-releases break their build and they start to fear new Rust versions.

[...] what if you have code that uses libc [...]

libc hasn't changed too much in a long time, even in the face of severe security issues. Also we're already abstracting over it, so I don't think this will be a problem. Even if it comes to that, we can figure out how to deal with it then.

[...] the best place for a target API flag would however be in the Cargo.toml file

I thought about this, too, but this has two downsides:

  1. I for one don't always use cargo (for example when creating small tests or benchmarks), so this would at least require an additional way to add it to the build without cargo – perhaps a rustc argument, but this is easy enough to forget or get wrong.
  2. When copying code from elsewhere, you must also copy the target version from the Cargo.toml, lest your copied code will probably stop working. This is at least unintuitive; whereas having an attribute on the crate root is in line with other crate-global definitions we already have.

no target? compile with latest

I sure hope you meant this as a rule for Rust 12.0.0 – because if enacted now it would break all builds with the next incompatible release.

@db47h
Copy link

db47h commented Jun 17, 2015

@llogiq

Well, working for an open source minded shop, I didn't think about the untouchable proprietary crate.

My concern here is that making provisions to target point version APIs minor Rust versions would pave the way for real breaking changes in point releases and dependency hell. The issue discussed in this PR should be an exception: everyone who's been bitten by this has already added 'static all over the place, and although it is a potentially breaking change, it does not seem to break any publicly available code. As for closed code for which no one has spoken up yet, a phased implementation should make everyone happy.

libc hasn't changed too much in a long time [...]

I only picked libc as an example, my point is still valid for any other crate.

[...] Finally it's about all users, because we're going to lose an awful lot of them once the next few point-releases break their build and they start to fear new Rust versions.

I fully agree. Then don't allow breaking changes at all. As much as I'd like to see this PR merged, I'd rather see it postponed to Rust 2.0 and properly documented as a known issue than open Pandora's box to dependency hell (nothing wrong with known issues as long as they are properly documented).

[...] perhaps a rustc argument [...]

Why not, as long as it's only for major releases ;) But that's not incompatible with having it as a config value in Cargo.toml or a crate root attribute.

I sure hope you meant this as a rule for Rust 12.0.0 – because if enacted now it would break all builds with the next incompatible release.

Well, I jumped to version 12 for a reason ;) But I'm not sure what you mean. When the next backward incompatible Rust version will come out (say 2.0 for example), for which Rust version will existing code be compiled if there is no target hint in the sources? 1.0, 2.0? Take for example gcc 4.8.2: although supporting some of the C++11 standard, it defaults to -std=gnu90. Do you mean that Rust will/should take the same approach? EDIT: found your RFC PR where you explain this :) But API version and Rust version is a different matter, or am I missing something?

@llogiq
Copy link
Contributor

llogiq commented Jun 17, 2015

@db47h I don't see your point.

Why should we stop evolving the language (and API) when we can do so safely – that is, as long as the old behavior is accessible via target version/legacy/future combination and we can ensure crates of all target versions continue to work together? Where is this dependency hell you keep alluding to?

Yes, it would force people to upgrade their rust versions to use code that is written for the new versions. That is a good thing and it's no different than in, say, Java, or C++. We want people to use the newest Rust, because if they don't, they'll miss out on all the fixes for all problems we may have found in the meantime.

[...] for which Rust version will existing code be compiled if there is no target hint in the sources?

Currently, that will be 1.0, because we cannot ask people to retrofit a target version to all their code. And yes, this is much like the default of --std=c90. Note that my proposal included that cargo new insert the current version as target by default, so this doesn't apply to all code, only code written without cargo.

Also future incompatible versions will likely have incompatible APIs, which (as I've written in my RFC PR) could be use to determine a lower bound for the target version. In any event, we may have some time until the next major release hits, so once most code declares its target version, a warning + best-effort-compilation with the current version could be the most useful way to go (this could of course be overridden by a compiler argument).

@db47h
Copy link

db47h commented Jun 17, 2015

@llogiq : Took me a while to find the RFC PR you mention :-P And yes, makes more sense now that I've read it.

I'm not saying the language should stop evolving, not at all. Sorry if that's how I came through. There's nothing wrong with your RFC-PR, on the contrary.

The dependency hell I'm referring to occurs for example when your code and some other crate it depends on both use a different target version of yet another crate (see my poorly chosen libc example). I don't know about Java, but in my experience this is all too common with C/C++ (game engines come to mind) . You refer to it as "breakage" in your RFC-PR, I used a different term, coming from a different perspective, that's all.

Of course, it won't happen if your PR goes through and if everyone adopts newer Rust versions quickly.

@llogiq
Copy link
Contributor

llogiq commented Jun 17, 2015

Please note that target version as currently defined refers to rust (including its std) only. Whether we want to allow others to use the concept is an open question (however, I've made the syntax extensible to allow for this case).

Also as of now, crates already can refer to different versions of other crates via Cargo.

@comex
Copy link

comex commented Jun 17, 2015

As for opt-out and target API version, keep in mind that in a thriving ecosystem, unmaintained code that just works never stays unmaintained for long

I'd generally disagree with this - haven't you ever found some website offering (relatively obscure) software in a tarball that's 5 years old, with no development, upstream or fork, in sight? I've encountered many. Or even on GitHub, where there may be, say, 125 "forks", of which, according to GitHub's network view, the vast majority are unchanged from their fork date, a dozen have one or more commits added, which may or may not fix the problem you're having, and none show signs of active ongoing development. Those numbers come from a real example - a C library where the maintainer introduced a trivial but critical bug back in 2013 and has not paid attention to the resulting issue report, or practically any issues/PRs, since. At least GitHub makes it easier to fork a repository, but all the links on the Internet still point to the old version, and committing to the necessary advertising plus attention to the issue/PR queue to make your fork 'canonical' is... not exactly easy.

what if you have code that uses libc targeted at the current Rust version while using at the same time a crate that uses a libc version targeted at previous, incompatible, Rust version? I know it's doable, but no thanks.

To slightly elaborate on what @llogiq just said, this question wouldn't make sense, because depending on a different version of a crate is orthogonal to a Rust version attribute; the attribute, for its part, would be per crate, and crates targeting different versions would fully interoperate.

@db47h
Copy link

db47h commented Jun 17, 2015

I'd generally disagree with this - haven't you ever found some website offering (relatively obscure) software [...]

I meant "useful unmaintained code", and discounted the relatively obscure software ;) I just wouldn't worry too much about making sure that old bitrotted code still compiles 10 years from now.

@comex
Copy link

comex commented Jun 17, 2015

Most software is relatively obscure - the so-called long tail.

@llogiq
Copy link
Contributor

llogiq commented Jun 18, 2015

@db47h besides, that kind of thinking brought us the y2k problem. It's shortsighted and bad engineering.

Just a heads-up: I have rewritten my RFC proposal (which is now called "Target Version") to include the points made here. As it stands, it would also apply to this proposal if accepted (thus removing any remaining breakage risk from this change).

@llogiq llogiq mentioned this pull request Jun 18, 2015
@nikomatsakis
Copy link
Contributor Author

It's official... the language subteam has decided to accept this RFC and go forward with this specific change. Given its significance, this PR was also discussed specifically at a recent core team meeting.

Note: although it's been decided to make this particular change, this is explicitly not considered a binding precedent. The more general policy questions are still under active discussion and we plan to eventually open a separate RFC on that topic. For the moment, I am currently working on a post for internals summarizing my thoughts on breaking changes, LTS releases, major versions, and so forth, but this is not yet complete. (Given the upcoming Mozilla work week, this may not get posted right away.) And of course there is also RFC #1147 by @llogiq, which though it was born from a discussion on API deprecation is clearly relevant. --nmatsakis

@llogiq
Copy link
Contributor

llogiq commented Jun 19, 2015

@nikomatsakis: Thank you for working with us to make this change a reality. I believe this is a good change for Rust and for us all.

Incidentially – and mostly inspired by the discussion here and elsewhere – I have rewritten and streamlined my proposal considerably. As it is now, it incorporates the things that were discussed here, so double thanks to all who argued with me. 😄

@aturon
Copy link
Member

aturon commented Jun 19, 2015

Merged. Tracking issue

bors added a commit to rust-lang/rust that referenced this pull request Jul 4, 2015
…komatsakis

This is an implementation of RFC rust-lang/rfcs#1156. It includes the code to implement the new rules, but that code is currently disabled. It also includes code to issue warnings when the change will cause breakage. These warnings try hard to be targeted but are also somewhat approximate. They could, with some effort, be made *more* targeted by adjusting the code in ty_relate that propagates the "will change" flag to consider the specific operation. Might be worth doing.

r? @pnkfelix (I think you understand region inference best)
@Centril Centril added A-traits Trait system related proposals & ideas A-trait-object Proposals relating to trait objects. labels Nov 23, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-trait-object Proposals relating to trait objects. A-traits Trait system related proposals & ideas final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.