From 5d944e1e65b7bd0e5290da300e74f1c0dbb2e372 Mon Sep 17 00:00:00 2001 From: Adwin White Date: Mon, 5 Aug 2024 10:59:17 +0800 Subject: [PATCH] check type error recursively in typeck --- compiler/rustc_hir_typeck/src/lib.rs | 44 ++++++++++++++++++++++- tests/ui/typeck/wide_ptr_transmute.rs | 13 +++++++ tests/ui/typeck/wide_ptr_transmute.stderr | 19 ++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 tests/ui/typeck/wide_ptr_transmute.rs create mode 100644 tests/ui/typeck/wide_ptr_transmute.stderr diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index fa78b9ced1282..3bd67b18cdda3 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -42,8 +42,11 @@ mod typeck_root_ctxt; mod upvar; mod writeback; +use core::ops::ControlFlow; + pub use coercion::can_coerce; use fn_ctxt::FnCtxt; +use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; use rustc_errors::{struct_span_code_err, Applicability, ErrorGuaranteed}; @@ -55,7 +58,7 @@ use rustc_hir_analysis::check::check_abi; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc}; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_middle::{bug, span_bug}; use rustc_session::config; use rustc_span::def_id::LocalDefId; @@ -116,6 +119,40 @@ pub fn inspect_typeck<'tcx>( typeck_with_fallback(tcx, def_id, fallback, Some(inspect)) } +struct RecursiveHasErrorVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + // To avoid cycle when visiting recursive types. + visited_tys: FxHashSet>, +} + +impl<'tcx> RecursiveHasErrorVisitor<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> Self { + RecursiveHasErrorVisitor { tcx, visited_tys: Default::default() } + } +} + +impl<'tcx> TypeVisitor> for RecursiveHasErrorVisitor<'tcx> { + type Result = ControlFlow; + + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + if self.visited_tys.insert(t.clone()) { + if let ty::Adt(def, args) = t.kind() { + let field_tys: Vec<_> = def.all_fields().map(|f| f.ty(self.tcx, args)).collect(); + for field_ty in field_tys { + field_ty.visit_with(self)?; + } + } + t.super_visit_with(self) + } else { + ControlFlow::Continue(()) + } + } + + fn visit_error(&mut self, guar: ErrorGuaranteed) -> Self::Result { + ControlFlow::Break(guar) + } +} + #[instrument(level = "debug", skip(tcx, fallback, inspector), ret)] fn typeck_with_fallback<'tcx>( tcx: TyCtxt<'tcx>, @@ -168,6 +205,11 @@ fn typeck_with_fallback<'tcx>( let expected_type = fcx.normalize(body.value.span, expected_type); + let mut error_visitor = RecursiveHasErrorVisitor::new(tcx); + if let ControlFlow::Break(guar) = expected_type.visit_with(&mut error_visitor) { + fcx.set_tainted_by_errors(guar); + } + let wf_code = ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(def_id))); fcx.register_wf_obligation(expected_type.into(), body.value.span, wf_code); diff --git a/tests/ui/typeck/wide_ptr_transmute.rs b/tests/ui/typeck/wide_ptr_transmute.rs new file mode 100644 index 0000000000000..64a72ebcdce1e --- /dev/null +++ b/tests/ui/typeck/wide_ptr_transmute.rs @@ -0,0 +1,13 @@ +// issue #127742 +struct Vtable(dyn Cap); +//~^ ERROR missing lifetime specifier + +trait Cap<'a> {} + +union Transmute { + t: u64, + u: &'static Vtable, +} + +const G: &'static Vtable = unsafe { Transmute { t: 1 }.u }; +fn main() {} diff --git a/tests/ui/typeck/wide_ptr_transmute.stderr b/tests/ui/typeck/wide_ptr_transmute.stderr new file mode 100644 index 0000000000000..5f28cf93417d2 --- /dev/null +++ b/tests/ui/typeck/wide_ptr_transmute.stderr @@ -0,0 +1,19 @@ +error[E0106]: missing lifetime specifier + --> $DIR/wide_ptr_transmute.rs:2:19 + | +LL | struct Vtable(dyn Cap); + | ^^^ expected named lifetime parameter + | + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the bound lifetime-generic with a new `'a` lifetime + | +LL | struct Vtable(dyn for<'a> Cap<'a>); + | +++++++ ++++ +help: consider introducing a named lifetime parameter + | +LL | struct Vtable<'a>(dyn Cap<'a>); + | ++++ ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0106`.