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