Skip to content

Commit 6a6c6b8

Browse files
pnkfelixcelinval
authored andcommitted
Separate contract feature gates for the internal machinery
The extended syntax for function signature that includes contract clauses should never be user exposed versus the interface we want to ship externally eventually.
1 parent b279ff9 commit 6a6c6b8

20 files changed

+210
-28
lines changed

compiler/rustc_ast_passes/src/feature_gate.rs

+2
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
548548
gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
549549
gate_all!(unsafe_fields, "`unsafe` fields are experimental");
550550
gate_all!(unsafe_binders, "unsafe binder types are experimental");
551+
gate_all!(rustc_contracts, "contracts are experimental");
552+
gate_all!(rustc_contracts_internals, "contract internal machinery is for internal use only");
551553

552554
if !visitor.features.never_patterns() {
553555
if let Some(spans) = spans.get(&sym::never_patterns) {

compiler/rustc_builtin_macros/src/contracts.rs

+12-8
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,17 @@ impl AttrProcMacro for ExpandEnsures {
3636
}
3737

3838
fn expand_injecting_circa_where_clause(
39-
_ecx: &mut ExtCtxt<'_>,
39+
ecx: &mut ExtCtxt<'_>,
4040
attr_span: Span,
4141
annotated: TokenStream,
4242
inject: impl FnOnce(&mut Vec<TokenTree>) -> Result<(), ErrorGuaranteed>,
4343
) -> Result<TokenStream, ErrorGuaranteed> {
4444
let mut new_tts = Vec::with_capacity(annotated.len());
45-
let mut cursor = annotated.into_trees();
45+
let mut cursor = annotated.iter();
4646

4747
// Find the `fn name<G,...>(x:X,...)` and inject the AST contract forms right after
4848
// the formal parameters (and return type if any).
49-
while let Some(tt) = cursor.next_ref() {
49+
while let Some(tt) = cursor.next() {
5050
new_tts.push(tt.clone());
5151
if let TokenTree::Token(tok, _) = tt
5252
&& tok.is_ident_named(kw::Fn)
@@ -58,7 +58,7 @@ fn expand_injecting_circa_where_clause(
5858
// Found the `fn` keyword, now find the formal parameters.
5959
//
6060
// FIXME: can this fail if you have parentheticals in a generics list, like `fn foo<F: Fn(X) -> Y>` ?
61-
while let Some(tt) = cursor.next_ref() {
61+
while let Some(tt) = cursor.next() {
6262
new_tts.push(tt.clone());
6363

6464
if let TokenTree::Delimited(_, _, token::Delimiter::Parenthesis, _) = tt {
@@ -81,7 +81,7 @@ fn expand_injecting_circa_where_clause(
8181
// parse the type expression itself. But rather than try to fix things with hacks like that,
8282
// time might be better spent extending the attribute expander to suport tt-annotation atop
8383
// ast-annotated, which would be an elegant way to sidestep all of this.
84-
let mut opt_next_tt = cursor.next_ref();
84+
let mut opt_next_tt = cursor.next();
8585
while let Some(next_tt) = opt_next_tt {
8686
if let TokenTree::Token(tok, _) = next_tt
8787
&& tok.is_ident_named(kw::Where)
@@ -97,8 +97,7 @@ fn expand_injecting_circa_where_clause(
9797

9898
// for anything else, transcribe the tt and keep looking.
9999
new_tts.push(next_tt.clone());
100-
opt_next_tt = cursor.next_ref();
101-
continue;
100+
opt_next_tt = cursor.next();
102101
}
103102

104103
// At this point, we've transcribed everything from the `fn` through the formal parameter list
@@ -118,10 +117,15 @@ fn expand_injecting_circa_where_clause(
118117
if let Some(tt) = opt_next_tt {
119118
new_tts.push(tt.clone());
120119
}
121-
while let Some(tt) = cursor.next_ref() {
120+
while let Some(tt) = cursor.next() {
122121
new_tts.push(tt.clone());
123122
}
124123

124+
// Record the span as a contract attribute expansion.
125+
// This is used later to stop users from using the extended syntax directly
126+
// which is gated via `rustc_contracts_internals`.
127+
ecx.psess().contract_attribute_spans.push(attr_span);
128+
125129
Ok(TokenStream::new(new_tts))
126130
}
127131

compiler/rustc_feature/src/unstable.rs

+4
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,10 @@ declare_features! (
608608
(unstable, return_type_notation, "1.70.0", Some(109417)),
609609
/// Allows `extern "rust-cold"`.
610610
(unstable, rust_cold_cc, "1.63.0", Some(97544)),
611+
/// Allows use of contracts attributes.
612+
(unstable, rustc_contracts, "CURRENT_RUSTC_VERSION", Some(133866)),
613+
/// Allows access to internal machinery used to implement contracts.
614+
(unstable, rustc_contracts_internals, "CURRENT_RUSTC_VERSION", Some(133866)),
611615
/// Allows use of x86 SHA512, SM3 and SM4 target-features and intrinsics
612616
(unstable, sha512_sm_x86, "1.82.0", Some(126624)),
613617
/// Allows the use of SIMD types in functions declared in `extern` blocks.

compiler/rustc_parse/src/parser/generics.rs

+17-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_ast::{
44
WhereClause, token,
55
};
66
use rustc_errors::{Applicability, PResult};
7-
use rustc_span::{Ident, Span, kw};
7+
use rustc_span::{Ident, Span, kw, sym};
88
use thin_vec::ThinVec;
99

1010
use super::{ForceCollect, Parser, Trailing, UsePreAttrPos};
@@ -302,13 +302,27 @@ impl<'a> Parser<'a> {
302302
pub(super) fn parse_contract(
303303
&mut self,
304304
) -> PResult<'a, Option<rustc_ast::ptr::P<ast::FnContract>>> {
305+
let gate = |span| {
306+
if self.psess.contract_attribute_spans.contains(span) {
307+
// span was generated via a builtin contracts attribute, so gate as end-user visible
308+
self.psess.gated_spans.gate(sym::rustc_contracts, span);
309+
} else {
310+
// span was not generated via a builtin contracts attribute, so gate as internal machinery
311+
self.psess.gated_spans.gate(sym::rustc_contracts_internals, span);
312+
}
313+
};
314+
305315
let requires = if self.eat_keyword_noexpect(exp!(RustcContractRequires).kw) {
306-
Some(self.parse_expr()?)
316+
let precond = self.parse_expr()?;
317+
gate(precond.span);
318+
Some(precond)
307319
} else {
308320
None
309321
};
310322
let ensures = if self.eat_keyword_noexpect(exp!(RustcContractEnsures).kw) {
311-
Some(self.parse_expr()?)
323+
let postcond = self.parse_expr()?;
324+
gate(postcond.span);
325+
Some(postcond)
312326
} else {
313327
None
314328
};

compiler/rustc_session/src/parse.rs

+5
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ pub struct ParseSess {
207207
pub config: Cfg,
208208
pub check_config: CheckCfg,
209209
pub edition: Edition,
210+
/// Places where contract attributes were expanded into unstable AST forms.
211+
/// This is used to allowlist those spans (so that we only check them against the feature
212+
/// gate for the externally visible interface, and not internal implmentation machinery).
213+
pub contract_attribute_spans: AppendOnlyVec<Span>,
210214
/// Places where raw identifiers were used. This is used to avoid complaining about idents
211215
/// clashing with keywords in new editions.
212216
pub raw_identifier_spans: AppendOnlyVec<Span>,
@@ -255,6 +259,7 @@ impl ParseSess {
255259
config: Cfg::default(),
256260
check_config: CheckCfg::default(),
257261
edition: ExpnId::root().expn_data().edition,
262+
contract_attribute_spans: Default::default(),
258263
raw_identifier_spans: Default::default(),
259264
bad_unicode_identifiers: Lock::new(Default::default()),
260265
source_map,

compiler/rustc_span/src/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1716,6 +1716,8 @@ symbols! {
17161716
rustc_const_stable,
17171717
rustc_const_stable_indirect,
17181718
rustc_const_unstable,
1719+
rustc_contracts,
1720+
rustc_contracts_internals,
17191721
rustc_conversion_suggestion,
17201722
rustc_deallocator,
17211723
rustc_def_path,

library/core/src/contracts.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub use crate::macros::builtin::contracts_requires as requires;
88
/// Emitted by rustc as a desugaring of `#[requires(PRED)] fn foo(x: X) { ... }`
99
/// into: `fn foo(x: X) { check_requires(|| PRED) ... }`
1010
#[cfg(not(bootstrap))]
11-
#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)]
11+
#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
1212
#[lang = "contract_check_requires"]
1313
#[track_caller]
1414
pub fn check_requires<C: FnOnce() -> bool>(c: C) {
@@ -21,7 +21,7 @@ pub fn check_requires<C: FnOnce() -> bool>(c: C) {
2121
/// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }`
2222
/// (including the implicit return of the tail expression, if any).
2323
#[cfg(not(bootstrap))]
24-
#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)]
24+
#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
2525
#[lang = "contract_build_check_ensures"]
2626
#[track_caller]
2727
pub fn build_check_ensures<Ret, C>(c: C) -> impl (FnOnce(Ret) -> Ret) + Copy

library/core/src/intrinsics/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -4051,8 +4051,8 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize)
40514051
/// checking is turned on, so that we can specify contracts in libstd
40524052
/// and let an end user opt into turning them on.
40534053
#[cfg(not(bootstrap))]
4054-
#[rustc_const_unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)]
4055-
#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)]
4054+
#[rustc_const_unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
4055+
#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
40564056
#[inline(always)]
40574057
#[rustc_intrinsic]
40584058
pub const fn contract_checks() -> bool {
@@ -4063,14 +4063,14 @@ pub const fn contract_checks() -> bool {
40634063
}
40644064

40654065
#[cfg(not(bootstrap))]
4066-
#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)]
4066+
#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
40674067
#[rustc_intrinsic]
40684068
pub fn contract_check_requires<C: FnOnce() -> bool>(c: C) -> bool {
40694069
c()
40704070
}
40714071

40724072
#[cfg(not(bootstrap))]
4073-
#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)]
4073+
#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)]
40744074
#[rustc_intrinsic]
40754075
pub fn contract_check_ensures<'a, Ret, C: FnOnce(&'a Ret) -> bool>(ret: &'a Ret, c: C) -> bool {
40764076
c(ret)

library/core/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ pub mod autodiff {
248248
}
249249

250250
#[cfg(not(bootstrap))]
251-
#[unstable(feature = "rustc_contracts", issue = "none")]
251+
#[unstable(feature = "rustc_contracts", issue = "133866")]
252252
pub mod contracts;
253253

254254
#[unstable(feature = "cfg_match", issue = "115585")]

library/core/src/macros/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1783,8 +1783,8 @@ pub(crate) mod builtin {
17831783
/// eventually parsed as a unary closure expression that is
17841784
/// invoked on a reference to the return value.
17851785
#[cfg(not(bootstrap))]
1786-
#[unstable(feature = "rustc_contracts", issue = "none")]
1787-
#[allow_internal_unstable(core_intrinsics)]
1786+
#[unstable(feature = "rustc_contracts", issue = "133866")]
1787+
#[allow_internal_unstable(rustc_contracts_internals)]
17881788
#[rustc_builtin_macro]
17891789
pub macro contracts_ensures($item:item) {
17901790
/* compiler built-in */
@@ -1796,8 +1796,8 @@ pub(crate) mod builtin {
17961796
/// eventually parsed as an boolean expression with access to the
17971797
/// function's formal parameters
17981798
#[cfg(not(bootstrap))]
1799-
#[unstable(feature = "rustc_contracts", issue = "none")]
1800-
#[allow_internal_unstable(core_intrinsics)]
1799+
#[unstable(feature = "rustc_contracts", issue = "133866")]
1800+
#[allow_internal_unstable(rustc_contracts_internals)]
18011801
#[rustc_builtin_macro]
18021802
pub macro contracts_requires($item:item) {
18031803
/* compiler built-in */

tests/ui/contracts/contract-ast-extensions-nest.rs renamed to tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes
1717
//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes
1818

19-
#![feature(rustc_contracts)]
19+
#![feature(rustc_contracts_internals)]
2020

2121
fn nest(x: Baz) -> i32
2222
rustc_contract_requires(|| x.baz > 0)

tests/ui/contracts/contract-ast-extensions-tail.rs renamed to tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes
1717
//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes
1818

19-
#![feature(rustc_contracts)]
19+
#![feature(rustc_contracts_internals)]
2020

2121
fn tail(x: Baz) -> i32
2222
rustc_contract_requires(|| x.baz > 0)

tests/ui/contracts/contract-intrinsics.rs renamed to tests/ui/contracts/internal_machinery/contract-intrinsics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//@ revisions: yes no none
33
//@ [yes] compile-flags: -Zcontract-checks=yes
44
//@ [no] compile-flags: -Zcontract-checks=no
5-
#![feature(cfg_contract_checks, rustc_contracts, core_intrinsics)]
5+
#![feature(cfg_contract_checks, rustc_contracts_internals, core_intrinsics)]
66

77
fn main() {
88
#[cfg(none)] // default: disabled

tests/ui/contracts/contract-lang-items.rs renamed to tests/ui/contracts/internal_machinery/contract-lang-items.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes
1717
//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes
1818

19-
#![feature(rustc_contracts)]
19+
#![feature(rustc_contracts)] // to access core::contracts
20+
#![feature(rustc_contracts_internals)] // to access check_requires lang item
2021

2122
fn foo(x: Baz) -> i32 {
2223
core::contracts::check_requires(|| x.baz > 0);

tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs renamed to tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//@ run-pass
22
//@ compile-flags: -Zcontract-checks=yes
3-
#![feature(rustc_contracts)]
3+
#![feature(rustc_contracts_internals)]
44

55
fn outer() -> i32
66
rustc_contract_ensures(|ret| *ret > 0)

tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs renamed to tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//@ run-pass
22
//@ compile-flags: -Zcontract-checks=yes
3-
#![feature(rustc_contracts)]
3+
#![feature(rustc_contracts_internals)]
44

55
struct Outer { outer: std::cell::Cell<i32> }
66

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// gate-test-rustc_contracts_internals
2+
3+
fn main() {
4+
// intrinsics are guarded by rustc_contracts_internals feature gate.
5+
core::intrinsics::contract_checks();
6+
//~^ ERROR use of unstable library feature `rustc_contracts_internals`
7+
core::intrinsics::contract_check_requires(|| true);
8+
//~^ ERROR use of unstable library feature `rustc_contracts_internals`
9+
core::intrinsics::contract_check_ensures(&1, |_|true);
10+
//~^ ERROR use of unstable library feature `rustc_contracts_internals`
11+
12+
// lang items are guarded by rustc_contracts_internals feature gate.
13+
core::contracts::check_requires(|| true);
14+
//~^ ERROR use of unstable library feature `rustc_contracts_internals`
15+
core::contracts::build_check_ensures(|_: &()| true);
16+
//~^ ERROR use of unstable library feature `rustc_contracts_internals`
17+
18+
// ast extensions are guarded by rustc_contracts_internals feature gate
19+
fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 }
20+
//~^ ERROR contract internal machinery is for internal use only
21+
fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 }
22+
//~^ ERROR contract internal machinery is for internal use only
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
error[E0658]: contract internal machinery is for internal use only
2+
--> $DIR/internal-feature-gating.rs:19:51
3+
|
4+
LL | fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 }
5+
| ^^^^^^^^^
6+
|
7+
= note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
8+
= help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
9+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
10+
11+
error[E0658]: contract internal machinery is for internal use only
12+
--> $DIR/internal-feature-gating.rs:21:50
13+
|
14+
LL | fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 }
15+
| ^^^^^^^^^^
16+
|
17+
= note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
18+
= help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
19+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
20+
21+
error[E0658]: use of unstable library feature `rustc_contracts_internals`
22+
--> $DIR/internal-feature-gating.rs:5:5
23+
|
24+
LL | core::intrinsics::contract_checks();
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
|
27+
= note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
28+
= help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
29+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
30+
31+
error[E0658]: use of unstable library feature `rustc_contracts_internals`
32+
--> $DIR/internal-feature-gating.rs:7:5
33+
|
34+
LL | core::intrinsics::contract_check_requires(|| true);
35+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
36+
|
37+
= note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
38+
= help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
39+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
40+
41+
error[E0658]: use of unstable library feature `rustc_contracts_internals`
42+
--> $DIR/internal-feature-gating.rs:9:5
43+
|
44+
LL | core::intrinsics::contract_check_ensures(&1, |_|true);
45+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
46+
|
47+
= note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
48+
= help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
49+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
50+
51+
error[E0658]: use of unstable library feature `rustc_contracts_internals`
52+
--> $DIR/internal-feature-gating.rs:13:5
53+
|
54+
LL | core::contracts::check_requires(|| true);
55+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
56+
|
57+
= note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
58+
= help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
59+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
60+
61+
error[E0658]: use of unstable library feature `rustc_contracts_internals`
62+
--> $DIR/internal-feature-gating.rs:15:5
63+
|
64+
LL | core::contracts::build_check_ensures(|_: &()| true);
65+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66+
|
67+
= note: see issue #133866 <https://github.com/rust-lang/rust/issues/133866> for more information
68+
= help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable
69+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
70+
71+
error: aborting due to 7 previous errors
72+
73+
For more information about this error, try `rustc --explain E0658`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![crate_type = "lib"]
2+
3+
#[core::contracts::requires(x > 0)]
4+
pub fn requires_needs_it(x: i32) { }
5+
//~^^ ERROR use of unstable library feature `rustc_contracts`
6+
//~^^^ ERROR contracts are experimental
7+
8+
#[core::contracts::ensures(|ret| *ret > 0)]
9+
pub fn ensures_needs_it() -> i32 { 10 }
10+
//~^^ ERROR use of unstable library feature `rustc_contracts`
11+
//~^^^ ERROR contracts are experimental

0 commit comments

Comments
 (0)