]> git.proxmox.com Git - rustc.git/blame - src/librustc_ast_passes/feature_gate.rs
New upstream version 1.45.0+dfsg1
[rustc.git] / src / librustc_ast_passes / feature_gate.rs
CommitLineData
74b04a01
XL
1use rustc_ast::ast::{self, AssocTyConstraint, AssocTyConstraintKind, NodeId};
2use rustc_ast::ast::{GenericParam, GenericParamKind, PatKind, RangeEnd, VariantData};
3use rustc_ast::attr;
4use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
dfeec247
XL
5use rustc_errors::{struct_span_err, Handler};
6use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP};
7use rustc_feature::{Features, GateIssue, UnstableFeatures};
74b04a01 8use rustc_session::parse::{feature_err, feature_err_issue, ParseSess};
dfeec247 9use rustc_span::source_map::Spanned;
f9f354fc 10use rustc_span::symbol::{sym, Symbol};
dfeec247 11use rustc_span::Span;
dfeec247
XL
12
13use log::debug;
14
15macro_rules! gate_feature_fn {
16 ($cx: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
17 let (cx, has_feature, span, name, explain) = (&*$cx, $has_feature, $span, $name, $explain);
18 let has_feature: bool = has_feature(&$cx.features);
19 debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
20 if !has_feature && !span.allows_unstable($name) {
21 feature_err_issue(cx.parse_sess, name, span, GateIssue::Language, explain).emit();
22 }
23 }};
24}
25
26macro_rules! gate_feature_post {
27 ($cx: expr, $feature: ident, $span: expr, $explain: expr) => {
28 gate_feature_fn!($cx, |x: &Features| x.$feature, $span, sym::$feature, $explain)
29 };
30}
31
32pub fn check_attribute(attr: &ast::Attribute, parse_sess: &ParseSess, features: &Features) {
33 PostExpansionVisitor { parse_sess, features }.visit_attribute(attr)
34}
35
36struct PostExpansionVisitor<'a> {
37 parse_sess: &'a ParseSess,
38 features: &'a Features,
39}
40
41impl<'a> PostExpansionVisitor<'a> {
42 fn check_abi(&self, abi: ast::StrLit) {
43 let ast::StrLit { symbol_unescaped, span, .. } = abi;
44
45 match &*symbol_unescaped.as_str() {
46 // Stable
47 "Rust" | "C" | "cdecl" | "stdcall" | "fastcall" | "aapcs" | "win64" | "sysv64"
48 | "system" => {}
49 "rust-intrinsic" => {
50 gate_feature_post!(&self, intrinsics, span, "intrinsics are subject to change");
51 }
52 "platform-intrinsic" => {
53 gate_feature_post!(
54 &self,
55 platform_intrinsics,
56 span,
57 "platform intrinsics are experimental and possibly buggy"
58 );
59 }
60 "vectorcall" => {
61 gate_feature_post!(
62 &self,
63 abi_vectorcall,
64 span,
65 "vectorcall is experimental and subject to change"
66 );
67 }
68 "thiscall" => {
69 gate_feature_post!(
70 &self,
71 abi_thiscall,
72 span,
73 "thiscall is experimental and subject to change"
74 );
75 }
76 "rust-call" => {
77 gate_feature_post!(
78 &self,
79 unboxed_closures,
80 span,
81 "rust-call ABI is subject to change"
82 );
83 }
84 "ptx-kernel" => {
85 gate_feature_post!(
86 &self,
87 abi_ptx,
88 span,
89 "PTX ABIs are experimental and subject to change"
90 );
91 }
92 "unadjusted" => {
93 gate_feature_post!(
94 &self,
95 abi_unadjusted,
96 span,
97 "unadjusted ABI is an implementation detail and perma-unstable"
98 );
99 }
100 "msp430-interrupt" => {
101 gate_feature_post!(
102 &self,
103 abi_msp430_interrupt,
104 span,
105 "msp430-interrupt ABI is experimental and subject to change"
106 );
107 }
108 "x86-interrupt" => {
109 gate_feature_post!(
110 &self,
111 abi_x86_interrupt,
112 span,
113 "x86-interrupt ABI is experimental and subject to change"
114 );
115 }
116 "amdgpu-kernel" => {
117 gate_feature_post!(
118 &self,
119 abi_amdgpu_kernel,
120 span,
121 "amdgpu-kernel ABI is experimental and subject to change"
122 );
123 }
124 "efiapi" => {
125 gate_feature_post!(
126 &self,
127 abi_efiapi,
128 span,
129 "efiapi ABI is experimental and subject to change"
130 );
131 }
132 abi => self
133 .parse_sess
134 .span_diagnostic
135 .delay_span_bug(span, &format!("unrecognized ABI not caught in lowering: {}", abi)),
136 }
137 }
138
139 fn check_extern(&self, ext: ast::Extern) {
140 if let ast::Extern::Explicit(abi) = ext {
141 self.check_abi(abi);
142 }
143 }
144
145 fn maybe_report_invalid_custom_discriminants(&self, variants: &[ast::Variant]) {
146 let has_fields = variants.iter().any(|variant| match variant.data {
147 VariantData::Tuple(..) | VariantData::Struct(..) => true,
148 VariantData::Unit(..) => false,
149 });
150
151 let discriminant_spans = variants
152 .iter()
153 .filter(|variant| match variant.data {
154 VariantData::Tuple(..) | VariantData::Struct(..) => false,
155 VariantData::Unit(..) => true,
156 })
157 .filter_map(|variant| variant.disr_expr.as_ref().map(|c| c.value.span))
158 .collect::<Vec<_>>();
159
160 if !discriminant_spans.is_empty() && has_fields {
161 let mut err = feature_err(
162 self.parse_sess,
163 sym::arbitrary_enum_discriminant,
164 discriminant_spans.clone(),
165 "custom discriminant values are not allowed in enums with tuple or struct variants",
166 );
167 for sp in discriminant_spans {
168 err.span_label(sp, "disallowed custom discriminant");
169 }
170 for variant in variants.iter() {
171 match &variant.data {
172 VariantData::Struct(..) => {
173 err.span_label(variant.span, "struct variant defined here");
174 }
175 VariantData::Tuple(..) => {
176 err.span_label(variant.span, "tuple variant defined here");
177 }
178 VariantData::Unit(..) => {}
179 }
180 }
181 err.emit();
182 }
183 }
184
185 fn check_gat(&self, generics: &ast::Generics, span: Span) {
186 if !generics.params.is_empty() {
187 gate_feature_post!(
188 &self,
189 generic_associated_types,
190 span,
191 "generic associated types are unstable"
192 );
193 }
194 if !generics.where_clause.predicates.is_empty() {
195 gate_feature_post!(
196 &self,
197 generic_associated_types,
198 span,
199 "where clauses on associated types are unstable"
200 );
201 }
202 }
203
204 /// Feature gate `impl Trait` inside `type Alias = $type_expr;`.
205 fn check_impl_trait(&self, ty: &ast::Ty) {
206 struct ImplTraitVisitor<'a> {
207 vis: &'a PostExpansionVisitor<'a>,
208 }
209 impl Visitor<'_> for ImplTraitVisitor<'_> {
210 fn visit_ty(&mut self, ty: &ast::Ty) {
211 if let ast::TyKind::ImplTrait(..) = ty.kind {
212 gate_feature_post!(
213 &self.vis,
214 type_alias_impl_trait,
215 ty.span,
216 "`impl Trait` in type aliases is unstable"
217 );
218 }
219 visit::walk_ty(self, ty);
220 }
221 }
222 ImplTraitVisitor { vis: self }.visit_ty(ty);
223 }
224}
225
226impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
227 fn visit_attribute(&mut self, attr: &ast::Attribute) {
228 let attr_info =
229 attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a);
230 // Check feature gates for built-in attributes.
231 if let Some((.., AttributeGate::Gated(_, name, descr, has_feature))) = attr_info {
232 gate_feature_fn!(self, has_feature, attr.span, name, descr);
233 }
234 // Check unstable flavors of the `#[doc]` attribute.
235 if attr.check_name(sym::doc) {
236 for nested_meta in attr.meta_item_list().unwrap_or_default() {
237 macro_rules! gate_doc { ($($name:ident => $feature:ident)*) => {
238 $(if nested_meta.check_name(sym::$name) {
239 let msg = concat!("`#[doc(", stringify!($name), ")]` is experimental");
240 gate_feature_post!(self, $feature, attr.span, msg);
241 })*
242 }}
243
244 gate_doc!(
245 include => external_doc
246 cfg => doc_cfg
247 masked => doc_masked
dfeec247
XL
248 alias => doc_alias
249 keyword => doc_keyword
250 );
251 }
252 }
253 }
254
f9f354fc 255 fn visit_name(&mut self, sp: Span, name: Symbol) {
dfeec247
XL
256 if !name.as_str().is_ascii() {
257 gate_feature_post!(
258 &self,
259 non_ascii_idents,
ba9703b0 260 self.parse_sess.source_map().guess_head_span(sp),
dfeec247
XL
261 "non-ascii idents are not fully supported"
262 );
263 }
264 }
265
266 fn visit_item(&mut self, i: &'a ast::Item) {
267 match i.kind {
268 ast::ItemKind::ForeignMod(ref foreign_module) => {
269 if let Some(abi) = foreign_module.abi {
270 self.check_abi(abi);
271 }
272 }
273
274 ast::ItemKind::Fn(..) => {
275 if attr::contains_name(&i.attrs[..], sym::plugin_registrar) {
276 gate_feature_post!(
277 &self,
278 plugin_registrar,
279 i.span,
280 "compiler plugins are experimental and possibly buggy"
281 );
282 }
283 if attr::contains_name(&i.attrs[..], sym::start) {
284 gate_feature_post!(
285 &self,
286 start,
287 i.span,
288 "`#[start]` functions are experimental \
ba9703b0
XL
289 and their signature may change \
290 over time"
dfeec247
XL
291 );
292 }
293 if attr::contains_name(&i.attrs[..], sym::main) {
294 gate_feature_post!(
295 &self,
296 main,
297 i.span,
298 "declaration of a non-standard `#[main]` \
ba9703b0
XL
299 function may change over time, for now \
300 a top-level `fn main()` is required"
dfeec247
XL
301 );
302 }
303 }
304
305 ast::ItemKind::Struct(..) => {
306 for attr in attr::filter_by_name(&i.attrs[..], sym::repr) {
307 for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
308 if item.check_name(sym::simd) {
309 gate_feature_post!(
310 &self,
311 repr_simd,
312 attr.span,
313 "SIMD types are experimental and possibly buggy"
314 );
315 }
316 }
317 }
318 }
319
320 ast::ItemKind::Enum(ast::EnumDef { ref variants, .. }, ..) => {
321 for variant in variants {
322 match (&variant.data, &variant.disr_expr) {
323 (ast::VariantData::Unit(..), _) => {}
324 (_, Some(disr_expr)) => gate_feature_post!(
325 &self,
326 arbitrary_enum_discriminant,
327 disr_expr.value.span,
328 "discriminants on non-unit variants are experimental"
329 ),
330 _ => {}
331 }
332 }
333
334 let has_feature = self.features.arbitrary_enum_discriminant;
335 if !has_feature && !i.span.allows_unstable(sym::arbitrary_enum_discriminant) {
336 self.maybe_report_invalid_custom_discriminants(&variants);
337 }
338 }
339
ba9703b0
XL
340 ast::ItemKind::Impl { polarity, defaultness, ref of_trait, .. } => {
341 if let ast::ImplPolarity::Negative(span) = polarity {
dfeec247
XL
342 gate_feature_post!(
343 &self,
ba9703b0
XL
344 negative_impls,
345 span.to(of_trait.as_ref().map(|t| t.path.span).unwrap_or(span)),
dfeec247 346 "negative trait bounds are not yet fully implemented; \
ba9703b0 347 use marker types for now"
dfeec247
XL
348 );
349 }
350
74b04a01 351 if let ast::Defaultness::Default(_) = defaultness {
dfeec247
XL
352 gate_feature_post!(&self, specialization, i.span, "specialization is unstable");
353 }
354 }
355
356 ast::ItemKind::Trait(ast::IsAuto::Yes, ..) => {
357 gate_feature_post!(
358 &self,
359 optin_builtin_traits,
360 i.span,
361 "auto traits are experimental and possibly buggy"
362 );
363 }
364
365 ast::ItemKind::TraitAlias(..) => {
366 gate_feature_post!(&self, trait_alias, i.span, "trait aliases are experimental");
367 }
368
ba9703b0 369 ast::ItemKind::MacroDef(ast::MacroDef { macro_rules: false, .. }) => {
dfeec247
XL
370 let msg = "`macro` is experimental";
371 gate_feature_post!(&self, decl_macro, i.span, msg);
372 }
373
74b04a01 374 ast::ItemKind::TyAlias(_, _, _, Some(ref ty)) => self.check_impl_trait(&ty),
dfeec247
XL
375
376 _ => {}
377 }
378
379 visit::walk_item(self, i);
380 }
381
382 fn visit_foreign_item(&mut self, i: &'a ast::ForeignItem) {
383 match i.kind {
384 ast::ForeignItemKind::Fn(..) | ast::ForeignItemKind::Static(..) => {
385 let link_name = attr::first_attr_value_str_by_name(&i.attrs, sym::link_name);
386 let links_to_llvm = match link_name {
387 Some(val) => val.as_str().starts_with("llvm."),
388 _ => false,
389 };
390 if links_to_llvm {
391 gate_feature_post!(
392 &self,
393 link_llvm_intrinsics,
394 i.span,
395 "linking to LLVM intrinsics is experimental"
396 );
397 }
398 }
74b04a01 399 ast::ForeignItemKind::TyAlias(..) => {
dfeec247
XL
400 gate_feature_post!(&self, extern_types, i.span, "extern types are experimental");
401 }
ba9703b0 402 ast::ForeignItemKind::MacCall(..) => {}
dfeec247
XL
403 }
404
405 visit::walk_foreign_item(self, i)
406 }
407
408 fn visit_ty(&mut self, ty: &'a ast::Ty) {
409 match ty.kind {
410 ast::TyKind::BareFn(ref bare_fn_ty) => {
411 self.check_extern(bare_fn_ty.ext);
412 }
413 ast::TyKind::Never => {
414 gate_feature_post!(&self, never_type, ty.span, "the `!` type is experimental");
415 }
416 _ => {}
417 }
418 visit::walk_ty(self, ty)
419 }
420
74b04a01
XL
421 fn visit_fn_ret_ty(&mut self, ret_ty: &'a ast::FnRetTy) {
422 if let ast::FnRetTy::Ty(ref output_ty) = *ret_ty {
dfeec247
XL
423 if let ast::TyKind::Never = output_ty.kind {
424 // Do nothing.
425 } else {
426 self.visit_ty(output_ty)
427 }
428 }
429 }
430
431 fn visit_expr(&mut self, e: &'a ast::Expr) {
432 match e.kind {
433 ast::ExprKind::Box(_) => {
434 gate_feature_post!(
435 &self,
436 box_syntax,
437 e.span,
438 "box expression syntax is experimental; you can call `Box::new` instead"
439 );
440 }
441 ast::ExprKind::Type(..) => {
442 // To avoid noise about type ascription in common syntax errors, only emit if it
443 // is the *only* error.
444 if self.parse_sess.span_diagnostic.err_count() == 0 {
445 gate_feature_post!(
446 &self,
447 type_ascription,
448 e.span,
449 "type ascription is experimental"
450 );
451 }
452 }
453 ast::ExprKind::TryBlock(_) => {
454 gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental");
455 }
456 ast::ExprKind::Block(_, opt_label) => {
457 if let Some(label) = opt_label {
458 gate_feature_post!(
459 &self,
460 label_break_value,
461 label.ident.span,
462 "labels on blocks are unstable"
463 );
464 }
465 }
466 _ => {}
467 }
468 visit::walk_expr(self, e)
469 }
470
471 fn visit_pat(&mut self, pattern: &'a ast::Pat) {
472 match &pattern.kind {
473 PatKind::Box(..) => {
474 gate_feature_post!(
475 &self,
476 box_patterns,
477 pattern.span,
478 "box pattern syntax is experimental"
479 );
480 }
481 PatKind::Range(_, _, Spanned { node: RangeEnd::Excluded, .. }) => {
482 gate_feature_post!(
483 &self,
484 exclusive_range_pattern,
485 pattern.span,
486 "exclusive range pattern syntax is experimental"
487 );
488 }
489 _ => {}
490 }
491 visit::walk_pat(self, pattern)
492 }
493
74b04a01 494 fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) {
dfeec247 495 if let Some(header) = fn_kind.header() {
74b04a01 496 // Stability of const fn methods are covered in `visit_assoc_item` below.
dfeec247 497 self.check_extern(header.ext);
74b04a01
XL
498
499 if let (ast::Const::Yes(_), ast::Extern::Implicit)
500 | (ast::Const::Yes(_), ast::Extern::Explicit(_)) = (header.constness, header.ext)
501 {
502 gate_feature_post!(
503 &self,
504 const_extern_fn,
505 span,
506 "`const extern fn` definitions are unstable"
507 );
508 }
dfeec247
XL
509 }
510
74b04a01 511 if fn_kind.ctxt() != Some(FnCtxt::Foreign) && fn_kind.decl().c_variadic() {
dfeec247
XL
512 gate_feature_post!(&self, c_variadic, span, "C-variadic functions are unstable");
513 }
514
74b04a01 515 visit::walk_fn(self, fn_kind, span)
dfeec247
XL
516 }
517
518 fn visit_generic_param(&mut self, param: &'a GenericParam) {
ba9703b0
XL
519 if let GenericParamKind::Const { .. } = param.kind {
520 gate_feature_post!(
dfeec247
XL
521 &self,
522 const_generics,
523 param.ident.span,
524 "const generics are unstable"
ba9703b0 525 )
dfeec247
XL
526 }
527 visit::walk_generic_param(self, param)
528 }
529
530 fn visit_assoc_ty_constraint(&mut self, constraint: &'a AssocTyConstraint) {
ba9703b0
XL
531 if let AssocTyConstraintKind::Bound { .. } = constraint.kind {
532 gate_feature_post!(
dfeec247
XL
533 &self,
534 associated_type_bounds,
535 constraint.span,
536 "associated type bounds are unstable"
ba9703b0 537 )
dfeec247
XL
538 }
539 visit::walk_assoc_ty_constraint(self, constraint)
540 }
541
74b04a01 542 fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) {
ba9703b0 543 let is_fn = match i.kind {
74b04a01
XL
544 ast::AssocItemKind::Fn(_, ref sig, _, _) => {
545 if let (ast::Const::Yes(_), AssocCtxt::Trait) = (sig.header.constness, ctxt) {
546 gate_feature_post!(&self, const_fn, i.span, "const fn is unstable");
dfeec247 547 }
ba9703b0 548 true
dfeec247 549 }
74b04a01
XL
550 ast::AssocItemKind::TyAlias(_, ref generics, _, ref ty) => {
551 if let (Some(_), AssocCtxt::Trait) = (ty, ctxt) {
dfeec247
XL
552 gate_feature_post!(
553 &self,
554 associated_type_defaults,
74b04a01 555 i.span,
dfeec247
XL
556 "associated type defaults are unstable"
557 );
558 }
dfeec247
XL
559 if let Some(ty) = ty {
560 self.check_impl_trait(ty);
561 }
74b04a01 562 self.check_gat(generics, i.span);
ba9703b0 563 false
dfeec247 564 }
ba9703b0
XL
565 _ => false,
566 };
567 if let ast::Defaultness::Default(_) = i.kind.defaultness() {
568 // Limit `min_specialization` to only specializing functions.
569 gate_feature_fn!(
570 &self,
571 |x: &Features| x.specialization || (is_fn && x.min_specialization),
572 i.span,
573 sym::specialization,
574 "specialization is unstable"
575 );
dfeec247 576 }
74b04a01 577 visit::walk_assoc_item(self, i, ctxt)
dfeec247
XL
578 }
579
580 fn visit_vis(&mut self, vis: &'a ast::Visibility) {
581 if let ast::VisibilityKind::Crate(ast::CrateSugar::JustCrate) = vis.node {
582 gate_feature_post!(
583 &self,
584 crate_visibility_modifier,
585 vis.span,
586 "`crate` visibility modifier is experimental"
587 );
588 }
589 visit::walk_vis(self, vis)
590 }
591}
592
593pub fn check_crate(
594 krate: &ast::Crate,
595 parse_sess: &ParseSess,
596 features: &Features,
597 unstable: UnstableFeatures,
598) {
599 maybe_stage_features(&parse_sess.span_diagnostic, krate, unstable);
600 let mut visitor = PostExpansionVisitor { parse_sess, features };
601
602 let spans = parse_sess.gated_spans.spans.borrow();
603 macro_rules! gate_all {
604 ($gate:ident, $msg:literal) => {
605 for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
606 gate_feature_post!(&visitor, $gate, *span, $msg);
607 }
608 };
609 }
610 gate_all!(let_chains, "`let` expressions in this position are experimental");
611 gate_all!(async_closure, "async closures are unstable");
612 gate_all!(generators, "yield syntax is experimental");
613 gate_all!(or_patterns, "or-patterns syntax is experimental");
dfeec247
XL
614 gate_all!(raw_ref_op, "raw address of syntax is experimental");
615 gate_all!(const_trait_bound_opt_out, "`?const` on trait bounds is experimental");
616 gate_all!(const_trait_impl, "const trait impls are experimental");
617 gate_all!(half_open_range_patterns, "half-open range patterns are unstable");
618
619 // All uses of `gate_all!` below this point were added in #65742,
620 // and subsequently disabled (with the non-early gating readded).
621 macro_rules! gate_all {
622 ($gate:ident, $msg:literal) => {
623 // FIXME(eddyb) do something more useful than always
624 // disabling these uses of early feature-gatings.
625 if false {
626 for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
627 gate_feature_post!(&visitor, $gate, *span, $msg);
628 }
629 }
630 };
631 }
632
633 gate_all!(trait_alias, "trait aliases are experimental");
634 gate_all!(associated_type_bounds, "associated type bounds are unstable");
635 gate_all!(crate_visibility_modifier, "`crate` visibility modifier is experimental");
636 gate_all!(const_generics, "const generics are unstable");
637 gate_all!(decl_macro, "`macro` is experimental");
638 gate_all!(box_patterns, "box pattern syntax is experimental");
639 gate_all!(exclusive_range_pattern, "exclusive range pattern syntax is experimental");
640 gate_all!(try_blocks, "`try` blocks are unstable");
641 gate_all!(label_break_value, "labels on blocks are unstable");
642 gate_all!(box_syntax, "box expression syntax is experimental; you can call `Box::new` instead");
643 // To avoid noise about type ascription in common syntax errors,
644 // only emit if it is the *only* error. (Also check it last.)
645 if parse_sess.span_diagnostic.err_count() == 0 {
646 gate_all!(type_ascription, "type ascription is experimental");
647 }
648
649 visit::walk_crate(&mut visitor, krate);
650}
651
652fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate, unstable: UnstableFeatures) {
653 if !unstable.is_nightly_build() {
654 for attr in krate.attrs.iter().filter(|attr| attr.check_name(sym::feature)) {
655 struct_span_err!(
656 span_handler,
657 attr.span,
658 E0554,
659 "`#![feature]` may not be used on the {} release channel",
660 option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)")
661 )
662 .emit();
663 }
664 }
665}