Skip to content

Commit 5d43134

Browse files
Check for ?const in invalid contexts during AST validation
1 parent 45eb0f8 commit 5d43134

File tree

1 file changed

+74
-0
lines changed

1 file changed

+74
-0
lines changed

src/librustc_passes/ast_validation.rs

+74
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,23 @@ use syntax::{span_err, struct_span_err, walk_list};
2424

2525
use rustc_error_codes::*;
2626

27+
#[derive(Clone, Copy)]
28+
enum BoundContext {
29+
ImplTrait,
30+
TraitBounds,
31+
TraitObject,
32+
}
33+
34+
impl BoundContext {
35+
fn description(&self) -> &'static str {
36+
match self {
37+
Self::ImplTrait => "`impl Trait`",
38+
Self::TraitBounds => "supertraits",
39+
Self::TraitObject => "trait objects",
40+
}
41+
}
42+
}
43+
2744
struct AstValidator<'a> {
2845
session: &'a Session,
2946
has_proc_macro_decls: bool,
@@ -33,6 +50,11 @@ struct AstValidator<'a> {
3350
/// e.g., `impl Iterator<Item = impl Debug>`.
3451
outer_impl_trait: Option<Span>,
3552

53+
/// Tracks the context in which a bound can appear.
54+
///
55+
/// This is used to forbid `?const Trait` bounds in certain contexts.
56+
bound_context_stack: Vec<Option<BoundContext>>,
57+
3658
/// Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
3759
/// or `Foo::Bar<impl Trait>`
3860
is_impl_trait_banned: bool,
@@ -58,9 +80,21 @@ impl<'a> AstValidator<'a> {
5880
}
5981

6082
fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) {
83+
self.bound_context_stack.push(outer.map(|_| BoundContext::ImplTrait));
6184
let old = mem::replace(&mut self.outer_impl_trait, outer);
6285
f(self);
6386
self.outer_impl_trait = old;
87+
self.bound_context_stack.pop();
88+
}
89+
90+
fn with_bound_context(&mut self, ctx: Option<BoundContext>, f: impl FnOnce(&mut Self)) {
91+
self.bound_context_stack.push(ctx);
92+
f(self);
93+
self.bound_context_stack.pop();
94+
}
95+
96+
fn innermost_bound_context(&mut self) -> Option<BoundContext> {
97+
self.bound_context_stack.iter().rev().find(|x| x.is_some()).copied().flatten()
6498
}
6599

66100
fn visit_assoc_ty_constraint_from_generic_args(&mut self, constraint: &'a AssocTyConstraint) {
@@ -84,6 +118,11 @@ impl<'a> AstValidator<'a> {
84118
TyKind::ImplTrait(..) => {
85119
self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t))
86120
}
121+
TyKind::TraitObject(..) => {
122+
self.with_bound_context(Some(BoundContext::TraitObject), |this| {
123+
visit::walk_ty(this, t)
124+
});
125+
}
87126
TyKind::Path(ref qself, ref path) => {
88127
// We allow these:
89128
// - `Option<impl Trait>`
@@ -192,6 +231,8 @@ impl<'a> AstValidator<'a> {
192231
}
193232
}
194233

234+
// FIXME(ecstaticmorse): Instead, use the `bound_context_stack` to check this in
235+
// `visit_param_bound`.
195236
fn no_questions_in_bounds(&self, bounds: &GenericBounds, where_: &str, is_trait: bool) {
196237
for bound in bounds {
197238
if let GenericBound::Trait(ref poly, TraitBoundModifier::Maybe) = *bound {
@@ -678,6 +719,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
678719
}
679720
}
680721
self.no_questions_in_bounds(bounds, "supertraits", true);
722+
723+
// Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
724+
// context for the supertraits.
725+
self.visit_generics(generics);
726+
self.with_bound_context(Some(BoundContext::TraitBounds), |this| {
727+
walk_list!(this, visit_param_bound, bounds);
728+
});
729+
walk_list!(self, visit_trait_item, trait_items);
730+
return;
681731
}
682732
ItemKind::Mod(_) => {
683733
// Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
@@ -822,6 +872,29 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
822872
visit::walk_generic_param(self, param);
823873
}
824874

875+
fn visit_param_bound(&mut self, bound: &'a GenericBound) {
876+
if let GenericBound::Trait(poly, maybe_bound) = bound {
877+
match poly.trait_ref.constness {
878+
Some(Constness::NotConst) => {
879+
if *maybe_bound == TraitBoundModifier::Maybe {
880+
self.err_handler()
881+
.span_err(bound.span(), "`?const` and `?` are mutually exclusive");
882+
}
883+
884+
if let Some(ctx) = self.innermost_bound_context() {
885+
let msg = format!("`?const` is not permitted in {}", ctx.description());
886+
self.err_handler().span_err(bound.span(), &msg);
887+
}
888+
}
889+
890+
Some(Constness::Const) => bug!("Parser should reject bare `const` on bounds"),
891+
None => {}
892+
}
893+
}
894+
895+
visit::walk_param_bound(self, bound)
896+
}
897+
825898
fn visit_pat(&mut self, pat: &'a Pat) {
826899
match pat.kind {
827900
PatKind::Lit(ref expr) => {
@@ -930,6 +1003,7 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut lint::LintBuffe
9301003
session,
9311004
has_proc_macro_decls: false,
9321005
outer_impl_trait: None,
1006+
bound_context_stack: Vec::new(),
9331007
is_impl_trait_banned: false,
9341008
is_assoc_ty_bound_banned: false,
9351009
lint_buffer: lints,

0 commit comments

Comments
 (0)