From 5e11a99bb662629cc7d0ad3ce7475b7738c9c2cb Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 15 Feb 2024 04:22:56 +0100 Subject: Remove hacky branch in `sort_candidate` Reusing `self.test` wasn't actually pulling a lot of weight. --- compiler/rustc_mir_build/src/build/matches/test.rs | 37 ++++++++-------------- 1 file changed, 14 insertions(+), 23 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 1c97de58863..a97d0de27e3 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -673,6 +673,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } } + (TestKind::Len { .. }, _) => { + fully_matched = false; + None + } (TestKind::Range(test), &TestCase::Range(pat)) => { if test.as_ref() == pat { @@ -700,29 +704,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None } - (&TestKind::Eq { .. } | &TestKind::Len { .. }, _) => { - // The call to `self.test(&match_pair)` below is not actually used to generate any - // MIR. Instead, we just want to compare with `test` (the parameter of the method) - // to see if it is the same. - // - // However, at this point we can still encounter or-patterns that were extracted - // from previous calls to `sort_candidate`, so we need to manually address that - // case to avoid panicking in `self.test()`. - if let TestCase::Or { .. } = &match_pair.test_case { - return None; - } - - // These are all binary tests. - // - // FIXME(#29623) we can be more clever here - let pattern_test = self.test(match_pair); - if pattern_test.kind == test.kind { - fully_matched = true; - Some(0) - } else { - fully_matched = false; - None - } + // FIXME(#29623): return `Some(1)` when the values are different. + (TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val }) + if test_val == case_val => + { + fully_matched = true; + Some(0) + } + (TestKind::Eq { .. }, _) => { + fully_matched = false; + None } }; -- cgit 1.4.1-3-g733a5 From f363c1a3fe58f66924955755a6408dd1caedbdac Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 20 Feb 2024 22:59:11 +0100 Subject: Group default cases in `sort_candidate` --- compiler/rustc_mir_build/src/build/matches/test.rs | 26 ++++++++-------------- 1 file changed, 9 insertions(+), 17 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index a97d0de27e3..3712b8fb95b 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -575,10 +575,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fully_matched = true; Some(variant_index.as_usize()) } - (&TestKind::Switch { .. }, _) => { - fully_matched = false; - None - } // If we are performing a switch over integers, then this informs integer // equality, but nothing else. @@ -603,10 +599,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { options.len() }) } - (&TestKind::SwitchInt { .. }, _) => { - fully_matched = false; - None - } ( &TestKind::Len { len: test_len, op: BinOp::Eq }, @@ -673,10 +665,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } } - (TestKind::Len { .. }, _) => { - fully_matched = false; - None - } (TestKind::Range(test), &TestCase::Range(pat)) => { if test.as_ref() == pat { @@ -699,10 +687,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None } } - (&TestKind::Range { .. }, _) => { - fully_matched = false; - None - } // FIXME(#29623): return `Some(1)` when the values are different. (TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val }) @@ -711,7 +695,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fully_matched = true; Some(0) } - (TestKind::Eq { .. }, _) => { + + ( + TestKind::Switch { .. } + | TestKind::SwitchInt { .. } + | TestKind::Len { .. } + | TestKind::Range { .. } + | TestKind::Eq { .. }, + _, + ) => { fully_matched = false; None } -- cgit 1.4.1-3-g733a5 From 9d713862521f70c610b687d1629ee8fc4fb1e5ce Mon Sep 17 00:00:00 2001 From: Qiu Chaofan Date: Wed, 28 Feb 2024 17:41:12 +0800 Subject: Add profiling support to AIX AIX ld needs special option to merge objects with profiling. Also, profiler_builtins should include builtins for AIX from compiler-rt. --- compiler/rustc_codegen_ssa/src/back/linker.rs | 4 +++- library/profiler_builtins/build.rs | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'compiler') diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 1f3383815e2..e52efd86955 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1631,7 +1631,9 @@ impl<'a> Linker for AixLinker<'a> { fn optimize(&mut self) {} - fn pgo_gen(&mut self) {} + fn pgo_gen(&mut self) { + self.cmd.arg("-bdbg:namedsects:ss"); + } fn control_flow_guard(&mut self) {} diff --git a/library/profiler_builtins/build.rs b/library/profiler_builtins/build.rs index 8e7b72f8372..9d1c1ba305b 100644 --- a/library/profiler_builtins/build.rs +++ b/library/profiler_builtins/build.rs @@ -26,6 +26,7 @@ fn main() { "InstrProfilingMerge.c", "InstrProfilingMergeFile.c", "InstrProfilingNameVar.c", + "InstrProfilingPlatformAIX.c", "InstrProfilingPlatformDarwin.c", "InstrProfilingPlatformFuchsia.c", "InstrProfilingPlatformLinux.c", -- cgit 1.4.1-3-g733a5 From d6332ae79cf43b9c6ea74478cc5a4333dae685bb Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 28 Feb 2024 17:46:31 +0100 Subject: Separate the `bool` case from other integers in `TestKind` --- compiler/rustc_mir_build/src/build/matches/mod.rs | 17 ++--- compiler/rustc_mir_build/src/build/matches/test.rs | 77 +++++++++++----------- ...lifyCfg-initial.after-ElaborateDrops.after.diff | 15 ++--- ...lifyCfg-initial.after-ElaborateDrops.after.diff | 15 ++--- 4 files changed, 59 insertions(+), 65 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 641a278c1d3..e94489b8189 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1091,21 +1091,18 @@ enum TestKind<'tcx> { variants: BitSet, }, - /// Test what value an integer, `bool`, or `char` has. + /// Test what value an integer or `char` has. SwitchInt { - /// The type of the value that we're testing. - switch_ty: Ty<'tcx>, /// The (ordered) set of values that we test for. /// - /// For integers and `char`s we create a branch to each of the values in - /// `options`, as well as an "otherwise" branch for all other values, even - /// in the (rare) case that `options` is exhaustive. - /// - /// For `bool` we always generate two edges, one for `true` and one for - /// `false`. + /// We create a branch to each of the values in `options`, as well as an "otherwise" branch + /// for all other values, even in the (rare) case that `options` is exhaustive. options: FxIndexMap, u128>, }, + /// Test what value a `bool` has. + If, + /// Test for equality with value, possibly after an unsizing coercion to /// `ty`, Eq { @@ -1611,7 +1608,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // a test like SwitchInt, we may want to add cases based on the candidates that are // available match test.kind { - TestKind::SwitchInt { switch_ty: _, ref mut options } => { + TestKind::SwitchInt { ref mut options } => { for candidate in candidates.iter() { if !self.add_cases_to_switch(&match_place, candidate, options) { break; diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 1c97de58863..7a5a8a1a81a 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -34,12 +34,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TestKind::Switch { adt_def, variants: BitSet::new_empty(adt_def.variants().len()) } } + TestCase::Constant { .. } if match_pair.pattern.ty.is_bool() => TestKind::If, + TestCase::Constant { .. } if is_switch_ty(match_pair.pattern.ty) => { // For integers, we use a `SwitchInt` match, which allows // us to handle more cases. TestKind::SwitchInt { - switch_ty: match_pair.pattern.ty, - // these maps are empty to start; cases are // added below in add_cases_to_switch options: Default::default(), @@ -182,31 +182,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); } - TestKind::SwitchInt { switch_ty, ref options } => { - let terminator = if *switch_ty.kind() == ty::Bool { - assert!(!options.is_empty() && options.len() <= 2); - let [first_bb, second_bb] = *target_blocks else { - bug!("`TestKind::SwitchInt` on `bool` should have two targets") - }; - let (true_bb, false_bb) = match options[0] { - 1 => (first_bb, second_bb), - 0 => (second_bb, first_bb), - v => span_bug!(test.span, "expected boolean value but got {:?}", v), - }; - TerminatorKind::if_(Operand::Copy(place), true_bb, false_bb) - } else { - // The switch may be inexhaustive so we have a catch all block - debug_assert_eq!(options.len() + 1, target_blocks.len()); - let otherwise_block = *target_blocks.last().unwrap(); - let switch_targets = SwitchTargets::new( - options.values().copied().zip(target_blocks), - otherwise_block, - ); - TerminatorKind::SwitchInt { - discr: Operand::Copy(place), - targets: switch_targets, - } + TestKind::SwitchInt { ref options } => { + // The switch may be inexhaustive so we have a catch-all block + debug_assert_eq!(options.len() + 1, target_blocks.len()); + let otherwise_block = *target_blocks.last().unwrap(); + let switch_targets = SwitchTargets::new( + options.values().copied().zip(target_blocks), + otherwise_block, + ); + let terminator = TerminatorKind::SwitchInt { + discr: Operand::Copy(place), + targets: switch_targets, + }; + self.cfg.terminate(block, self.source_info(match_start_span), terminator); + } + + TestKind::If => { + let [false_bb, true_bb] = *target_blocks else { + bug!("`TestKind::If` should have two targets") }; + let terminator = TerminatorKind::if_(Operand::Copy(place), true_bb, false_bb); self.cfg.terminate(block, self.source_info(match_start_span), terminator); } @@ -585,14 +580,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // // FIXME(#29623) we could use PatKind::Range to rule // things out here, in some cases. - (TestKind::SwitchInt { switch_ty: _, options }, TestCase::Constant { value }) + (TestKind::SwitchInt { options }, TestCase::Constant { value }) if is_switch_ty(match_pair.pattern.ty) => { fully_matched = true; let index = options.get_index_of(value).unwrap(); Some(index) } - (TestKind::SwitchInt { switch_ty: _, options }, TestCase::Range(range)) => { + (TestKind::SwitchInt { options }, TestCase::Range(range)) => { fully_matched = false; let not_contained = self.values_not_contained_in_range(&*range, options).unwrap_or(false); @@ -608,6 +603,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None } + (&TestKind::If, TestCase::Constant { value }) => { + fully_matched = true; + let value = value.try_eval_bool(self.tcx, self.param_env).unwrap_or_else(|| { + span_bug!(test.span, "expected boolean value but got {value:?}") + }); + Some(value as usize) + } + (&TestKind::If, _) => { + fully_matched = false; + None + } + ( &TestKind::Len { len: test_len, op: BinOp::Eq }, &TestCase::Slice { len, variable_length }, @@ -755,7 +762,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { impl Test<'_> { pub(super) fn targets(&self) -> usize { match self.kind { - TestKind::Eq { .. } | TestKind::Range(_) | TestKind::Len { .. } => 2, + TestKind::Eq { .. } | TestKind::Range(_) | TestKind::Len { .. } | TestKind::If => 2, TestKind::Switch { adt_def, .. } => { // While the switch that we generate doesn't test for all // variants, we have a target for each variant and the @@ -763,21 +770,13 @@ impl Test<'_> { // specified have the same block. adt_def.variants().len() + 1 } - TestKind::SwitchInt { switch_ty, ref options, .. } => { - if switch_ty.is_bool() { - // `bool` is special cased in `perform_test` to always - // branch to two blocks. - 2 - } else { - options.len() + 1 - } - } + TestKind::SwitchInt { ref options } => options.len() + 1, } } } fn is_switch_ty(ty: Ty<'_>) -> bool { - ty.is_integral() || ty.is_char() || ty.is_bool() + ty.is_integral() || ty.is_char() } fn trait_method<'tcx>( diff --git a/tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff b/tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff index 307f7105dd2..619fda339a6 100644 --- a/tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff +++ b/tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff @@ -42,15 +42,11 @@ } bb2: { -- switchInt((_2.0: bool)) -> [0: bb4, otherwise: bb3]; +- switchInt((_2.0: bool)) -> [0: bb3, otherwise: bb4]; + switchInt((_2.0: bool)) -> [0: bb3, otherwise: bb17]; } bb3: { -- falseEdge -> [real: bb20, imaginary: bb4]; -- } -- -- bb4: { StorageLive(_15); _15 = (_2.1: bool); StorageLive(_16); @@ -59,8 +55,12 @@ + goto -> bb16; } + bb4: { +- falseEdge -> [real: bb20, imaginary: bb3]; +- } +- - bb5: { -- falseEdge -> [real: bb13, imaginary: bb3]; +- falseEdge -> [real: bb13, imaginary: bb4]; - } - - bb6: { @@ -68,7 +68,6 @@ - } - - bb7: { -+ bb4: { _0 = const 1_i32; - drop(_7) -> [return: bb18, unwind: bb25]; + drop(_7) -> [return: bb15, unwind: bb22]; @@ -184,7 +183,7 @@ StorageDead(_12); StorageDead(_8); StorageDead(_6); -- falseEdge -> [real: bb2, imaginary: bb3]; +- falseEdge -> [real: bb2, imaginary: bb4]; + goto -> bb2; } diff --git a/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff b/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff index 307f7105dd2..619fda339a6 100644 --- a/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff +++ b/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff @@ -42,15 +42,11 @@ } bb2: { -- switchInt((_2.0: bool)) -> [0: bb4, otherwise: bb3]; +- switchInt((_2.0: bool)) -> [0: bb3, otherwise: bb4]; + switchInt((_2.0: bool)) -> [0: bb3, otherwise: bb17]; } bb3: { -- falseEdge -> [real: bb20, imaginary: bb4]; -- } -- -- bb4: { StorageLive(_15); _15 = (_2.1: bool); StorageLive(_16); @@ -59,8 +55,12 @@ + goto -> bb16; } + bb4: { +- falseEdge -> [real: bb20, imaginary: bb3]; +- } +- - bb5: { -- falseEdge -> [real: bb13, imaginary: bb3]; +- falseEdge -> [real: bb13, imaginary: bb4]; - } - - bb6: { @@ -68,7 +68,6 @@ - } - - bb7: { -+ bb4: { _0 = const 1_i32; - drop(_7) -> [return: bb18, unwind: bb25]; + drop(_7) -> [return: bb15, unwind: bb22]; @@ -184,7 +183,7 @@ StorageDead(_12); StorageDead(_8); StorageDead(_6); -- falseEdge -> [real: bb2, imaginary: bb3]; +- falseEdge -> [real: bb2, imaginary: bb4]; + goto -> bb2; } -- cgit 1.4.1-3-g733a5 From e6f48fa0bb2c66bb29661bc81d4df5fadad32515 Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 16 Feb 2024 00:25:56 +0800 Subject: Suggest removing superfluous semicolos when statements used as expressions --- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 6 +- .../rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 2 +- .../rustc_infer/src/infer/error_reporting/mod.rs | 1 + .../src/infer/error_reporting/suggest.rs | 96 +++++++++++++++ tests/ui/inference/issue-105431-stmts-as-exp.rs | 76 ++++++++++++ .../ui/inference/issue-105431-stmts-as-exp.stderr | 129 +++++++++++++++++++++ 6 files changed, 308 insertions(+), 2 deletions(-) create mode 100644 tests/ui/inference/issue-105431-stmts-as-exp.rs create mode 100644 tests/ui/inference/issue-105431-stmts-as-exp.stderr (limited to 'compiler') diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 61c52422d19..bf58840f0f9 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1744,7 +1744,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_unit(self.tcx), ); } - if !self.consider_removing_semicolon(blk, expected_ty, err) { + if !self.err_ctxt().consider_removing_semicolon( + blk, + expected_ty, + err, + ) { self.err_ctxt().consider_returning_binding( blk, expected_ty, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index ccd9b38bf62..d5161b00fbc 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -22,7 +22,7 @@ use rustc_hir::{ Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, }; use rustc_hir_analysis::astconv::AstConv; -use rustc_infer::traits::{self, StatementAsExpression}; +use rustc_infer::traits::{self}; use rustc_middle::lint::in_external_macro; use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::print::with_no_trimmed_paths; diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 911b2f16c8b..6bb8c119942 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1976,6 +1976,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); self.suggest_await_on_expect_found(cause, span, &exp_found, diag); self.suggest_function_pointers(cause, span, &exp_found, diag); + self.suggest_for_statments_as_exp(cause, &exp_found, diag); } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index cfe8b75bdd7..be2437bfd60 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -1,8 +1,13 @@ +use crate::infer::error_reporting::hir::Path; use hir::def::CtorKind; use hir::intravisit::{walk_expr, walk_stmt, Visitor}; +use hir::{Local, QPath}; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Applicability, Diag}; use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::MatchSource; +use rustc_hir::Node; use rustc_middle::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, StatementAsExpression, @@ -293,6 +298,97 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } + pub(super) fn suggest_for_statments_as_exp( + &self, + cause: &ObligationCause<'tcx>, + exp_found: &ty::error::ExpectedFound>, + diag: &mut Diagnostic, + ) { + let ty::error::ExpectedFound { expected, found } = exp_found; + if !found.peel_refs().is_unit() { + return; + } + + let ObligationCauseCode::BlockTailExpression(hir_id, MatchSource::Normal) = cause.code() + else { + return; + }; + + let node = self.tcx.hir_node(*hir_id); + let mut blocks = vec![]; + if let hir::Node::Block(block) = node + && let Some(expr) = block.expr + && let hir::ExprKind::Path(QPath::Resolved(_, Path { res, .. })) = expr.kind + && let Res::Local(local) = res + && let Node::Local(Local { init: Some(init), .. }) = self.tcx.parent_hir_node(*local) + { + fn collect_blocks<'hir>(expr: &hir::Expr<'hir>, blocks: &mut Vec<&hir::Block<'hir>>) { + match expr.kind { + // `blk1` and `blk2` must be have the same types, it will be reported before reaching here + hir::ExprKind::If(_, blk1, Some(blk2)) => { + collect_blocks(blk1, blocks); + collect_blocks(blk2, blocks); + } + hir::ExprKind::Match(_, arms, _) => { + // all arms must have same types + for arm in arms.iter() { + collect_blocks(arm.body, blocks); + } + } + hir::ExprKind::Block(blk, _) => { + blocks.push(blk); + } + _ => {} + } + } + collect_blocks(init, &mut blocks); + } + + let expected_inner: Ty<'_> = expected.peel_refs(); + for block in blocks.iter() { + self.consider_removing_semicolon(block, expected_inner, diag); + } + } + + /// A common error is to add an extra semicolon: + /// + /// ```compile_fail,E0308 + /// fn foo() -> usize { + /// 22; + /// } + /// ``` + /// + /// This routine checks if the final statement in a block is an + /// expression with an explicit semicolon whose type is compatible + /// with `expected_ty`. If so, it suggests removing the semicolon. + pub fn consider_removing_semicolon( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + err: &mut Diagnostic, + ) -> bool { + if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) { + if let StatementAsExpression::NeedsBoxing = boxed { + err.span_suggestion_verbose( + span_semi, + "consider removing this semicolon and boxing the expression", + "", + Applicability::HasPlaceholders, + ); + } else { + err.span_suggestion_short( + span_semi, + "remove this semicolon to return this value", + "", + Applicability::MachineApplicable, + ); + } + true + } else { + false + } + } + pub(super) fn suggest_function_pointers( &self, cause: &ObligationCause<'tcx>, diff --git a/tests/ui/inference/issue-105431-stmts-as-exp.rs b/tests/ui/inference/issue-105431-stmts-as-exp.rs new file mode 100644 index 00000000000..b5adb4a2b66 --- /dev/null +++ b/tests/ui/inference/issue-105431-stmts-as-exp.rs @@ -0,0 +1,76 @@ +#![allow(unused)] + +fn test_if() -> i32 { + let x = if true { + eprintln!("hello"); + 3; + } + else { + 4; + }; + x //~ ERROR mismatched types +} + +fn test_if_without_binding() -> i32 { + if true { //~ ERROR mismatched types + eprintln!("hello"); + 3; + } + else { //~ ERROR mismatched types + 4; + } +} + +fn test_match() -> i32 { + let v = 1; + let res = match v { + 1 => { 1; } + _ => { 2; } + }; + res //~ ERROR mismatched types +} + +fn test_match_match_without_binding() -> i32 { + let v = 1; + match v { + 1 => { 1; } //~ ERROR mismatched types + _ => { 2; } //~ ERROR mismatched types + } +} + +fn test_match_arm_different_types() -> i32 { + let v = 1; + let res = match v { + 1 => { if 1 < 2 { 1 } else { 2 } } + _ => { 2; } //~ ERROR `match` arms have incompatible types + }; + res +} + +fn test_if_match_mixed() -> i32 { + let x = if true { + 3; + } else { + match 1 { + 1 => { 1 } + _ => { 2 } + }; + }; + x //~ ERROR mismatched types +} + +fn test_if_match_mixed_failed() -> i32 { + let x = if true { + 3; + } else { + // because this is a tailed expr, so we won't check deeper + match 1 { + 1 => { 33; } + _ => { 44; } + } + }; + x //~ ERROR mismatched types +} + + +fn main() {} diff --git a/tests/ui/inference/issue-105431-stmts-as-exp.stderr b/tests/ui/inference/issue-105431-stmts-as-exp.stderr new file mode 100644 index 00000000000..2878d602ec5 --- /dev/null +++ b/tests/ui/inference/issue-105431-stmts-as-exp.stderr @@ -0,0 +1,129 @@ +error[E0308]: mismatched types + --> $DIR/issue-105431-stmts-as-exp.rs:11:5 + | +LL | fn test_if() -> i32 { + | --- expected `i32` because of return type +... +LL | x + | ^ expected `i32`, found `()` + | +help: remove this semicolon to return this value + | +LL - 3; +LL + 3 + | +help: remove this semicolon to return this value + | +LL - 4; +LL + 4 + | + +error[E0308]: mismatched types + --> $DIR/issue-105431-stmts-as-exp.rs:15:13 + | +LL | if true { + | _____________^ +LL | | eprintln!("hello"); +LL | | 3; + | | - help: remove this semicolon to return this value +LL | | } + | |_____^ expected `i32`, found `()` + +error[E0308]: mismatched types + --> $DIR/issue-105431-stmts-as-exp.rs:19:10 + | +LL | else { + | __________^ +LL | | 4; + | | - help: remove this semicolon to return this value +LL | | } + | |_____^ expected `i32`, found `()` + +error[E0308]: mismatched types + --> $DIR/issue-105431-stmts-as-exp.rs:30:5 + | +LL | fn test_match() -> i32 { + | --- expected `i32` because of return type +... +LL | res + | ^^^ expected `i32`, found `()` + | +help: remove this semicolon to return this value + | +LL - 1 => { 1; } +LL + 1 => { 1 } + | +help: remove this semicolon to return this value + | +LL - _ => { 2; } +LL + _ => { 2 } + | + +error[E0308]: mismatched types + --> $DIR/issue-105431-stmts-as-exp.rs:36:14 + | +LL | 1 => { 1; } + | ^^^-^^ + | | | + | | help: remove this semicolon to return this value + | expected `i32`, found `()` + +error[E0308]: mismatched types + --> $DIR/issue-105431-stmts-as-exp.rs:37:14 + | +LL | _ => { 2; } + | ^^^-^^ + | | | + | | help: remove this semicolon to return this value + | expected `i32`, found `()` + +error[E0308]: `match` arms have incompatible types + --> $DIR/issue-105431-stmts-as-exp.rs:45:16 + | +LL | let res = match v { + | _______________- +LL | | 1 => { if 1 < 2 { 1 } else { 2 } } + | | ------------------------- this is found to be of type `{integer}` +LL | | _ => { 2; } + | | ^- + | | || + | | |help: consider removing this semicolon + | | expected integer, found `()` +LL | | }; + | |_____- `match` arms have incompatible types + +error[E0308]: mismatched types + --> $DIR/issue-105431-stmts-as-exp.rs:59:5 + | +LL | fn test_if_match_mixed() -> i32 { + | --- expected `i32` because of return type +... +LL | x + | ^ expected `i32`, found `()` + | +help: remove this semicolon to return this value + | +LL - 3; +LL + 3 + | +help: remove this semicolon to return this value + | +LL - }; +LL + } + | + +error[E0308]: mismatched types + --> $DIR/issue-105431-stmts-as-exp.rs:72:5 + | +LL | fn test_if_match_mixed_failed() -> i32 { + | --- expected `i32` because of return type +LL | let x = if true { +LL | 3; + | - help: remove this semicolon to return this value +... +LL | x + | ^ expected `i32`, found `()` + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0308`. -- cgit 1.4.1-3-g733a5 From e2ce5d74a5f87f9fb6baadd191288025ed974296 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 21 Feb 2024 09:37:45 +0800 Subject: renaming test cases --- .../rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 39 ------- .../rustc_infer/src/infer/error_reporting/mod.rs | 2 +- .../src/infer/error_reporting/suggest.rs | 10 +- tests/ui/inference/issue-105431-stmts-as-exp.rs | 76 ------------ .../ui/inference/issue-105431-stmts-as-exp.stderr | 129 --------------------- tests/ui/inference/stmts-as-exp-105431.rs | 76 ++++++++++++ tests/ui/inference/stmts-as-exp-105431.stderr | 129 +++++++++++++++++++++ 7 files changed, 211 insertions(+), 250 deletions(-) delete mode 100644 tests/ui/inference/issue-105431-stmts-as-exp.rs delete mode 100644 tests/ui/inference/issue-105431-stmts-as-exp.stderr create mode 100644 tests/ui/inference/stmts-as-exp-105431.rs create mode 100644 tests/ui/inference/stmts-as-exp-105431.stderr (limited to 'compiler') diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index d5161b00fbc..809102557ac 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1791,45 +1791,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - /// A common error is to add an extra semicolon: - /// - /// ```compile_fail,E0308 - /// fn foo() -> usize { - /// 22; - /// } - /// ``` - /// - /// This routine checks if the final statement in a block is an - /// expression with an explicit semicolon whose type is compatible - /// with `expected_ty`. If so, it suggests removing the semicolon. - pub(crate) fn consider_removing_semicolon( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - err: &mut Diag<'_>, - ) -> bool { - if let Some((span_semi, boxed)) = self.err_ctxt().could_remove_semicolon(blk, expected_ty) { - if let StatementAsExpression::NeedsBoxing = boxed { - err.span_suggestion_verbose( - span_semi, - "consider removing this semicolon and boxing the expression", - "", - Applicability::HasPlaceholders, - ); - } else { - err.span_suggestion_short( - span_semi, - "remove this semicolon to return this value", - "", - Applicability::MachineApplicable, - ); - } - true - } else { - false - } - } - pub(crate) fn is_field_suggestable( &self, field: &ty::FieldDef, diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 6bb8c119942..e19879ae06d 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1976,7 +1976,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); self.suggest_await_on_expect_found(cause, span, &exp_found, diag); self.suggest_function_pointers(cause, span, &exp_found, diag); - self.suggest_for_statments_as_exp(cause, &exp_found, diag); + self.suggest_turning_stmt_into_expr(cause, &exp_found, diag); } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index be2437bfd60..472dab639d5 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -298,11 +298,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - pub(super) fn suggest_for_statments_as_exp( + pub(super) fn suggest_turning_stmt_into_expr( &self, cause: &ObligationCause<'tcx>, exp_found: &ty::error::ExpectedFound>, - diag: &mut Diagnostic, + diag: &mut Diag<'_>, ) { let ty::error::ExpectedFound { expected, found } = exp_found; if !found.peel_refs().is_unit() { @@ -365,18 +365,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, blk: &'tcx hir::Block<'tcx>, expected_ty: Ty<'tcx>, - err: &mut Diagnostic, + diag: &mut Diag<'_>, ) -> bool { if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) { if let StatementAsExpression::NeedsBoxing = boxed { - err.span_suggestion_verbose( + diag.span_suggestion_verbose( span_semi, "consider removing this semicolon and boxing the expression", "", Applicability::HasPlaceholders, ); } else { - err.span_suggestion_short( + diag.span_suggestion_short( span_semi, "remove this semicolon to return this value", "", diff --git a/tests/ui/inference/issue-105431-stmts-as-exp.rs b/tests/ui/inference/issue-105431-stmts-as-exp.rs deleted file mode 100644 index b5adb4a2b66..00000000000 --- a/tests/ui/inference/issue-105431-stmts-as-exp.rs +++ /dev/null @@ -1,76 +0,0 @@ -#![allow(unused)] - -fn test_if() -> i32 { - let x = if true { - eprintln!("hello"); - 3; - } - else { - 4; - }; - x //~ ERROR mismatched types -} - -fn test_if_without_binding() -> i32 { - if true { //~ ERROR mismatched types - eprintln!("hello"); - 3; - } - else { //~ ERROR mismatched types - 4; - } -} - -fn test_match() -> i32 { - let v = 1; - let res = match v { - 1 => { 1; } - _ => { 2; } - }; - res //~ ERROR mismatched types -} - -fn test_match_match_without_binding() -> i32 { - let v = 1; - match v { - 1 => { 1; } //~ ERROR mismatched types - _ => { 2; } //~ ERROR mismatched types - } -} - -fn test_match_arm_different_types() -> i32 { - let v = 1; - let res = match v { - 1 => { if 1 < 2 { 1 } else { 2 } } - _ => { 2; } //~ ERROR `match` arms have incompatible types - }; - res -} - -fn test_if_match_mixed() -> i32 { - let x = if true { - 3; - } else { - match 1 { - 1 => { 1 } - _ => { 2 } - }; - }; - x //~ ERROR mismatched types -} - -fn test_if_match_mixed_failed() -> i32 { - let x = if true { - 3; - } else { - // because this is a tailed expr, so we won't check deeper - match 1 { - 1 => { 33; } - _ => { 44; } - } - }; - x //~ ERROR mismatched types -} - - -fn main() {} diff --git a/tests/ui/inference/issue-105431-stmts-as-exp.stderr b/tests/ui/inference/issue-105431-stmts-as-exp.stderr deleted file mode 100644 index 2878d602ec5..00000000000 --- a/tests/ui/inference/issue-105431-stmts-as-exp.stderr +++ /dev/null @@ -1,129 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/issue-105431-stmts-as-exp.rs:11:5 - | -LL | fn test_if() -> i32 { - | --- expected `i32` because of return type -... -LL | x - | ^ expected `i32`, found `()` - | -help: remove this semicolon to return this value - | -LL - 3; -LL + 3 - | -help: remove this semicolon to return this value - | -LL - 4; -LL + 4 - | - -error[E0308]: mismatched types - --> $DIR/issue-105431-stmts-as-exp.rs:15:13 - | -LL | if true { - | _____________^ -LL | | eprintln!("hello"); -LL | | 3; - | | - help: remove this semicolon to return this value -LL | | } - | |_____^ expected `i32`, found `()` - -error[E0308]: mismatched types - --> $DIR/issue-105431-stmts-as-exp.rs:19:10 - | -LL | else { - | __________^ -LL | | 4; - | | - help: remove this semicolon to return this value -LL | | } - | |_____^ expected `i32`, found `()` - -error[E0308]: mismatched types - --> $DIR/issue-105431-stmts-as-exp.rs:30:5 - | -LL | fn test_match() -> i32 { - | --- expected `i32` because of return type -... -LL | res - | ^^^ expected `i32`, found `()` - | -help: remove this semicolon to return this value - | -LL - 1 => { 1; } -LL + 1 => { 1 } - | -help: remove this semicolon to return this value - | -LL - _ => { 2; } -LL + _ => { 2 } - | - -error[E0308]: mismatched types - --> $DIR/issue-105431-stmts-as-exp.rs:36:14 - | -LL | 1 => { 1; } - | ^^^-^^ - | | | - | | help: remove this semicolon to return this value - | expected `i32`, found `()` - -error[E0308]: mismatched types - --> $DIR/issue-105431-stmts-as-exp.rs:37:14 - | -LL | _ => { 2; } - | ^^^-^^ - | | | - | | help: remove this semicolon to return this value - | expected `i32`, found `()` - -error[E0308]: `match` arms have incompatible types - --> $DIR/issue-105431-stmts-as-exp.rs:45:16 - | -LL | let res = match v { - | _______________- -LL | | 1 => { if 1 < 2 { 1 } else { 2 } } - | | ------------------------- this is found to be of type `{integer}` -LL | | _ => { 2; } - | | ^- - | | || - | | |help: consider removing this semicolon - | | expected integer, found `()` -LL | | }; - | |_____- `match` arms have incompatible types - -error[E0308]: mismatched types - --> $DIR/issue-105431-stmts-as-exp.rs:59:5 - | -LL | fn test_if_match_mixed() -> i32 { - | --- expected `i32` because of return type -... -LL | x - | ^ expected `i32`, found `()` - | -help: remove this semicolon to return this value - | -LL - 3; -LL + 3 - | -help: remove this semicolon to return this value - | -LL - }; -LL + } - | - -error[E0308]: mismatched types - --> $DIR/issue-105431-stmts-as-exp.rs:72:5 - | -LL | fn test_if_match_mixed_failed() -> i32 { - | --- expected `i32` because of return type -LL | let x = if true { -LL | 3; - | - help: remove this semicolon to return this value -... -LL | x - | ^ expected `i32`, found `()` - -error: aborting due to 9 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/inference/stmts-as-exp-105431.rs b/tests/ui/inference/stmts-as-exp-105431.rs new file mode 100644 index 00000000000..b5adb4a2b66 --- /dev/null +++ b/tests/ui/inference/stmts-as-exp-105431.rs @@ -0,0 +1,76 @@ +#![allow(unused)] + +fn test_if() -> i32 { + let x = if true { + eprintln!("hello"); + 3; + } + else { + 4; + }; + x //~ ERROR mismatched types +} + +fn test_if_without_binding() -> i32 { + if true { //~ ERROR mismatched types + eprintln!("hello"); + 3; + } + else { //~ ERROR mismatched types + 4; + } +} + +fn test_match() -> i32 { + let v = 1; + let res = match v { + 1 => { 1; } + _ => { 2; } + }; + res //~ ERROR mismatched types +} + +fn test_match_match_without_binding() -> i32 { + let v = 1; + match v { + 1 => { 1; } //~ ERROR mismatched types + _ => { 2; } //~ ERROR mismatched types + } +} + +fn test_match_arm_different_types() -> i32 { + let v = 1; + let res = match v { + 1 => { if 1 < 2 { 1 } else { 2 } } + _ => { 2; } //~ ERROR `match` arms have incompatible types + }; + res +} + +fn test_if_match_mixed() -> i32 { + let x = if true { + 3; + } else { + match 1 { + 1 => { 1 } + _ => { 2 } + }; + }; + x //~ ERROR mismatched types +} + +fn test_if_match_mixed_failed() -> i32 { + let x = if true { + 3; + } else { + // because this is a tailed expr, so we won't check deeper + match 1 { + 1 => { 33; } + _ => { 44; } + } + }; + x //~ ERROR mismatched types +} + + +fn main() {} diff --git a/tests/ui/inference/stmts-as-exp-105431.stderr b/tests/ui/inference/stmts-as-exp-105431.stderr new file mode 100644 index 00000000000..f3da04b39a3 --- /dev/null +++ b/tests/ui/inference/stmts-as-exp-105431.stderr @@ -0,0 +1,129 @@ +error[E0308]: mismatched types + --> $DIR/stmts-as-exp-105431.rs:11:5 + | +LL | fn test_if() -> i32 { + | --- expected `i32` because of return type +... +LL | x + | ^ expected `i32`, found `()` + | +help: remove this semicolon to return this value + | +LL - 3; +LL + 3 + | +help: remove this semicolon to return this value + | +LL - 4; +LL + 4 + | + +error[E0308]: mismatched types + --> $DIR/stmts-as-exp-105431.rs:15:13 + | +LL | if true { + | _____________^ +LL | | eprintln!("hello"); +LL | | 3; + | | - help: remove this semicolon to return this value +LL | | } + | |_____^ expected `i32`, found `()` + +error[E0308]: mismatched types + --> $DIR/stmts-as-exp-105431.rs:19:10 + | +LL | else { + | __________^ +LL | | 4; + | | - help: remove this semicolon to return this value +LL | | } + | |_____^ expected `i32`, found `()` + +error[E0308]: mismatched types + --> $DIR/stmts-as-exp-105431.rs:30:5 + | +LL | fn test_match() -> i32 { + | --- expected `i32` because of return type +... +LL | res + | ^^^ expected `i32`, found `()` + | +help: remove this semicolon to return this value + | +LL - 1 => { 1; } +LL + 1 => { 1 } + | +help: remove this semicolon to return this value + | +LL - _ => { 2; } +LL + _ => { 2 } + | + +error[E0308]: mismatched types + --> $DIR/stmts-as-exp-105431.rs:36:14 + | +LL | 1 => { 1; } + | ^^^-^^ + | | | + | | help: remove this semicolon to return this value + | expected `i32`, found `()` + +error[E0308]: mismatched types + --> $DIR/stmts-as-exp-105431.rs:37:14 + | +LL | _ => { 2; } + | ^^^-^^ + | | | + | | help: remove this semicolon to return this value + | expected `i32`, found `()` + +error[E0308]: `match` arms have incompatible types + --> $DIR/stmts-as-exp-105431.rs:45:16 + | +LL | let res = match v { + | _______________- +LL | | 1 => { if 1 < 2 { 1 } else { 2 } } + | | ------------------------- this is found to be of type `{integer}` +LL | | _ => { 2; } + | | ^- + | | || + | | |help: consider removing this semicolon + | | expected integer, found `()` +LL | | }; + | |_____- `match` arms have incompatible types + +error[E0308]: mismatched types + --> $DIR/stmts-as-exp-105431.rs:59:5 + | +LL | fn test_if_match_mixed() -> i32 { + | --- expected `i32` because of return type +... +LL | x + | ^ expected `i32`, found `()` + | +help: remove this semicolon to return this value + | +LL - 3; +LL + 3 + | +help: remove this semicolon to return this value + | +LL - }; +LL + } + | + +error[E0308]: mismatched types + --> $DIR/stmts-as-exp-105431.rs:72:5 + | +LL | fn test_if_match_mixed_failed() -> i32 { + | --- expected `i32` because of return type +LL | let x = if true { +LL | 3; + | - help: remove this semicolon to return this value +... +LL | x + | ^ expected `i32`, found `()` + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0308`. -- cgit 1.4.1-3-g733a5 From 5ec9b8d778b91cb579dc177eedaff4ba69bba33f Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 23 Feb 2024 10:12:08 +0100 Subject: distinguish recursion limit based overflow for diagnostics also change the number of allowed fixpoint steps to be fixed instead of using the `log` of the total recursion depth. --- compiler/rustc_infer/src/traits/mod.rs | 10 ++++--- .../rustc_infer/src/traits/structural_impls.rs | 6 ++-- compiler/rustc_middle/src/traits/solve.rs | 30 +++++++++++++------- .../src/solve/alias_relate.rs | 6 ++-- .../src/solve/eval_ctxt/mod.rs | 33 +++++++++++----------- .../rustc_trait_selection/src/solve/fulfill.rs | 11 +++++--- compiler/rustc_trait_selection/src/solve/mod.rs | 11 ++++++++ .../src/solve/search_graph.rs | 21 ++++++-------- .../traits/error_reporting/type_err_ctxt_ext.rs | 14 +++++---- .../rustc_trait_selection/src/traits/fulfill.rs | 2 +- .../trait-bounds/issue-95230.next.stderr | 1 - .../inherent-impls-overflow.next.stderr | 2 -- .../traits/next-solver/alias-bound-unsound.stderr | 10 ------- .../trait_ref_is_knowable-norm-overflow.stderr | 3 ++ .../coinduction/fixpoint-exponential-growth.stderr | 1 - .../double-cycle-inductive-coinductive.stderr | 2 -- .../cycles/inductive-fixpoint-hang.stderr | 1 - .../cycles/inductive-not-on-stack.stderr | 2 -- .../next-solver/cycles/mixed-cycles-1.stderr | 1 - .../next-solver/cycles/mixed-cycles-2.stderr | 1 - .../next-solver/normalize-param-env-2.stderr | 8 ------ .../next-solver/normalize-param-env-4.next.stderr | 5 ---- .../overflow/exponential-trait-goals.stderr | 1 - .../overflow/recursion-limit-zero-issue-115351.rs | 7 +---- .../recursion-limit-zero-issue-115351.stderr | 27 ------------------ 25 files changed, 91 insertions(+), 125 deletions(-) delete mode 100644 tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.stderr (limited to 'compiler') diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 72ec07375ac..4808a1defdd 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -135,16 +135,18 @@ pub struct FulfillmentError<'tcx> { #[derive(Clone)] pub enum FulfillmentErrorCode<'tcx> { - /// Inherently impossible to fulfill; this trait is implemented if and only if it is already implemented. + /// Inherently impossible to fulfill; this trait is implemented if and only + /// if it is already implemented. Cycle(Vec>), SelectionError(SelectionError<'tcx>), ProjectionError(MismatchedProjectionTypes<'tcx>), SubtypeError(ExpectedFound>, TypeError<'tcx>), // always comes from a SubtypePredicate ConstEquateError(ExpectedFound>, TypeError<'tcx>), Ambiguity { - /// Overflow reported from the new solver `-Znext-solver`, which will - /// be reported as an regular error as opposed to a fatal error. - overflow: bool, + /// Overflow is only `Some(suggest_recursion_limit)` when using the next generation + /// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by + /// emitting a fatal error instead. + overflow: Option, }, } diff --git a/compiler/rustc_infer/src/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs index 3b4050fcd27..bf4f88530d0 100644 --- a/compiler/rustc_infer/src/traits/structural_impls.rs +++ b/compiler/rustc_infer/src/traits/structural_impls.rs @@ -47,8 +47,10 @@ impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> { ConstEquateError(ref a, ref b) => { write!(f, "CodeConstEquateError({a:?}, {b:?})") } - Ambiguity { overflow: false } => write!(f, "Ambiguity"), - Ambiguity { overflow: true } => write!(f, "Overflow"), + Ambiguity { overflow: None } => write!(f, "Ambiguity"), + Ambiguity { overflow: Some(suggest_increasing_limit) } => { + write!(f, "Overflow({suggest_increasing_limit})") + } Cycle(ref cycle) => write!(f, "Cycle({cycle:?})"), } } diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index 048df367bd6..0dc65126011 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -60,7 +60,6 @@ pub enum Certainty { impl Certainty { pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity); - pub const OVERFLOW: Certainty = Certainty::Maybe(MaybeCause::Overflow); /// Use this function to merge the certainty of multiple nested subgoals. /// @@ -79,16 +78,13 @@ impl Certainty { (Certainty::Yes, Certainty::Yes) => Certainty::Yes, (Certainty::Yes, Certainty::Maybe(_)) => other, (Certainty::Maybe(_), Certainty::Yes) => self, - (Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(MaybeCause::Ambiguity)) => { - Certainty::Maybe(MaybeCause::Ambiguity) - } - (Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(MaybeCause::Overflow)) - | (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Ambiguity)) - | (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => { - Certainty::Maybe(MaybeCause::Overflow) - } + (Certainty::Maybe(a), Certainty::Maybe(b)) => Certainty::Maybe(a.unify_with(b)), } } + + pub const fn overflow(suggest_increasing_limit: bool) -> Certainty { + Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }) + } } /// Why we failed to evaluate a goal. @@ -99,7 +95,21 @@ pub enum MaybeCause { /// or we hit a case where we just don't bother, e.g. `?x: Trait` goals. Ambiguity, /// We gave up due to an overflow, most often by hitting the recursion limit. - Overflow, + Overflow { suggest_increasing_limit: bool }, +} + +impl MaybeCause { + fn unify_with(self, other: MaybeCause) -> MaybeCause { + match (self, other) { + (MaybeCause::Ambiguity, MaybeCause::Ambiguity) => MaybeCause::Ambiguity, + (MaybeCause::Ambiguity, MaybeCause::Overflow { .. }) => other, + (MaybeCause::Overflow { .. }, MaybeCause::Ambiguity) => self, + ( + MaybeCause::Overflow { suggest_increasing_limit: a }, + MaybeCause::Overflow { suggest_increasing_limit: b }, + ) => MaybeCause::Overflow { suggest_increasing_limit: a || b }, + } + } } #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)] diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index afd9d95cb57..67657c81cf6 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -36,11 +36,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let Goal { param_env, predicate: (lhs, rhs, direction) } = goal; let Some(lhs) = self.try_normalize_term(param_env, lhs)? else { - return self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); + return self + .evaluate_added_goals_and_make_canonical_response(Certainty::overflow(true)); }; let Some(rhs) = self.try_normalize_term(param_env, rhs)? else { - return self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); + return self + .evaluate_added_goals_and_make_canonical_response(Certainty::overflow(true)); }; let variance = match direction { diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs index ed428bb8e66..e20cec18790 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs @@ -7,6 +7,7 @@ use rustc_infer::infer::{ BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt, }; use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::solve::MaybeCause; use rustc_infer::traits::ObligationCause; use rustc_middle::infer::canonical::CanonicalVarInfos; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; @@ -29,7 +30,7 @@ use std::ops::ControlFlow; use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; use super::inspect::ProofTreeBuilder; -use super::{search_graph, GoalEvaluationKind}; +use super::{search_graph, GoalEvaluationKind, FIXPOINT_STEP_LIMIT}; use super::{search_graph::SearchGraph, Goal}; use super::{GoalSource, SolverMode}; pub use select::InferCtxtSelectExt; @@ -154,10 +155,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { self.search_graph.solver_mode() } - pub(super) fn local_overflow_limit(&self) -> usize { - self.search_graph.local_overflow_limit() - } - /// Creates a root evaluation context and search graph. This should only be /// used from outside of any evaluation, and other methods should be preferred /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]). @@ -167,7 +164,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> R, ) -> (R, Option>) { let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal }; - let mut search_graph = search_graph::SearchGraph::new(infcx.tcx, mode); + let mut search_graph = search_graph::SearchGraph::new(mode); let mut ecx = EvalCtxt { search_graph: &mut search_graph, @@ -388,16 +385,18 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { && source != GoalSource::ImplWhereBound }; - if response.value.certainty == Certainty::OVERFLOW && !keep_overflow_constraints() { - (Certainty::OVERFLOW, false) - } else { - let has_changed = !response.value.var_values.is_identity_modulo_regions() - || !response.value.external_constraints.opaque_types.is_empty(); - - let certainty = - self.instantiate_and_apply_query_response(param_env, original_values, response); - (certainty, has_changed) + if let Certainty::Maybe(MaybeCause::Overflow { .. }) = response.value.certainty + && !keep_overflow_constraints() + { + return (response.value.certainty, false); } + + let has_changed = !response.value.var_values.is_identity_modulo_regions() + || !response.value.external_constraints.opaque_types.is_empty(); + + let certainty = + self.instantiate_and_apply_query_response(param_env, original_values, response); + (certainty, has_changed) } fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> { @@ -466,8 +465,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let inspect = self.inspect.new_evaluate_added_goals(); let inspect = core::mem::replace(&mut self.inspect, inspect); - let mut response = Ok(Certainty::OVERFLOW); - for _ in 0..self.local_overflow_limit() { + let mut response = Ok(Certainty::overflow(false)); + for _ in 0..FIXPOINT_STEP_LIMIT { // FIXME: This match is a bit ugly, it might be nice to change the inspect // stuff to use a closure instead. which should hopefully simplify this a bit. match self.evaluate_added_goals_step() { diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index c1b07765e50..368e2e8810c 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -80,11 +80,14 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { .0 { Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => { - FulfillmentErrorCode::Ambiguity { overflow: false } - } - Ok((_, Certainty::Maybe(MaybeCause::Overflow))) => { - FulfillmentErrorCode::Ambiguity { overflow: true } + FulfillmentErrorCode::Ambiguity { overflow: None } } + Ok(( + _, + Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }), + )) => FulfillmentErrorCode::Ambiguity { + overflow: Some(suggest_increasing_limit), + }, Ok((_, Certainty::Yes)) => { bug!("did not expect successful goal when collecting ambiguity errors") } diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 51094b781c0..0bf28f520a4 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -42,6 +42,17 @@ pub use fulfill::FulfillmentCtxt; pub(crate) use normalize::deeply_normalize_for_diagnostics; pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes}; +/// How many fixpoint iterations we should attempt inside of the solver before bailing +/// with overflow. +/// +/// We previously used `tcx.recursion_limit().0.checked_ilog2().unwrap_or(0)` for this. +/// However, it feels unlikely that uncreasing the recursion limit by a power of two +/// to get one more itereation is every useful or desirable. We now instead used a constant +/// here. If there ever ends up some use-cases where a bigger number of fixpoint iterations +/// is required, we can add a new attribute for that or revert this to be dependant on the +/// recursion limit again. However, this feels very unlikely. +const FIXPOINT_STEP_LIMIT: usize = 8; + #[derive(Debug, Clone, Copy)] enum SolverMode { /// Ordinary trait solving, using everywhere except for coherence. diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs index bede94a2e43..07a8aca85a0 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph.rs @@ -1,3 +1,5 @@ +use crate::solve::FIXPOINT_STEP_LIMIT; + use super::inspect; use super::inspect::ProofTreeBuilder; use super::SolverMode; @@ -99,7 +101,6 @@ impl<'tcx> ProvisionalCacheEntry<'tcx> { pub(super) struct SearchGraph<'tcx> { mode: SolverMode, - local_overflow_limit: usize, /// The stack of goals currently being computed. /// /// An element is *deeper* in the stack if its index is *lower*. @@ -116,10 +117,9 @@ pub(super) struct SearchGraph<'tcx> { } impl<'tcx> SearchGraph<'tcx> { - pub(super) fn new(tcx: TyCtxt<'tcx>, mode: SolverMode) -> SearchGraph<'tcx> { + pub(super) fn new(mode: SolverMode) -> SearchGraph<'tcx> { Self { mode, - local_overflow_limit: tcx.recursion_limit().0.checked_ilog2().unwrap_or(0) as usize, stack: Default::default(), provisional_cache: Default::default(), cycle_participants: Default::default(), @@ -130,10 +130,6 @@ impl<'tcx> SearchGraph<'tcx> { self.mode } - pub(super) fn local_overflow_limit(&self) -> usize { - self.local_overflow_limit - } - /// Update the stack and reached depths on cache hits. #[instrument(level = "debug", skip(self))] fn on_cache_hit(&mut self, additional_depth: usize, encountered_overflow: bool) { @@ -277,7 +273,7 @@ impl<'tcx> SearchGraph<'tcx> { } inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::Overflow); - return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW); + return Self::response_no_constraints(tcx, input, Certainty::overflow(true)); }; // Try to fetch the goal from the global cache. @@ -370,7 +366,7 @@ impl<'tcx> SearchGraph<'tcx> { } else if is_coinductive_cycle { Self::response_no_constraints(tcx, input, Certainty::Yes) } else { - Self::response_no_constraints(tcx, input, Certainty::OVERFLOW) + Self::response_no_constraints(tcx, input, Certainty::overflow(false)) }; } else { // No entry, we push this goal on the stack and try to prove it. @@ -398,7 +394,7 @@ impl<'tcx> SearchGraph<'tcx> { // of this we continuously recompute the cycle until the result // of the previous iteration is equal to the final result, at which // point we are done. - for _ in 0..self.local_overflow_limit() { + for _ in 0..FIXPOINT_STEP_LIMIT { let result = prove_goal(self, inspect); let stack_entry = self.pop_stack(); debug_assert_eq!(stack_entry.input, input); @@ -431,7 +427,8 @@ impl<'tcx> SearchGraph<'tcx> { } else if stack_entry.has_been_used == HasBeenUsed::COINDUCTIVE_CYCLE { Self::response_no_constraints(tcx, input, Certainty::Yes) == result } else if stack_entry.has_been_used == HasBeenUsed::INDUCTIVE_CYCLE { - Self::response_no_constraints(tcx, input, Certainty::OVERFLOW) == result + Self::response_no_constraints(tcx, input, Certainty::overflow(false)) + == result } else { false }; @@ -452,7 +449,7 @@ impl<'tcx> SearchGraph<'tcx> { debug!("canonical cycle overflow"); let current_entry = self.pop_stack(); debug_assert!(current_entry.has_been_used.is_empty()); - let result = Self::response_no_constraints(tcx, input, Certainty::OVERFLOW); + let result = Self::response_no_constraints(tcx, input, Certainty::overflow(false)); (current_entry, result) }); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index dcbb63f00f7..cbf52ea9e5a 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -335,12 +335,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); } - fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed { + fn report_overflow_no_abort( + &self, + obligation: PredicateObligation<'tcx>, + suggest_increasing_limit: bool, + ) -> ErrorGuaranteed { let obligation = self.resolve_vars_if_possible(obligation); let mut err = self.build_overflow_error( OverflowCause::TraitSolver(obligation.predicate), obligation.cause.span, - true, + suggest_increasing_limit, ); self.note_obligation_cause(&mut err, &obligation); self.point_at_returns_when_relevant(&mut err, &obligation); @@ -1422,11 +1426,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { FulfillmentErrorCode::ProjectionError(ref e) => { self.report_projection_error(&error.obligation, e) } - FulfillmentErrorCode::Ambiguity { overflow: false } => { + FulfillmentErrorCode::Ambiguity { overflow: None } => { self.maybe_report_ambiguity(&error.obligation) } - FulfillmentErrorCode::Ambiguity { overflow: true } => { - self.report_overflow_no_abort(error.obligation.clone()) + FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => { + self.report_overflow_no_abort(error.obligation.clone(), suggest_increasing_limit) } FulfillmentErrorCode::SubtypeError(ref expected_found, ref err) => self .report_mismatched_types( diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index b91698af942..2fd64f474d5 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -138,7 +138,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { _infcx: &InferCtxt<'tcx>, ) -> Vec> { self.predicates - .to_errors(FulfillmentErrorCode::Ambiguity { overflow: false }) + .to_errors(FulfillmentErrorCode::Ambiguity { overflow: None }) .into_iter() .map(to_fulfillment_error) .collect() diff --git a/tests/ui/higher-ranked/trait-bounds/issue-95230.next.stderr b/tests/ui/higher-ranked/trait-bounds/issue-95230.next.stderr index 6155579c9fa..9b0efe9abe6 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-95230.next.stderr +++ b/tests/ui/higher-ranked/trait-bounds/issue-95230.next.stderr @@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `for<'a> &'a mut Bar well-form LL | for<'a> &'a mut Self:; | ^^^^^^^^^^^^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_95230`) note: required by a bound in `Bar` --> $DIR/issue-95230.rs:9:13 | diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr b/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr index 85e8061f173..80377bf6c20 100644 --- a/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr +++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr @@ -3,8 +3,6 @@ error[E0275]: overflow evaluating the requirement `Loop == _` | LL | impl Loop {} | ^^^^ - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inherent_impls_overflow`) error[E0392]: type parameter `T` is never used --> $DIR/inherent-impls-overflow.rs:13:12 diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.stderr b/tests/ui/traits/next-solver/alias-bound-unsound.stderr index 2408e05728a..a5c2f215134 100644 --- a/tests/ui/traits/next-solver/alias-bound-unsound.stderr +++ b/tests/ui/traits/next-solver/alias-bound-unsound.stderr @@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `String: Copy` LL | type Item = String where String: Copy; | ^^^^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`) note: the requirement `String: Copy` appears on the `impl`'s associated type `Item` but not on the corresponding trait's associated type --> $DIR/alias-bound-unsound.rs:8:10 | @@ -18,32 +17,24 @@ error[E0275]: overflow evaluating the requirement `String <: <() as Foo>::Item` | LL | drop(<() as Foo>::copy_me(&x)); | ^^ - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`) error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` --> $DIR/alias-bound-unsound.rs:24:10 | LL | drop(<() as Foo>::copy_me(&x)); | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`) error[E0275]: overflow evaluating the requirement `&<() as Foo>::Item well-formed` --> $DIR/alias-bound-unsound.rs:24:31 | LL | drop(<() as Foo>::copy_me(&x)); | ^^ - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`) error[E0275]: overflow evaluating the requirement `<() as Foo>::Item well-formed` --> $DIR/alias-bound-unsound.rs:24:10 | LL | drop(<() as Foo>::copy_me(&x)); | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`) error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` --> $DIR/alias-bound-unsound.rs:24:10 @@ -51,7 +42,6 @@ error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` LL | drop(<() as Foo>::copy_me(&x)); | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`) = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 6 previous errors diff --git a/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr b/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr index fc145b81196..a04fa1ab8a1 100644 --- a/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr +++ b/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr @@ -6,6 +6,9 @@ LL | impl Trait for T {} LL | struct LocalTy; LL | impl Trait for ::Assoc {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation + | + = note: overflow evaluating the requirement `_ == ::Assoc` + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`trait_ref_is_knowable_norm_overflow`) error[E0275]: overflow evaluating the requirement `::Assoc: Sized` --> $DIR/trait_ref_is_knowable-norm-overflow.rs:10:18 diff --git a/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr b/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr index 150100f2c53..8d7d8cee08a 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr +++ b/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr @@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `W<_>: Trait` LL | impls::>(); | ^^^^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`fixpoint_exponential_growth`) note: required by a bound in `impls` --> $DIR/fixpoint-exponential-growth.rs:30:13 | diff --git a/tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.stderr b/tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.stderr index a3404da51f0..7cedb4d36c9 100644 --- a/tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.stderr +++ b/tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.stderr @@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `(): Trait` LL | impls_trait::<()>(); | ^^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`double_cycle_inductive_coinductive`) note: required by a bound in `impls_trait` --> $DIR/double-cycle-inductive-coinductive.rs:17:19 | @@ -17,7 +16,6 @@ error[E0275]: overflow evaluating the requirement `(): TraitRev` LL | impls_trait_rev::<()>(); | ^^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`double_cycle_inductive_coinductive`) note: required by a bound in `impls_trait_rev` --> $DIR/double-cycle-inductive-coinductive.rs:29:23 | diff --git a/tests/ui/traits/next-solver/cycles/inductive-fixpoint-hang.stderr b/tests/ui/traits/next-solver/cycles/inductive-fixpoint-hang.stderr index 42451920744..a2a5c028cf8 100644 --- a/tests/ui/traits/next-solver/cycles/inductive-fixpoint-hang.stderr +++ b/tests/ui/traits/next-solver/cycles/inductive-fixpoint-hang.stderr @@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `W<_>: Trait` LL | impls_trait::>(); | ^^^^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inductive_fixpoint_hang`) note: required by a bound in `impls_trait` --> $DIR/inductive-fixpoint-hang.rs:28:19 | diff --git a/tests/ui/traits/next-solver/cycles/inductive-not-on-stack.stderr b/tests/ui/traits/next-solver/cycles/inductive-not-on-stack.stderr index 859b3f3f1c7..e9cc6bc6c81 100644 --- a/tests/ui/traits/next-solver/cycles/inductive-not-on-stack.stderr +++ b/tests/ui/traits/next-solver/cycles/inductive-not-on-stack.stderr @@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `(): A` LL | impls_a::<()>(); | ^^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inductive_not_on_stack`) note: required by a bound in `impls_a` --> $DIR/inductive-not-on-stack.rs:25:15 | @@ -17,7 +16,6 @@ error[E0275]: overflow evaluating the requirement `(): AR` LL | impls_ar::<()>(); | ^^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inductive_not_on_stack`) note: required by a bound in `impls_ar` --> $DIR/inductive-not-on-stack.rs:38:16 | diff --git a/tests/ui/traits/next-solver/cycles/mixed-cycles-1.stderr b/tests/ui/traits/next-solver/cycles/mixed-cycles-1.stderr index e828bdeb16b..17544eb1da5 100644 --- a/tests/ui/traits/next-solver/cycles/mixed-cycles-1.stderr +++ b/tests/ui/traits/next-solver/cycles/mixed-cycles-1.stderr @@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `(): A` LL | impls_a::<()>(); | ^^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`mixed_cycles_1`) note: required by a bound in `impls_a` --> $DIR/mixed-cycles-1.rs:34:15 | diff --git a/tests/ui/traits/next-solver/cycles/mixed-cycles-2.stderr b/tests/ui/traits/next-solver/cycles/mixed-cycles-2.stderr index ec13093f707..a9be1016c74 100644 --- a/tests/ui/traits/next-solver/cycles/mixed-cycles-2.stderr +++ b/tests/ui/traits/next-solver/cycles/mixed-cycles-2.stderr @@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `(): A` LL | impls_a::<()>(); | ^^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`mixed_cycles_2`) note: required by a bound in `impls_a` --> $DIR/mixed-cycles-2.rs:27:15 | diff --git a/tests/ui/traits/next-solver/normalize-param-env-2.stderr b/tests/ui/traits/next-solver/normalize-param-env-2.stderr index 86729eb8a4b..74a0a90885d 100644 --- a/tests/ui/traits/next-solver/normalize-param-env-2.stderr +++ b/tests/ui/traits/next-solver/normalize-param-env-2.stderr @@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `<() as A>::Assoc: A` LL | Self::Assoc: A, | ^^^^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_2`) note: the requirement `<() as A>::Assoc: A` appears on the `impl`'s method `f` but not on the corresponding trait's method --> $DIR/normalize-param-env-2.rs:12:8 | @@ -19,24 +18,18 @@ error[E0275]: overflow evaluating the requirement `<() as A>::Assoc: A` | LL | Self::Assoc: A, | ^^^^ - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_2`) error[E0275]: overflow evaluating the requirement `<() as A>::Assoc well-formed` --> $DIR/normalize-param-env-2.rs:24:22 | LL | Self::Assoc: A, | ^^^^ - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_2`) error[E0275]: overflow evaluating the requirement `(): A` --> $DIR/normalize-param-env-2.rs:27:10 | LL | <() as A>::f(); | ^^ - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_2`) error[E0275]: overflow evaluating the requirement `<() as A>::Assoc: A` --> $DIR/normalize-param-env-2.rs:27:9 @@ -44,7 +37,6 @@ error[E0275]: overflow evaluating the requirement `<() as A>::Assoc: A` LL | <() as A>::f(); | ^^^^^^^^^^^^^^^^^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_2`) note: required by a bound in `A::f` --> $DIR/normalize-param-env-2.rs:14:22 | diff --git a/tests/ui/traits/next-solver/normalize-param-env-4.next.stderr b/tests/ui/traits/next-solver/normalize-param-env-4.next.stderr index 2a017fac104..1bee8ee88ff 100644 --- a/tests/ui/traits/next-solver/normalize-param-env-4.next.stderr +++ b/tests/ui/traits/next-solver/normalize-param-env-4.next.stderr @@ -3,16 +3,12 @@ error[E0275]: overflow evaluating the requirement `::Assoc: Trait` | LL | ::Assoc: Trait, | ^^^^^ - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_4`) error[E0275]: overflow evaluating the requirement `::Assoc well-formed` --> $DIR/normalize-param-env-4.rs:18:26 | LL | ::Assoc: Trait, | ^^^^^ - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_4`) error[E0275]: overflow evaluating the requirement `T: Trait` --> $DIR/normalize-param-env-4.rs:31:19 @@ -20,7 +16,6 @@ error[E0275]: overflow evaluating the requirement `T: Trait` LL | impls_trait::(); | ^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_4`) note: required by a bound in `impls_trait` --> $DIR/normalize-param-env-4.rs:14:19 | diff --git a/tests/ui/traits/next-solver/overflow/exponential-trait-goals.stderr b/tests/ui/traits/next-solver/overflow/exponential-trait-goals.stderr index 90b54b1e789..b032ae3e740 100644 --- a/tests/ui/traits/next-solver/overflow/exponential-trait-goals.stderr +++ b/tests/ui/traits/next-solver/overflow/exponential-trait-goals.stderr @@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `W<_>: Trait` LL | impls::>(); | ^^^^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`exponential_trait_goals`) note: required by a bound in `impls` --> $DIR/exponential-trait-goals.rs:14:13 | diff --git a/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.rs b/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.rs index fb668f83b01..1b80287d9da 100644 --- a/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.rs +++ b/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.rs @@ -1,12 +1,7 @@ -//~ ERROR overflow evaluating the requirement `Self well-formed` -//~| ERROR overflow evaluating the requirement `Self: Trait` - // This is a non-regression test for issue #115351, where a recursion limit of 0 caused an ICE. //@ compile-flags: -Znext-solver --crate-type=lib -//@ check-fail +//@ check-pass #![recursion_limit = "0"] trait Trait {} impl Trait for u32 {} -//~^ ERROR overflow evaluating the requirement `u32: Trait` -//~| ERROR overflow evaluating the requirement `u32 well-formed` diff --git a/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.stderr b/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.stderr deleted file mode 100644 index 16b25d90ace..00000000000 --- a/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[E0275]: overflow evaluating the requirement `Self: Trait` - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`recursion_limit_zero_issue_115351`) - -error[E0275]: overflow evaluating the requirement `Self well-formed` - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`recursion_limit_zero_issue_115351`) - -error[E0275]: overflow evaluating the requirement `u32: Trait` - --> $DIR/recursion-limit-zero-issue-115351.rs:10:16 - | -LL | impl Trait for u32 {} - | ^^^ - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`recursion_limit_zero_issue_115351`) - -error[E0275]: overflow evaluating the requirement `u32 well-formed` - --> $DIR/recursion-limit-zero-issue-115351.rs:10:16 - | -LL | impl Trait for u32 {} - | ^^^ - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`recursion_limit_zero_issue_115351`) - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0275`. -- cgit 1.4.1-3-g733a5 From dd4be4cb2f182bfd0ca8a107f5e374f53c05704b Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 23 Feb 2024 10:35:43 +0100 Subject: extract fulfillment err creation --- .../rustc_trait_selection/src/solve/fulfill.rs | 155 ++++++++++----------- 1 file changed, 73 insertions(+), 82 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 368e2e8810c..b3579d745b1 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -73,36 +73,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec> { self.obligations .drain(..) - .map(|obligation| { - let code = infcx.probe(|_| { - match infcx - .evaluate_root_goal(obligation.clone().into(), GenerateProofTree::IfEnabled) - .0 - { - Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => { - FulfillmentErrorCode::Ambiguity { overflow: None } - } - Ok(( - _, - Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }), - )) => FulfillmentErrorCode::Ambiguity { - overflow: Some(suggest_increasing_limit), - }, - Ok((_, Certainty::Yes)) => { - bug!("did not expect successful goal when collecting ambiguity errors") - } - Err(_) => { - bug!("did not expect selection error when collecting ambiguity errors") - } - } - }); - - FulfillmentError { - obligation: obligation.clone(), - code, - root_obligation: obligation, - } - }) + .map(|obligation| fulfillment_error_for_stalled(infcx, obligation)) .collect() } @@ -125,58 +96,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { let (changed, certainty) = match result { Ok(result) => result, Err(NoSolution) => { - errors.push(FulfillmentError { - obligation: obligation.clone(), - code: match goal.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => { - FulfillmentErrorCode::ProjectionError( - // FIXME: This could be a `Sorts` if the term is a type - MismatchedProjectionTypes { err: TypeError::Mismatch }, - ) - } - ty::PredicateKind::NormalizesTo(..) => { - FulfillmentErrorCode::ProjectionError( - MismatchedProjectionTypes { err: TypeError::Mismatch }, - ) - } - ty::PredicateKind::AliasRelate(_, _, _) => { - FulfillmentErrorCode::ProjectionError( - MismatchedProjectionTypes { err: TypeError::Mismatch }, - ) - } - ty::PredicateKind::Subtype(pred) => { - let (a, b) = infcx.enter_forall_and_leak_universe( - goal.predicate.kind().rebind((pred.a, pred.b)), - ); - let expected_found = ExpectedFound::new(true, a, b); - FulfillmentErrorCode::SubtypeError( - expected_found, - TypeError::Sorts(expected_found), - ) - } - ty::PredicateKind::Coerce(pred) => { - let (a, b) = infcx.enter_forall_and_leak_universe( - goal.predicate.kind().rebind((pred.a, pred.b)), - ); - let expected_found = ExpectedFound::new(false, a, b); - FulfillmentErrorCode::SubtypeError( - expected_found, - TypeError::Sorts(expected_found), - ) - } - ty::PredicateKind::Clause(_) - | ty::PredicateKind::ObjectSafe(_) - | ty::PredicateKind::Ambiguous => { - FulfillmentErrorCode::SelectionError( - SelectionError::Unimplemented, - ) - } - ty::PredicateKind::ConstEquate(..) => { - bug!("unexpected goal: {goal:?}") - } - }, - root_obligation: obligation, - }); + errors.push(fulfillment_error_for_no_solution(infcx, obligation)); continue; } }; @@ -206,3 +126,74 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { std::mem::take(&mut self.obligations) } } + +fn fulfillment_error_for_no_solution<'tcx>( + infcx: &InferCtxt<'tcx>, + obligation: PredicateObligation<'tcx>, +) -> FulfillmentError<'tcx> { + let code = match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => { + FulfillmentErrorCode::ProjectionError( + // FIXME: This could be a `Sorts` if the term is a type + MismatchedProjectionTypes { err: TypeError::Mismatch }, + ) + } + ty::PredicateKind::NormalizesTo(..) => { + FulfillmentErrorCode::ProjectionError(MismatchedProjectionTypes { + err: TypeError::Mismatch, + }) + } + ty::PredicateKind::AliasRelate(_, _, _) => { + FulfillmentErrorCode::ProjectionError(MismatchedProjectionTypes { + err: TypeError::Mismatch, + }) + } + ty::PredicateKind::Subtype(pred) => { + let (a, b) = infcx.enter_forall_and_leak_universe( + obligation.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(true, a, b); + FulfillmentErrorCode::SubtypeError(expected_found, TypeError::Sorts(expected_found)) + } + ty::PredicateKind::Coerce(pred) => { + let (a, b) = infcx.enter_forall_and_leak_universe( + obligation.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(false, a, b); + FulfillmentErrorCode::SubtypeError(expected_found, TypeError::Sorts(expected_found)) + } + ty::PredicateKind::Clause(_) + | ty::PredicateKind::ObjectSafe(_) + | ty::PredicateKind::Ambiguous => { + FulfillmentErrorCode::SelectionError(SelectionError::Unimplemented) + } + ty::PredicateKind::ConstEquate(..) => { + bug!("unexpected goal: {obligation:?}") + } + }; + FulfillmentError { root_obligation: obligation.clone(), code, obligation } +} + +fn fulfillment_error_for_stalled<'tcx>( + infcx: &InferCtxt<'tcx>, + obligation: PredicateObligation<'tcx>, +) -> FulfillmentError<'tcx> { + let code = infcx.probe(|_| { + match infcx.evaluate_root_goal(obligation.clone().into(), GenerateProofTree::Never).0 { + Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => { + FulfillmentErrorCode::Ambiguity { overflow: None } + } + Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => { + FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } + } + Ok((_, Certainty::Yes)) => { + bug!("did not expect successful goal when collecting ambiguity errors") + } + Err(_) => { + bug!("did not expect selection error when collecting ambiguity errors") + } + } + }); + + FulfillmentError { obligation: obligation.clone(), code, root_obligation: obligation } +} -- cgit 1.4.1-3-g733a5 From 3605a09ca236d9643a44f00494a40d6f15d86618 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 23 Feb 2024 11:25:57 +0100 Subject: stash overflowing obligations in fulfill --- .../rustc_trait_selection/src/solve/fulfill.rs | 87 +++++++++++++++++++--- 1 file changed, 75 insertions(+), 12 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index b3579d745b1..bc2bae9da61 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -24,7 +24,7 @@ use super::{Certainty, InferCtxtEvalExt}; /// It is also likely that we want to use slightly different datastructures /// here as this will have to deal with far more root goals than `evaluate_all`. pub struct FulfillmentCtxt<'tcx> { - obligations: Vec>, + obligations: ObligationStorage<'tcx>, /// The snapshot in which this context was created. Using the context /// outside of this snapshot leads to subtle bugs if the snapshot @@ -33,6 +33,57 @@ pub struct FulfillmentCtxt<'tcx> { usable_in_snapshot: usize, } +#[derive(Default)] +struct ObligationStorage<'tcx> { + /// Obligations which resulted in an overflow in fulfillment itself. + /// + /// We cannot eagerly return these as error so we instead store them here + /// to avoid recomputing them each time `select_where_possible` is called. + /// This also allows us to return the correct `FulfillmentError` for them. + overflowed: Vec>, + pending: Vec>, +} + +impl<'tcx> ObligationStorage<'tcx> { + fn register(&mut self, obligation: PredicateObligation<'tcx>) { + self.pending.push(obligation); + } + + fn clone_pending(&self) -> Vec> { + let mut obligations = self.pending.clone(); + obligations.extend(self.overflowed.iter().cloned()); + obligations + } + + fn take_pending(&mut self) -> Vec> { + let mut obligations = mem::take(&mut self.pending); + obligations.extend(self.overflowed.drain(..)); + obligations + } + + fn unstalled_for_select(&mut self) -> impl Iterator> { + mem::take(&mut self.pending).into_iter() + } + + fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) { + infcx.probe(|_| { + // IMPORTANT: we must not use solve any inference variables in the obligations + // as this is all happening inside of a probe. We use a probe to make sure + // we get all obligations involved in the overflow. We pretty much check: if + // we were to do another step of `select_where_possible`, which goals would + // change. + self.overflowed.extend(self.pending.extract_if(|o| { + let goal = o.clone().into(); + let result = infcx.evaluate_root_goal(goal, GenerateProofTree::Never).0; + match result { + Ok((has_changed, _)) => has_changed, + _ => false, + } + })); + }) + } +} + impl<'tcx> FulfillmentCtxt<'tcx> { pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx> { assert!( @@ -40,7 +91,10 @@ impl<'tcx> FulfillmentCtxt<'tcx> { "new trait solver fulfillment context created when \ infcx is set up for old trait solver" ); - FulfillmentCtxt { obligations: Vec::new(), usable_in_snapshot: infcx.num_open_snapshots() } + FulfillmentCtxt { + obligations: Default::default(), + usable_in_snapshot: infcx.num_open_snapshots(), + } } fn inspect_evaluated_obligation( @@ -67,14 +121,24 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { obligation: PredicateObligation<'tcx>, ) { assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); - self.obligations.push(obligation); + self.obligations.register(obligation); } fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec> { - self.obligations + let mut errors: Vec<_> = self + .obligations + .pending .drain(..) .map(|obligation| fulfillment_error_for_stalled(infcx, obligation)) - .collect() + .collect(); + + errors.extend(self.obligations.overflowed.drain(..).map(|obligation| FulfillmentError { + root_obligation: obligation.clone(), + code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) }, + obligation, + })); + + errors } fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec> { @@ -82,14 +146,13 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { let mut errors = Vec::new(); for i in 0.. { if !infcx.tcx.recursion_limit().value_within_limit(i) { - // Only return true errors that we have accumulated while processing; - // keep ambiguities around, *including overflows*, because they shouldn't - // be considered true errors. + self.obligations.on_fulfillment_overflow(infcx); + // Only return true errors that we have accumulated while processing. return errors; } let mut has_changed = false; - for obligation in mem::take(&mut self.obligations) { + for obligation in self.obligations.unstalled_for_select() { let goal = obligation.clone().into(); let result = infcx.evaluate_root_goal(goal, GenerateProofTree::IfEnabled).0; self.inspect_evaluated_obligation(infcx, &obligation, &result); @@ -103,7 +166,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { has_changed |= changed; match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => self.obligations.push(obligation), + Certainty::Maybe(_) => self.obligations.register(obligation), } } @@ -116,14 +179,14 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { } fn pending_obligations(&self) -> Vec> { - self.obligations.clone() + self.obligations.clone_pending() } fn drain_unstalled_obligations( &mut self, _: &InferCtxt<'tcx>, ) -> Vec> { - std::mem::take(&mut self.obligations) + self.obligations.take_pending() } } -- cgit 1.4.1-3-g733a5 From 8c5e83df85e2fd884a9519e3a8743754d43d7ef9 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 23 Feb 2024 11:57:59 +0100 Subject: track overflowing goals for overfow errors --- .../rustc_trait_selection/src/traits/coherence.rs | 105 +++++++++++++++------ .../traits/error_reporting/type_err_ctxt_ext.rs | 38 ++++---- .../src/traits/specialize/mod.rs | 9 ++ .../src/traits/specialize/specialization_graph.rs | 1 + .../next-solver/coherence-fulfill-overflow.stderr | 3 + 5 files changed, 111 insertions(+), 45 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 6778cb7b7a9..68111c4cc1f 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -22,7 +22,7 @@ use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::{util, TraitEngine, TraitEngineExt}; +use rustc_infer::traits::{util, FulfillmentErrorCode, TraitEngine, TraitEngineExt}; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal}; use rustc_middle::traits::specialization_graph::OverlapMode; @@ -35,6 +35,8 @@ use rustc_span::DUMMY_SP; use std::fmt::Debug; use std::ops::ControlFlow; +use super::error_reporting::suggest_new_overflow_limit; + /// Whether we do the orphan check relative to this crate or /// to some remote crate. #[derive(Copy, Clone, Debug)] @@ -56,6 +58,9 @@ pub struct OverlapResult<'tcx> { /// `true` if the overlap might've been permitted before the shift /// to universes. pub involves_placeholder: bool, + + /// Used in the new solver to suggest increasing the recursion limit. + pub overflowing_predicates: Vec>, } pub fn add_placeholder_note(err: &mut Diag<'_, G>) { @@ -65,6 +70,18 @@ pub fn add_placeholder_note(err: &mut Diag<'_, G>) { ); } +pub fn suggest_increasing_recursion_limit<'tcx, G: EmissionGuarantee>( + tcx: TyCtxt<'tcx>, + err: &mut Diag<'_, G>, + overflowing_predicates: &[ty::Predicate<'tcx>], +) { + for pred in overflowing_predicates { + err.note(format!("overflow evaluating the requirement `{}`", pred)); + } + + suggest_new_overflow_limit(tcx, err); +} + #[derive(Debug, Clone, Copy)] enum TrackAmbiguityCauses { Yes, @@ -221,11 +238,13 @@ fn overlap<'tcx>( ), ); + let mut overflowing_predicates = Vec::new(); if overlap_mode.use_implicit_negative() { - if let Some(_failing_obligation) = - impl_intersection_has_impossible_obligation(selcx, &obligations) - { - return None; + match impl_intersection_has_impossible_obligation(selcx, &obligations) { + IntersectionHasImpossibleObligations::Yes => return None, + IntersectionHasImpossibleObligations::No { overflowing_predicates: p } => { + overflowing_predicates = p + } } } @@ -261,7 +280,12 @@ fn overlap<'tcx>( impl_header = deeply_normalize_for_diagnostics(&infcx, param_env, impl_header); } - Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder }) + Some(OverlapResult { + impl_header, + intercrate_ambiguity_causes, + involves_placeholder, + overflowing_predicates, + }) } #[instrument(level = "debug", skip(infcx), ret)] @@ -287,6 +311,19 @@ fn equate_impl_headers<'tcx>( result.map(|infer_ok| infer_ok.obligations).ok() } +/// The result of [fn impl_intersection_has_impossible_obligation]. +enum IntersectionHasImpossibleObligations<'tcx> { + Yes, + No { + /// With `-Znext-solver=coherence`, some obligations may + /// fail if only the user increased the recursion limit. + /// + /// We return those obligations here and mention them in the + /// error message. + overflowing_predicates: Vec>, + }, +} + /// Check if both impls can be satisfied by a common type by considering whether /// any of either impl's obligations is not known to hold. /// @@ -308,7 +345,7 @@ fn equate_impl_headers<'tcx>( fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligations: &'a [PredicateObligation<'tcx>], -) -> Option> { +) -> IntersectionHasImpossibleObligations<'tcx> { let infcx = selcx.infcx; if infcx.next_trait_solver() { @@ -317,28 +354,42 @@ fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>( // We only care about the obligations that are *definitely* true errors. // Ambiguities do not prove the disjointness of two impls. - let mut errors = fulfill_cx.select_where_possible(infcx); - errors.pop().map(|err| err.obligation) + let errors = fulfill_cx.select_where_possible(infcx); + if errors.is_empty() { + let overflow_errors = fulfill_cx.collect_remaining_errors(infcx); + let overflowing_predicates = overflow_errors + .into_iter() + .filter(|e| match e.code { + FulfillmentErrorCode::Ambiguity { overflow: Some(true) } => true, + _ => false, + }) + .map(|e| infcx.resolve_vars_if_possible(e.obligation.predicate)) + .collect(); + IntersectionHasImpossibleObligations::No { overflowing_predicates } + } else { + IntersectionHasImpossibleObligations::Yes + } } else { - obligations - .iter() - .find(|obligation| { - // We use `evaluate_root_obligation` to correctly track intercrate - // ambiguity clauses. We cannot use this in the new solver. - let evaluation_result = selcx.evaluate_root_obligation(obligation); - - match evaluation_result { - Ok(result) => !result.may_apply(), - // If overflow occurs, we need to conservatively treat the goal as possibly holding, - // since there can be instantiations of this goal that don't overflow and result in - // success. This isn't much of a problem in the old solver, since we treat overflow - // fatally (this still can be encountered: ), - // but in the new solver, this is very important for correctness, since overflow - // *must* be treated as ambiguity for completeness. - Err(_overflow) => false, + for obligation in obligations { + // We use `evaluate_root_obligation` to correctly track intercrate + // ambiguity clauses. + let evaluation_result = selcx.evaluate_root_obligation(obligation); + + match evaluation_result { + Ok(result) => { + if !result.may_apply() { + return IntersectionHasImpossibleObligations::Yes; + } } - }) - .cloned() + // If overflow occurs, we need to conservatively treat the goal as possibly holding, + // since there can be instantiations of this goal that don't overflow and result in + // success. While this isn't much of a problem in the old solver, since we treat overflow + // fatally, this still can be encountered: . + Err(_overflow) => {} + } + } + + IntersectionHasImpossibleObligations::No { overflowing_predicates: Vec::new() } } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index cbf52ea9e5a..0023fd60287 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -20,10 +20,9 @@ use crate::traits::{ SelectionError, SignatureMismatch, TraitNotObjectSafe, }; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; -use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, FatalError, - MultiSpan, StashKey, StringPart, -}; +use rustc_errors::codes::*; +use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan, StringPart}; +use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, FatalError, StashKey}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -62,6 +61,22 @@ pub enum OverflowCause<'tcx> { TraitSolver(ty::Predicate<'tcx>), } +pub fn suggest_new_overflow_limit<'tcx, G: EmissionGuarantee>( + tcx: TyCtxt<'tcx>, + err: &mut Diag<'_, G>, +) { + let suggested_limit = match tcx.recursion_limit() { + Limit(0) => Limit(2), + limit => limit * 2, + }; + err.help(format!( + "consider increasing the recursion limit by adding a \ + `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)", + suggested_limit, + tcx.crate_name(LOCAL_CRATE), + )); +} + #[extension(pub trait TypeErrCtxtExt<'tcx>)] impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn report_fulfillment_errors( @@ -263,7 +278,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; if suggest_increasing_limit { - self.suggest_new_overflow_limit(&mut err); + suggest_new_overflow_limit(self.tcx, &mut err); } err @@ -303,19 +318,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); } - fn suggest_new_overflow_limit(&self, err: &mut Diag<'_>) { - let suggested_limit = match self.tcx.recursion_limit() { - Limit(0) => Limit(2), - limit => limit * 2, - }; - err.help(format!( - "consider increasing the recursion limit by adding a \ - `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)", - suggested_limit, - self.tcx.crate_name(LOCAL_CRATE), - )); - } - /// Reports that a cycle was detected which led to overflow and halts /// compilation. This is equivalent to `report_overflow_obligation` except /// that we can give a more helpful error message (and, in particular, diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index b329739609c..f5bc6c3ad2c 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -39,6 +39,7 @@ pub struct OverlapError<'tcx> { pub self_ty: Option>, pub intercrate_ambiguity_causes: FxIndexSet>, pub involves_placeholder: bool, + pub overflowing_predicates: Vec>, } /// Given the generic parameters for the requested impl, translate it to the generic parameters @@ -435,6 +436,14 @@ fn report_conflicting_impls<'tcx>( if overlap.involves_placeholder { coherence::add_placeholder_note(err); } + + if !overlap.overflowing_predicates.is_empty() { + coherence::suggest_increasing_recursion_limit( + tcx, + err, + &overlap.overflowing_predicates, + ); + } } let msg = DelayDm(|| { diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index f3b77d68922..95db9e2092f 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -103,6 +103,7 @@ impl<'tcx> Children { self_ty: self_ty.has_concrete_skeleton().then_some(self_ty), intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes, involves_placeholder: overlap.involves_placeholder, + overflowing_predicates: overlap.overflowing_predicates, } }; diff --git a/tests/ui/traits/next-solver/coherence-fulfill-overflow.stderr b/tests/ui/traits/next-solver/coherence-fulfill-overflow.stderr index 406c0ccca97..57cba790b55 100644 --- a/tests/ui/traits/next-solver/coherence-fulfill-overflow.stderr +++ b/tests/ui/traits/next-solver/coherence-fulfill-overflow.stderr @@ -5,6 +5,9 @@ LL | impl Trait for W {} | ------------------------------------- first implementation here LL | impl Trait for T {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `W>>>>>>>>>>>>>>>>>>>>` + | + = note: overflow evaluating the requirement `W>>>>>>>>>>>>>>>>>>>>: TwoW` + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` attribute to your crate (`coherence_fulfill_overflow`) error: aborting due to 1 previous error -- cgit 1.4.1-3-g733a5 From 5ce15af6b1d0c16df722db929dc81b9a97c2bd1f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 9 Jan 2024 12:02:55 +0100 Subject: Make `invalid_doc_attributes` deny by default --- compiler/rustc_lint_defs/src/builtin.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 1cddb45428c..94f8bbe2437 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3586,18 +3586,9 @@ declare_lint! { /// being validated. Usually these should be rejected as a hard error, /// but this lint was introduced to avoid breaking any existing /// crates which included them. - /// - /// This is a [future-incompatible] lint to transition this to a hard - /// error in the future. See [issue #82730] for more details. - /// - /// [issue #82730]: https://github.com/rust-lang/rust/issues/82730 pub INVALID_DOC_ATTRIBUTES, - Warn, + Deny, "detects invalid `#[doc(...)]` attributes", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, - reference: "issue #82730 ", - }; } declare_lint! { -- cgit 1.4.1-3-g733a5 From dab3d5bb281bd4be59b68adaa6f1f003c20ea303 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Wed, 28 Feb 2024 22:32:29 +0000 Subject: Never say "`Trait` is implemented for `{type error}`" When a trait bound error occurs, we look for alternative types that would have made the bound succeed. For some reason `{type error}` sometimes would appear as a type that would do so. We now remove `{type error}` from the list in every case to avoid nonsensical `note`s. --- .../src/traits/error_reporting/type_err_ctxt_ext.rs | 6 +++++- tests/ui/associated-consts/issue-105330.stderr | 2 -- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index dcbb63f00f7..de3231a0e86 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -1908,6 +1908,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ct_op: |ct| ct.normalize(self.tcx, ty::ParamEnv::empty()), }, ); + if cand.references_error() { + return false; + } err.highlighted_help(vec![ StringPart::normal(format!("the trait `{}` ", cand.print_trait_sugared())), StringPart::highlighted("is"), @@ -1932,7 +1935,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } let other = if other { "other " } else { "" }; - let report = |candidates: Vec>, err: &mut Diag<'_>| { + let report = |mut candidates: Vec>, err: &mut Diag<'_>| { + candidates.retain(|tr| !tr.references_error()); if candidates.is_empty() { return false; } diff --git a/tests/ui/associated-consts/issue-105330.stderr b/tests/ui/associated-consts/issue-105330.stderr index 50ce69b3381..bde3675b48c 100644 --- a/tests/ui/associated-consts/issue-105330.stderr +++ b/tests/ui/associated-consts/issue-105330.stderr @@ -55,7 +55,6 @@ error[E0277]: the trait bound `Demo: TraitWAssocConst` is not satisfied LL | foo::()(); | ^^^^ the trait `TraitWAssocConst` is not implemented for `Demo` | - = help: the trait `TraitWAssocConst` is implemented for `{type error}` note: required by a bound in `foo` --> $DIR/issue-105330.rs:11:11 | @@ -92,7 +91,6 @@ error[E0277]: the trait bound `Demo: TraitWAssocConst` is not satisfied LL | foo::(); | ^^^^ the trait `TraitWAssocConst` is not implemented for `Demo` | - = help: the trait `TraitWAssocConst` is implemented for `{type error}` note: required by a bound in `foo` --> $DIR/issue-105330.rs:11:11 | -- cgit 1.4.1-3-g733a5 From 367126d49af3237eee24889191dfbb02dfc9320c Mon Sep 17 00:00:00 2001 From: clubby789 Date: Fri, 26 Jan 2024 15:32:59 +0000 Subject: If suggestion would leave an empty line, delete it --- compiler/rustc_errors/src/json.rs | 12 +++++++- src/tools/clippy/tests/ui/derivable_impls.fixed | 8 ------ src/tools/clippy/tests/ui/empty_drop.fixed | 2 -- src/tools/clippy/tests/ui/must_use_unit.fixed | 3 -- .../tests/ui/single_component_path_imports.fixed | 1 - tests/ui/associated-types/impl-wf-cycle-6.fixed | 1 - tests/ui/generics/generic-no-mangle.fixed | 2 -- tests/ui/imports/issue-52891.fixed | 2 -- tests/ui/imports/unused-import-issue-87973.fixed | 1 - .../ui/lazy-type-alias/leading-where-clause.fixed | 2 -- tests/ui/lint/suggestions.fixed | 1 - tests/ui/lint/unused/import_remove_line.fixed | 11 ++++++++ tests/ui/lint/unused/import_remove_line.rs | 13 +++++++++ tests/ui/lint/unused/import_remove_line.stderr | 32 ++++++++++++++++++++++ .../resolve-conflict-import-vs-import.fixed | 1 - .../rust-2018/extern-crate-idiomatic-in-2018.fixed | 1 - ...issue-54400-unused-extern-crate-attr-span.fixed | 1 - 17 files changed, 67 insertions(+), 27 deletions(-) create mode 100644 tests/ui/lint/unused/import_remove_line.fixed create mode 100644 tests/ui/lint/unused/import_remove_line.rs create mode 100644 tests/ui/lint/unused/import_remove_line.stderr (limited to 'compiler') diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index bc1822f83fc..af82d8092c2 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -428,7 +428,7 @@ impl DiagnosticSpan { } fn from_span_full( - span: Span, + mut span: Span, is_primary: bool, label: Option, suggestion: Option<(&String, Applicability)>, @@ -436,6 +436,16 @@ impl DiagnosticSpan { je: &JsonEmitter, ) -> DiagnosticSpan { let start = je.sm.lookup_char_pos(span.lo()); + // If this goes from the start of a line to the end and the replacement + // is an empty string, increase the length to include the newline so we don't + // leave an empty line + if start.col.0 == 0 + && suggestion.map_or(false, |(s, _)| s.is_empty()) + && let Ok(after) = je.sm.span_to_next_source(span) + && after.starts_with('\n') + { + span = span.with_hi(span.hi() + rustc_span::BytePos(1)); + } let end = je.sm.lookup_char_pos(span.hi()); let backtrace_step = backtrace.next().map(|bt| { let call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je); diff --git a/src/tools/clippy/tests/ui/derivable_impls.fixed b/src/tools/clippy/tests/ui/derivable_impls.fixed index 68c5a5c5ca4..c85f384fd6e 100644 --- a/src/tools/clippy/tests/ui/derivable_impls.fixed +++ b/src/tools/clippy/tests/ui/derivable_impls.fixed @@ -19,12 +19,10 @@ struct FooDefault<'a> { } - #[derive(Default)] struct TupleDefault(bool, i32, u64); - struct FooND1 { a: bool, } @@ -73,7 +71,6 @@ impl Default for FooNDVec { struct StrDefault<'a>(&'a str); - #[derive(Default)] struct AlreadyDerived(i32, bool); @@ -96,7 +93,6 @@ mac!(0); #[derive(Default)] struct Y(u32); - struct RustIssue26925 { a: Option, } @@ -132,12 +128,10 @@ struct WithoutSelfCurly { } - #[derive(Default)] struct WithoutSelfParan(bool); - // https://github.com/rust-lang/rust-clippy/issues/7655 pub struct SpecializedImpl2 { @@ -184,7 +178,6 @@ pub struct RepeatDefault1 { } - pub struct RepeatDefault2 { a: [i8; 33], } @@ -216,7 +209,6 @@ pub enum SimpleEnum { } - pub enum NonExhaustiveEnum { Foo, #[non_exhaustive] diff --git a/src/tools/clippy/tests/ui/empty_drop.fixed b/src/tools/clippy/tests/ui/empty_drop.fixed index 949d0d8b399..17cfdcdc9c6 100644 --- a/src/tools/clippy/tests/ui/empty_drop.fixed +++ b/src/tools/clippy/tests/ui/empty_drop.fixed @@ -5,7 +5,6 @@ struct Foo; - // shouldn't cause an error struct Bar; @@ -19,5 +18,4 @@ impl Drop for Bar { struct Baz; - fn main() {} diff --git a/src/tools/clippy/tests/ui/must_use_unit.fixed b/src/tools/clippy/tests/ui/must_use_unit.fixed index 75f91e66824..f255cb66652 100644 --- a/src/tools/clippy/tests/ui/must_use_unit.fixed +++ b/src/tools/clippy/tests/ui/must_use_unit.fixed @@ -6,13 +6,10 @@ extern crate proc_macros; use proc_macros::external; - pub fn must_use_default() {} - pub fn must_use_unit() -> () {} - pub fn must_use_with_note() {} fn main() { diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.fixed b/src/tools/clippy/tests/ui/single_component_path_imports.fixed index fdff336c281..3e81bcd5e48 100644 --- a/src/tools/clippy/tests/ui/single_component_path_imports.fixed +++ b/src/tools/clippy/tests/ui/single_component_path_imports.fixed @@ -4,7 +4,6 @@ use core; - use serde as edres; pub use serde; diff --git a/tests/ui/associated-types/impl-wf-cycle-6.fixed b/tests/ui/associated-types/impl-wf-cycle-6.fixed index 45143be1e74..ce98b9c2f02 100644 --- a/tests/ui/associated-types/impl-wf-cycle-6.fixed +++ b/tests/ui/associated-types/impl-wf-cycle-6.fixed @@ -21,7 +21,6 @@ impl Grault for () { impl Grault for (T,) //~^ ERROR overflow evaluating the requirement `<(T,) as Grault>::A == _` - { type A = (); type B = bool; diff --git a/tests/ui/generics/generic-no-mangle.fixed b/tests/ui/generics/generic-no-mangle.fixed index f20ea0edaa6..69db712f9dc 100644 --- a/tests/ui/generics/generic-no-mangle.fixed +++ b/tests/ui/generics/generic-no-mangle.fixed @@ -2,10 +2,8 @@ #![allow(dead_code)] #![deny(no_mangle_generic_items)] - pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled - pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled #[no_mangle] diff --git a/tests/ui/imports/issue-52891.fixed b/tests/ui/imports/issue-52891.fixed index 9faef1703ac..0382960db2f 100644 --- a/tests/ui/imports/issue-52891.fixed +++ b/tests/ui/imports/issue-52891.fixed @@ -27,10 +27,8 @@ use issue_52891::{l, use issue_52891::a::inner; use issue_52891::b::inner as other_inner; //~ ERROR `inner` is defined multiple times - //~^ ERROR `issue_52891` is defined multiple times - #[macro_use] use issue_52891::n; //~ ERROR `n` is defined multiple times diff --git a/tests/ui/imports/unused-import-issue-87973.fixed b/tests/ui/imports/unused-import-issue-87973.fixed index 96508fa3ea2..d1167d7d486 100644 --- a/tests/ui/imports/unused-import-issue-87973.fixed +++ b/tests/ui/imports/unused-import-issue-87973.fixed @@ -2,7 +2,6 @@ #![deny(unused_imports)] // Check that attributes get removed too. See #87973. - //~^ ERROR unused import fn main() {} diff --git a/tests/ui/lazy-type-alias/leading-where-clause.fixed b/tests/ui/lazy-type-alias/leading-where-clause.fixed index ca0ab7b5c7d..003eaa6c54e 100644 --- a/tests/ui/lazy-type-alias/leading-where-clause.fixed +++ b/tests/ui/lazy-type-alias/leading-where-clause.fixed @@ -7,11 +7,9 @@ // Check that we *reject* leading where-clauses on lazy type aliases. pub type Leading0 - = T where String: From; pub type Leading1 - = (T, U) where U: Copy, String: From; diff --git a/tests/ui/lint/suggestions.fixed b/tests/ui/lint/suggestions.fixed index b017438a8bd..66d097b121f 100644 --- a/tests/ui/lint/suggestions.fixed +++ b/tests/ui/lint/suggestions.fixed @@ -7,7 +7,6 @@ //~^ ERROR const items should never be `#[no_mangle]` //~| HELP try a static value - //~^ HELP remove this attribute pub fn defiant(_t: T) {} //~^ WARN functions generic over types or consts must be mangled diff --git a/tests/ui/lint/unused/import_remove_line.fixed b/tests/ui/lint/unused/import_remove_line.fixed new file mode 100644 index 00000000000..45876cd4ebc --- /dev/null +++ b/tests/ui/lint/unused/import_remove_line.fixed @@ -0,0 +1,11 @@ +//@ run-rustfix +//@ check-pass + +#![crate_type = "lib"] +#![warn(unused_imports)] + +//~^ WARN unused imports +//~^ WARN unused import + +//~^ WARN unused import +//~| WARN unused import diff --git a/tests/ui/lint/unused/import_remove_line.rs b/tests/ui/lint/unused/import_remove_line.rs new file mode 100644 index 00000000000..fd45f04eb80 --- /dev/null +++ b/tests/ui/lint/unused/import_remove_line.rs @@ -0,0 +1,13 @@ +//@ run-rustfix +//@ check-pass + +#![crate_type = "lib"] +#![warn(unused_imports)] + +use std::time::{Duration, Instant}; +//~^ WARN unused imports +use std::time::SystemTime; +//~^ WARN unused import +use std::time::SystemTimeError;use std::time::TryFromFloatSecsError; +//~^ WARN unused import +//~| WARN unused import diff --git a/tests/ui/lint/unused/import_remove_line.stderr b/tests/ui/lint/unused/import_remove_line.stderr new file mode 100644 index 00000000000..0e8c5de3558 --- /dev/null +++ b/tests/ui/lint/unused/import_remove_line.stderr @@ -0,0 +1,32 @@ +warning: unused imports: `Duration`, `Instant` + --> $DIR/import_remove_line.rs:7:17 + | +LL | use std::time::{Duration, Instant}; + | ^^^^^^^^ ^^^^^^^ + | +note: the lint level is defined here + --> $DIR/import_remove_line.rs:5:9 + | +LL | #![warn(unused_imports)] + | ^^^^^^^^^^^^^^ + +warning: unused import: `std::time::SystemTime` + --> $DIR/import_remove_line.rs:9:5 + | +LL | use std::time::SystemTime; + | ^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `std::time::SystemTimeError` + --> $DIR/import_remove_line.rs:11:5 + | +LL | use std::time::SystemTimeError;use std::time::TryFromFloatSecsError; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `std::time::TryFromFloatSecsError` + --> $DIR/import_remove_line.rs:11:36 + | +LL | use std::time::SystemTimeError;use std::time::TryFromFloatSecsError; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: 4 warnings emitted + diff --git a/tests/ui/resolve/resolve-conflict-import-vs-import.fixed b/tests/ui/resolve/resolve-conflict-import-vs-import.fixed index 2ebf2a194b8..d981d629c46 100644 --- a/tests/ui/resolve/resolve-conflict-import-vs-import.fixed +++ b/tests/ui/resolve/resolve-conflict-import-vs-import.fixed @@ -2,7 +2,6 @@ #[allow(unused_imports)] use std::mem::transmute; - //~^ ERROR the name `transmute` is defined multiple times fn main() { diff --git a/tests/ui/rust-2018/extern-crate-idiomatic-in-2018.fixed b/tests/ui/rust-2018/extern-crate-idiomatic-in-2018.fixed index fcab56ac819..ca8422c03a3 100644 --- a/tests/ui/rust-2018/extern-crate-idiomatic-in-2018.fixed +++ b/tests/ui/rust-2018/extern-crate-idiomatic-in-2018.fixed @@ -9,7 +9,6 @@ #![deny(rust_2018_idioms)] #![allow(dead_code)] - //~^ ERROR unused extern crate // Shouldn't suggest changing to `use`, as `bar` diff --git a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.fixed b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.fixed index 2625cdc8b48..75b3918be1d 100644 --- a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.fixed +++ b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.fixed @@ -8,7 +8,6 @@ // The suggestion span should include the attribute. - //~^ ERROR unused extern crate fn main() {} -- cgit 1.4.1-3-g733a5 From e3ac2c68b89c95e01440c1ab5fc7897e93a35c41 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Thu, 29 Feb 2024 15:23:44 -0800 Subject: Implement missing ABI structures in StableMIR --- compiler/rustc_smir/src/rustc_smir/convert/abi.rs | 66 ++++++++- compiler/stable_mir/src/abi.rs | 159 +++++++++++++++++++++- compiler/stable_mir/src/error.rs | 11 +- compiler/stable_mir/src/target.rs | 10 +- compiler/stable_mir/src/ty.rs | 7 +- tests/ui-fulldeps/stable-mir/check_abi.rs | 45 ++++-- 6 files changed, 273 insertions(+), 25 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs index 088a836c901..071c02e0381 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs @@ -6,11 +6,12 @@ use crate::rustc_smir::{Stable, Tables}; use rustc_middle::ty; use rustc_target::abi::call::Conv; use stable_mir::abi::{ - ArgAbi, CallConvention, FieldsShape, FnAbi, Layout, LayoutShape, PassMode, TagEncoding, - TyAndLayout, ValueAbi, VariantsShape, + AddressSpace, ArgAbi, CallConvention, FieldsShape, FnAbi, IntegerLength, Layout, LayoutShape, + PassMode, Primitive, Scalar, TagEncoding, TyAndLayout, ValueAbi, VariantsShape, WrappingRange, }; -use stable_mir::ty::{Align, IndexedVal, Size, VariantIdx}; -use stable_mir::{opaque, Opaque}; +use stable_mir::opaque; +use stable_mir::target::MachineSize as Size; +use stable_mir::ty::{Align, IndexedVal, VariantIdx}; impl<'tcx> Stable<'tcx> for rustc_target::abi::VariantIdx { type T = VariantIdx; @@ -220,7 +221,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Size { type T = Size; fn stable(&self, _tables: &mut Tables<'_>) -> Self::T { - self.bytes_usize() + Size::from_bits(self.bits_usize()) } } @@ -233,9 +234,60 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Align { } impl<'tcx> Stable<'tcx> for rustc_abi::Scalar { - type T = Opaque; + type T = Scalar; + + fn stable(&self, tables: &mut Tables<'_>) -> Self::T { + match self { + rustc_abi::Scalar::Initialized { value, valid_range } => Scalar::Initialized { + value: value.stable(tables), + valid_range: valid_range.stable(tables), + }, + rustc_abi::Scalar::Union { value } => Scalar::Union { value: value.stable(tables) }, + } + } +} + +impl<'tcx> Stable<'tcx> for rustc_abi::Primitive { + type T = Primitive; + + fn stable(&self, tables: &mut Tables<'_>) -> Self::T { + match self { + rustc_abi::Primitive::Int(length, signed) => { + Primitive::Int { length: length.stable(tables), signed: *signed } + } + rustc_abi::Primitive::F32 => Primitive::F32, + rustc_abi::Primitive::F64 => Primitive::F64, + rustc_abi::Primitive::Pointer(space) => Primitive::Pointer(space.stable(tables)), + } + } +} + +impl<'tcx> Stable<'tcx> for rustc_abi::AddressSpace { + type T = AddressSpace; + + fn stable(&self, _tables: &mut Tables<'_>) -> Self::T { + AddressSpace(self.0) + } +} + +impl<'tcx> Stable<'tcx> for rustc_abi::Integer { + type T = IntegerLength; + + fn stable(&self, _tables: &mut Tables<'_>) -> Self::T { + match self { + rustc_abi::Integer::I8 => IntegerLength::I8, + rustc_abi::Integer::I16 => IntegerLength::I16, + rustc_abi::Integer::I32 => IntegerLength::I32, + rustc_abi::Integer::I64 => IntegerLength::I64, + rustc_abi::Integer::I128 => IntegerLength::I128, + } + } +} + +impl<'tcx> Stable<'tcx> for rustc_abi::WrappingRange { + type T = WrappingRange; fn stable(&self, _tables: &mut Tables<'_>) -> Self::T { - opaque(self) + WrappingRange { start: self.start, end: self.end } } } diff --git a/compiler/stable_mir/src/abi.rs b/compiler/stable_mir/src/abi.rs index a15fd3e0999..1c5e3275673 100644 --- a/compiler/stable_mir/src/abi.rs +++ b/compiler/stable_mir/src/abi.rs @@ -1,7 +1,11 @@ use crate::compiler_interface::with; +use crate::error; use crate::mir::FieldIdx; -use crate::ty::{Align, IndexedVal, Size, Ty, VariantIdx}; +use crate::target::{MachineInfo, MachineSize as Size}; +use crate::ty::{Align, IndexedVal, Ty, VariantIdx}; +use crate::Error; use crate::Opaque; +use std::fmt::{self, Debug}; use std::num::NonZeroUsize; use std::ops::RangeInclusive; @@ -100,7 +104,7 @@ impl LayoutShape { /// Returns `true` if the type is sized and a 1-ZST (meaning it has size 0 and alignment 1). pub fn is_1zst(&self) -> bool { - self.is_sized() && self.size == 0 && self.abi_align == 1 + self.is_sized() && self.size.bits() == 0 && self.abi_align == 1 } } @@ -245,8 +249,155 @@ impl ValueAbi { } } -/// We currently do not support `Scalar`, and use opaque instead. -type Scalar = Opaque; +/// Information about one scalar component of a Rust type. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum Scalar { + Initialized { + /// The primitive type used to represent this value. + value: Primitive, + /// The range that represents valid values. + /// The range must be valid for the `primitive` size. + valid_range: WrappingRange, + }, + Union { + /// Unions never have niches, so there is no `valid_range`. + /// Even for unions, we need to use the correct registers for the kind of + /// values inside the union, so we keep the `Primitive` type around. + /// It is also used to compute the size of the scalar. + value: Primitive, + }, +} + +impl Scalar { + pub fn has_niche(&self, target: &MachineInfo) -> bool { + match self { + Scalar::Initialized { value, valid_range } => { + !valid_range.is_full(value.size(target)).unwrap() + } + Scalar::Union { .. } => false, + } + } +} + +/// Fundamental unit of memory access and layout. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum Primitive { + /// The `bool` is the signedness of the `Integer` type. + /// + /// One would think we would not care about such details this low down, + /// but some ABIs are described in terms of C types and ISAs where the + /// integer arithmetic is done on {sign,zero}-extended registers, e.g. + /// a negative integer passed by zero-extension will appear positive in + /// the callee, and most operations on it will produce the wrong values. + Int { + length: IntegerLength, + signed: bool, + }, + F32, + F64, + Pointer(AddressSpace), +} + +impl Primitive { + pub fn size(self, target: &MachineInfo) -> Size { + match self { + Primitive::Int { length, .. } => Size::from_bits(length.bits()), + Primitive::F32 => Size::from_bits(32), + Primitive::F64 => Size::from_bits(64), + Primitive::Pointer(_) => target.pointer_width, + } + } +} + +/// Enum representing the existing integer lengths. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum IntegerLength { + I8, + I16, + I32, + I64, + I128, +} + +impl IntegerLength { + pub fn bits(self) -> usize { + match self { + IntegerLength::I8 => 8, + IntegerLength::I16 => 16, + IntegerLength::I32 => 32, + IntegerLength::I64 => 64, + IntegerLength::I128 => 128, + } + } +} + +/// An identifier that specifies the address space that some operation +/// should operate on. Special address spaces have an effect on code generation, +/// depending on the target and the address spaces it implements. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct AddressSpace(pub u32); + +impl AddressSpace { + /// The default address space, corresponding to data space. + pub const DATA: Self = AddressSpace(0); +} + +/// Inclusive wrap-around range of valid values (bitwise representation), that is, if +/// start > end, it represents `start..=MAX`, followed by `0..=end`. +/// +/// That is, for an i8 primitive, a range of `254..=2` means following +/// sequence: +/// +/// 254 (-2), 255 (-1), 0, 1, 2 +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct WrappingRange { + pub start: u128, + pub end: u128, +} + +impl WrappingRange { + /// Returns `true` if `size` completely fills the range. + #[inline] + pub fn is_full(&self, size: Size) -> Result { + let Some(max_value) = size.unsigned_int_max() else { + return Err(error!("Expected size <= 128 bits, but found {} instead", size.bits())); + }; + if self.start <= max_value && self.end <= max_value { + Ok(self.start == 0 && max_value == self.end) + } else { + Err(error!("Range `{self:?}` out of bounds for size `{}` bits.", size.bits())) + } + } + + /// Returns `true` if `v` is contained in the range. + #[inline(always)] + pub fn contains(&self, v: u128) -> bool { + if self.wraps_around() { + self.start <= v || v <= self.end + } else { + self.start <= v && v <= self.end + } + } + + /// Returns `true` if the range wraps around. + /// I.e., the range represents the union of `self.start..=MAX` and `0..=self.end`. + /// Returns `false` if this is a non-wrapping range, i.e.: `self.start..=self.end`. + #[inline] + pub fn wraps_around(&self) -> bool { + self.start > self.end + } +} + +impl Debug for WrappingRange { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.start > self.end { + write!(fmt, "(..={}) | ({}..)", self.end, self.start)?; + } else { + write!(fmt, "{}..={}", self.start, self.end)?; + } + Ok(()) + } +} /// General language calling conventions. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] diff --git a/compiler/stable_mir/src/error.rs b/compiler/stable_mir/src/error.rs index 9e3f4936944..050752e41eb 100644 --- a/compiler/stable_mir/src/error.rs +++ b/compiler/stable_mir/src/error.rs @@ -5,12 +5,14 @@ //! - [Error]: Generic error that represents the reason why a request that could not be fulfilled. use std::fmt::{Debug, Display, Formatter}; -use std::{error, fmt, io}; +use std::{fmt, io}; macro_rules! error { ($fmt: literal $(,)?) => { Error(format!($fmt)) }; ($fmt: literal, $($arg:tt)*) => { Error(format!($fmt, $($arg)*)) }; - } +} + +pub(crate) use error; /// An error type used to represent an error that has already been reported by the compiler. #[derive(Clone, Copy, PartialEq, Eq)] @@ -72,8 +74,9 @@ where } } -impl error::Error for Error {} -impl error::Error for CompilerError where T: Display + Debug {} +impl std::error::Error for Error {} + +impl std::error::Error for CompilerError where T: Display + Debug {} impl From for Error { fn from(value: io::Error) -> Self { diff --git a/compiler/stable_mir/src/target.rs b/compiler/stable_mir/src/target.rs index 41ec205cfc7..3a9011a2ffe 100644 --- a/compiler/stable_mir/src/target.rs +++ b/compiler/stable_mir/src/target.rs @@ -30,21 +30,29 @@ pub enum Endian { } /// Represent the size of a component. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct MachineSize { num_bits: usize, } impl MachineSize { + #[inline(always)] pub fn bytes(self) -> usize { self.num_bits / 8 } + #[inline(always)] pub fn bits(self) -> usize { self.num_bits } + #[inline(always)] pub fn from_bits(num_bits: usize) -> MachineSize { MachineSize { num_bits } } + + #[inline] + pub fn unsigned_int_max(self) -> Option { + (self.num_bits <= 128).then(|| u128::MAX >> (128 - self.bits())) + } } diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index ed4a4290246..86cc748eaec 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -324,7 +324,9 @@ impl TyKind { #[inline] pub fn is_cstr(&self) -> bool { - let TyKind::RigidTy(RigidTy::Adt(def, _)) = self else { return false }; + let TyKind::RigidTy(RigidTy::Adt(def, _)) = self else { + return false; + }; with(|cx| cx.adt_is_cstr(*def)) } @@ -1032,10 +1034,13 @@ pub struct BoundTy { } pub type Bytes = Vec>; + +/// Size in bytes. pub type Size = usize; #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub struct Prov(pub AllocId); + pub type Align = u64; pub type Promoted = u32; pub type InitMaskMaterialized = Vec; diff --git a/tests/ui-fulldeps/stable-mir/check_abi.rs b/tests/ui-fulldeps/stable-mir/check_abi.rs index c345987955e..74801e007c4 100644 --- a/tests/ui-fulldeps/stable-mir/check_abi.rs +++ b/tests/ui-fulldeps/stable-mir/check_abi.rs @@ -19,8 +19,12 @@ extern crate rustc_interface; extern crate stable_mir; use rustc_smir::rustc_internal; -use stable_mir::abi::{ArgAbi, CallConvention, FieldsShape, PassMode, VariantsShape}; +use stable_mir::abi::{ + ArgAbi, CallConvention, FieldsShape, IntegerLength, PassMode, Primitive, Scalar, ValueAbi, + VariantsShape, +}; use stable_mir::mir::mono::Instance; +use stable_mir::target::MachineInfo; use stable_mir::{CrateDef, CrateItem, CrateItems, ItemKind}; use std::assert_matches::assert_matches; use std::convert::TryFrom; @@ -39,11 +43,12 @@ fn test_stable_mir() -> ControlFlow<()> { let instance = Instance::try_from(target_fn).unwrap(); let fn_abi = instance.fn_abi().unwrap(); assert_eq!(fn_abi.conv, CallConvention::Rust); - assert_eq!(fn_abi.args.len(), 2); + assert_eq!(fn_abi.args.len(), 3); check_ignore(&fn_abi.args[0]); check_primitive(&fn_abi.args[1]); - check_result(fn_abi.ret); + check_niche(&fn_abi.args[2]); + check_result(&fn_abi.ret); // Test variadic function. let variadic_fn = *get_item(&items, (ItemKind::Fn, "variadic_fn")).unwrap(); @@ -85,7 +90,7 @@ fn check_primitive(abi: &ArgAbi) { } /// Check the return value: `Result`. -fn check_result(abi: ArgAbi) { +fn check_result(abi: &ArgAbi) { assert!(abi.ty.kind().is_enum()); assert_matches!(abi.mode, PassMode::Indirect { .. }); let layout = abi.layout.shape(); @@ -94,6 +99,25 @@ fn check_result(abi: ArgAbi) { assert_matches!(layout.variants, VariantsShape::Multiple { .. }) } +/// Check the niche information about: `NonZeroU8` +fn check_niche(abi: &ArgAbi) { + assert!(abi.ty.kind().is_struct()); + assert_matches!(abi.mode, PassMode::Direct { .. }); + let layout = abi.layout.shape(); + assert!(layout.is_sized()); + assert_eq!(layout.size.bytes(), 1); + + let ValueAbi::Scalar(scalar) = layout.abi else { unreachable!() }; + assert!(scalar.has_niche(&MachineInfo::target()), "Opps: {:?}", scalar); + + let Scalar::Initialized { value, valid_range } = scalar else { unreachable!() }; + assert_matches!(value, Primitive::Int { length: IntegerLength::I8, signed: false }); + assert_eq!(valid_range.start, 1); + assert_eq!(valid_range.end, u8::MAX.into()); + assert!(!valid_range.contains(0)); + assert!(!valid_range.wraps_around()); +} + fn get_item<'a>( items: &'a CrateItems, item: (ItemKind, &str), @@ -126,11 +150,16 @@ fn generate_input(path: &str) -> std::io::Result<()> { #![feature(c_variadic)] #![allow(unused_variables)] - pub fn fn_abi(ignore: [u8; 0], primitive: char) -> Result {{ - // We only care about the signature. - todo!() - }} + use std::num::NonZeroU8; + pub fn fn_abi( + ignore: [u8; 0], + primitive: char, + niche: NonZeroU8, + ) -> Result {{ + // We only care about the signature. + todo!() + }} pub unsafe extern "C" fn variadic_fn(n: usize, mut args: ...) -> usize {{ 0 -- cgit 1.4.1-3-g733a5 From 056716293375ec9ed2e431655d2860afb67f6780 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Fri, 1 Mar 2024 11:16:35 -0800 Subject: Add support to new float types --- compiler/rustc_smir/src/rustc_smir/convert/abi.rs | 11 +++++---- compiler/stable_mir/src/abi.rs | 28 +++++++++++++++++++---- 2 files changed, 31 insertions(+), 8 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs index 071c02e0381..6fb1560ac6c 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs @@ -6,8 +6,9 @@ use crate::rustc_smir::{Stable, Tables}; use rustc_middle::ty; use rustc_target::abi::call::Conv; use stable_mir::abi::{ - AddressSpace, ArgAbi, CallConvention, FieldsShape, FnAbi, IntegerLength, Layout, LayoutShape, - PassMode, Primitive, Scalar, TagEncoding, TyAndLayout, ValueAbi, VariantsShape, WrappingRange, + AddressSpace, ArgAbi, CallConvention, FieldsShape, FloatLength, FnAbi, IntegerLength, Layout, + LayoutShape, PassMode, Primitive, Scalar, TagEncoding, TyAndLayout, ValueAbi, VariantsShape, + WrappingRange, }; use stable_mir::opaque; use stable_mir::target::MachineSize as Size; @@ -255,8 +256,10 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Primitive { rustc_abi::Primitive::Int(length, signed) => { Primitive::Int { length: length.stable(tables), signed: *signed } } - rustc_abi::Primitive::F32 => Primitive::F32, - rustc_abi::Primitive::F64 => Primitive::F64, + rustc_abi::Primitive::F16 => Primitive::Float { length: FloatLength::F16 }, + rustc_abi::Primitive::F32 => Primitive::Float { length: FloatLength::F32 }, + rustc_abi::Primitive::F64 => Primitive::Float { length: FloatLength::F64 }, + rustc_abi::Primitive::F128 => Primitive::Float { length: FloatLength::F128 }, rustc_abi::Primitive::Pointer(space) => Primitive::Pointer(space.stable(tables)), } } diff --git a/compiler/stable_mir/src/abi.rs b/compiler/stable_mir/src/abi.rs index 1c5e3275673..7fda9ceb79a 100644 --- a/compiler/stable_mir/src/abi.rs +++ b/compiler/stable_mir/src/abi.rs @@ -293,8 +293,9 @@ pub enum Primitive { length: IntegerLength, signed: bool, }, - F32, - F64, + Float { + length: FloatLength, + }, Pointer(AddressSpace), } @@ -302,8 +303,7 @@ impl Primitive { pub fn size(self, target: &MachineInfo) -> Size { match self { Primitive::Int { length, .. } => Size::from_bits(length.bits()), - Primitive::F32 => Size::from_bits(32), - Primitive::F64 => Size::from_bits(64), + Primitive::Float { length } => Size::from_bits(length.bits()), Primitive::Pointer(_) => target.pointer_width, } } @@ -319,6 +319,15 @@ pub enum IntegerLength { I128, } +/// Enum representing the existing float lengths. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum FloatLength { + F16, + F32, + F64, + F128, +} + impl IntegerLength { pub fn bits(self) -> usize { match self { @@ -331,6 +340,17 @@ impl IntegerLength { } } +impl FloatLength { + pub fn bits(self) -> usize { + match self { + FloatLength::F16 => 16, + FloatLength::F32 => 32, + FloatLength::F64 => 64, + FloatLength::F128 => 128, + } + } +} + /// An identifier that specifies the address space that some operation /// should operate on. Special address spaces have an effect on code generation, /// depending on the target and the address spaces it implements. -- cgit 1.4.1-3-g733a5