diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/eval_queries.rs | 13 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/validity.rs | 28 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/lib.rs | 572 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/coherence/builtin.rs | 42 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/coherence/mod.rs | 5 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/coherence/unsafety.rs | 149 |
6 files changed, 397 insertions, 412 deletions
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 0844cdbe99b..c55d899e4d5 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -356,22 +356,13 @@ pub fn const_validate_mplace<'mir, 'tcx>( let mut inner = false; while let Some((mplace, path)) = ref_tracking.todo.pop() { let mode = match ecx.tcx.static_mutability(cid.instance.def_id()) { - Some(_) if cid.promoted.is_some() => { - // Promoteds in statics are consts that re allowed to point to statics. - CtfeValidationMode::Const { - allow_immutable_unsafe_cell: false, - allow_extern_static_ptrs: true, - } - } + _ if cid.promoted.is_some() => CtfeValidationMode::Promoted, Some(mutbl) => CtfeValidationMode::Static { mutbl }, // a `static` None => { // In normal `const` (not promoted), the outermost allocation is always only copied, // so having `UnsafeCell` in there is okay despite them being in immutable memory. let allow_immutable_unsafe_cell = cid.promoted.is_none() && !inner; - CtfeValidationMode::Const { - allow_immutable_unsafe_cell, - allow_extern_static_ptrs: false, - } + CtfeValidationMode::Const { allow_immutable_unsafe_cell } } }; ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?; diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 38aeace02ba..05e28db652f 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -129,17 +129,20 @@ pub enum PathElem { pub enum CtfeValidationMode { /// Validation of a `static` Static { mutbl: Mutability }, - /// Validation of a `const` (including promoteds). + /// Validation of a promoted. + Promoted, + /// Validation of a `const`. /// `allow_immutable_unsafe_cell` says whether we allow `UnsafeCell` in immutable memory (which is the /// case for the top-level allocation of a `const`, where this is fine because the allocation will be /// copied at each use site). - Const { allow_immutable_unsafe_cell: bool, allow_extern_static_ptrs: bool }, + Const { allow_immutable_unsafe_cell: bool }, } impl CtfeValidationMode { fn allow_immutable_unsafe_cell(self) -> bool { match self { CtfeValidationMode::Static { .. } => false, + CtfeValidationMode::Promoted { .. } => false, CtfeValidationMode::Const { allow_immutable_unsafe_cell, .. } => { allow_immutable_unsafe_cell } @@ -149,6 +152,7 @@ impl CtfeValidationMode { fn may_contain_mutable_ref(self) -> bool { match self { CtfeValidationMode::Static { mutbl } => mutbl == Mutability::Mut, + CtfeValidationMode::Promoted { .. } => false, CtfeValidationMode::Const { .. } => false, } } @@ -476,34 +480,32 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' throw_validation_failure!(self.path, MutableRefToImmutable); } } + // Mode-specific checks match self.ctfe_mode { - Some(CtfeValidationMode::Static { .. }) => { + Some( + CtfeValidationMode::Static { .. } + | CtfeValidationMode::Promoted { .. }, + ) => { // We skip recursively checking other statics. These statics must be sound by // themselves, and the only way to get broken statics here is by using // unsafe code. // The reasons we don't check other statics is twofold. For one, in all // sound cases, the static was already validated on its own, and second, we // trigger cycle errors if we try to compute the value of the other static - // and that static refers back to us. + // and that static refers back to us (potentially through a promoted). // This could miss some UB, but that's fine. return Ok(()); } - Some(CtfeValidationMode::Const { - allow_extern_static_ptrs, .. - }) => { + Some(CtfeValidationMode::Const { .. }) => { // For consts on the other hand we have to recursively check; // pattern matching assumes a valid value. However we better make // sure this is not mutable. if is_mut { throw_validation_failure!(self.path, ConstRefToMutable); } + // We can't recursively validate `extern static`, so we better reject them. if self.ecx.tcx.is_foreign_item(did) { - if !allow_extern_static_ptrs { - throw_validation_failure!(self.path, ConstRefToExtern); - } else { - // We can't validate this... - return Ok(()); - } + throw_validation_failure!(self.path, ConstRefToExtern); } } None => {} diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 16077b3d98f..da9ef6627be 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -720,217 +720,6 @@ impl DiagCtxt { self.inner.borrow_mut().emit_stashed_diagnostics() } - /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. - /// - /// An `emit` call on the builder will only emit if `can_emit_warnings` is `true`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_span_warn( - &self, - span: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, - ) -> DiagnosticBuilder<'_, ()> { - self.struct_warn(msg).with_span(span) - } - - /// Construct a builder at the `Warning` level with the `msg`. - /// - /// An `emit` call on the builder will only emit if `can_emit_warnings` is `true`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { - DiagnosticBuilder::new(self, Warning, msg) - } - - /// Construct a builder at the `Allow` level with the `msg`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { - DiagnosticBuilder::new(self, Allow, msg) - } - - /// Construct a builder at the `Expect` level with the `msg`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_expect( - &self, - msg: impl Into<DiagnosticMessage>, - id: LintExpectationId, - ) -> DiagnosticBuilder<'_, ()> { - DiagnosticBuilder::new(self, Expect(id), msg) - } - - /// Construct a builder at the `Error` level at the given `span` and with the `msg`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_span_err( - &self, - span: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, - ) -> DiagnosticBuilder<'_> { - self.struct_err(msg).with_span(span) - } - - /// Construct a builder at the `Error` level with the `msg`. - // FIXME: This method should be removed (every error should have an associated error code). - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_err(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_> { - DiagnosticBuilder::new(self, Error, msg) - } - - /// Construct a builder at the `Fatal` level at the given `span` and with the `msg`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_span_fatal( - &self, - span: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, - ) -> DiagnosticBuilder<'_, FatalAbort> { - self.struct_fatal(msg).with_span(span) - } - - /// Construct a builder at the `Fatal` level with the `msg`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_fatal( - &self, - msg: impl Into<DiagnosticMessage>, - ) -> DiagnosticBuilder<'_, FatalAbort> { - DiagnosticBuilder::new(self, Fatal, msg) - } - - /// Construct a builder at the `Help` level with the `msg`. - #[rustc_lint_diagnostics] - pub fn struct_help(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { - DiagnosticBuilder::new(self, Help, msg) - } - - /// Construct a builder at the `Note` level with the `msg`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_note(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { - DiagnosticBuilder::new(self, Note, msg) - } - - /// Construct a builder at the `Bug` level with the `msg`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_bug(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, BugAbort> { - DiagnosticBuilder::new(self, Bug, msg) - } - - /// Construct a builder at the `Bug` level at the given `span` with the `msg`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_span_bug( - &self, - span: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, - ) -> DiagnosticBuilder<'_, BugAbort> { - self.struct_bug(msg).with_span(span) - } - - #[rustc_lint_diagnostics] - #[track_caller] - pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { - self.struct_span_fatal(span, msg).emit() - } - - #[rustc_lint_diagnostics] - #[track_caller] - pub fn span_err( - &self, - span: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, - ) -> ErrorGuaranteed { - self.struct_span_err(span, msg).emit() - } - - #[rustc_lint_diagnostics] - #[track_caller] - pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) { - self.struct_span_warn(span, msg).emit() - } - - #[track_caller] - pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { - self.struct_span_bug(span, msg).emit() - } - - /// Ensures that compilation cannot succeed. - /// - /// If this function has been called but no errors have been emitted and - /// compilation succeeds, it will cause an internal compiler error (ICE). - /// - /// This can be used in code paths that should never run on successful compilations. - /// For example, it can be used to create an [`ErrorGuaranteed`] - /// (but you should prefer threading through the [`ErrorGuaranteed`] from an error emission - /// directly). - #[track_caller] - pub fn delayed_bug(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { - DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).emit() - } - - /// Like `delayed_bug`, but takes an additional span. - /// - /// Note: this function used to be called `delay_span_bug`. It was renamed - /// to match similar functions like `span_err`, `span_warn`, etc. - #[track_caller] - pub fn span_delayed_bug( - &self, - sp: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, - ) -> ErrorGuaranteed { - DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).with_span(sp).emit() - } - - /// Ensures that a diagnostic is printed. See `Level::GoodPathDelayedBug`. - pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) { - DiagnosticBuilder::<()>::new(self, GoodPathDelayedBug, msg).emit() - } - - #[track_caller] - #[rustc_lint_diagnostics] - pub fn span_note(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) { - self.struct_span_note(span, msg).emit() - } - - #[track_caller] - #[rustc_lint_diagnostics] - pub fn struct_span_note( - &self, - span: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, - ) -> DiagnosticBuilder<'_, ()> { - DiagnosticBuilder::new(self, Note, msg).with_span(span) - } - - #[rustc_lint_diagnostics] - pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> ! { - self.struct_fatal(msg).emit() - } - - #[rustc_lint_diagnostics] - pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { - self.struct_err(msg).emit() - } - - #[rustc_lint_diagnostics] - pub fn warn(&self, msg: impl Into<DiagnosticMessage>) { - self.struct_warn(msg).emit() - } - - #[rustc_lint_diagnostics] - pub fn note(&self, msg: impl Into<DiagnosticMessage>) { - self.struct_note(msg).emit() - } - - #[rustc_lint_diagnostics] - pub fn bug(&self, msg: impl Into<DiagnosticMessage>) -> ! { - self.struct_bug(msg).emit() - } - /// This excludes lint errors, delayed bugs, and stashed errors. #[inline] pub fn err_count(&self) -> usize { @@ -1062,84 +851,6 @@ impl DiagCtxt { self.inner.borrow_mut().emit_diagnostic(diagnostic) } - #[track_caller] - pub fn emit_err<'a>(&'a self, err: impl IntoDiagnostic<'a>) -> ErrorGuaranteed { - self.create_err(err).emit() - } - - #[track_caller] - pub fn create_err<'a>(&'a self, err: impl IntoDiagnostic<'a>) -> DiagnosticBuilder<'a> { - err.into_diagnostic(self, Error) - } - - #[track_caller] - pub fn create_warn<'a>( - &'a self, - warning: impl IntoDiagnostic<'a, ()>, - ) -> DiagnosticBuilder<'a, ()> { - warning.into_diagnostic(self, Warning) - } - - #[track_caller] - pub fn emit_warn<'a>(&'a self, warning: impl IntoDiagnostic<'a, ()>) { - self.create_warn(warning).emit() - } - - #[track_caller] - pub fn create_almost_fatal<'a>( - &'a self, - fatal: impl IntoDiagnostic<'a, FatalError>, - ) -> DiagnosticBuilder<'a, FatalError> { - fatal.into_diagnostic(self, Fatal) - } - - #[track_caller] - pub fn emit_almost_fatal<'a>( - &'a self, - fatal: impl IntoDiagnostic<'a, FatalError>, - ) -> FatalError { - self.create_almost_fatal(fatal).emit() - } - - #[track_caller] - pub fn create_fatal<'a>( - &'a self, - fatal: impl IntoDiagnostic<'a, FatalAbort>, - ) -> DiagnosticBuilder<'a, FatalAbort> { - fatal.into_diagnostic(self, Fatal) - } - - #[track_caller] - pub fn emit_fatal<'a>(&'a self, fatal: impl IntoDiagnostic<'a, FatalAbort>) -> ! { - self.create_fatal(fatal).emit() - } - - #[track_caller] - pub fn create_bug<'a>( - &'a self, - bug: impl IntoDiagnostic<'a, BugAbort>, - ) -> DiagnosticBuilder<'a, BugAbort> { - bug.into_diagnostic(self, Bug) - } - - #[track_caller] - pub fn emit_bug<'a>(&'a self, bug: impl IntoDiagnostic<'a, diagnostic_builder::BugAbort>) -> ! { - self.create_bug(bug).emit() - } - - #[track_caller] - pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, ()>) { - self.create_note(note).emit() - } - - #[track_caller] - pub fn create_note<'a>( - &'a self, - note: impl IntoDiagnostic<'a, ()>, - ) -> DiagnosticBuilder<'a, ()> { - note.into_diagnostic(self, Note) - } - pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) { self.inner.borrow_mut().emitter.emit_artifact_notification(path, artifact_type); } @@ -1227,6 +938,288 @@ impl DiagCtxt { } } +// This `impl` block contains only the public diagnostic creation/emission API. +// +// Functions beginning with `struct_`/`create_` create a diagnostic. Other +// functions create and emit a diagnostic all in one go. +impl DiagCtxt { + // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + #[track_caller] + pub fn struct_bug(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, BugAbort> { + DiagnosticBuilder::new(self, Bug, msg) + } + + // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + #[track_caller] + pub fn bug(&self, msg: impl Into<DiagnosticMessage>) -> ! { + self.struct_bug(msg).emit() + } + + // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + #[track_caller] + pub fn struct_span_bug( + &self, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, BugAbort> { + self.struct_bug(msg).with_span(span) + } + + // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + #[track_caller] + pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { + self.struct_span_bug(span, msg).emit() + } + + #[track_caller] + pub fn create_bug<'a>( + &'a self, + bug: impl IntoDiagnostic<'a, BugAbort>, + ) -> DiagnosticBuilder<'a, BugAbort> { + bug.into_diagnostic(self, Bug) + } + + #[track_caller] + pub fn emit_bug<'a>(&'a self, bug: impl IntoDiagnostic<'a, BugAbort>) -> ! { + self.create_bug(bug).emit() + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn struct_fatal( + &self, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, FatalAbort> { + DiagnosticBuilder::new(self, Fatal, msg) + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> ! { + self.struct_fatal(msg).emit() + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn struct_span_fatal( + &self, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, FatalAbort> { + self.struct_fatal(msg).with_span(span) + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { + self.struct_span_fatal(span, msg).emit() + } + + #[track_caller] + pub fn create_fatal<'a>( + &'a self, + fatal: impl IntoDiagnostic<'a, FatalAbort>, + ) -> DiagnosticBuilder<'a, FatalAbort> { + fatal.into_diagnostic(self, Fatal) + } + + #[track_caller] + pub fn emit_fatal<'a>(&'a self, fatal: impl IntoDiagnostic<'a, FatalAbort>) -> ! { + self.create_fatal(fatal).emit() + } + + #[track_caller] + pub fn create_almost_fatal<'a>( + &'a self, + fatal: impl IntoDiagnostic<'a, FatalError>, + ) -> DiagnosticBuilder<'a, FatalError> { + fatal.into_diagnostic(self, Fatal) + } + + #[track_caller] + pub fn emit_almost_fatal<'a>( + &'a self, + fatal: impl IntoDiagnostic<'a, FatalError>, + ) -> FatalError { + self.create_almost_fatal(fatal).emit() + } + + // FIXME: This method should be removed (every error should have an associated error code). + #[rustc_lint_diagnostics] + #[track_caller] + pub fn struct_err(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_> { + DiagnosticBuilder::new(self, Error, msg) + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { + self.struct_err(msg).emit() + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn struct_span_err( + &self, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_> { + self.struct_err(msg).with_span(span) + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn span_err( + &self, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + ) -> ErrorGuaranteed { + self.struct_span_err(span, msg).emit() + } + + #[track_caller] + pub fn create_err<'a>(&'a self, err: impl IntoDiagnostic<'a>) -> DiagnosticBuilder<'a> { + err.into_diagnostic(self, Error) + } + + #[track_caller] + pub fn emit_err<'a>(&'a self, err: impl IntoDiagnostic<'a>) -> ErrorGuaranteed { + self.create_err(err).emit() + } + + /// Ensures that an error is printed. See `Level::DelayedBug`. + // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + #[track_caller] + pub fn delayed_bug(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { + DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).emit() + } + + /// Ensures that an error is printed. See `Level::DelayedBug`. + /// + /// Note: this function used to be called `delay_span_bug`. It was renamed + /// to match similar functions like `span_err`, `span_warn`, etc. + // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + #[track_caller] + pub fn span_delayed_bug( + &self, + sp: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + ) -> ErrorGuaranteed { + DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).with_span(sp).emit() + } + + /// Ensures that a diagnostic is printed. See `Level::GoodPathDelayedBug`. + // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + #[track_caller] + pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) { + DiagnosticBuilder::<()>::new(self, GoodPathDelayedBug, msg).emit() + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, Warning, msg) + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn warn(&self, msg: impl Into<DiagnosticMessage>) { + self.struct_warn(msg).emit() + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn struct_span_warn( + &self, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, ()> { + self.struct_warn(msg).with_span(span) + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) { + self.struct_span_warn(span, msg).emit() + } + + #[track_caller] + pub fn create_warn<'a>( + &'a self, + warning: impl IntoDiagnostic<'a, ()>, + ) -> DiagnosticBuilder<'a, ()> { + warning.into_diagnostic(self, Warning) + } + + #[track_caller] + pub fn emit_warn<'a>(&'a self, warning: impl IntoDiagnostic<'a, ()>) { + self.create_warn(warning).emit() + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn struct_note(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, Note, msg) + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn note(&self, msg: impl Into<DiagnosticMessage>) { + self.struct_note(msg).emit() + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn struct_span_note( + &self, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, Note, msg).with_span(span) + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn span_note(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) { + self.struct_span_note(span, msg).emit() + } + + #[track_caller] + pub fn create_note<'a>( + &'a self, + note: impl IntoDiagnostic<'a, ()>, + ) -> DiagnosticBuilder<'a, ()> { + note.into_diagnostic(self, Note) + } + + #[track_caller] + pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, ()>) { + self.create_note(note).emit() + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn struct_help(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, Help, msg) + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, Allow, msg) + } + + #[rustc_lint_diagnostics] + #[track_caller] + pub fn struct_expect( + &self, + msg: impl Into<DiagnosticMessage>, + id: LintExpectationId, + ) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, Expect(id), msg) + } +} + // Note: we prefer implementing operations on `DiagCtxt`, rather than // `DiagCtxtInner`, whenever possible. This minimizes functions where // `DiagCtxt::foo()` just borrows `inner` and forwards a call to @@ -1596,6 +1589,7 @@ pub enum Level { ForceWarning(Option<LintExpectationId>), /// A warning about the code being compiled. Does not prevent compilation from finishing. + /// Will be skipped if `can_emit_warnings` is false. Warning, /// A message giving additional context. diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 5a387844593..fc911ecdad2 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -25,9 +25,13 @@ use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::{self, ObligationCause}; use std::collections::BTreeMap; -pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) -> Result<(), ErrorGuaranteed> { +pub fn check_trait( + tcx: TyCtxt<'_>, + trait_def_id: DefId, + impl_def_id: LocalDefId, +) -> Result<(), ErrorGuaranteed> { let lang_items = tcx.lang_items(); - let checker = Checker { tcx, trait_def_id }; + let checker = Checker { tcx, trait_def_id, impl_def_id }; let mut res = checker.check(lang_items.drop_trait(), visit_implementation_of_drop); res = res.and(checker.check(lang_items.copy_trait(), visit_implementation_of_copy)); res = res.and( @@ -45,20 +49,16 @@ pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) -> Result<(), ErrorGuar struct Checker<'tcx> { tcx: TyCtxt<'tcx>, trait_def_id: DefId, + impl_def_id: LocalDefId, } impl<'tcx> Checker<'tcx> { - fn check<F>(&self, trait_def_id: Option<DefId>, mut f: F) -> Result<(), ErrorGuaranteed> - where - F: FnMut(TyCtxt<'tcx>, LocalDefId) -> Result<(), ErrorGuaranteed>, - { - let mut res = Ok(()); - if Some(self.trait_def_id) == trait_def_id { - for &impl_def_id in self.tcx.hir().trait_impls(self.trait_def_id) { - res = res.and(f(self.tcx, impl_def_id)); - } - } - res + fn check( + &self, + trait_def_id: Option<DefId>, + f: impl FnOnce(TyCtxt<'tcx>, LocalDefId) -> Result<(), ErrorGuaranteed>, + ) -> Result<(), ErrorGuaranteed> { + if Some(self.trait_def_id) == trait_def_id { f(self.tcx, self.impl_def_id) } else { Ok(()) } } } @@ -92,10 +92,10 @@ fn visit_implementation_of_copy( debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type); - let span = match tcx.hir().expect_item(impl_did).expect_impl() { - hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. } => return Ok(()), - hir::Impl { self_ty, .. } => self_ty.span, - }; + if let ty::ImplPolarity::Negative = tcx.impl_polarity(impl_did) { + return Ok(()); + } + let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; let cause = traits::ObligationCause::misc(span, impl_did); match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) { @@ -121,10 +121,10 @@ fn visit_implementation_of_const_param_ty( let param_env = tcx.param_env(impl_did); - let span = match tcx.hir().expect_item(impl_did).expect_impl() { - hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. } => return Ok(()), - impl_ => impl_.self_ty.span, - }; + if let ty::ImplPolarity::Negative = tcx.impl_polarity(impl_did) { + return Ok(()); + } + let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; let cause = traits::ObligationCause::misc(span, impl_did); match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) { diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index c52448c60b9..7f59763f2a0 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -137,11 +137,12 @@ fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Result<(), ErrorGuaranteed> res = res.and(check_impl(tcx, impl_def_id, trait_ref)); res = res.and(check_object_overlap(tcx, impl_def_id, trait_ref)); - res = res.and(unsafety::check_item(tcx, impl_def_id)); + res = res.and(unsafety::check_item(tcx, impl_def_id, trait_ref)); res = res.and(tcx.ensure().orphan_check_impl(impl_def_id)); + res = res.and(builtin::check_trait(tcx, def_id, impl_def_id)); } - res.and(builtin::check_trait(tcx, def_id)) + res } /// Checks whether an impl overlaps with the automatic `impl Trait for dyn Trait`. diff --git a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs index e3b5c724cde..d217d53587d 100644 --- a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs +++ b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs @@ -4,94 +4,91 @@ use rustc_errors::{codes::*, struct_span_code_err}; use rustc_hir as hir; use rustc_hir::Unsafety; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{TraitRef, TyCtxt}; use rustc_span::def_id::LocalDefId; use rustc_span::ErrorGuaranteed; -pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> { +pub(super) fn check_item( + tcx: TyCtxt<'_>, + def_id: LocalDefId, + trait_ref: TraitRef<'_>, +) -> Result<(), ErrorGuaranteed> { let item = tcx.hir().expect_item(def_id); let impl_ = item.expect_impl(); + let trait_def = tcx.trait_def(trait_ref.def_id); + let unsafe_attr = impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle"); + match (trait_def.unsafety, unsafe_attr, impl_.unsafety, impl_.polarity) { + (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => { + return Err(struct_span_code_err!( + tcx.dcx(), + tcx.def_span(def_id), + E0199, + "implementing the trait `{}` is not unsafe", + trait_ref.print_trait_sugared() + ) + .with_span_suggestion_verbose( + item.span.with_hi(item.span.lo() + rustc_span::BytePos(7)), + "remove `unsafe` from this trait implementation", + "", + rustc_errors::Applicability::MachineApplicable, + ) + .emit()); + } - if let Some(trait_ref) = tcx.impl_trait_ref(item.owner_id) { - let trait_ref = trait_ref.instantiate_identity(); - let trait_def = tcx.trait_def(trait_ref.def_id); - let unsafe_attr = - impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle"); - match (trait_def.unsafety, unsafe_attr, impl_.unsafety, impl_.polarity) { - (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => { - return Err(struct_span_code_err!( - tcx.dcx(), - tcx.def_span(def_id), - E0199, - "implementing the trait `{}` is not unsafe", - trait_ref.print_trait_sugared() - ) - .with_span_suggestion_verbose( - item.span.with_hi(item.span.lo() + rustc_span::BytePos(7)), - "remove `unsafe` from this trait implementation", - "", - rustc_errors::Applicability::MachineApplicable, - ) - .emit()); - } - - (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => { - return Err(struct_span_code_err!( - tcx.dcx(), - tcx.def_span(def_id), - E0200, - "the trait `{}` requires an `unsafe impl` declaration", - trait_ref.print_trait_sugared() - ) - .with_note(format!( - "the trait `{}` enforces invariants that the compiler can't check. \ + (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => { + return Err(struct_span_code_err!( + tcx.dcx(), + tcx.def_span(def_id), + E0200, + "the trait `{}` requires an `unsafe impl` declaration", + trait_ref.print_trait_sugared() + ) + .with_note(format!( + "the trait `{}` enforces invariants that the compiler can't check. \ Review the trait documentation and make sure this implementation \ upholds those invariants before adding the `unsafe` keyword", - trait_ref.print_trait_sugared() - )) - .with_span_suggestion_verbose( - item.span.shrink_to_lo(), - "add `unsafe` to this trait implementation", - "unsafe ", - rustc_errors::Applicability::MaybeIncorrect, - ) - .emit()); - } + trait_ref.print_trait_sugared() + )) + .with_span_suggestion_verbose( + item.span.shrink_to_lo(), + "add `unsafe` to this trait implementation", + "unsafe ", + rustc_errors::Applicability::MaybeIncorrect, + ) + .emit()); + } - (Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => { - return Err(struct_span_code_err!( - tcx.dcx(), - tcx.def_span(def_id), - E0569, - "requires an `unsafe impl` declaration due to `#[{}]` attribute", - attr_name - ) - .with_note(format!( - "the trait `{}` enforces invariants that the compiler can't check. \ + (Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => { + return Err(struct_span_code_err!( + tcx.dcx(), + tcx.def_span(def_id), + E0569, + "requires an `unsafe impl` declaration due to `#[{}]` attribute", + attr_name + ) + .with_note(format!( + "the trait `{}` enforces invariants that the compiler can't check. \ Review the trait documentation and make sure this implementation \ upholds those invariants before adding the `unsafe` keyword", - trait_ref.print_trait_sugared() - )) - .with_span_suggestion_verbose( - item.span.shrink_to_lo(), - "add `unsafe` to this trait implementation", - "unsafe ", - rustc_errors::Applicability::MaybeIncorrect, - ) - .emit()); - } + trait_ref.print_trait_sugared() + )) + .with_span_suggestion_verbose( + item.span.shrink_to_lo(), + "add `unsafe` to this trait implementation", + "unsafe ", + rustc_errors::Applicability::MaybeIncorrect, + ) + .emit()); + } - (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => { - // Reported in AST validation - tcx.dcx().span_delayed_bug(item.span, "unsafe negative impl"); - } - (_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_)) - | (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive) - | (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive) - | (Unsafety::Normal, None, Unsafety::Normal, _) => { - // OK - } + (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => { + // Reported in AST validation + tcx.dcx().span_delayed_bug(item.span, "unsafe negative impl"); + Ok(()) } + (_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_)) + | (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive) + | (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive) + | (Unsafety::Normal, None, Unsafety::Normal, _) => Ok(()), } - Ok(()) } |
