From 47a7a91c969ed2edd12c674ca05c1baf867f6f6f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 4 Aug 2022 05:08:09 +0000 Subject: [PATCH 1/5] Use (actually) dummy place for let-else divergence --- compiler/rustc_mir_build/src/build/matches/mod.rs | 2 +- src/test/ui/let-else/issue-100103.rs | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/let-else/issue-100103.rs diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 58b1564cc5d8c..cefb5f36b6a94 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -2334,7 +2334,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // This place is not really used because this destination place // should never be used to take values at the end of the failure // block. - let dummy_place = Place { local: RETURN_PLACE, projection: ty::List::empty() }; + let dummy_place = self.temp(self.tcx.types.never, else_block.span); let failure_block; unpack!( failure_block = self.ast_block( diff --git a/src/test/ui/let-else/issue-100103.rs b/src/test/ui/let-else/issue-100103.rs new file mode 100644 index 0000000000000..e393deab764f3 --- /dev/null +++ b/src/test/ui/let-else/issue-100103.rs @@ -0,0 +1,15 @@ +// edition:2021 +// check-pass + +#![feature(try_blocks)] +#![feature(let_else)] + +fn main() { + let _: Result = try { + let Some(x) = Some(0) else { + Err(1)? + }; + + x + }; +} From 2b15fc6d9acc50072b1717a7631ddc6e0968970f Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 5 Aug 2022 19:20:03 +0800 Subject: [PATCH 2/5] recover require,include instead of use in item --- compiler/rustc_parse/src/parser/item.rs | 5 ++++- compiler/rustc_span/src/symbol.rs | 1 + .../ui/did_you_mean/use_instead_of_import.fixed | 8 ++++++++ .../ui/did_you_mean/use_instead_of_import.rs | 8 ++++++++ .../ui/did_you_mean/use_instead_of_import.stderr | 16 ++++++++++++++-- 5 files changed, 35 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 5670729253dac..ee26578ac10df 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -271,7 +271,10 @@ impl<'a> Parser<'a> { // MACRO_RULES ITEM self.parse_item_macro_rules(vis, has_bang)? } else if self.isnt_macro_invocation() - && (self.token.is_ident_named(sym::import) || self.token.is_ident_named(sym::using)) + && (self.token.is_ident_named(sym::import) + || self.token.is_ident_named(sym::using) + || self.token.is_ident_named(sym::include) + || self.token.is_ident_named(sym::require)) { return self.recover_import_as_use(); } else if self.isnt_macro_invocation() && vis.kind.is_pub() { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 791160ff6944e..f81a69c1cce0a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1170,6 +1170,7 @@ symbols! { repr_packed, repr_simd, repr_transparent, + require, residual, result, rhs, diff --git a/src/test/ui/did_you_mean/use_instead_of_import.fixed b/src/test/ui/did_you_mean/use_instead_of_import.fixed index 87d453e1565c5..a8aae76f4fcb3 100644 --- a/src/test/ui/did_you_mean/use_instead_of_import.fixed +++ b/src/test/ui/did_you_mean/use_instead_of_import.fixed @@ -6,10 +6,18 @@ use std::{ rc::Rc, }; +use std::time::Duration; +//~^ ERROR expected item, found `require` + +use std::time::Instant; +//~^ ERROR expected item, found `include` + pub use std::io; //~^ ERROR expected item, found `using` fn main() { let x = Rc::new(1); let _ = write!(io::stdout(), "{:?}", x); + let _ = Duration::new(5, 0); + let _ = Instant::now(); } diff --git a/src/test/ui/did_you_mean/use_instead_of_import.rs b/src/test/ui/did_you_mean/use_instead_of_import.rs index 59e83732328d9..2db7c24075219 100644 --- a/src/test/ui/did_you_mean/use_instead_of_import.rs +++ b/src/test/ui/did_you_mean/use_instead_of_import.rs @@ -6,10 +6,18 @@ import std::{ rc::Rc, }; +require std::time::Duration; +//~^ ERROR expected item, found `require` + +include std::time::Instant; +//~^ ERROR expected item, found `include` + pub using std::io; //~^ ERROR expected item, found `using` fn main() { let x = Rc::new(1); let _ = write!(io::stdout(), "{:?}", x); + let _ = Duration::new(5, 0); + let _ = Instant::now(); } diff --git a/src/test/ui/did_you_mean/use_instead_of_import.stderr b/src/test/ui/did_you_mean/use_instead_of_import.stderr index b22954af80f06..2aac8f68c5ebd 100644 --- a/src/test/ui/did_you_mean/use_instead_of_import.stderr +++ b/src/test/ui/did_you_mean/use_instead_of_import.stderr @@ -4,11 +4,23 @@ error: expected item, found `import` LL | import std::{ | ^^^^^^ help: items are imported using the `use` keyword +error: expected item, found `require` + --> $DIR/use_instead_of_import.rs:9:1 + | +LL | require std::time::Duration; + | ^^^^^^^ help: items are imported using the `use` keyword + +error: expected item, found `include` + --> $DIR/use_instead_of_import.rs:12:1 + | +LL | include std::time::Instant; + | ^^^^^^^ help: items are imported using the `use` keyword + error: expected item, found `using` - --> $DIR/use_instead_of_import.rs:9:5 + --> $DIR/use_instead_of_import.rs:15:5 | LL | pub using std::io; | ^^^^^ help: items are imported using the `use` keyword -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors From 9815667b8b59e5351c257f7a62f6c0874fabd7ca Mon Sep 17 00:00:00 2001 From: Yiming Lei Date: Tue, 2 Aug 2022 21:46:07 -0700 Subject: [PATCH 3/5] implement #98982 when loop as tail expression for miss match type E0308 error, recursively get the return statement and add diagnostic information on it use rustc_hir::intravisit to collect the return expression modified: compiler/rustc_typeck/src/check/coercion.rs new file: src/test/ui/typeck/issue-98982.rs new file: src/test/ui/typeck/issue-98982.stderr --- compiler/rustc_typeck/src/check/coercion.rs | 49 ++++++++++++++++++- .../break-while-condition.stderr | 8 +++ src/test/ui/typeck/issue-98982.rs | 9 ++++ src/test/ui/typeck/issue-98982.stderr | 24 +++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/typeck/issue-98982.rs create mode 100644 src/test/ui/typeck/issue-98982.stderr diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index 639cab98f1741..fc7ace0e6aa4c 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -38,10 +38,12 @@ use crate::astconv::AstConv; use crate::check::FnCtxt; use rustc_errors::{ - struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::Expr; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{Coercion, InferOk, InferResult}; use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt}; @@ -87,6 +89,19 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> { type CoerceResult<'tcx> = InferResult<'tcx, (Vec>, Ty<'tcx>)>; +struct CollectRetsVisitor<'tcx> { + ret_exprs: Vec<&'tcx hir::Expr<'tcx>>, +} + +impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if let hir::ExprKind::Ret(_) = expr.kind { + self.ret_exprs.push(expr); + } + intravisit::walk_expr(self, expr); + } +} + /// Coercing a mutable reference to an immutable works, while /// coercing `&T` to `&mut T` should be forbidden. fn coerce_mutbls<'tcx>( @@ -1481,6 +1496,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { let mut err; let mut unsized_return = false; + let mut visitor = CollectRetsVisitor { ret_exprs: vec![] }; match *cause.code() { ObligationCauseCode::ReturnNoExpression => { err = struct_span_err!( @@ -1506,6 +1522,10 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if !fcx.tcx.features().unsized_locals { unsized_return = self.is_return_ty_unsized(fcx, blk_id); } + if let Some(expression) = expression + && let hir::ExprKind::Loop(loop_blk, ..) = expression.kind { + intravisit::walk_block(& mut visitor, loop_blk); + } } ObligationCauseCode::ReturnValue(id) => { err = self.report_return_mismatched_types( @@ -1551,12 +1571,39 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { ); } + if visitor.ret_exprs.len() > 0 && let Some(expr) = expression { + self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs); + } err.emit_unless(unsized_return); self.final_ty = Some(fcx.tcx.ty_error()); } } } + fn note_unreachable_loop_return<'a>( + &self, + err: &mut DiagnosticBuilder<'a, ErrorGuaranteed>, + expr: &hir::Expr<'tcx>, + ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>, + ) { + let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else { return;}; + let mut span: MultiSpan = vec![loop_span].into(); + span.push_span_label(loop_span, "this might have zero elements to iterate on".to_string()); + for ret_expr in ret_exprs { + span.push_span_label( + ret_expr.span, + "if the loop doesn't execute, this value would never get returned".to_string(), + ); + } + err.span_note( + span, + "the function expects a value to always be returned, but loops might run zero times", + ); + err.help( + "return a value for the case when the loop has zero elements to iterate on, or \ + consider changing the return type to account for that possibility", + ); + } fn report_return_mismatched_types<'a>( &self, diff --git a/src/test/ui/for-loop-while/break-while-condition.stderr b/src/test/ui/for-loop-while/break-while-condition.stderr index 6960c4fd86735..e79f6a75fdec5 100644 --- a/src/test/ui/for-loop-while/break-while-condition.stderr +++ b/src/test/ui/for-loop-while/break-while-condition.stderr @@ -31,6 +31,14 @@ LL | | } | = note: expected type `!` found unit type `()` +note: the function expects a value to always be returned, but loops might run zero times + --> $DIR/break-while-condition.rs:24:13 + | +LL | while false { + | ^^^^^^^^^^^ this might have zero elements to iterate on +LL | return + | ------ if the loop doesn't execute, this value would never get returned + = help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility error: aborting due to 3 previous errors diff --git a/src/test/ui/typeck/issue-98982.rs b/src/test/ui/typeck/issue-98982.rs new file mode 100644 index 0000000000000..2553824bbfebb --- /dev/null +++ b/src/test/ui/typeck/issue-98982.rs @@ -0,0 +1,9 @@ +fn foo() -> i32 { + for i in 0..0 { + //~^ ERROR: mismatched types [E0308] + return i; + } + //~| help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility +} + +fn main() {} diff --git a/src/test/ui/typeck/issue-98982.stderr b/src/test/ui/typeck/issue-98982.stderr new file mode 100644 index 0000000000000..3c9806ac965fb --- /dev/null +++ b/src/test/ui/typeck/issue-98982.stderr @@ -0,0 +1,24 @@ +error[E0308]: mismatched types + --> $DIR/issue-98982.rs:2:5 + | +LL | fn foo() -> i32 { + | --- expected `i32` because of return type +LL | / for i in 0..0 { +LL | | +LL | | return i; +LL | | } + | |_____^ expected `i32`, found `()` + | +note: the function expects a value to always be returned, but loops might run zero times + --> $DIR/issue-98982.rs:2:5 + | +LL | for i in 0..0 { + | ^^^^^^^^^^^^^ this might have zero elements to iterate on +LL | +LL | return i; + | -------- if the loop doesn't execute, this value would never get returned + = help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From ca70ed8c8112b97bcd721f37933a30aeaa622dc3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 6 Aug 2022 11:52:27 +0200 Subject: [PATCH 4/5] remove Clean trait implementation for hir::GenericBound --- src/librustdoc/clean/mod.rs | 82 ++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 312a593761bfe..6573350d03aa3 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -126,40 +126,40 @@ impl<'tcx> Clean<'tcx, Item> for DocModule<'tcx> { } } -impl<'tcx> Clean<'tcx, Option> for hir::GenericBound<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> Option { - Some(match *self { - hir::GenericBound::Outlives(lt) => GenericBound::Outlives(clean_lifetime(lt, cx)), - hir::GenericBound::LangItemTrait(lang_item, span, _, generic_args) => { - let def_id = cx.tcx.require_lang_item(lang_item, Some(span)); - - let trait_ref = ty::TraitRef::identity(cx.tcx, def_id).skip_binder(); - - let generic_args = generic_args.clean(cx); - let GenericArgs::AngleBracketed { bindings, .. } = generic_args - else { - bug!("clean: parenthesized `GenericBound::LangItemTrait`"); - }; +fn clean_generic_bound<'tcx>( + bound: &hir::GenericBound<'tcx>, + cx: &mut DocContext<'tcx>, +) -> Option { + Some(match *bound { + hir::GenericBound::Outlives(lt) => GenericBound::Outlives(clean_lifetime(lt, cx)), + hir::GenericBound::LangItemTrait(lang_item, span, _, generic_args) => { + let def_id = cx.tcx.require_lang_item(lang_item, Some(span)); + + let trait_ref = ty::TraitRef::identity(cx.tcx, def_id).skip_binder(); + + let generic_args = generic_args.clean(cx); + let GenericArgs::AngleBracketed { bindings, .. } = generic_args + else { + bug!("clean: parenthesized `GenericBound::LangItemTrait`"); + }; - let trait_ = clean_trait_ref_with_bindings(cx, trait_ref, &bindings); - GenericBound::TraitBound( - PolyTrait { trait_, generic_params: vec![] }, - hir::TraitBoundModifier::None, - ) + let trait_ = clean_trait_ref_with_bindings(cx, trait_ref, &bindings); + GenericBound::TraitBound( + PolyTrait { trait_, generic_params: vec![] }, + hir::TraitBoundModifier::None, + ) + } + hir::GenericBound::Trait(ref t, modifier) => { + // `T: ~const Destruct` is hidden because `T: Destruct` is a no-op. + if modifier == hir::TraitBoundModifier::MaybeConst + && cx.tcx.lang_items().destruct_trait() == Some(t.trait_ref.trait_def_id().unwrap()) + { + return None; } - hir::GenericBound::Trait(ref t, modifier) => { - // `T: ~const Destruct` is hidden because `T: Destruct` is a no-op. - if modifier == hir::TraitBoundModifier::MaybeConst - && cx.tcx.lang_items().destruct_trait() - == Some(t.trait_ref.trait_def_id().unwrap()) - { - return None; - } - GenericBound::TraitBound(clean_poly_trait_ref(t, cx), modifier) - } - }) - } + GenericBound::TraitBound(clean_poly_trait_ref(t, cx), modifier) + } + }) } pub(crate) fn clean_trait_ref_with_bindings<'tcx>( @@ -294,14 +294,14 @@ impl<'tcx> Clean<'tcx, Option> for hir::WherePredicate<'tcx> { .collect(); WherePredicate::BoundPredicate { ty: clean_ty(wbp.bounded_ty, cx), - bounds: wbp.bounds.iter().filter_map(|x| x.clean(cx)).collect(), + bounds: wbp.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), bound_params, } } hir::WherePredicate::RegionPredicate(ref wrp) => WherePredicate::RegionPredicate { lifetime: clean_lifetime(wrp.lifetime, cx), - bounds: wrp.bounds.iter().filter_map(|x| x.clean(cx)).collect(), + bounds: wrp.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), }, hir::WherePredicate::EqPredicate(ref wrp) => WherePredicate::EqPredicate { @@ -531,7 +531,7 @@ fn clean_generic_param<'tcx>( .bounds_for_param(did) .filter(|bp| bp.origin != PredicateOrigin::WhereClause) .flat_map(|bp| bp.bounds) - .filter_map(|x| x.clean(cx)) + .filter_map(|x| clean_generic_bound(x, cx)) .collect() } else { Vec::new() @@ -1041,7 +1041,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext } hir::TraitItemKind::Type(bounds, Some(default)) => { let generics = enter_impl_trait(cx, |cx| trait_item.generics.clean(cx)); - let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect(); + let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(); let item_type = clean_middle_ty(hir_ty_to_ty(cx.tcx, default), cx, None); AssocTypeItem( Box::new(Typedef { @@ -1054,7 +1054,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext } hir::TraitItemKind::Type(bounds, None) => { let generics = enter_impl_trait(cx, |cx| trait_item.generics.clean(cx)); - let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect(); + let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(); TyAssocTypeItem(Box::new(generics), bounds) } }; @@ -1507,7 +1507,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T TyKind::OpaqueDef(item_id, _) => { let item = cx.tcx.hir().item(item_id); if let hir::ItemKind::OpaqueTy(ref ty) = item.kind { - ImplTrait(ty.bounds.iter().filter_map(|x| x.clean(cx)).collect()) + ImplTrait(ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect()) } else { unreachable!() } @@ -1911,7 +1911,7 @@ fn clean_maybe_renamed_item<'tcx>( kind: ConstantKind::Local { body: body_id, def_id }, }), ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy { - bounds: ty.bounds.iter().filter_map(|x| x.clean(cx)).collect(), + bounds: ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), generics: ty.generics.clean(cx), }), ItemKind::TyAlias(hir_ty, generics) => { @@ -1929,7 +1929,7 @@ fn clean_maybe_renamed_item<'tcx>( }), ItemKind::TraitAlias(generics, bounds) => TraitAliasItem(TraitAlias { generics: generics.clean(cx), - bounds: bounds.iter().filter_map(|x| x.clean(cx)).collect(), + bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), }), ItemKind::Union(ref variant_data, generics) => UnionItem(Union { generics: generics.clean(cx), @@ -1961,7 +1961,7 @@ fn clean_maybe_renamed_item<'tcx>( def_id, items, generics: generics.clean(cx), - bounds: bounds.iter().filter_map(|x| x.clean(cx)).collect(), + bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), }) } ItemKind::ExternCrate(orig_name) => { @@ -2241,7 +2241,7 @@ fn clean_type_binding<'tcx>( TypeBindingKind::Equality { term: clean_hir_term(term, cx) } } hir::TypeBindingKind::Constraint { bounds } => TypeBindingKind::Constraint { - bounds: bounds.iter().filter_map(|b| b.clean(cx)).collect(), + bounds: bounds.iter().filter_map(|b| clean_generic_bound(b, cx)).collect(), }, }, } From 71edb3168fcc351e8693d84d3bec6cfcb452560a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 6 Aug 2022 11:54:54 +0200 Subject: [PATCH 5/5] remove Clean trait implementation for hir::PolyTraitRef --- src/librustdoc/clean/mod.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 6573350d03aa3..14d81fc09466b 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -207,12 +207,6 @@ fn clean_poly_trait_ref_with_bindings<'tcx>( ) } -impl<'tcx> Clean<'tcx, GenericBound> for ty::PolyTraitRef<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> GenericBound { - clean_poly_trait_ref_with_bindings(cx, *self, &[]) - } -} - fn clean_lifetime<'tcx>(lifetime: hir::Lifetime, cx: &mut DocContext<'tcx>) -> Lifetime { let def = cx.tcx.named_region(lifetime.hir_id); if let Some( @@ -349,7 +343,7 @@ fn clean_poly_trait_predicate<'tcx>( let poly_trait_ref = pred.map_bound(|pred| pred.trait_ref); Some(WherePredicate::BoundPredicate { ty: clean_middle_ty(poly_trait_ref.skip_binder().self_ty(), cx, None), - bounds: vec![poly_trait_ref.clean(cx)], + bounds: vec![clean_poly_trait_ref_with_bindings(cx, poly_trait_ref, &[])], bound_params: Vec::new(), }) }