1
Fork 0

Rollup merge of #136835 - compiler-errors:contracts-span-hack, r=celinval

Stop using span hack for contracts feature gating

The contracts machinery is a pretty straightforward case of an *external* feature using a (perma-unstable) *internal* feature within its implementation. There's no reason why it needs to be implemented any differently than other features by using global span tracking hacks to change whether the internals are gated behind the `contracts` or `contracts_internals` feature gate -- for the case of macro expansions we already have `allow_internal_unstable` for exactly this situation.

This PR changes the internal, perma-unstable AST syntax to use the `contracts_internals` gate always, and adjusts the macro expansion to use the right spans so that `allow_internal_unstable` works correctly.

As a follow-up, there's really no reason to have `contracts` be a *compiler feature* since it's at this point fully a *library feature*; the only reason it's a compiler feature today is so we can mark it as incomplete, but that seems like a weak reason. I didn't do anything in this PR for this.

r? ``@celinval``
This commit is contained in:
Jacob Pratt 2025-02-11 01:02:43 -05:00 committed by GitHub
commit 94bf32e719
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 17 additions and 59 deletions

View file

@ -1,11 +1,9 @@
#![allow(unused_imports, unused_variables)]
use rustc_ast::token; use rustc_ast::token;
use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
use rustc_errors::ErrorGuaranteed; use rustc_errors::ErrorGuaranteed;
use rustc_expand::base::{AttrProcMacro, ExtCtxt}; use rustc_expand::base::{AttrProcMacro, ExtCtxt};
use rustc_span::Span; use rustc_span::Span;
use rustc_span::symbol::{Ident, Symbol, kw, sym}; use rustc_span::symbol::{Ident, Symbol, kw};
pub(crate) struct ExpandRequires; pub(crate) struct ExpandRequires;
@ -121,23 +119,19 @@ fn expand_contract_clause(
} }
} }
// Record the span as a contract attribute expansion.
// This is used later to stop users from using the extended syntax directly
// which is gated via `contracts_internals`.
ecx.psess().contract_attribute_spans.push(attr_span);
Ok(new_tts) Ok(new_tts)
} }
fn expand_requires_tts( fn expand_requires_tts(
_ecx: &mut ExtCtxt<'_>, ecx: &mut ExtCtxt<'_>,
attr_span: Span, attr_span: Span,
annotation: TokenStream, annotation: TokenStream,
annotated: TokenStream, annotated: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> { ) -> Result<TokenStream, ErrorGuaranteed> {
expand_contract_clause(_ecx, attr_span, annotated, |new_tts| { let feature_span = ecx.with_def_site_ctxt(attr_span);
expand_contract_clause(ecx, attr_span, annotated, |new_tts| {
new_tts.push_tree(TokenTree::Token( new_tts.push_tree(TokenTree::Token(
token::Token::from_ast_ident(Ident::new(kw::ContractRequires, attr_span)), token::Token::from_ast_ident(Ident::new(kw::ContractRequires, feature_span)),
Spacing::Joint, Spacing::Joint,
)); ));
new_tts.push_tree(TokenTree::Token( new_tts.push_tree(TokenTree::Token(
@ -155,14 +149,15 @@ fn expand_requires_tts(
} }
fn expand_ensures_tts( fn expand_ensures_tts(
_ecx: &mut ExtCtxt<'_>, ecx: &mut ExtCtxt<'_>,
attr_span: Span, attr_span: Span,
annotation: TokenStream, annotation: TokenStream,
annotated: TokenStream, annotated: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> { ) -> Result<TokenStream, ErrorGuaranteed> {
expand_contract_clause(_ecx, attr_span, annotated, |new_tts| { let feature_span = ecx.with_def_site_ctxt(attr_span);
expand_contract_clause(ecx, attr_span, annotated, |new_tts| {
new_tts.push_tree(TokenTree::Token( new_tts.push_tree(TokenTree::Token(
token::Token::from_ast_ident(Ident::new(kw::ContractEnsures, attr_span)), token::Token::from_ast_ident(Ident::new(kw::ContractEnsures, feature_span)),
Spacing::Joint, Spacing::Joint,
)); ));
new_tts.push_tree(TokenTree::Delimited( new_tts.push_tree(TokenTree::Delimited(

View file

@ -302,26 +302,16 @@ impl<'a> Parser<'a> {
pub(super) fn parse_contract( pub(super) fn parse_contract(
&mut self, &mut self,
) -> PResult<'a, Option<rustc_ast::ptr::P<ast::FnContract>>> { ) -> PResult<'a, Option<rustc_ast::ptr::P<ast::FnContract>>> {
let gate = |span| {
if self.psess.contract_attribute_spans.contains(span) {
// span was generated via a builtin contracts attribute, so gate as end-user visible
self.psess.gated_spans.gate(sym::contracts, span);
} else {
// span was not generated via a builtin contracts attribute, so gate as internal machinery
self.psess.gated_spans.gate(sym::contracts_internals, span);
}
};
let requires = if self.eat_keyword_noexpect(exp!(ContractRequires).kw) { let requires = if self.eat_keyword_noexpect(exp!(ContractRequires).kw) {
self.psess.gated_spans.gate(sym::contracts_internals, self.prev_token.span);
let precond = self.parse_expr()?; let precond = self.parse_expr()?;
gate(precond.span);
Some(precond) Some(precond)
} else { } else {
None None
}; };
let ensures = if self.eat_keyword_noexpect(exp!(ContractEnsures).kw) { let ensures = if self.eat_keyword_noexpect(exp!(ContractEnsures).kw) {
self.psess.gated_spans.gate(sym::contracts_internals, self.prev_token.span);
let postcond = self.parse_expr()?; let postcond = self.parse_expr()?;
gate(postcond.span);
Some(postcond) Some(postcond)
} else { } else {
None None

View file

@ -208,10 +208,6 @@ pub struct ParseSess {
pub config: Cfg, pub config: Cfg,
pub check_config: CheckCfg, pub check_config: CheckCfg,
pub edition: Edition, pub edition: Edition,
/// Places where contract attributes were expanded into unstable AST forms.
/// This is used to allowlist those spans (so that we only check them against the feature
/// gate for the externally visible interface, and not internal implmentation machinery).
pub contract_attribute_spans: AppendOnlyVec<Span>,
/// Places where raw identifiers were used. This is used to avoid complaining about idents /// Places where raw identifiers were used. This is used to avoid complaining about idents
/// clashing with keywords in new editions. /// clashing with keywords in new editions.
pub raw_identifier_spans: AppendOnlyVec<Span>, pub raw_identifier_spans: AppendOnlyVec<Span>,
@ -260,7 +256,6 @@ impl ParseSess {
config: Cfg::default(), config: Cfg::default(),
check_config: CheckCfg::default(), check_config: CheckCfg::default(),
edition: ExpnId::root().expn_data().edition, edition: ExpnId::root().expn_data().edition,
contract_attribute_spans: Default::default(),
raw_identifier_spans: Default::default(), raw_identifier_spans: Default::default(),
bad_unicode_identifiers: Lock::new(Default::default()), bad_unicode_identifiers: Lock::new(Default::default()),
source_map, source_map,

View file

@ -1,18 +1,18 @@
error[E0658]: contract internal machinery is for internal use only error[E0658]: contract internal machinery is for internal use only
--> $DIR/internal-feature-gating.rs:16:45 --> $DIR/internal-feature-gating.rs:16:28
| |
LL | fn identity_1() -> i32 contract_requires(|| true) { 10 } LL | fn identity_1() -> i32 contract_requires(|| true) { 10 }
| ^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
| |
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information = note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= help: add `#![feature(contracts_internals)]` to the crate attributes to enable = help: add `#![feature(contracts_internals)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: contract internal machinery is for internal use only error[E0658]: contract internal machinery is for internal use only
--> $DIR/internal-feature-gating.rs:18:44 --> $DIR/internal-feature-gating.rs:18:28
| |
LL | fn identity_2() -> i32 contract_ensures(|_| true) { 10 } LL | fn identity_2() -> i32 contract_ensures(|_| true) { 10 }
| ^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
| |
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information = note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= help: add `#![feature(contracts_internals)]` to the crate attributes to enable = help: add `#![feature(contracts_internals)]` to the crate attributes to enable

View file

@ -3,9 +3,7 @@
#[core::contracts::requires(x > 0)] #[core::contracts::requires(x > 0)]
pub fn requires_needs_it(x: i32) { } pub fn requires_needs_it(x: i32) { }
//~^^ ERROR use of unstable library feature `contracts` //~^^ ERROR use of unstable library feature `contracts`
//~^^^ ERROR contracts are incomplete
#[core::contracts::ensures(|ret| *ret > 0)] #[core::contracts::ensures(|ret| *ret > 0)]
pub fn ensures_needs_it() -> i32 { 10 } pub fn ensures_needs_it() -> i32 { 10 }
//~^^ ERROR use of unstable library feature `contracts` //~^^ ERROR use of unstable library feature `contracts`
//~^^^ ERROR contracts are incomplete

View file

@ -9,7 +9,7 @@ LL | #[core::contracts::requires(x > 0)]
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: use of unstable library feature `contracts` error[E0658]: use of unstable library feature `contracts`
--> $DIR/feature-gate-contracts.rs:8:3 --> $DIR/feature-gate-contracts.rs:7:3
| |
LL | #[core::contracts::ensures(|ret| *ret > 0)] LL | #[core::contracts::ensures(|ret| *ret > 0)]
| ^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^
@ -18,26 +18,6 @@ LL | #[core::contracts::ensures(|ret| *ret > 0)]
= help: add `#![feature(contracts)]` to the crate attributes to enable = help: add `#![feature(contracts)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: contracts are incomplete error: aborting due to 2 previous errors
--> $DIR/feature-gate-contracts.rs:3:1
|
LL | #[core::contracts::requires(x > 0)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= help: add `#![feature(contracts)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: contracts are incomplete
--> $DIR/feature-gate-contracts.rs:8:1
|
LL | #[core::contracts::ensures(|ret| *ret > 0)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
= help: add `#![feature(contracts)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0658`. For more information about this error, try `rustc --explain E0658`.