From 5fd4f5bceb456bd3681381bb390562466fdc4356 Mon Sep 17 00:00:00 2001 From: Wilco Kusee Date: Fri, 27 Jan 2023 20:45:03 +0100 Subject: [PATCH 01/13] Add candidates for DiscriminantKind builtin --- .../src/solve/assembly.rs | 8 ++++++++ .../src/solve/project_goals.rs | 20 +++++++++++++++++++ .../src/solve/trait_goals.rs | 8 ++++++++ 3 files changed, 36 insertions(+) diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 5690b6536bb..ccdf6246083 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -81,6 +81,7 @@ pub(super) enum CandidateSource { AliasBound, } +/// Methods used to assemble candidates for either trait or projection goals. pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq { fn self_ty(self) -> Ty<'tcx>; @@ -188,6 +189,11 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> Vec>; + + fn consider_builtin_discriminant_kind_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; } impl<'tcx> EvalCtxt<'_, 'tcx> { @@ -320,6 +326,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_builtin_generator_candidate(self, goal) } else if lang_items.unsize_trait() == Some(trait_def_id) { G::consider_builtin_unsize_candidate(self, goal) + } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { + G::consider_builtin_discriminant_kind_candidate(self, goal) } else { Err(NoSolution) }; diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index a23fdd24b4e..48627ee6096 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -581,6 +581,26 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) -> Vec> { bug!("`Unsize` does not have an associated type: {:?}", goal); } + + fn consider_builtin_discriminant_kind_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let self_ty = goal.predicate.self_ty(); + + let tcx = ecx.tcx(); + let term = self_ty.discriminant_ty(tcx).into(); + + Self::consider_assumption( + ecx, + goal, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty: tcx.mk_alias_ty(goal.predicate.def_id(), [self_ty]), + term, + }) + .to_predicate(tcx), + ) + } } /// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 29ee9da38e0..0003dfeaee7 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -439,6 +439,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { responses } + + fn consider_builtin_discriminant_kind_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + _goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + // `DiscriminantKind` is automatically implemented for every type. + ecx.make_canonical_response(Certainty::Yes) + } } impl<'tcx> EvalCtxt<'_, 'tcx> { From de50a86a12d6db76e7fec4c8f15e17ae199acb7e Mon Sep 17 00:00:00 2001 From: Wilco Kusee Date: Wed, 1 Feb 2023 17:13:57 +0100 Subject: [PATCH 02/13] Simplify discriminant_kind goal using new helper function --- .../src/solve/project_goals.rs | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 48627ee6096..170b560d7b6 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -586,20 +586,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - let self_ty = goal.predicate.self_ty(); - - let tcx = ecx.tcx(); - let term = self_ty.discriminant_ty(tcx).into(); - - Self::consider_assumption( - ecx, - goal, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty: tcx.mk_alias_ty(goal.predicate.def_id(), [self_ty]), - term, - }) - .to_predicate(tcx), - ) + let discriminant = goal.predicate.self_ty().discriminant_ty(ecx.tcx()); + let nested_goals = ecx.infcx.eq( + goal.param_env, + goal.predicate.term.ty().expect("expected ty goal"), + discriminant, + )?; + ecx.evaluate_all_and_make_canonical_response(nested_goals) } } From 64f5293956de9dc4e336906f1c7fef8415c292de Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 2 Feb 2023 05:49:07 +0000 Subject: [PATCH 03/13] Don't cause a cycle when formatting query description that references a FnDef --- compiler/rustc_middle/src/ty/print/pretty.rs | 23 +++++++++++++++---- compiler/rustc_query_impl/src/plumbing.rs | 13 +++++++---- tests/ui/async-await/no-const-async.stderr | 4 ++-- tests/ui/impl-trait/auto-trait-leak.stderr | 4 ++-- .../ui/parser/fn-header-semantic-fail.stderr | 12 +++++----- ...no-query-in-printing-during-query-descr.rs | 6 +++++ ...uery-in-printing-during-query-descr.stderr | 9 ++++++++ 7 files changed, 51 insertions(+), 20 deletions(-) create mode 100644 tests/ui/query-system/no-query-in-printing-during-query-descr.rs create mode 100644 tests/ui/query-system/no-query-in-printing-during-query-descr.stderr diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 6a20f62b6f9..cb47522877c 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -675,8 +675,12 @@ pub trait PrettyPrinter<'tcx>: p!(")") } ty::FnDef(def_id, substs) => { - let sig = self.tcx().fn_sig(def_id).subst(self.tcx(), substs); - p!(print(sig), " {{", print_value_path(def_id, substs), "}}"); + if NO_QUERIES.with(|q| q.get()) { + p!(print_def_path(def_id, substs)); + } else { + let sig = self.tcx().fn_sig(def_id).subst(self.tcx(), substs); + p!(print(sig), " {{", print_value_path(def_id, substs), "}}"); + } } ty::FnPtr(ref bare_fn) => p!(print(bare_fn)), ty::Infer(infer_ty) => { @@ -732,13 +736,13 @@ pub trait PrettyPrinter<'tcx>: } ty::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)), ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => { - // FIXME(eddyb) print this with `print_def_path`. // We use verbose printing in 'NO_QUERIES' mode, to // avoid needing to call `predicates_of`. This should // only affect certain debug messages (e.g. messages printed // from `rustc_middle::ty` during the computation of `tcx.predicates_of`), // and should have no effect on any compiler output. - if self.should_print_verbose() || NO_QUERIES.with(|q| q.get()) { + if self.should_print_verbose() { + // FIXME(eddyb) print this with `print_def_path`. p!(write("Opaque({:?}, {:?})", def_id, substs)); return Ok(self); } @@ -746,6 +750,8 @@ pub trait PrettyPrinter<'tcx>: let parent = self.tcx().parent(def_id); match self.tcx().def_kind(parent) { DefKind::TyAlias | DefKind::AssocTy => { + // NOTE: I know we should check for NO_QUERIES here, but it's alright. + // `type_of` on a TAIT should never cause a cycle. if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: d, .. }) = *self.tcx().type_of(parent).kind() { @@ -760,7 +766,14 @@ pub trait PrettyPrinter<'tcx>: p!(print_def_path(def_id, substs)); return Ok(self); } - _ => return self.pretty_print_opaque_impl_type(def_id, substs), + _ => { + if NO_QUERIES.with(|q| q.get()) { + p!(print_def_path(def_id, &[])); + return Ok(self); + } else { + return self.pretty_print_opaque_impl_type(def_id, substs); + } + } } } ty::Str => p!("str"), diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 6125ad4eff1..4dea03c1ef6 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -314,11 +314,14 @@ pub(crate) fn create_query_frame< kind: DepKind, name: &'static str, ) -> QueryStackFrame { - // Disable visible paths printing for performance reasons. - // Showing visible path instead of any path is not that important in production. - let description = ty::print::with_no_visible_paths!( - // Force filename-line mode to avoid invoking `type_of` query. - ty::print::with_forced_impl_filename_line!(do_describe(tcx.tcx, key)) + // Avoid calling queries while formatting the description + let description = ty::print::with_no_queries!( + // Disable visible paths printing for performance reasons. + // Showing visible path instead of any path is not that important in production. + ty::print::with_no_visible_paths!( + // Force filename-line mode to avoid invoking `type_of` query. + ty::print::with_forced_impl_filename_line!(do_describe(tcx.tcx, key)) + ) ); let description = if tcx.sess.verbose() { format!("{description} [{name:?}]") } else { description }; diff --git a/tests/ui/async-await/no-const-async.stderr b/tests/ui/async-await/no-const-async.stderr index c5bd520aaea..71c228958f6 100644 --- a/tests/ui/async-await/no-const-async.stderr +++ b/tests/ui/async-await/no-const-async.stderr @@ -28,8 +28,8 @@ note: ...which requires const checking `x`... | LL | pub const async fn x() {} | ^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing whether `impl core::future::future::Future` is freeze... - = note: ...which requires evaluating trait selection obligation `impl core::future::future::Future: core::marker::Freeze`... + = note: ...which requires computing whether `x::{opaque#0}` is freeze... + = note: ...which requires evaluating trait selection obligation `x::{opaque#0}: core::marker::Freeze`... = note: ...which again requires computing type of `x::{opaque#0}`, completing the cycle note: cycle used when checking item types in top-level module --> $DIR/no-const-async.rs:4:1 diff --git a/tests/ui/impl-trait/auto-trait-leak.stderr b/tests/ui/impl-trait/auto-trait-leak.stderr index feedfc40aaf..fd0358421eb 100644 --- a/tests/ui/impl-trait/auto-trait-leak.stderr +++ b/tests/ui/impl-trait/auto-trait-leak.stderr @@ -39,7 +39,7 @@ note: ...which requires type-checking `cycle1`... | LL | send(cycle2().clone()); | ^^^^ - = note: ...which requires evaluating trait selection obligation `impl core::clone::Clone: core::marker::Send`... + = note: ...which requires evaluating trait selection obligation `cycle2::{opaque#0}: core::marker::Send`... note: ...which requires computing type of `cycle2::{opaque#0}`... --> $DIR/auto-trait-leak.rs:19:16 | @@ -80,7 +80,7 @@ note: ...which requires type-checking `cycle2`... | LL | send(cycle1().clone()); | ^^^^ - = note: ...which requires evaluating trait selection obligation `impl core::clone::Clone: core::marker::Send`... + = note: ...which requires evaluating trait selection obligation `cycle1::{opaque#0}: core::marker::Send`... = note: ...which again requires computing type of `cycle1::{opaque#0}`, completing the cycle note: cycle used when checking item types in top-level module --> $DIR/auto-trait-leak.rs:1:1 diff --git a/tests/ui/parser/fn-header-semantic-fail.stderr b/tests/ui/parser/fn-header-semantic-fail.stderr index 038fdfb2d51..2d8bd19a731 100644 --- a/tests/ui/parser/fn-header-semantic-fail.stderr +++ b/tests/ui/parser/fn-header-semantic-fail.stderr @@ -209,8 +209,8 @@ note: ...which requires const checking `main::ff5`... | LL | const async unsafe extern "C" fn ff5() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing whether `impl core::future::future::Future` is freeze... - = note: ...which requires evaluating trait selection obligation `impl core::future::future::Future: core::marker::Freeze`... + = note: ...which requires computing whether `main::ff5::{opaque#0}` is freeze... + = note: ...which requires evaluating trait selection obligation `main::ff5::{opaque#0}: core::marker::Freeze`... = note: ...which again requires computing type of `main::ff5::{opaque#0}`, completing the cycle note: cycle used when checking item types in top-level module --> $DIR/fn-header-semantic-fail.rs:5:1 @@ -245,8 +245,8 @@ note: ...which requires const checking `main::` is freeze... - = note: ...which requires evaluating trait selection obligation `impl core::future::future::Future: core::marker::Freeze`... + = note: ...which requires computing whether `main::::ft5::{opaque#0}` is freeze... + = note: ...which requires evaluating trait selection obligation `main::::ft5::{opaque#0}: core::marker::Freeze`... = note: ...which again requires computing type of `main::::ft5::{opaque#0}`, completing the cycle note: cycle used when checking item types in top-level module --> $DIR/fn-header-semantic-fail.rs:5:1 @@ -281,8 +281,8 @@ note: ...which requires const checking `main::` is freeze... - = note: ...which requires evaluating trait selection obligation `impl core::future::future::Future: core::marker::Freeze`... + = note: ...which requires computing whether `main::::fi5::{opaque#0}` is freeze... + = note: ...which requires evaluating trait selection obligation `main::::fi5::{opaque#0}: core::marker::Freeze`... = note: ...which again requires computing type of `main::::fi5::{opaque#0}`, completing the cycle note: cycle used when checking item types in top-level module --> $DIR/fn-header-semantic-fail.rs:5:1 diff --git a/tests/ui/query-system/no-query-in-printing-during-query-descr.rs b/tests/ui/query-system/no-query-in-printing-during-query-descr.rs new file mode 100644 index 00000000000..45b7043e2f6 --- /dev/null +++ b/tests/ui/query-system/no-query-in-printing-during-query-descr.rs @@ -0,0 +1,6 @@ +fn a() -> _ { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + &a +} + +fn main() {} diff --git a/tests/ui/query-system/no-query-in-printing-during-query-descr.stderr b/tests/ui/query-system/no-query-in-printing-during-query-descr.stderr new file mode 100644 index 00000000000..35e608b6b23 --- /dev/null +++ b/tests/ui/query-system/no-query-in-printing-during-query-descr.stderr @@ -0,0 +1,9 @@ +error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types + --> $DIR/no-query-in-printing-during-query-descr.rs:1:11 + | +LL | fn a() -> _ { + | ^ not allowed in type signatures + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0121`. From c3a71ede7cc459b84ddac53f3435391712fa1f12 Mon Sep 17 00:00:00 2001 From: Aidan Olsen Date: Tue, 31 Jan 2023 16:48:08 -0700 Subject: [PATCH 04/13] Emit warnings on unused parens/braces in index expressions --- compiler/rustc_lint/src/unused.rs | 4 +++ tests/ui/lint/unused/issue-96606.rs | 8 ++++++ tests/ui/lint/unused/issue-96606.stderr | 33 +++++++++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 tests/ui/lint/unused/issue-96606.rs create mode 100644 tests/ui/lint/unused/issue-96606.stderr diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 4c9b3df2dbd..d829ca43328 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -495,6 +495,7 @@ enum UnusedDelimsCtx { ArrayLenExpr, AnonConst, MatchArmExpr, + IndexExpr, } impl From for &'static str { @@ -514,6 +515,7 @@ impl From for &'static str { UnusedDelimsCtx::LetScrutineeExpr => "`let` scrutinee expression", UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression", UnusedDelimsCtx::MatchArmExpr => "match arm expression", + UnusedDelimsCtx::IndexExpr => "index expression", } } } @@ -733,6 +735,8 @@ trait UnusedDelimLint { (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None) } + Index(_, ref value) => (value, UnusedDelimsCtx::IndexExpr, false, None, None), + Assign(_, ref value, _) | AssignOp(.., ref value) => { (value, UnusedDelimsCtx::AssignedValue, false, None, None) } diff --git a/tests/ui/lint/unused/issue-96606.rs b/tests/ui/lint/unused/issue-96606.rs new file mode 100644 index 00000000000..4e7c290fa2a --- /dev/null +++ b/tests/ui/lint/unused/issue-96606.rs @@ -0,0 +1,8 @@ +#[deny(unused)] +fn main() { + let arr = [0; 10]; + let _ = arr[(0)]; //~ ERROR unnecessary parentheses around index expression + let _ = arr[{0}]; //~ ERROR unnecessary braces around index expression + let _ = arr[1 + (0)]; + let _ = arr[{ let x = 0; x }]; +} diff --git a/tests/ui/lint/unused/issue-96606.stderr b/tests/ui/lint/unused/issue-96606.stderr new file mode 100644 index 00000000000..e3627718116 --- /dev/null +++ b/tests/ui/lint/unused/issue-96606.stderr @@ -0,0 +1,33 @@ +error: unnecessary parentheses around index expression + --> $DIR/issue-96606.rs:4:17 + | +LL | let _ = arr[(0)]; + | ^ ^ + | +note: the lint level is defined here + --> $DIR/issue-96606.rs:1:8 + | +LL | #[deny(unused)] + | ^^^^^^ + = note: `#[deny(unused_parens)]` implied by `#[deny(unused)]` +help: remove these parentheses + | +LL - let _ = arr[(0)]; +LL + let _ = arr[0]; + | + +error: unnecessary braces around index expression + --> $DIR/issue-96606.rs:5:17 + | +LL | let _ = arr[{0}]; + | ^ ^ + | + = note: `#[deny(unused_braces)]` implied by `#[deny(unused)]` +help: remove these braces + | +LL - let _ = arr[{0}]; +LL + let _ = arr[0]; + | + +error: aborting due to 2 previous errors + From 745d60c239d7cf66f1bebca921cf0df0c052ade0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 2 Feb 2023 15:02:21 -0800 Subject: [PATCH 05/13] Tweak misleading comment --- compiler/rustc_middle/src/ty/print/pretty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index cb47522877c..4ed58e221e6 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -751,7 +751,7 @@ pub trait PrettyPrinter<'tcx>: match self.tcx().def_kind(parent) { DefKind::TyAlias | DefKind::AssocTy => { // NOTE: I know we should check for NO_QUERIES here, but it's alright. - // `type_of` on a TAIT should never cause a cycle. + // `type_of` on a type alias or assoc type should never cause a cycle. if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: d, .. }) = *self.tcx().type_of(parent).kind() { From af1d16e82ddc27166438e8bc2a520087862f12af Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 1 Feb 2023 10:53:00 +1100 Subject: [PATCH 06/13] Improve doc comment desugaring. Sometimes the parser needs to desugar a doc comment into `#[doc = r"foo"]`. Currently it does this in a hacky way: by pushing a "fake" new frame (one without a delimiter) onto the `TokenCursor` stack. This commit changes things so that the token stream itself is modified in place. The nice thing about this is that it means `TokenCursorFrame::delim_sp` is now only `None` for the outermost frame. --- compiler/rustc_ast/src/tokenstream.rs | 9 +++++ compiler/rustc_parse/src/parser/mod.rs | 48 +++++++++++--------------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index fabd43a1618..dd01fc8ffc5 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -614,6 +614,15 @@ impl Cursor { pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> { self.stream.0.get(self.index + n) } + + // Replace the previously obtained token tree with `tts`, and rewind to + // just before them. + pub fn replace_prev_and_rewind(&mut self, tts: Vec) { + assert!(self.index > 0); + self.index -= 1; + let stream = Lrc::make_mut(&mut self.stream.0); + stream.splice(self.index..self.index + 1, tts); + } } #[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)] diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index ffb23b50a16..0499a56a09d 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -224,7 +224,7 @@ impl<'a> Drop for Parser<'a> { #[derive(Clone)] struct TokenCursor { // The current (innermost) frame. `frame` and `stack` could be combined, - // but it's faster to have them separately to access `frame` directly + // but it's faster to keep them separate and access `frame` directly // rather than via something like `stack.last().unwrap()` or // `stack[stack.len() - 1]`. frame: TokenCursorFrame, @@ -259,6 +259,7 @@ struct TokenCursor { #[derive(Clone)] struct TokenCursorFrame { + // This is `None` only for the outermost frame. delim_sp: Option<(Delimiter, DelimSpan)>, tree_cursor: tokenstream::Cursor, } @@ -285,7 +286,9 @@ impl TokenCursor { match tree { &TokenTree::Token(ref token, spacing) => match (desugar_doc_comments, token) { (true, &Token { kind: token::DocComment(_, attr_style, data), span }) => { - return self.desugar(attr_style, data, span); + let desugared = self.desugar(attr_style, data, span); + self.frame.tree_cursor.replace_prev_and_rewind(desugared); + // Continue to get the first token of the desugared doc comment. } _ => return (token.clone(), spacing), }, @@ -300,19 +303,22 @@ impl TokenCursor { } }; } else if let Some(frame) = self.stack.pop() { - if let Some((delim, span)) = self.frame.delim_sp && delim != Delimiter::Invisible { - self.frame = frame; + // We have exhausted this frame. Move back to its parent frame. + let (delim, span) = self.frame.delim_sp.unwrap(); + self.frame = frame; + if delim != Delimiter::Invisible { return (Token::new(token::CloseDelim(delim), span.close), Spacing::Alone); } - self.frame = frame; // No close delimiter to return; continue on to the next iteration. } else { + // We have exhausted the outermost frame. return (Token::new(token::Eof, DUMMY_SP), Spacing::Alone); } } } - fn desugar(&mut self, attr_style: AttrStyle, data: Symbol, span: Span) -> (Token, Spacing) { + // Desugar a doc comment into something like `#[doc = r"foo"]`. + fn desugar(&mut self, attr_style: AttrStyle, data: Symbol, span: Span) -> Vec { // Searches for the occurrences of `"#*` and returns the minimum number of `#`s // required to wrap the text. E.g. // - `abc d` is wrapped as `r"abc d"` (num_of_hashes = 0) @@ -346,27 +352,15 @@ impl TokenCursor { .collect::(), ); - self.stack.push(mem::replace( - &mut self.frame, - TokenCursorFrame::new( - None, - if attr_style == AttrStyle::Inner { - [ - TokenTree::token_alone(token::Pound, span), - TokenTree::token_alone(token::Not, span), - body, - ] - .into_iter() - .collect::() - } else { - [TokenTree::token_alone(token::Pound, span), body] - .into_iter() - .collect::() - }, - ), - )); - - self.next(/* desugar_doc_comments */ false) + if attr_style == AttrStyle::Inner { + vec![ + TokenTree::token_alone(token::Pound, span), + TokenTree::token_alone(token::Not, span), + body, + ] + } else { + vec![TokenTree::token_alone(token::Pound, span), body] + } } } From b23f272db017c3bfd8cdf57fad6e5fdd057168c6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 1 Feb 2023 11:21:28 +1100 Subject: [PATCH 07/13] Make clear that `TokenTree::Token` shouldn't contain a delimiter. --- compiler/rustc_ast/src/tokenstream.rs | 3 ++- compiler/rustc_parse/src/parser/mod.rs | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index dd01fc8ffc5..ab554d70c46 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -41,7 +41,8 @@ use std::{fmt, iter}; /// Nothing special happens to misnamed or misplaced `SubstNt`s. #[derive(Debug, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)] pub enum TokenTree { - /// A single token. + /// A single token. Should never be `OpenDelim` or `CloseDelim`, because + /// delimiters are implicitly represented by `Delimited`. Token(Token, Spacing), /// A delimited sequence of token trees. Delimited(DelimSpan, Delimiter, TokenStream), diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 0499a56a09d..510588f8a5f 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -290,7 +290,13 @@ impl TokenCursor { self.frame.tree_cursor.replace_prev_and_rewind(desugared); // Continue to get the first token of the desugared doc comment. } - _ => return (token.clone(), spacing), + _ => { + debug_assert!(!matches!( + token.kind, + token::OpenDelim(_) | token::CloseDelim(_) + )); + return (token.clone(), spacing); + } }, &TokenTree::Delimited(sp, delim, ref tts) => { // Set `open_delim` to true here because we deal with it immediately. From b5ecbbb998aae80541d70bf5c93be92e6c59a7a8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 1 Feb 2023 12:43:13 +1100 Subject: [PATCH 08/13] Remove `TokenCursorFrame`. The motivation here is to eliminate the `Option<(Delimiter, DelimSpan)>`, which is `None` for the outermost token stream and `Some` for all other token streams. We are already treating the innermost frame specially -- this is the `frame` vs `stack` distinction in `TokenCursor`. We can push that further so that `frame` only contains the cursor, and `stack` elements contain the delimiters for their children. When we are in the outermost token stream `stack` is empty, so there are no stored delimiters, which is what we want because the outermost token stream *has* no delimiters. This change also shrinks `TokenCursor`, which shrinks `Parser` and `LazyAttrTokenStreamImpl`, which is nice. --- .../rustc_parse/src/parser/attr_wrapper.rs | 2 +- compiler/rustc_parse/src/parser/expr.rs | 2 +- compiler/rustc_parse/src/parser/mod.rs | 72 +++++++++---------- 3 files changed, 34 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index b97f22417cb..dbd3b76786f 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -469,6 +469,6 @@ mod size_asserts { use rustc_data_structures::static_assert_size; // tidy-alphabetical-start static_assert_size!(AttrWrapper, 16); - static_assert_size!(LazyAttrTokenStreamImpl, 144); + static_assert_size!(LazyAttrTokenStreamImpl, 120); // tidy-alphabetical-end } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index b7a023868fc..68849f817aa 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2141,7 +2141,7 @@ impl<'a> Parser<'a> { } if self.token.kind == TokenKind::Semi - && matches!(self.token_cursor.frame.delim_sp, Some((Delimiter::Parenthesis, _))) + && matches!(self.token_cursor.stack.last(), Some((_, Delimiter::Parenthesis, _))) && self.may_recover() { // It is likely that the closure body is a block but where the diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 510588f8a5f..982fde727c8 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -168,7 +168,7 @@ pub struct Parser<'a> { // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure // it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Parser<'_>, 336); +rustc_data_structures::static_assert_size!(Parser<'_>, 312); /// Stores span information about a closure. #[derive(Clone)] @@ -223,16 +223,21 @@ impl<'a> Drop for Parser<'a> { #[derive(Clone)] struct TokenCursor { - // The current (innermost) frame. `frame` and `stack` could be combined, - // but it's faster to keep them separate and access `frame` directly - // rather than via something like `stack.last().unwrap()` or - // `stack[stack.len() - 1]`. - frame: TokenCursorFrame, - // Additional frames that enclose `frame`. - stack: Vec, + // Cursor for the current (innermost) token stream. The delimiters for this + // token stream are found in `self.stack.last()`; when that is `None` then + // we are in the outermost token stream which never has delimiters. + tree_cursor: tokenstream::Cursor, + + // Token streams surrounding the current one. The delimiters for stack[n]'s + // tokens are in `stack[n-1]`. `stack[0]` (when present) has no delimiters + // because it's the outermost token stream which never has delimiters. + stack: Vec<(tokenstream::Cursor, Delimiter, DelimSpan)>, + desugar_doc_comments: bool, + // Counts the number of calls to `{,inlined_}next`. num_next_calls: usize, + // During parsing, we may sometimes need to 'unglue' a // glued token into two component tokens // (e.g. '>>' into '>' and '>), so that the parser @@ -257,19 +262,6 @@ struct TokenCursor { break_last_token: bool, } -#[derive(Clone)] -struct TokenCursorFrame { - // This is `None` only for the outermost frame. - delim_sp: Option<(Delimiter, DelimSpan)>, - tree_cursor: tokenstream::Cursor, -} - -impl TokenCursorFrame { - fn new(delim_sp: Option<(Delimiter, DelimSpan)>, tts: TokenStream) -> Self { - TokenCursorFrame { delim_sp, tree_cursor: tts.into_trees() } - } -} - impl TokenCursor { fn next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) { self.inlined_next(desugar_doc_comments) @@ -282,12 +274,12 @@ impl TokenCursor { // FIXME: we currently don't return `Delimiter` open/close delims. To fix #67062 we will // need to, whereupon the `delim != Delimiter::Invisible` conditions below can be // removed. - if let Some(tree) = self.frame.tree_cursor.next_ref() { + if let Some(tree) = self.tree_cursor.next_ref() { match tree { &TokenTree::Token(ref token, spacing) => match (desugar_doc_comments, token) { (true, &Token { kind: token::DocComment(_, attr_style, data), span }) => { let desugared = self.desugar(attr_style, data, span); - self.frame.tree_cursor.replace_prev_and_rewind(desugared); + self.tree_cursor.replace_prev_and_rewind(desugared); // Continue to get the first token of the desugared doc comment. } _ => { @@ -299,25 +291,23 @@ impl TokenCursor { } }, &TokenTree::Delimited(sp, delim, ref tts) => { - // Set `open_delim` to true here because we deal with it immediately. - let frame = TokenCursorFrame::new(Some((delim, sp)), tts.clone()); - self.stack.push(mem::replace(&mut self.frame, frame)); + let trees = tts.clone().into_trees(); + self.stack.push((mem::replace(&mut self.tree_cursor, trees), delim, sp)); if delim != Delimiter::Invisible { return (Token::new(token::OpenDelim(delim), sp.open), Spacing::Alone); } // No open delimiter to return; continue on to the next iteration. } }; - } else if let Some(frame) = self.stack.pop() { - // We have exhausted this frame. Move back to its parent frame. - let (delim, span) = self.frame.delim_sp.unwrap(); - self.frame = frame; + } else if let Some((tree_cursor, delim, span)) = self.stack.pop() { + // We have exhausted this token stream. Move back to its parent token stream. + self.tree_cursor = tree_cursor; if delim != Delimiter::Invisible { return (Token::new(token::CloseDelim(delim), span.close), Spacing::Alone); } // No close delimiter to return; continue on to the next iteration. } else { - // We have exhausted the outermost frame. + // We have exhausted the outermost token stream. return (Token::new(token::Eof, DUMMY_SP), Spacing::Alone); } } @@ -475,7 +465,7 @@ impl<'a> Parser<'a> { restrictions: Restrictions::empty(), expected_tokens: Vec::new(), token_cursor: TokenCursor { - frame: TokenCursorFrame::new(None, tokens), + tree_cursor: tokens.into_trees(), stack: Vec::new(), num_next_calls: 0, desugar_doc_comments, @@ -1142,14 +1132,16 @@ impl<'a> Parser<'a> { return looker(&self.token); } - let frame = &self.token_cursor.frame; - if let Some((delim, span)) = frame.delim_sp && delim != Delimiter::Invisible { + let tree_cursor = &self.token_cursor.tree_cursor; + if let Some(&(_, delim, span)) = self.token_cursor.stack.last() + && delim != Delimiter::Invisible + { let all_normal = (0..dist).all(|i| { - let token = frame.tree_cursor.look_ahead(i); + let token = tree_cursor.look_ahead(i); !matches!(token, Some(TokenTree::Delimited(_, Delimiter::Invisible, _))) }); if all_normal { - return match frame.tree_cursor.look_ahead(dist - 1) { + return match tree_cursor.look_ahead(dist - 1) { Some(tree) => match tree { TokenTree::Token(token, _) => looker(token), TokenTree::Delimited(dspan, delim, _) => { @@ -1310,10 +1302,10 @@ impl<'a> Parser<'a> { pub(crate) fn parse_token_tree(&mut self) -> TokenTree { match self.token.kind { token::OpenDelim(..) => { - // Grab the tokens from this frame. - let frame = &self.token_cursor.frame; - let stream = frame.tree_cursor.stream.clone(); - let (delim, span) = frame.delim_sp.unwrap(); + // Grab the tokens within the delimiters. + let tree_cursor = &self.token_cursor.tree_cursor; + let stream = tree_cursor.stream.clone(); + let (_, delim, span) = *self.token_cursor.stack.last().unwrap(); // Advance the token cursor through the entire delimited // sequence. After getting the `OpenDelim` we are *within* the From a86fc727fa9b9fa1ac60b67147736783b3376e91 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 1 Feb 2023 12:58:04 +1100 Subject: [PATCH 09/13] Rename `Cursor`/`CursorRef` as `TokenTreeCursor`/`RefTokenTreeCursor`. This makes it clear they return token trees, and makes for a nice comparison against `TokenCursor` which returns tokens. --- compiler/rustc_ast/src/tokenstream.rs | 30 ++++++++++--------- compiler/rustc_expand/src/mbe/metavar_expr.rs | 12 ++++---- compiler/rustc_parse/src/parser/mod.rs | 13 ++++---- src/tools/rustfmt/src/macros.rs | 8 ++--- 4 files changed, 34 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index ab554d70c46..f0a6a5e0725 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -389,12 +389,12 @@ impl TokenStream { self.0.len() } - pub fn trees(&self) -> CursorRef<'_> { - CursorRef::new(self) + pub fn trees(&self) -> RefTokenTreeCursor<'_> { + RefTokenTreeCursor::new(self) } - pub fn into_trees(self) -> Cursor { - Cursor::new(self) + pub fn into_trees(self) -> TokenTreeCursor { + TokenTreeCursor::new(self) } /// Compares two `TokenStream`s, checking equality without regarding span information. @@ -552,16 +552,17 @@ impl TokenStream { } } -/// By-reference iterator over a [`TokenStream`]. +/// By-reference iterator over a [`TokenStream`], that produces `&TokenTree` +/// items. #[derive(Clone)] -pub struct CursorRef<'t> { +pub struct RefTokenTreeCursor<'t> { stream: &'t TokenStream, index: usize, } -impl<'t> CursorRef<'t> { +impl<'t> RefTokenTreeCursor<'t> { fn new(stream: &'t TokenStream) -> Self { - CursorRef { stream, index: 0 } + RefTokenTreeCursor { stream, index: 0 } } pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> { @@ -569,7 +570,7 @@ impl<'t> CursorRef<'t> { } } -impl<'t> Iterator for CursorRef<'t> { +impl<'t> Iterator for RefTokenTreeCursor<'t> { type Item = &'t TokenTree; fn next(&mut self) -> Option<&'t TokenTree> { @@ -580,15 +581,16 @@ impl<'t> Iterator for CursorRef<'t> { } } -/// Owning by-value iterator over a [`TokenStream`]. +/// Owning by-value iterator over a [`TokenStream`], that produces `TokenTree` +/// items. // FIXME: Many uses of this can be replaced with by-reference iterator to avoid clones. #[derive(Clone)] -pub struct Cursor { +pub struct TokenTreeCursor { pub stream: TokenStream, index: usize, } -impl Iterator for Cursor { +impl Iterator for TokenTreeCursor { type Item = TokenTree; fn next(&mut self) -> Option { @@ -599,9 +601,9 @@ impl Iterator for Cursor { } } -impl Cursor { +impl TokenTreeCursor { fn new(stream: TokenStream) -> Self { - Cursor { stream, index: 0 } + TokenTreeCursor { stream, index: 0 } } #[inline] diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index 99fe474541e..de34df0114a 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -1,5 +1,5 @@ use rustc_ast::token::{self, Delimiter}; -use rustc_ast::tokenstream::{CursorRef, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree}; use rustc_ast::{LitIntType, LitKind}; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, PResult}; @@ -72,7 +72,7 @@ impl MetaVarExpr { // Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}` fn check_trailing_token<'sess>( - iter: &mut CursorRef<'_>, + iter: &mut RefTokenTreeCursor<'_>, sess: &'sess ParseSess, ) -> PResult<'sess, ()> { if let Some(tt) = iter.next() { @@ -88,7 +88,7 @@ fn check_trailing_token<'sess>( /// Parse a meta-variable `count` expression: `count(ident[, depth])` fn parse_count<'sess>( - iter: &mut CursorRef<'_>, + iter: &mut RefTokenTreeCursor<'_>, sess: &'sess ParseSess, span: Span, ) -> PResult<'sess, MetaVarExpr> { @@ -99,7 +99,7 @@ fn parse_count<'sess>( /// Parses the depth used by index(depth) and length(depth). fn parse_depth<'sess>( - iter: &mut CursorRef<'_>, + iter: &mut RefTokenTreeCursor<'_>, sess: &'sess ParseSess, span: Span, ) -> PResult<'sess, usize> { @@ -126,7 +126,7 @@ fn parse_depth<'sess>( /// Parses an generic ident fn parse_ident<'sess>( - iter: &mut CursorRef<'_>, + iter: &mut RefTokenTreeCursor<'_>, sess: &'sess ParseSess, span: Span, ) -> PResult<'sess, Ident> { @@ -152,7 +152,7 @@ fn parse_ident<'sess>( /// Tries to move the iterator forward returning `true` if there is a comma. If not, then the /// iterator is not modified and the result is `false`. -fn try_eat_comma(iter: &mut CursorRef<'_>) -> bool { +fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool { if let Some(TokenTree::Token(token::Token { kind: token::Comma, .. }, _)) = iter.look_ahead(0) { let _ = iter.next(); return true; diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 982fde727c8..2ea55f838a3 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -19,9 +19,8 @@ pub use path::PathStyle; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind}; -use rustc_ast::tokenstream::AttributesData; -use rustc_ast::tokenstream::{self, DelimSpan, Spacing}; -use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::tokenstream::{AttributesData, DelimSpan, Spacing}; +use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor}; use rustc_ast::util::case::Case; use rustc_ast::AttrId; use rustc_ast::DUMMY_NODE_ID; @@ -221,17 +220,21 @@ impl<'a> Drop for Parser<'a> { } } +/// Iterator over a `TokenStream` that produces `Token`s. It's a bit odd that +/// we (a) lex tokens into a nice tree structure (`TokenStream`), and then (b) +/// use this type to emit them as a linear sequence. But a linear sequence is +/// what the parser expects, for the most part. #[derive(Clone)] struct TokenCursor { // Cursor for the current (innermost) token stream. The delimiters for this // token stream are found in `self.stack.last()`; when that is `None` then // we are in the outermost token stream which never has delimiters. - tree_cursor: tokenstream::Cursor, + tree_cursor: TokenTreeCursor, // Token streams surrounding the current one. The delimiters for stack[n]'s // tokens are in `stack[n-1]`. `stack[0]` (when present) has no delimiters // because it's the outermost token stream which never has delimiters. - stack: Vec<(tokenstream::Cursor, Delimiter, DelimSpan)>, + stack: Vec<(TokenTreeCursor, Delimiter, DelimSpan)>, desugar_doc_comments: bool, diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index d58f7547fef..7978d8cba95 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -13,7 +13,7 @@ use std::collections::HashMap; use std::panic::{catch_unwind, AssertUnwindSafe}; use rustc_ast::token::{BinOpToken, Delimiter, Token, TokenKind}; -use rustc_ast::tokenstream::{Cursor, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor}; use rustc_ast::{ast, ptr}; use rustc_ast_pretty::pprust; use rustc_span::{ @@ -736,7 +736,7 @@ impl MacroArgParser { self.buf.clear(); } - fn add_meta_variable(&mut self, iter: &mut Cursor) -> Option<()> { + fn add_meta_variable(&mut self, iter: &mut TokenTreeCursor) -> Option<()> { match iter.next() { Some(TokenTree::Token( Token { @@ -768,7 +768,7 @@ impl MacroArgParser { &mut self, inner: Vec, delim: Delimiter, - iter: &mut Cursor, + iter: &mut TokenTreeCursor, ) -> Option<()> { let mut buffer = String::new(); let mut first = true; @@ -1121,7 +1121,7 @@ pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> D // Currently we do not attempt to parse any further than that. #[derive(new)] struct MacroParser { - toks: Cursor, + toks: TokenTreeCursor, } impl MacroParser { From f29000eba9ebd43dca5ae97bab39148cce9319bc Mon Sep 17 00:00:00 2001 From: Wilco Kusee Date: Fri, 3 Feb 2023 10:04:15 +0100 Subject: [PATCH 10/13] Use new helper inside probe --- compiler/rustc_trait_selection/src/solve/project_goals.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 170b560d7b6..9f62f686af6 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -587,12 +587,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { let discriminant = goal.predicate.self_ty().discriminant_ty(ecx.tcx()); - let nested_goals = ecx.infcx.eq( - goal.param_env, - goal.predicate.term.ty().expect("expected ty goal"), - discriminant, - )?; - ecx.evaluate_all_and_make_canonical_response(nested_goals) + ecx.infcx + .probe(|_| ecx.eq_term_and_make_canonical_response(goal, Certainty::Yes, discriminant)) } } From f874f6768caa8b59b99feffc708e868328129fd5 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Fri, 3 Feb 2023 11:10:24 +0000 Subject: [PATCH 11/13] Fix suggestion for coercing Option<&String> to Option<&str> --- .../locales/en-US/hir_typeck.ftl | 2 + .../src/fn_ctxt/suggestions.rs | 13 +++-- tests/ui/typeck/issue-89856.fixed | 18 +++++++ tests/ui/typeck/issue-89856.rs | 14 ++++- tests/ui/typeck/issue-89856.stderr | 54 ++++++++++++++++--- 5 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 tests/ui/typeck/issue-89856.fixed diff --git a/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl b/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl index 0f13d29d0fc..05ac8db0db8 100644 --- a/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl +++ b/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl @@ -61,3 +61,5 @@ hir_typeck_lang_start_incorrect_ret_ty = the return type of the `start` lang ite hir_typeck_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml` hir_typeck_help_set_edition_standalone = pass `--edition {$edition}` to `rustc` hir_typeck_note_edition_guide = for more on editions, read https://doc.rust-lang.org/edition-guide + +hir_typeck_convert_to_str = try converting the passed type into a `&str` diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 6046e55c65c..3f433a0928c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -3,7 +3,7 @@ use super::FnCtxt; use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; -use rustc_errors::{Applicability, Diagnostic, MultiSpan}; +use rustc_errors::{fluent, Applicability, Diagnostic, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; use rustc_hir::lang_items::LangItem; @@ -414,11 +414,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::Adt(adt, _) = peeled.kind() && Some(adt.did()) == self.tcx.lang_items().string() { + let sugg = if ref_cnt == 0 { + ".as_deref()" + } else { + ".map(|x| x.as_str())" + }; err.span_suggestion_verbose( expr.span.shrink_to_hi(), - "try converting the passed type into a `&str`", - format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)), - Applicability::MaybeIncorrect, + fluent::hir_typeck_convert_to_str, + sugg, + Applicability::MachineApplicable, ); return true; } diff --git a/tests/ui/typeck/issue-89856.fixed b/tests/ui/typeck/issue-89856.fixed new file mode 100644 index 00000000000..3e1a006efa0 --- /dev/null +++ b/tests/ui/typeck/issue-89856.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +fn take_str_maybe(_: Option<&str>) { } +fn main() { + let string = String::from("Hello, world"); + + let option: Option = Some(string.clone()); + take_str_maybe(option.as_deref()); + //~^ ERROR: mismatched types [E0308] + + let option_ref = Some(&string); + take_str_maybe(option_ref.map(|x| x.as_str())); + //~^ ERROR: mismatched types [E0308] + + let option_ref_ref = option_ref.as_ref(); + take_str_maybe(option_ref_ref.map(|x| x.as_str())); + //~^ ERROR: mismatched types [E0308] +} diff --git a/tests/ui/typeck/issue-89856.rs b/tests/ui/typeck/issue-89856.rs index b021e349e35..cfe6e19b303 100644 --- a/tests/ui/typeck/issue-89856.rs +++ b/tests/ui/typeck/issue-89856.rs @@ -1,8 +1,18 @@ -fn take_str_maybe(x: Option<&str>) -> Option<&str> { None } +// run-rustfix +fn take_str_maybe(_: Option<&str>) { } fn main() { let string = String::from("Hello, world"); - let option = Some(&string); + + let option: Option = Some(string.clone()); take_str_maybe(option); //~^ ERROR: mismatched types [E0308] + + let option_ref = Some(&string); + take_str_maybe(option_ref); + //~^ ERROR: mismatched types [E0308] + + let option_ref_ref = option_ref.as_ref(); + take_str_maybe(option_ref_ref); + //~^ ERROR: mismatched types [E0308] } diff --git a/tests/ui/typeck/issue-89856.stderr b/tests/ui/typeck/issue-89856.stderr index 6b9cbe52c25..bd76f172468 100644 --- a/tests/ui/typeck/issue-89856.stderr +++ b/tests/ui/typeck/issue-89856.stderr @@ -1,23 +1,63 @@ error[E0308]: mismatched types - --> $DIR/issue-89856.rs:6:20 + --> $DIR/issue-89856.rs:8:20 | LL | take_str_maybe(option); - | -------------- ^^^^^^ expected `Option<&str>`, found `Option<&String>` + | -------------- ^^^^^^ expected `Option<&str>`, found `Option` + | | + | arguments to this function are incorrect + | + = note: expected enum `Option<&str>` + found enum `Option` +note: function defined here + --> $DIR/issue-89856.rs:3:4 + | +LL | fn take_str_maybe(_: Option<&str>) { } + | ^^^^^^^^^^^^^^ --------------- +help: try converting the passed type into a `&str` + | +LL | take_str_maybe(option.as_deref()); + | +++++++++++ + +error[E0308]: mismatched types + --> $DIR/issue-89856.rs:12:20 + | +LL | take_str_maybe(option_ref); + | -------------- ^^^^^^^^^^ expected `Option<&str>`, found `Option<&String>` | | | arguments to this function are incorrect | = note: expected enum `Option<&str>` found enum `Option<&String>` note: function defined here - --> $DIR/issue-89856.rs:1:4 + --> $DIR/issue-89856.rs:3:4 | -LL | fn take_str_maybe(x: Option<&str>) -> Option<&str> { None } +LL | fn take_str_maybe(_: Option<&str>) { } | ^^^^^^^^^^^^^^ --------------- help: try converting the passed type into a `&str` | -LL | take_str_maybe(option.map(|x| &**x)); - | ++++++++++++++ +LL | take_str_maybe(option_ref.map(|x| x.as_str())); + | ++++++++++++++++++++ -error: aborting due to previous error +error[E0308]: mismatched types + --> $DIR/issue-89856.rs:16:20 + | +LL | take_str_maybe(option_ref_ref); + | -------------- ^^^^^^^^^^^^^^ expected `Option<&str>`, found `Option<&&String>` + | | + | arguments to this function are incorrect + | + = note: expected enum `Option<&str>` + found enum `Option<&&String>` +note: function defined here + --> $DIR/issue-89856.rs:3:4 + | +LL | fn take_str_maybe(_: Option<&str>) { } + | ^^^^^^^^^^^^^^ --------------- +help: try converting the passed type into a `&str` + | +LL | take_str_maybe(option_ref_ref.map(|x| x.as_str())); + | ++++++++++++++++++++ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. From 9e1c600f74f966ec583f0ac39d0c2a103a2560a7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 18 Jan 2023 22:43:05 -0800 Subject: [PATCH 12/13] Disallow impl autotrait for trait object --- compiler/rustc_data_structures/src/sync.rs | 4 +- .../src/coherence/orphan.rs | 201 ++++++++++++++---- ...ce-impl-trait-for-marker-trait-negative.rs | 23 +- ...mpl-trait-for-marker-trait-negative.stderr | 44 +++- ...ce-impl-trait-for-marker-trait-positive.rs | 23 +- ...mpl-trait-for-marker-trait-positive.stderr | 44 +++- 6 files changed, 269 insertions(+), 70 deletions(-) diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index ed5341c40ef..ad71dcdf9d9 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -31,8 +31,8 @@ cfg_if! { pub auto trait Send {} pub auto trait Sync {} - impl Send for T {} - impl Sync for T {} + impl Send for T {} + impl Sync for T {} #[macro_export] macro_rules! rustc_erase_owner { diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 95b03eb8263..5c478b96fe6 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -8,7 +8,7 @@ use rustc_hir as hir; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::util::IgnoreRegions; use rustc_middle::ty::{ - self, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, + self, AliasKind, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; use rustc_session::lint; use rustc_span::def_id::{DefId, LocalDefId}; @@ -86,7 +86,7 @@ fn do_orphan_check_impl<'tcx>( // struct B { } // impl Foo for A { } // impl Foo for B { } - // impl !Send for (A, B) { } + // impl !Foo for (A, B) { } // ``` // // This final impl is legal according to the orphan @@ -99,50 +99,171 @@ fn do_orphan_check_impl<'tcx>( tcx.trait_is_auto(trait_def_id) ); - if tcx.trait_is_auto(trait_def_id) && !trait_def_id.is_local() { + if tcx.trait_is_auto(trait_def_id) { let self_ty = trait_ref.self_ty(); - let opt_self_def_id = match *self_ty.kind() { - ty::Adt(self_def, _) => Some(self_def.did()), - ty::Foreign(did) => Some(did), - _ => None, + + // If the impl is in the same crate as the auto-trait, almost anything + // goes. + // + // impl MyAuto for Rc {} // okay + // impl !MyAuto for *const T {} // okay + // impl MyAuto for T {} // okay + // + // But there is one important exception: implementing for a trait object + // is not allowed. + // + // impl MyAuto for dyn Trait {} // NOT OKAY + // impl MyAuto for T {} // NOT OKAY + // + enum LocalImpl { + Allow, + Disallow { problematic_kind: &'static str }, + } + + // If the auto-trait is from a dependency, it must only be getting + // implemented for a nominal type, and specifically one local to the + // current crate. + // + // impl Sync for MyStruct {} // okay + // + // impl Sync for Rc {} // NOT OKAY + enum NonlocalImpl { + Allow, + DisallowBecauseNonlocal, + DisallowOther, + } + + // Exhaustive match considering that this logic is essential for + // soundness. + let (local_impl, nonlocal_impl) = match self_ty.kind() { + // struct Struct; + // impl AutoTrait for Struct {} + ty::Adt(self_def, _) => ( + LocalImpl::Allow, + if self_def.did().is_local() { + NonlocalImpl::Allow + } else { + NonlocalImpl::DisallowBecauseNonlocal + }, + ), + + // extern { type OpaqueType; } + // impl AutoTrait for OpaqueType {} + ty::Foreign(did) => ( + LocalImpl::Allow, + if did.is_local() { + NonlocalImpl::Allow + } else { + NonlocalImpl::DisallowBecauseNonlocal + }, + ), + + // impl AutoTrait for dyn Trait {} + ty::Dynamic(..) => ( + LocalImpl::Disallow { problematic_kind: "trait object" }, + NonlocalImpl::DisallowOther, + ), + + // impl AutoTrait for T {} + // impl AutoTrait for T {} + ty::Param(..) => ( + if self_ty.is_sized(tcx, tcx.param_env(def_id)) { + LocalImpl::Allow + } else { + LocalImpl::Disallow { problematic_kind: "generic type" } + }, + NonlocalImpl::DisallowOther, + ), + + // trait Id { type This: ?Sized; } + // impl Id for T { + // type This = T; + // } + // impl AutoTrait for ::This {} + ty::Alias(AliasKind::Projection, _) => ( + LocalImpl::Disallow { problematic_kind: "associated type" }, + NonlocalImpl::DisallowOther, + ), + + // type Opaque = impl Trait; + // impl AutoTrait for Opaque {} + ty::Alias(AliasKind::Opaque, _) => ( + LocalImpl::Disallow { problematic_kind: "opaque type" }, + NonlocalImpl::DisallowOther, + ), + + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Str + | ty::Array(..) + | ty::Slice(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Never + | ty::Tuple(..) => (LocalImpl::Allow, NonlocalImpl::DisallowOther), + + ty::Closure(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::GeneratorWitnessMIR(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Infer(..) => span_bug!(sp, "weird self type for autotrait impl"), + + ty::Error(..) => (LocalImpl::Allow, NonlocalImpl::Allow), }; - let msg = match opt_self_def_id { - // We only want to permit nominal types, but not *all* nominal types. - // They must be local to the current crate, so that people - // can't do `unsafe impl Send for Rc` or - // `impl !Send for Box`. - Some(self_def_id) => { - if self_def_id.is_local() { - None - } else { - Some(( - format!( - "cross-crate traits with a default impl, like `{}`, \ - can only be implemented for a struct/enum type \ - defined in the current crate", - tcx.def_path_str(trait_def_id) - ), - "can't implement cross-crate trait for type in another crate", - )) + if trait_def_id.is_local() { + match local_impl { + LocalImpl::Allow => {} + LocalImpl::Disallow { problematic_kind } => { + let msg = format!( + "traits with a default impl, like `{trait}`, \ + cannot be implemented for {problematic_kind} `{self_ty}`", + trait = tcx.def_path_str(trait_def_id), + ); + let label = format!( + "a trait object implements `{trait}` if and only if `{trait}` \ + is one of the trait object's trait bounds", + trait = tcx.def_path_str(trait_def_id), + ); + let reported = + struct_span_err!(tcx.sess, sp, E0321, "{}", msg).note(label).emit(); + return Err(reported); } } - _ => Some(( - format!( - "cross-crate traits with a default impl, like `{}`, can \ + } else { + if let Some((msg, label)) = match nonlocal_impl { + NonlocalImpl::Allow => None, + NonlocalImpl::DisallowBecauseNonlocal => Some(( + format!( + "cross-crate traits with a default impl, like `{}`, \ + can only be implemented for a struct/enum type \ + defined in the current crate", + tcx.def_path_str(trait_def_id) + ), + "can't implement cross-crate trait for type in another crate", + )), + NonlocalImpl::DisallowOther => Some(( + format!( + "cross-crate traits with a default impl, like `{}`, can \ only be implemented for a struct/enum type, not `{}`", - tcx.def_path_str(trait_def_id), - self_ty - ), - "can't implement cross-crate trait with a default impl for \ - non-struct/enum type", - )), - }; - - if let Some((msg, label)) = msg { - let reported = - struct_span_err!(tcx.sess, sp, E0321, "{}", msg).span_label(sp, label).emit(); - return Err(reported); + tcx.def_path_str(trait_def_id), + self_ty + ), + "can't implement cross-crate trait with a default impl for \ + non-struct/enum type", + )), + } { + let reported = + struct_span_err!(tcx.sess, sp, E0321, "{}", msg).span_label(sp, label).emit(); + return Err(reported); + } } } diff --git a/tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.rs b/tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.rs index 50d9a480ad1..98f1558b7ff 100644 --- a/tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.rs +++ b/tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.rs @@ -12,19 +12,26 @@ auto trait Marker2 {} trait Object: Marker1 {} // A supertrait marker is illegal... -impl !Marker1 for dyn Object + Marker2 { } //~ ERROR E0371 +impl !Marker1 for dyn Object + Marker2 {} //~ ERROR E0371 + //~^ ERROR 0321 // ...and also a direct component. -impl !Marker2 for dyn Object + Marker2 { } //~ ERROR E0371 - -// But implementing a marker if it is not present is OK. -impl !Marker2 for dyn Object {} // OK +impl !Marker2 for dyn Object + Marker2 {} //~ ERROR E0371 + //~^ ERROR 0321 // A non-principal trait-object type is orphan even in its crate. impl !Send for dyn Marker2 {} //~ ERROR E0117 -// And impl'ing a remote marker for a local trait object is forbidden -// by one of these special orphan-like rules. +// Implementing a marker for a local trait object is forbidden by a special +// orphan-like rule. +impl !Marker2 for dyn Object {} //~ ERROR E0321 impl !Send for dyn Object {} //~ ERROR E0321 impl !Send for dyn Object + Marker2 {} //~ ERROR E0321 -fn main() { } +// Blanket impl that applies to dyn Object is equally problematic. +auto trait Marker3 {} +impl !Marker3 for T {} //~ ERROR E0321 + +auto trait Marker4 {} +impl !Marker4 for T {} // okay + +fn main() {} diff --git a/tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr b/tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr index c364c707ff9..ea38afc40ce 100644 --- a/tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr +++ b/tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr @@ -1,17 +1,41 @@ error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1` --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:15:1 | -LL | impl !Marker1 for dyn Object + Marker2 { } +LL | impl !Marker1 for dyn Object + Marker2 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker1` -error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2` - --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:17:1 +error[E0321]: traits with a default impl, like `Marker1`, cannot be implemented for trait object `(dyn Object + Marker2 + 'static)` + --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:15:1 | -LL | impl !Marker2 for dyn Object + Marker2 { } +LL | impl !Marker1 for dyn Object + Marker2 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: a trait object implements `Marker1` if and only if `Marker1` is one of the trait object's trait bounds + +error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2` + --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:18:1 + | +LL | impl !Marker2 for dyn Object + Marker2 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2` +error[E0321]: traits with a default impl, like `Marker2`, cannot be implemented for trait object `(dyn Object + Marker2 + 'static)` + --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:18:1 + | +LL | impl !Marker2 for dyn Object + Marker2 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: a trait object implements `Marker2` if and only if `Marker2` is one of the trait object's trait bounds + +error[E0321]: traits with a default impl, like `Marker2`, cannot be implemented for trait object `(dyn Object + 'static)` + --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:26:1 + | +LL | impl !Marker2 for dyn Object {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: a trait object implements `Marker2` if and only if `Marker2` is one of the trait object's trait bounds + error[E0117]: only traits defined in the current crate can be implemented for arbitrary types - --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:23:1 + --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:22:1 | LL | impl !Send for dyn Marker2 {} | ^^^^^^^^^^^^^^^----------- @@ -33,7 +57,15 @@ error[E0321]: cross-crate traits with a default impl, like `Send`, can only be i LL | impl !Send for dyn Object + Marker2 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type -error: aborting due to 5 previous errors +error[E0321]: traits with a default impl, like `Marker3`, cannot be implemented for generic type `T` + --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:32:1 + | +LL | impl !Marker3 for T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: a trait object implements `Marker3` if and only if `Marker3` is one of the trait object's trait bounds + +error: aborting due to 9 previous errors Some errors have detailed explanations: E0117, E0321, E0371. For more information about an error, try `rustc --explain E0117`. diff --git a/tests/ui/coherence/coherence-impl-trait-for-marker-trait-positive.rs b/tests/ui/coherence/coherence-impl-trait-for-marker-trait-positive.rs index faac6d983f3..db2e2b4509a 100644 --- a/tests/ui/coherence/coherence-impl-trait-for-marker-trait-positive.rs +++ b/tests/ui/coherence/coherence-impl-trait-for-marker-trait-positive.rs @@ -12,19 +12,26 @@ auto trait Marker2 {} trait Object: Marker1 {} // A supertrait marker is illegal... -impl Marker1 for dyn Object + Marker2 { } //~ ERROR E0371 +impl Marker1 for dyn Object + Marker2 {} //~ ERROR E0371 + //~^ ERROR E0321 // ...and also a direct component. -impl Marker2 for dyn Object + Marker2 { } //~ ERROR E0371 - -// But implementing a marker if it is not present is OK. -impl Marker2 for dyn Object {} // OK +impl Marker2 for dyn Object + Marker2 {} //~ ERROR E0371 + //~^ ERROR E0321 // A non-principal trait-object type is orphan even in its crate. unsafe impl Send for dyn Marker2 {} //~ ERROR E0117 -// And impl'ing a remote marker for a local trait object is forbidden -// by one of these special orphan-like rules. +// Implementing a marker for a local trait object is forbidden by a special +// orphan-like rule. +impl Marker2 for dyn Object {} //~ ERROR E0321 unsafe impl Send for dyn Object {} //~ ERROR E0321 unsafe impl Send for dyn Object + Marker2 {} //~ ERROR E0321 -fn main() { } +// Blanket impl that applies to dyn Object is equally problematic. +auto trait Marker3 {} +impl Marker3 for T {} //~ ERROR E0321 + +auto trait Marker4 {} +impl Marker4 for T {} // okay + +fn main() {} diff --git a/tests/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr b/tests/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr index b80429794f9..2a8713bc327 100644 --- a/tests/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr +++ b/tests/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr @@ -1,17 +1,41 @@ error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1` --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:15:1 | -LL | impl Marker1 for dyn Object + Marker2 { } +LL | impl Marker1 for dyn Object + Marker2 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker1` -error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2` - --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:17:1 +error[E0321]: traits with a default impl, like `Marker1`, cannot be implemented for trait object `(dyn Object + Marker2 + 'static)` + --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:15:1 | -LL | impl Marker2 for dyn Object + Marker2 { } +LL | impl Marker1 for dyn Object + Marker2 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: a trait object implements `Marker1` if and only if `Marker1` is one of the trait object's trait bounds + +error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2` + --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:18:1 + | +LL | impl Marker2 for dyn Object + Marker2 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2` +error[E0321]: traits with a default impl, like `Marker2`, cannot be implemented for trait object `(dyn Object + Marker2 + 'static)` + --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:18:1 + | +LL | impl Marker2 for dyn Object + Marker2 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: a trait object implements `Marker2` if and only if `Marker2` is one of the trait object's trait bounds + +error[E0321]: traits with a default impl, like `Marker2`, cannot be implemented for trait object `(dyn Object + 'static)` + --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:26:1 + | +LL | impl Marker2 for dyn Object {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: a trait object implements `Marker2` if and only if `Marker2` is one of the trait object's trait bounds + error[E0117]: only traits defined in the current crate can be implemented for arbitrary types - --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:23:1 + --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:22:1 | LL | unsafe impl Send for dyn Marker2 {} | ^^^^^^^^^^^^^^^^^^^^^----------- @@ -33,7 +57,15 @@ error[E0321]: cross-crate traits with a default impl, like `Send`, can only be i LL | unsafe impl Send for dyn Object + Marker2 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type -error: aborting due to 5 previous errors +error[E0321]: traits with a default impl, like `Marker3`, cannot be implemented for generic type `T` + --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:32:1 + | +LL | impl Marker3 for T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: a trait object implements `Marker3` if and only if `Marker3` is one of the trait object's trait bounds + +error: aborting due to 9 previous errors Some errors have detailed explanations: E0117, E0321, E0371. For more information about an error, try `rustc --explain E0117`. From 4501d3abe17a3dc10f0dffcb38be04b58a33bafb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 19 Jan 2023 01:24:45 -0800 Subject: [PATCH 13/13] Autotrait bounds on dyn-safe trait methods --- .../src/coherence/orphan.rs | 22 +++++++ .../src/traits/object_safety.rs | 60 +++++++++++++++---- .../self-in-where-clause-allowed.rs | 23 +++++++ .../self-in-where-clause-allowed.stderr | 15 +++++ 4 files changed, 110 insertions(+), 10 deletions(-) create mode 100644 tests/ui/where-clauses/self-in-where-clause-allowed.rs create mode 100644 tests/ui/where-clauses/self-in-where-clause-allowed.stderr diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 5c478b96fe6..7d381d8902a 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -115,6 +115,28 @@ fn do_orphan_check_impl<'tcx>( // impl MyAuto for dyn Trait {} // NOT OKAY // impl MyAuto for T {} // NOT OKAY // + // With this restriction, it's guaranteed that an auto-trait is + // implemented for a trait object if and only if the auto-trait is one + // of the trait object's trait bounds (or a supertrait of a bound). In + // other words `dyn Trait + AutoTrait` always implements AutoTrait, + // while `dyn Trait` never implements AutoTrait. + // + // This is necessary in order for autotrait bounds on methods of trait + // objects to be sound. + // + // auto trait AutoTrait {} + // + // trait ObjectSafeTrait { + // fn f(&self) where Self: AutoTrait; + // } + // + // We can allow f to be called on `dyn ObjectSafeTrait + AutoTrait`. + // + // If we didn't deny `impl AutoTrait for dyn Trait`, it would be unsound + // for the ObjectSafeTrait shown above to be object safe because someone + // could take some type implementing ObjectSafeTrait but not AutoTrait, + // unsize it to `dyn ObjectSafeTrait`, and call .f() which has no + // concrete implementation (issue #50781). enum LocalImpl { Allow, Disallow { problematic_kind: &'static str }, diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 565cfca9090..8f548acfd2e 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -547,16 +547,56 @@ fn virtual_call_violation_for_method<'tcx>( // NOTE: This check happens last, because it results in a lint, and not a // hard error. - if tcx - .predicates_of(method.def_id) - .predicates - .iter() - // A trait object can't claim to live more than the concrete type, - // so outlives predicates will always hold. - .cloned() - .filter(|(p, _)| p.to_opt_type_outlives().is_none()) - .any(|pred| contains_illegal_self_type_reference(tcx, trait_def_id, pred)) - { + if tcx.predicates_of(method.def_id).predicates.iter().any(|&(pred, span)| { + // dyn Trait is okay: + // + // trait Trait { + // fn f(&self) where Self: 'static; + // } + // + // because a trait object can't claim to live longer than the concrete + // type. If the lifetime bound holds on dyn Trait then it's guaranteed + // to hold as well on the concrete type. + if pred.to_opt_type_outlives().is_some() { + return false; + } + + // dyn Trait is okay: + // + // auto trait AutoTrait {} + // + // trait Trait { + // fn f(&self) where Self: AutoTrait; + // } + // + // because `impl AutoTrait for dyn Trait` is disallowed by coherence. + // Traits with a default impl are implemented for a trait object if and + // only if the autotrait is one of the trait object's trait bounds, like + // in `dyn Trait + AutoTrait`. This guarantees that trait objects only + // implement auto traits if the underlying type does as well. + if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate { + trait_ref: pred_trait_ref, + constness: ty::BoundConstness::NotConst, + polarity: ty::ImplPolarity::Positive, + })) = pred.kind().skip_binder() + && pred_trait_ref.self_ty() == tcx.types.self_param + && tcx.trait_is_auto(pred_trait_ref.def_id) + { + // Consider bounds like `Self: Bound`. Auto traits are not + // allowed to have generic parameters so `auto trait Bound {}` + // would already have reported an error at the definition of the + // auto trait. + if pred_trait_ref.substs.len() != 1 { + tcx.sess.diagnostic().delay_span_bug( + span, + "auto traits cannot have generic parameters", + ); + } + return false; + } + + contains_illegal_self_type_reference(tcx, trait_def_id, pred.clone()) + }) { return Some(MethodViolationCode::WhereClauseReferencesSelf); } diff --git a/tests/ui/where-clauses/self-in-where-clause-allowed.rs b/tests/ui/where-clauses/self-in-where-clause-allowed.rs new file mode 100644 index 00000000000..6cf5ed2e46a --- /dev/null +++ b/tests/ui/where-clauses/self-in-where-clause-allowed.rs @@ -0,0 +1,23 @@ +// check-fail + +#![feature(auto_traits)] +#![deny(where_clauses_object_safety)] + +auto trait AutoTrait {} + +trait Trait { + fn static_lifetime_bound(&self) where Self: 'static {} + + fn arg_lifetime_bound<'a>(&self, _arg: &'a ()) where Self: 'a {} + + fn autotrait_bound(&self) where Self: AutoTrait {} +} + +impl Trait for () {} + +fn main() { + let trait_object = &() as &dyn Trait; + trait_object.static_lifetime_bound(); + trait_object.arg_lifetime_bound(&()); + trait_object.autotrait_bound(); //~ ERROR: the trait bound `dyn Trait: AutoTrait` is not satisfied +} diff --git a/tests/ui/where-clauses/self-in-where-clause-allowed.stderr b/tests/ui/where-clauses/self-in-where-clause-allowed.stderr new file mode 100644 index 00000000000..ea51f5084f8 --- /dev/null +++ b/tests/ui/where-clauses/self-in-where-clause-allowed.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `dyn Trait: AutoTrait` is not satisfied + --> $DIR/self-in-where-clause-allowed.rs:22:18 + | +LL | trait_object.autotrait_bound(); + | ^^^^^^^^^^^^^^^ the trait `AutoTrait` is not implemented for `dyn Trait` + | +note: required by a bound in `Trait::autotrait_bound` + --> $DIR/self-in-where-clause-allowed.rs:13:43 + | +LL | fn autotrait_bound(&self) where Self: AutoTrait {} + | ^^^^^^^^^ required by this bound in `Trait::autotrait_bound` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`.