diff options
64 files changed, 1015 insertions, 184 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4cf603519b1..540e1eb157e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -289,8 +289,9 @@ jobs: os: ubuntu-20.04-4core-16gb env: {} - name: x86_64-gnu-integration + env: + CI_ONLY_WHEN_CHANNEL: nightly os: ubuntu-20.04-16core-64gb - env: {} - name: x86_64-gnu-debug os: ubuntu-20.04-8core-32gb env: {} diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3d59c034210..929757fced8 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1249,7 +1249,7 @@ impl Expr { ExprKind::Let(..) => ExprPrecedence::Let, ExprKind::If(..) => ExprPrecedence::If, ExprKind::While(..) => ExprPrecedence::While, - ExprKind::ForLoop(..) => ExprPrecedence::ForLoop, + ExprKind::ForLoop { .. } => ExprPrecedence::ForLoop, ExprKind::Loop(..) => ExprPrecedence::Loop, ExprKind::Match(..) => ExprPrecedence::Match, ExprKind::Closure(..) => ExprPrecedence::Closure, @@ -1411,10 +1411,10 @@ pub enum ExprKind { While(P<Expr>, P<Block>, Option<Label>), /// A `for` loop, with an optional label. /// - /// `'label: for pat in expr { block }` + /// `'label: for await? pat in iter { block }` /// /// This is desugared to a combination of `loop` and `match` expressions. - ForLoop(P<Pat>, P<Expr>, P<Block>, Option<Label>), + ForLoop { pat: P<Pat>, iter: P<Expr>, body: P<Block>, label: Option<Label>, kind: ForLoopKind }, /// Conditionless loop (can be exited with `break`, `continue`, or `return`). /// /// `'label: loop { block }` @@ -1517,6 +1517,13 @@ pub enum ExprKind { Err, } +/// Used to differentiate between `for` loops and `for await` loops. +#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq, Eq)] +pub enum ForLoopKind { + For, + ForAwait, +} + /// Used to differentiate between `async {}` blocks and `gen {}` blocks. #[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)] pub enum GenBlockKind { diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 557ae02a8f9..82f28143630 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1389,7 +1389,7 @@ pub fn noop_visit_expr<T: MutVisitor>( vis.visit_block(body); visit_opt(label, |label| vis.visit_label(label)); } - ExprKind::ForLoop(pat, iter, body, label) => { + ExprKind::ForLoop { pat, iter, body, label, kind: _ } => { vis.visit_pat(pat); vis.visit_expr(iter); vis.visit_block(body); diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 4dece079783..65036bcdc36 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -19,7 +19,7 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool { | ast::ExprKind::Block(..) | ast::ExprKind::While(..) | ast::ExprKind::Loop(..) - | ast::ExprKind::ForLoop(..) + | ast::ExprKind::ForLoop { .. } | ast::ExprKind::TryBlock(..) | ast::ExprKind::ConstBlock(..) ) @@ -48,8 +48,16 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { Closure(closure) => { expr = &closure.body; } - Gen(..) | Block(..) | ForLoop(..) | If(..) | Loop(..) | Match(..) | Struct(..) - | TryBlock(..) | While(..) | ConstBlock(_) => break Some(expr), + Gen(..) + | Block(..) + | ForLoop { .. } + | If(..) + | Loop(..) + | Match(..) + | Struct(..) + | TryBlock(..) + | While(..) + | ConstBlock(_) => break Some(expr), // FIXME: These can end in `}`, but changing these would break stable code. InlineAsm(_) | OffsetOf(_, _) | MacCall(_) | IncludedBytes(_) | FormatArgs(_) => { diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 27f1b84f372..45261ca48fc 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -844,11 +844,11 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { visitor.visit_expr(subexpression); visitor.visit_block(block); } - ExprKind::ForLoop(pattern, subexpression, block, opt_label) => { - walk_list!(visitor, visit_label, opt_label); - visitor.visit_pat(pattern); - visitor.visit_expr(subexpression); - visitor.visit_block(block); + ExprKind::ForLoop { pat, iter, body, label, kind: _ } => { + walk_list!(visitor, visit_label, label); + visitor.visit_pat(pat); + visitor.visit_expr(iter); + visitor.visit_block(body); } ExprKind::Loop(block, opt_label, _) => { walk_list!(visitor, visit_label, opt_label); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 704f124dbcb..2d61f3bceec 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -56,12 +56,12 @@ impl<'hir> LoweringContext<'_, 'hir> { return ex; } // Desugar `ExprForLoop` - // from: `[opt_ident]: for <pat> in <head> <body>` + // from: `[opt_ident]: for await? <pat> in <iter> <body>` // // This also needs special handling because the HirId of the returned `hir::Expr` will not // correspond to the `e.id`, so `lower_expr_for` handles attribute lowering itself. - ExprKind::ForLoop(pat, head, body, opt_label) => { - return self.lower_expr_for(e, pat, head, body, *opt_label); + ExprKind::ForLoop { pat, iter, body, label, kind } => { + return self.lower_expr_for(e, pat, iter, body, *label, *kind); } _ => (), } @@ -337,7 +337,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ), ExprKind::Try(sub_expr) => self.lower_expr_try(e.span, sub_expr), - ExprKind::Paren(_) | ExprKind::ForLoop(..) => { + ExprKind::Paren(_) | ExprKind::ForLoop { .. } => { unreachable!("already handled") } @@ -874,6 +874,17 @@ impl<'hir> LoweringContext<'_, 'hir> { /// } /// ``` fn lower_expr_await(&mut self, await_kw_span: Span, expr: &Expr) -> hir::ExprKind<'hir> { + let expr = self.arena.alloc(self.lower_expr_mut(expr)); + self.make_lowered_await(await_kw_span, expr, FutureKind::Future) + } + + /// Takes an expr that has already been lowered and generates a desugared await loop around it + fn make_lowered_await( + &mut self, + await_kw_span: Span, + expr: &'hir hir::Expr<'hir>, + await_kind: FutureKind, + ) -> hir::ExprKind<'hir> { let full_span = expr.span.to(await_kw_span); let is_async_gen = match self.coroutine_kind { @@ -887,13 +898,16 @@ impl<'hir> LoweringContext<'_, 'hir> { } }; - let span = self.mark_span_with_reason(DesugaringKind::Await, await_kw_span, None); + let features = match await_kind { + FutureKind::Future => None, + FutureKind::AsyncIterator => Some(self.allow_for_await.clone()), + }; + let span = self.mark_span_with_reason(DesugaringKind::Await, await_kw_span, features); let gen_future_span = self.mark_span_with_reason( DesugaringKind::Await, full_span, Some(self.allow_gen_future.clone()), ); - let expr = self.lower_expr_mut(expr); let expr_hir_id = expr.hir_id; // Note that the name of this binding must not be changed to something else because @@ -934,11 +948,18 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::LangItem::GetContext, arena_vec![self; task_context], ); - let call = self.expr_call_lang_item_fn( - span, - hir::LangItem::FuturePoll, - arena_vec![self; new_unchecked, get_context], - ); + let call = match await_kind { + FutureKind::Future => self.expr_call_lang_item_fn( + span, + hir::LangItem::FuturePoll, + arena_vec![self; new_unchecked, get_context], + ), + FutureKind::AsyncIterator => self.expr_call_lang_item_fn( + span, + hir::LangItem::AsyncIteratorPollNext, + arena_vec![self; new_unchecked, get_context], + ), + }; self.arena.alloc(self.expr_unsafe(call)) }; @@ -1020,11 +1041,16 @@ impl<'hir> LoweringContext<'_, 'hir> { let awaitee_arm = self.arm(awaitee_pat, loop_expr); // `match ::std::future::IntoFuture::into_future(<expr>) { ... }` - let into_future_expr = self.expr_call_lang_item_fn( - span, - hir::LangItem::IntoFutureIntoFuture, - arena_vec![self; expr], - ); + let into_future_expr = match await_kind { + FutureKind::Future => self.expr_call_lang_item_fn( + span, + hir::LangItem::IntoFutureIntoFuture, + arena_vec![self; *expr], + ), + // Not needed for `for await` because we expect to have already called + // `IntoAsyncIterator::into_async_iter` on it. + FutureKind::AsyncIterator => expr, + }; // match <into_future_expr> { // mut __awaitee => loop { .. } @@ -1685,6 +1711,7 @@ impl<'hir> LoweringContext<'_, 'hir> { head: &Expr, body: &Block, opt_label: Option<Label>, + loop_kind: ForLoopKind, ) -> hir::Expr<'hir> { let head = self.lower_expr_mut(head); let pat = self.lower_pat(pat); @@ -1713,17 +1740,41 @@ impl<'hir> LoweringContext<'_, 'hir> { let (iter_pat, iter_pat_nid) = self.pat_ident_binding_mode(head_span, iter, hir::BindingAnnotation::MUT); - // `match Iterator::next(&mut iter) { ... }` let match_expr = { let iter = self.expr_ident(head_span, iter, iter_pat_nid); - let ref_mut_iter = self.expr_mut_addr_of(head_span, iter); - let next_expr = self.expr_call_lang_item_fn( - head_span, - hir::LangItem::IteratorNext, - arena_vec![self; ref_mut_iter], - ); + let next_expr = match loop_kind { + ForLoopKind::For => { + // `Iterator::next(&mut iter)` + let ref_mut_iter = self.expr_mut_addr_of(head_span, iter); + self.expr_call_lang_item_fn( + head_span, + hir::LangItem::IteratorNext, + arena_vec![self; ref_mut_iter], + ) + } + ForLoopKind::ForAwait => { + // we'll generate `unsafe { Pin::new_unchecked(&mut iter) })` and then pass this + // to make_lowered_await with `FutureKind::AsyncIterator` which will generator + // calls to `poll_next`. In user code, this would probably be a call to + // `Pin::as_mut` but here it's easy enough to do `new_unchecked`. + + // `&mut iter` + let iter = self.expr_mut_addr_of(head_span, iter); + // `Pin::new_unchecked(...)` + let iter = self.arena.alloc(self.expr_call_lang_item_fn_mut( + head_span, + hir::LangItem::PinNewUnchecked, + arena_vec![self; iter], + )); + // `unsafe { ... }` + let iter = self.arena.alloc(self.expr_unsafe(iter)); + let kind = self.make_lowered_await(head_span, iter, FutureKind::AsyncIterator); + self.arena.alloc(hir::Expr { hir_id: self.next_id(), kind, span: head_span }) + } + }; let arms = arena_vec![self; none_arm, some_arm]; + // `match $next_expr { ... }` self.expr_match(head_span, next_expr, arms, hir::MatchSource::ForLoopDesugar) }; let match_stmt = self.stmt_expr(for_span, match_expr); @@ -1743,13 +1794,16 @@ impl<'hir> LoweringContext<'_, 'hir> { // `mut iter => { ... }` let iter_arm = self.arm(iter_pat, loop_expr); - // `match ::std::iter::IntoIterator::into_iter(<head>) { ... }` - let into_iter_expr = { - self.expr_call_lang_item_fn( - head_span, - hir::LangItem::IntoIterIntoIter, - arena_vec![self; head], - ) + let into_iter_expr = match loop_kind { + ForLoopKind::For => { + // `::std::iter::IntoIterator::into_iter(<head>)` + self.expr_call_lang_item_fn( + head_span, + hir::LangItem::IntoIterIntoIter, + arena_vec![self; head], + ) + } + ForLoopKind::ForAwait => self.arena.alloc(head), }; let match_expr = self.arena.alloc(self.expr_match( @@ -2152,3 +2206,14 @@ impl<'hir> LoweringContext<'_, 'hir> { } } } + +/// Used by [`LoweringContext::make_lowered_await`] to customize the desugaring based on what kind +/// of future we are awaiting. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum FutureKind { + /// We are awaiting a normal future + Future, + /// We are awaiting something that's known to be an AsyncIterator (i.e. we are in the header of + /// a `for await` loop) + AsyncIterator, +} diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index ed033d86008..e35d7d62cad 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -130,6 +130,7 @@ struct LoweringContext<'a, 'hir> { allow_try_trait: Lrc<[Symbol]>, allow_gen_future: Lrc<[Symbol]>, allow_async_iterator: Lrc<[Symbol]>, + allow_for_await: Lrc<[Symbol]>, /// Mapping from generics `def_id`s to TAIT generics `def_id`s. /// For each captured lifetime (e.g., 'a), we create a new lifetime parameter that is a generic @@ -174,6 +175,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } else { [sym::gen_future].into() }, + allow_for_await: [sym::async_iterator].into(), // FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller` // interact with `gen`/`async gen` blocks allow_async_iterator: [sym::gen_future, sym::async_iterator].into(), diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 1cc9309c45c..b20a0b9461f 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -527,6 +527,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { "async closures are unstable", "to use an async block, remove the `||`: `async {`" ); + gate_all!(async_for_loop, "`for await` loops are experimental"); gate_all!( closure_lifetime_binder, "`for<...>` binders for closures are experimental", diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index f5ffcddb83d..f868beec812 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -1,5 +1,6 @@ use crate::pp::Breaks::Inconsistent; use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT}; +use ast::ForLoopKind; use itertools::{Itertools, Position}; use rustc_ast::ptr::P; use rustc_ast::token; @@ -418,20 +419,23 @@ impl<'a> State<'a> { self.space(); self.print_block_with_attrs(blk, attrs); } - ast::ExprKind::ForLoop(pat, iter, blk, opt_label) => { - if let Some(label) = opt_label { + ast::ExprKind::ForLoop { pat, iter, body, label, kind } => { + if let Some(label) = label { self.print_ident(label.ident); self.word_space(":"); } self.cbox(0); self.ibox(0); self.word_nbsp("for"); + if kind == &ForLoopKind::ForAwait { + self.word_nbsp("await"); + } self.print_pat(pat); self.space(); self.word_space("in"); self.print_expr_as_cond(iter); self.space(); - self.print_block_with_attrs(blk, attrs); + self.print_block_with_attrs(body, attrs); } ast::ExprKind::Loop(blk, opt_label, _) => { if let Some(label) = opt_label { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 247061c5ca7..43561a1c020 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -5,7 +5,6 @@ use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT}; use ast::StaticItem; use itertools::{Itertools, Position}; use rustc_ast as ast; -use rustc_ast::GenericBound; use rustc_ast::ModKind; use rustc_span::symbol::Ident; @@ -338,21 +337,9 @@ impl<'a> State<'a> { self.word_nbsp("trait"); self.print_ident(item.ident); self.print_generic_params(&generics.params); - let mut real_bounds = Vec::with_capacity(bounds.len()); - for bound in bounds.iter() { - if let GenericBound::Trait(ptr, modifiers) = bound - && let ast::BoundPolarity::Maybe(_) = modifiers.polarity - { - self.space(); - self.word_space("for ?"); - self.print_trait_ref(&ptr.trait_ref); - } else { - real_bounds.push(bound.clone()); - } - } - if !real_bounds.is_empty() { + if !bounds.is_empty() { self.word_nbsp(":"); - self.print_type_bounds(&real_bounds); + self.print_type_bounds(bounds); } self.print_where_clause(&generics.where_clause); self.word(" "); diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 924e68fa91d..60dbf7dc055 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -408,13 +408,18 @@ fn try_extract_error_from_region_constraints<'tcx>( mut region_var_origin: impl FnMut(RegionVid) -> RegionVariableOrigin, mut universe_of_region: impl FnMut(RegionVid) -> UniverseIndex, ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { + let placeholder_universe = match placeholder_region.kind() { + ty::RePlaceholder(p) => p.universe, + ty::ReVar(vid) => universe_of_region(vid), + _ => ty::UniverseIndex::ROOT, + }; let matches = |a_region: Region<'tcx>, b_region: Region<'tcx>| match (a_region.kind(), b_region.kind()) { (RePlaceholder(a_p), RePlaceholder(b_p)) => a_p.bound == b_p.bound, _ => a_region == b_region, }; - let check = |constraint: &Constraint<'tcx>, cause: &SubregionOrigin<'tcx>, exact| { - match *constraint { + let mut check = + |constraint: &Constraint<'tcx>, cause: &SubregionOrigin<'tcx>, exact| match *constraint { Constraint::RegSubReg(sub, sup) if ((exact && sup == placeholder_region) || (!exact && matches(sup, placeholder_region))) @@ -422,16 +427,16 @@ fn try_extract_error_from_region_constraints<'tcx>( { Some((sub, cause.clone())) } - // FIXME: Should this check the universe of the var? Constraint::VarSubReg(vid, sup) - if ((exact && sup == placeholder_region) - || (!exact && matches(sup, placeholder_region))) => + if (exact + && sup == placeholder_region + && !universe_of_region(vid).can_name(placeholder_universe)) + || (!exact && matches(sup, placeholder_region)) => { Some((ty::Region::new_var(infcx.tcx, vid), cause.clone())) } _ => None, - } - }; + }; let mut info = region_constraints .constraints .iter() diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index b54e119189a..d244897f8a5 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -303,7 +303,7 @@ impl<'cx, 'a> Context<'cx, 'a> { | ExprKind::Continue(_) | ExprKind::Err | ExprKind::Field(_, _) - | ExprKind::ForLoop(_, _, _, _) + | ExprKind::ForLoop { .. } | ExprKind::FormatArgs(_) | ExprKind::IncludedBytes(..) | ExprKind::InlineAsm(_) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 38c40471e29..60586f54fd5 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -357,6 +357,8 @@ declare_features! ( (unstable, async_closure, "1.37.0", Some(62290)), /// Allows `#[track_caller]` on async functions. (unstable, async_fn_track_caller, "1.73.0", Some(110011)), + /// Allows `for await` loops. + (unstable, async_for_loop, "CURRENT_RUSTC_VERSION", Some(118898)), /// Allows builtin # foo() syntax (unstable, builtin_syntax, "1.71.0", Some(110680)), /// Treat `extern "C"` function as nounwind. diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index b0b53bb7478..7691cd11c4f 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -307,6 +307,8 @@ language_item_table! { Context, sym::Context, context, Target::Struct, GenericRequirement::None; FuturePoll, sym::poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + AsyncIteratorPollNext, sym::async_iterator_poll_next, async_iterator_poll_next, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::Exact(0); + Option, sym::Option, option_type, Target::Enum, GenericRequirement::None; OptionSome, sym::Some, option_some_variant, Target::Variant, GenericRequirement::None; OptionNone, sym::None, option_none_variant, Target::Variant, GenericRequirement::None; diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index 1d737e17e82..a82853a1303 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -124,7 +124,10 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { if let Some(term_did) = tcx.lang_items().termination() { let return_ty = main_fnsig.output(); let return_ty_span = main_fn_return_type_span(tcx, main_def_id).unwrap_or(main_span); - let return_ty = return_ty.skip_binder(); + let Some(return_ty) = return_ty.no_bound_vars() else { + tcx.sess.emit_err(errors::MainFunctionReturnTypeGeneric { span: return_ty_span }); + return; + }; let infcx = tcx.infer_ctxt().build(); let cause = traits::ObligationCause::new( return_ty_span, diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 6715d01c9e0..feaec5ac620 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -553,17 +553,7 @@ impl<'a> State<'a> { } hir::ItemKind::OpaqueTy(opaque_ty) => { self.print_item_type(item, opaque_ty.generics, |state| { - let mut real_bounds = Vec::with_capacity(opaque_ty.bounds.len()); - for b in opaque_ty.bounds { - if let GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = b { - state.space(); - state.word_space("for ?"); - state.print_trait_ref(&ptr.trait_ref); - } else { - real_bounds.push(b); - } - } - state.print_bounds("= impl", real_bounds); + state.print_bounds("= impl", opaque_ty.bounds) }); } hir::ItemKind::Enum(ref enum_definition, params) => { @@ -625,17 +615,7 @@ impl<'a> State<'a> { self.word_nbsp("trait"); self.print_ident(item.ident); self.print_generic_params(generics.params); - let mut real_bounds = Vec::with_capacity(bounds.len()); - for b in bounds { - if let GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = b { - self.space(); - self.word_space("for ?"); - self.print_trait_ref(&ptr.trait_ref); - } else { - real_bounds.push(b); - } - } - self.print_bounds(":", real_bounds); + self.print_bounds(":", bounds); self.print_where_clause(generics); self.word(" "); self.bopen(); diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 0fbc4a0ce50..76d4dc24dce 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -135,6 +135,10 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { ) -> LexicalRegionResolutions<'tcx> { let mut var_data = self.construct_var_data(); + // Deduplicating constraints is shown to have a positive perf impact. + self.data.constraints.sort_by_key(|(constraint, _)| *constraint); + self.data.constraints.dedup_by_key(|(constraint, _)| *constraint); + if cfg!(debug_assertions) { self.dump_constraints(); } @@ -181,7 +185,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let mut constraints = IndexVec::from_elem(Vec::new(), &var_values.values); // Tracks the changed region vids. let mut changes = Vec::new(); - for constraint in self.data.constraints.keys() { + for (constraint, _) in &self.data.constraints { match *constraint { Constraint::RegSubVar(a_region, b_vid) => { let b_data = var_values.value_mut(b_vid); @@ -676,7 +680,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let dummy_source = graph.add_node(()); let dummy_sink = graph.add_node(()); - for constraint in self.data.constraints.keys() { + for (constraint, _) in &self.data.constraints { match *constraint { Constraint::VarSubVar(a_id, b_id) => { graph.add_edge(NodeIndex(a_id.index()), NodeIndex(b_id.index()), *constraint); @@ -883,9 +887,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } Constraint::RegSubVar(region, _) | Constraint::VarSubReg(_, region) => { + let constraint_idx = + this.constraints.binary_search_by(|(c, _)| c.cmp(&edge.data)).unwrap(); state.result.push(RegionAndOrigin { region, - origin: this.constraints.get(&edge.data).unwrap().clone(), + origin: this.constraints[constraint_idx].1.clone(), }); } diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs index e06596df7b9..b2bcbbf2e53 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -416,8 +416,8 @@ impl<'tcx> MiniGraph<'tcx> { region_constraints.undo_log.region_constraints_in_snapshot(&snapshot.undo_snapshot) { match undo_entry { - AddConstraint(constraint) => { - each_constraint(constraint); + &AddConstraint(i) => { + each_constraint(®ion_constraints.data().constraints[i].0); } &AddVerify(i) => span_bug!( region_constraints.data().verifys[i].origin.span(), @@ -430,8 +430,8 @@ impl<'tcx> MiniGraph<'tcx> { region_constraints .data() .constraints - .keys() - .for_each(|constraint| each_constraint(constraint)); + .iter() + .for_each(|(constraint, _)| each_constraint(constraint)); } } diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index cbd8040c9f1..71adf526097 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -20,7 +20,6 @@ use rustc_middle::ty::{ReBound, ReVar}; use rustc_middle::ty::{Region, RegionVid}; use rustc_span::Span; -use std::collections::BTreeMap; use std::ops::Range; use std::{cmp, fmt, mem}; @@ -90,7 +89,7 @@ pub type VarInfos = IndexVec<RegionVid, RegionVariableInfo>; pub struct RegionConstraintData<'tcx> { /// Constraints of the form `A <= B`, where either `A` or `B` can /// be a region variable (or neither, as it happens). - pub constraints: BTreeMap<Constraint<'tcx>, SubregionOrigin<'tcx>>, + pub constraints: Vec<(Constraint<'tcx>, SubregionOrigin<'tcx>)>, /// Constraints of the form `R0 member of [R1, ..., Rn]`, meaning that /// `R0` must be equal to one of the regions `R1..Rn`. These occur @@ -273,7 +272,7 @@ pub(crate) enum UndoLog<'tcx> { AddVar(RegionVid), /// We added the given `constraint`. - AddConstraint(Constraint<'tcx>), + AddConstraint(usize), /// We added the given `verify`. AddVerify(usize), @@ -319,8 +318,9 @@ impl<'tcx> RegionConstraintStorage<'tcx> { self.var_infos.pop().unwrap(); assert_eq!(self.var_infos.len(), vid.index()); } - AddConstraint(ref constraint) => { - self.data.constraints.remove(constraint); + AddConstraint(index) => { + self.data.constraints.pop().unwrap(); + assert_eq!(self.data.constraints.len(), index); } AddVerify(index) => { self.data.verifys.pop(); @@ -443,14 +443,9 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { // cannot add constraints once regions are resolved debug!("RegionConstraintCollector: add_constraint({:?})", constraint); - // never overwrite an existing (constraint, origin) - only insert one if it isn't - // present in the map yet. This prevents origins from outside the snapshot being - // replaced with "less informative" origins e.g., during calls to `can_eq` - let undo_log = &mut self.undo_log; - self.storage.data.constraints.entry(constraint).or_insert_with(|| { - undo_log.push(AddConstraint(constraint)); - origin - }); + let index = self.storage.data.constraints.len(); + self.storage.data.constraints.push((constraint, origin)); + self.undo_log.push(AddConstraint(index)); } fn add_verify(&mut self, verify: Verify<'tcx>) { diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 34cdee4ec5c..0386f2ec56c 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -852,8 +852,8 @@ trait UnusedDelimLint { (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right), true) } - ForLoop(_, ref cond, ref block, ..) => { - (cond, UnusedDelimsCtx::ForIterExpr, true, None, Some(block.span.lo()), true) + ForLoop { ref iter, ref body, .. } => { + (iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true) } Match(ref head, _) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { @@ -1085,7 +1085,7 @@ impl EarlyLintPass for UnusedParens { } match e.kind { - ExprKind::Let(ref pat, _, _, _) | ExprKind::ForLoop(ref pat, ..) => { + ExprKind::Let(ref pat, _, _, _) | ExprKind::ForLoop { ref pat, .. } => { self.check_unused_parens_pat(cx, pat, false, false, (true, true)); } // We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index c435f4023af..d67dc59c618 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -28,6 +28,7 @@ use rustc_span::hygiene::DesugaringKind; use rustc_span::Span; pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> { + let typeck_results = tcx.typeck(def_id); let (thir, expr) = tcx.thir_body(def_id)?; let thir = thir.borrow(); let pattern_arena = TypedArena::default(); @@ -35,6 +36,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err let mut visitor = MatchVisitor { tcx, thir: &*thir, + typeck_results, param_env: tcx.param_env(def_id), lint_level: tcx.local_def_id_to_hir_id(def_id), let_source: LetSource::None, @@ -80,6 +82,7 @@ enum LetSource { struct MatchVisitor<'thir, 'p, 'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, + typeck_results: &'tcx ty::TypeckResults<'tcx>, thir: &'thir Thir<'tcx>, lint_level: HirId, let_source: LetSource, @@ -382,6 +385,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> { scrutinee.map(|scrut| self.is_known_valid_scrutinee(scrut)).unwrap_or(true); MatchCheckCtxt { tcx: self.tcx, + typeck_results: self.typeck_results, param_env: self.param_env, module: self.tcx.parent_module(self.lint_level).to_def_id(), pattern_arena: self.pattern_arena, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index a21a1533848..e653d4064c8 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -10,7 +10,7 @@ use super::{ use crate::errors; use crate::maybe_recover_from_interpolated_ty_qpath; use ast::mut_visit::{noop_visit_expr, MutVisitor}; -use ast::{CoroutineKind, GenBlockKind, Pat, Path, PathSegment}; +use ast::{CoroutineKind, ForLoopKind, GenBlockKind, Pat, Path, PathSegment}; use core::mem; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; @@ -1801,7 +1801,7 @@ impl<'a> Parser<'a> { && matches!( expr.kind, ExprKind::While(_, _, None) - | ExprKind::ForLoop(_, _, _, None) + | ExprKind::ForLoop { label: None, .. } | ExprKind::Loop(_, None, _) | ExprKind::Block(_, None) ) @@ -2685,8 +2685,17 @@ impl<'a> Parser<'a> { Ok((pat, expr)) } - /// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten). + /// Parses `for await? <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten). fn parse_expr_for(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> { + let is_await = + self.token.uninterpolated_span().at_least_rust_2018() && self.eat_keyword(kw::Await); + + if is_await { + self.sess.gated_spans.gate(sym::async_for_loop, self.prev_token.span); + } + + let kind = if is_await { ForLoopKind::ForAwait } else { ForLoopKind::For }; + let (pat, expr) = self.parse_for_head()?; // Recover from missing expression in `for` loop if matches!(expr.kind, ExprKind::Block(..)) @@ -2699,13 +2708,13 @@ impl<'a> Parser<'a> { let block = self.mk_block(thin_vec![], BlockCheckMode::Default, self.prev_token.span); return Ok(self.mk_expr( lo.to(self.prev_token.span), - ExprKind::ForLoop(pat, err_expr, block, opt_label), + ExprKind::ForLoop { pat, iter: err_expr, body: block, label: opt_label, kind }, )); } let (attrs, loop_block) = self.parse_inner_attrs_and_block()?; - let kind = ExprKind::ForLoop(pat, expr, loop_block, opt_label); + let kind = ExprKind::ForLoop { pat, iter: expr, body: loop_block, label: opt_label, kind }; self.recover_loop_else("for", lo)?; @@ -3800,7 +3809,7 @@ impl MutVisitor for CondChecker<'_> { | ExprKind::Lit(_) | ExprKind::If(_, _, _) | ExprKind::While(_, _, _) - | ExprKind::ForLoop(_, _, _, _) + | ExprKind::ForLoop { .. } | ExprKind::Loop(_, _, _) | ExprKind::Match(_, _) | ExprKind::Closure(_) diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 785a60e9978..f00e6f18617 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -62,7 +62,8 @@ pub trait TypeCx: Sized + Clone + fmt::Debug { /// patterns during analysis. type PatData: Clone + Default; - fn is_opaque_ty(ty: Self::Ty) -> bool; + /// FIXME(Nadrieril): `Cx` should only give us revealed types. + fn reveal_opaque_ty(&self, ty: Self::Ty) -> Self::Ty; fn is_exhaustive_patterns_feature_on(&self) -> bool; /// The number of fields for this constructor. diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 072ef4836a8..450a5cb0a10 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -48,22 +48,14 @@ impl<'a, 'p, 'tcx> PatternColumn<'a, 'p, 'tcx> { fn is_empty(&self) -> bool { self.patterns.is_empty() } - fn head_ty(&self) -> Option<Ty<'tcx>> { + fn head_ty(&self, cx: MatchCtxt<'a, 'p, 'tcx>) -> Option<Ty<'tcx>> { if self.patterns.len() == 0 { return None; } - // If the type is opaque and it is revealed anywhere in the column, we take the revealed - // version. Otherwise we could encounter constructors for the revealed type and crash. - let first_ty = self.patterns[0].ty(); - if RustcMatchCheckCtxt::is_opaque_ty(first_ty) { - for pat in &self.patterns { - let ty = pat.ty(); - if !RustcMatchCheckCtxt::is_opaque_ty(ty) { - return Some(ty); - } - } - } - Some(first_ty) + + let ty = self.patterns[0].ty(); + // FIXME(Nadrieril): `Cx` should only give us revealed types. + Some(cx.tycx.reveal_opaque_ty(ty)) } /// Do constructor splitting on the constructors of the column. @@ -125,7 +117,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( cx: MatchCtxt<'a, 'p, 'tcx>, column: &PatternColumn<'a, 'p, 'tcx>, ) -> Vec<WitnessPat<'p, 'tcx>> { - let Some(ty) = column.head_ty() else { + let Some(ty) = column.head_ty(cx) else { return Vec::new(); }; let pcx = &PlaceCtxt::new_dummy(cx, ty); @@ -226,7 +218,7 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( cx: MatchCtxt<'a, 'p, 'tcx>, column: &PatternColumn<'a, 'p, 'tcx>, ) { - let Some(ty) = column.head_ty() else { + let Some(ty) = column.head_ty(cx) else { return; }; let pcx = &PlaceCtxt::new_dummy(cx, ty); diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 65c90aa9f1d..d2cfb9a8b06 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -44,6 +44,7 @@ pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat<RustcMatchCheckCtxt<'p, ' #[derive(Clone)] pub struct RustcMatchCheckCtxt<'p, 'tcx> { pub tcx: TyCtxt<'tcx>, + pub typeck_results: &'tcx ty::TypeckResults<'tcx>, /// The module in which the match occurs. This is necessary for /// checking inhabited-ness of types because whether a type is (visibly) /// inhabited can depend on whether it was defined in the current module or @@ -101,6 +102,21 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { } } + /// Type inference occasionally gives us opaque types in places where corresponding patterns + /// have more specific types. To avoid inconsistencies as well as detect opaque uninhabited + /// types, we use the corresponding concrete type if possible. + fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() { + if let Some(local_def_id) = alias_ty.def_id.as_local() { + let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args }; + if let Some(real_ty) = self.typeck_results.concrete_opaque_types.get(&key) { + return real_ty.ty; + } + } + } + ty + } + // In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide // uninhabited fields in order not to reveal the uninhabitedness of the whole variant. // This lists the fields we keep along with their types. @@ -873,8 +889,9 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { fn is_exhaustive_patterns_feature_on(&self) -> bool { self.tcx.features().exhaustive_patterns } - fn is_opaque_ty(ty: Self::Ty) -> bool { - matches!(ty.kind(), ty::Alias(ty::Opaque, ..)) + + fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> { + self.reveal_opaque_ty(ty) } fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: Self::Ty) -> usize { diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 6b1de807797..6b9fbd73003 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -865,24 +865,14 @@ impl<'a, 'p, Cx: TypeCx> Matrix<'a, 'p, Cx> { matrix } - fn head_ty(&self) -> Option<Cx::Ty> { + fn head_ty(&self, mcx: MatchCtxt<'a, 'p, Cx>) -> Option<Cx::Ty> { if self.column_count() == 0 { return None; } - let mut ty = self.wildcard_row.head().ty(); - // If the type is opaque and it is revealed anywhere in the column, we take the revealed - // version. Otherwise we could encounter constructors for the revealed type and crash. - if Cx::is_opaque_ty(ty) { - for pat in self.heads() { - let pat_ty = pat.ty(); - if !Cx::is_opaque_ty(pat_ty) { - ty = pat_ty; - break; - } - } - } - Some(ty) + let ty = self.wildcard_row.head().ty(); + // FIXME(Nadrieril): `Cx` should only give us revealed types. + Some(mcx.tycx.reveal_opaque_ty(ty)) } fn column_count(&self) -> usize { self.wildcard_row.len() @@ -1181,7 +1171,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( ) -> WitnessMatrix<Cx> { debug_assert!(matrix.rows().all(|r| r.len() == matrix.column_count())); - let Some(ty) = matrix.head_ty() else { + let Some(ty) = matrix.head_ty(mcx) else { // The base case: there are no columns in the matrix. We are morally pattern-matching on (). // A row is useful iff it has no (unguarded) rows above it. for row in matrix.rows_mut() { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index cf50f630bf2..bd4a2998e2d 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -4258,11 +4258,11 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { }); } - ExprKind::ForLoop(ref pat, ref iter_expr, ref block, label) => { - self.visit_expr(iter_expr); + ExprKind::ForLoop { ref pat, ref iter, ref body, label, kind: _ } => { + self.visit_expr(iter); self.with_rib(ValueNS, RibKind::Normal, |this| { this.resolve_pattern_top(pat, PatternSource::For); - this.resolve_labeled_block(label, expr.id, block); + this.resolve_labeled_block(label, expr.id, body); }); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 376ccfba9d8..5a02e7f3269 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1409,7 +1409,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { | ExprKind::Unary(..) | ExprKind::If(..) | ExprKind::While(..) - | ExprKind::ForLoop(..) + | ExprKind::ForLoop { .. } | ExprKind::Match(..), .. }), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 0333b5f04c3..95106cc64c1 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -426,7 +426,9 @@ symbols! { async_closure, async_fn_in_trait, async_fn_track_caller, + async_for_loop, async_iterator, + async_iterator_poll_next, atomic, atomic_mod, atomics, @@ -893,6 +895,7 @@ symbols! { instruction_set, integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below integral, + into_async_iter_into_iter, into_future, into_iter, intra_doc_pointers, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index b688c97311a..f3866f4904b 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1493,6 +1493,7 @@ supported_targets! { ("mips64-unknown-linux-muslabi64", mips64_unknown_linux_muslabi64), ("mips64el-unknown-linux-muslabi64", mips64el_unknown_linux_muslabi64), ("hexagon-unknown-linux-musl", hexagon_unknown_linux_musl), + ("hexagon-unknown-none-elf", hexagon_unknown_none_elf), ("mips-unknown-linux-uclibc", mips_unknown_linux_uclibc), ("mipsel-unknown-linux-uclibc", mipsel_unknown_linux_uclibc), diff --git a/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs new file mode 100644 index 00000000000..205f195a701 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs @@ -0,0 +1,27 @@ +use crate::spec::{PanicStrategy, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "hexagon-unknown-none-elf".into(), + pointer_width: 32, + data_layout: concat!( + "e-m:e-p:32:32:32-a:0-n16:32-i64:64:64-i32:32", + ":32-i16:16:16-i1:8:8-f32:32:32-f64:64:64-v32", + ":32:32-v64:64:64-v512:512:512-v1024:1024:1024-v2048", + ":2048:2048" + ) + .into(), + arch: "hexagon".into(), + + options: TargetOptions { + cpu: "hexagonv60".into(), + panic_strategy: PanicStrategy::Abort, + dynamic_linking: true, + features: "-small-data,+hvx-length128b".into(), + max_atomic_width: Some(32), + emit_debug_gdb_scripts: false, + c_enum_min_bits: Some(8), + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs index 257867b1b80..3fefd60f7cd 100644 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -116,7 +116,8 @@ impl Target { // Check dynamic linking stuff // BPF: when targeting user space vms (like rbpf), those can load dynamic libraries. - if self.os == "none" && self.arch != "bpf" { + // hexagon: when targeting QuRT, that OS can load dynamic libraries. + if self.os == "none" && (self.arch != "bpf" && self.arch != "hexagon") { assert!(!self.dynamic_linking); } if self.only_cdylib diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 13a09917c03..c6d029ddb65 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -477,7 +477,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { let mut vid_map: FxHashMap<RegionTarget<'cx>, RegionDeps<'cx>> = FxHashMap::default(); let mut finished_map = FxHashMap::default(); - for constraint in regions.constraints.keys() { + for (constraint, _) in ®ions.constraints { match constraint { &Constraint::VarSubVar(r1, r2) => { { diff --git a/library/core/src/async_iter/async_iter.rs b/library/core/src/async_iter/async_iter.rs index a8197332fd0..a7512ef6a24 100644 --- a/library/core/src/async_iter/async_iter.rs +++ b/library/core/src/async_iter/async_iter.rs @@ -47,6 +47,7 @@ pub trait AsyncIterator { /// Rust's usual rules apply: calls must never cause undefined behavior /// (memory corruption, incorrect use of `unsafe` functions, or the like), /// regardless of the async iterator's state. + #[cfg_attr(not(bootstrap), lang = "async_iterator_poll_next")] fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>; /// Returns the bounds on the remaining length of the async iterator. diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index cf3f5bc983e..57e63927c95 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -1001,6 +1001,7 @@ tool_doc!( "cargo-test-macro", "cargo-test-support", "cargo-util", + "cargo-util-schemas", "crates-io", "mdman", "rustfix", diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 800d8920951..3af370bf006 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -471,6 +471,11 @@ jobs: <<: *job-linux-4c - name: x86_64-gnu-integration + env: + # Only run this job on the nightly channel. Fuchsia requires + # nightly features to compile, and this job would fail if + # executed on beta and stable. + CI_ONLY_WHEN_CHANNEL: nightly <<: *job-linux-16c - name: x86_64-gnu-debug diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index fadd64a0353..1c8e1d1e316 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -38,6 +38,7 @@ - [\*-unknown-fuchsia](platform-support/fuchsia.md) - [\*-kmc-solid_\*](platform-support/kmc-solid.md) - [csky-unknown-linux-gnuabiv2\*](platform-support/csky-unknown-linux-gnuabiv2.md) + - [hexagon-unknown-none-elf](platform-support/hexagon-unknown-none-elf.md) - [loongarch\*-unknown-linux-\*](platform-support/loongarch-linux.md) - [loongarch\*-unknown-none\*](platform-support/loongarch-none.md) - [m68k-unknown-linux-gnu](platform-support/m68k-unknown-linux-gnu.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 59ef7441024..78c041ee511 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -265,6 +265,7 @@ target | std | host | notes `bpfel-unknown-none` | * | | BPF (little endian) `csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux (little endian) `csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian) +[`hexagon-unknown-none-elf`](platform-support/hexagon-unknown-none-elf.md)| * | Bare Hexagon (v60+, HVX) `hexagon-unknown-linux-musl` | ? | | `i386-apple-ios` | ✓ | | 32-bit x86 iOS [^x86_32-floats-return-ABI] [`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS [^x86_32-floats-return-ABI] diff --git a/src/doc/rustc/src/platform-support/hexagon-unknown-none-elf.md b/src/doc/rustc/src/platform-support/hexagon-unknown-none-elf.md new file mode 100644 index 00000000000..06423f0f8fa --- /dev/null +++ b/src/doc/rustc/src/platform-support/hexagon-unknown-none-elf.md @@ -0,0 +1,266 @@ +# `hexagon-unknown-none-elf` + +**Tier: 3** + +Rust for baremetal Hexagon DSPs. + +| Target | Descriptions | +| ------------------------ | ----------------------------------------- | +| hexagon-unknown-none-elf | Hexagon 32-bit (freestanding, hardfloat) | + +## Target maintainers + +- [Brian Cain](https://github.com/androm3da), `bcain@quicinc.com` + +## Requirements + +This target is cross-compiled. There is no support for `std`. There is no +default allocator, but it's possible to use `alloc` by supplying an allocator. + +By default, code generated with this target should run on Hexagon DSP hardware. + +- `-Ctarget-cpu=hexagonv73` adds support for instructions defined up to Hexagon V73. + +Functions marked `extern "C"` use the [Hexagon architecture calling convention](https://lists.llvm.org/pipermail/llvm-dev/attachments/20190916/21516a52/attachment-0001.pdf). + +This target generates PIC ELF binaries. + +## Building the target + +You can build Rust with support for the target by adding it to the `target` +list in `config.toml`: + +```toml +[build] +build-stage = 1 +host = ["<target for your host>"] +target = ["<target for your host>", "hexagon-unknown-none-elf"] + +[target.hexagon-unknown-none-elf] + +cc = "hexagon-unknown-none-elf-clang" +cxx = "hexagon-unknown-none-elf-clang++" +linker = "hexagon-unknown-none-elf-clang" +llvm-libunwind = 'in-tree' +``` + +Replace `<target for your host>` with `x86_64-unknown-linux-gnu` or whatever +else is appropriate for your host machine. + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. To compile for +this target, you will either need to build Rust with the target enabled (see +"Building the target" above), or build your own copy of `core` by using +`build-std` or similar. + +## Testing + +Since `hexagon-unknown-none-elf` supports a variety of different environments and +does not support `std`, this target does not support running the Rust test suite. + +## Cross-compilation toolchains and C code + +This target has been tested using `qemu-system-hexagon`. + +A common use case for `hexagon-unknown-none-elf` is building libraries that +link against C code and can be used in emulation or on a device with a +Hexagon DSP. + +The Hexagon SDK has libraries which are useful to link against when running +on a device. + + +# Standalone OS + +The script below will build an executable against "hexagon standalone OS" +which is suitable for emulation or bare-metal on-device testing. + +First, run `cargo new --bin demo1_hexagon` then add the source below as +`src/main.rs`. This program demonstrates the console output via semihosting. + +```rust,ignore (platform-specific,eh-personality-is-unstable) +#![no_std] +#![no_main] + +extern "C" { + fn putchar(ch: i32); + fn _exit(code: i32) -> !; +} + +#[no_mangle] +extern "C" fn main() -> i32 { + let message = "Hello, this is Rust!"; + for b in message.bytes() { + unsafe { + putchar(b as i32); + } + } + 0 +} + +#[panic_handler] +fn panic(_panic: &core::panic::PanicInfo) -> ! { + unsafe { + _exit(1); + } +} + +``` + +Next, save the script below as `build.sh` and edit it to suit your +environment. + +* `hex_toolchain` below refers to the [hexagon toolchain using exclusively +public open source repos](https://github.com/quic/toolchain_for_hexagon/releases). +* `cc` below refers to clang. You can use `clang` from your distribution, as +long as it's at least `clang-17`. Or you can use +`hexagon-unknown-none-elf-clang` from one of the [hexagon open source toolchain +releases](https://github.com/quic/toolchain_for_hexagon/releases). + +```sh +# Hexagon SDK, required for target libraries: +hex_sdk_root=/local/mnt/workspace/Qualcomm/Hexagon_SDK/5.3.0.0 +hex_sdk_toolchain=${hex_sdk_root}/tools/HEXAGON_Tools/8.6.06 + +sdk_libs=${hex_sdk_toolchain}/Tools/target/hexagon/lib +q6_arch=v65 +g0_lib_path=${sdk_libs}/${q6_arch}/G0 +pic_lib_path=${sdk_libs}/${q6_arch}/G0/pic + +cargo build --target=hexagon-unknown-none-elf -Zbuild-std + +# Builds an executable against "hexagon standalone OS" suitable for emulation: +${cc} --target=hexagon-unknown-none-elf -o testit \ + -fuse-ld=lld \ + -m${q6_arch} \ + -nodefaultlibs \ + -nostartfiles \ + ${g0_lib_path}/crt0_standalone.o \ + ${g0_lib_path}/crt0.o \ + ${g0_lib_path}/init.o \ + -L${sdk_libs}/${q6_arch}/ \ + -L${sdk_libs}/ \ + testit.c \ + target/hexagon-unknown-none-elf/debug/libmin_ex_lib_lin.rlib \ + target/hexagon-unknown-none-elf/debug/deps/libcore-*.rlib \ + target/hexagon-unknown-none-elf/debug/deps/libcompiler_builtins-*.rlib \ + -Wl,--start-group \ + -Wl,--defsym,_SDA_BASE_=0,--defsym,__sbss_start=0,--defsym,__sbss_end=0 \ + -lstandalone \ + ${g0_lib_path}/libc.a \ + -lgcc \ + -lc_eh \ + -Wl,--end-group \ + ${g0_lib_path}/fini.o \ + +${hex_toolchain}/x86_64-linux-gnu/bin/qemu-system-hexagon -monitor none -display none -kernel ./testit +``` + +# QuRT OS + +First, run `cargo new --lib demo2_hexagon` then add the source below as +`src/lib.rs`. This program demonstrates inline assembly and console output +via semihosting. + +```rust,ignore (platform-specific,eh-personality-is-unstable) +#![no_std] +#![no_main] +#![feature(lang_items)] +#![feature(asm_experimental_arch)] + +use core::arch::asm; + +extern "C" { + fn putchar(ch: i32); + fn _exit(code: i32) -> !; +} + +fn hexagon_specific() { + let mut buffer = [0_u8; 128]; + + unsafe { + let mut x = &buffer; + asm!( + "{{\n\t", + " v0=vmem({addr}+#0)\n\t", + " {tmp} = and({tmp}, #1)\n\t", + "}}\n\t", + addr = in(reg) x, + tmp = out(reg) _, + ); + } +} + +#[no_mangle] +extern "C" fn hello() -> i32 { + let message = "Hello, this is Rust!\n"; + for b in message.bytes() { + unsafe { + putchar(b as i32); + } + } + hexagon_specific(); + 0 +} + +#[panic_handler] +fn panic(_panic: &core::panic::PanicInfo) -> ! { + unsafe { + _exit(1); + } +} + +#[lang = "eh_personality"] +fn rust_eh_personality() {} + +``` + +Next, save the script below as `build.sh` and edit it to suit your +environment. The script below will build a shared object against the QuRT +RTOS which is suitable for emulation or on-device testing when loaded via +the fastrpc-shell. + + +```sh +# Hexagon SDK, required for target libraries: +hex_sdk_root=/local/mnt/workspace/Qualcomm/Hexagon_SDK/5.3.0.0 +hex_sdk_toolchain=${hex_sdk_root}/tools/HEXAGON_Tools/8.6.06 + +sdk_libs=${hex_sdk_toolchain}/Tools/target/hexagon/lib +q6_arch=v65 +g0_lib_path=${sdk_libs}/${q6_arch}/G0 +pic_lib_path=${sdk_libs}/${q6_arch}/G0/pic +runelf=${hex_sdk_root}/rtos/qurt/computev65/sdksim_bin/runelf.pbn +rmohs=${hex_sdk_root}/libs/run_main_on_hexagon/ship/hexagon_toolv86_${q6_arch}/run_main_on_hexagon_sim + +# Builds a library suitable for loading into "run_main_on_hexagon_sim" for +# emulation or frpc shell on real target: +${cc} --target=hexagon-unknown-none-elf -o testit.so \ + -fuse-ld=lld \ + -fPIC -shared \ + -nostdlib \ + -Wl,-Bsymbolic \ + -Wl,--wrap=malloc \ + -Wl,--wrap=calloc \ + -Wl,--wrap=free \ + -Wl,--wrap=realloc \ + -Wl,--wrap=memalign \ + -m${q6_arch} \ + testit.c \ + target/hexagon-unknown-none-elf/debug/libmin_ex_lib_lin.rlib \ + target/hexagon-unknown-none-elf/debug/deps/libcore-*.rlib \ + target/hexagon-unknown-none-elf/debug/deps/libcompiler_builtins-*.rlib \ + -Wl,-soname=testit \ + ${pic_lib_path}/libc.so + +# -Bsymbolic above for memory alloc funcs is necessary to access the heap on +# target, but otherwise not required. + +# multi-stage loader: runelf => run_main_on_hexagon_sim => testit.so{`main`} +${hex_toolchain}/x86_64-linux-gnu/bin/qemu-system-hexagon \ + -monitor none \ + -display none \ + -kernel ${runelf} \ + -append "${rmohs} -- ./testit.so" +``` diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index e692f4ef72e..9de547ba6dc 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -195,7 +195,7 @@ where // into a map. Each RegionTarget (either a RegionVid or a Region) maps // to its smaller and larger regions. Note that 'larger' regions correspond // to sub-regions in Rust code (e.g., in 'a: 'b, 'a is the larger region). - for constraint in regions.constraints.keys() { + for (constraint, _) in ®ions.constraints { match *constraint { Constraint::VarSubVar(r1, r2) => { { diff --git a/src/tools/cargo b/src/tools/cargo -Subproject 1a2666ddd14cf0a255d4ddb61c63531c259a7b3 +Subproject 363a2d11320faf531f6aacd1ea067c6bc08343b diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs index 4b9ab50e4fd..ff72b5e69ef 100644 --- a/src/tools/clippy/clippy_lints/src/needless_continue.rs +++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs @@ -220,7 +220,11 @@ where F: FnMut(&ast::Block, Option<&ast::Label>), { if let ast::ExprKind::While(_, loop_block, label) - | ast::ExprKind::ForLoop(_, _, loop_block, label) + | ast::ExprKind::ForLoop { + body: loop_block, + label, + .. + } | ast::ExprKind::Loop(loop_block, label, ..) = &expr.kind { func(loop_block, label.as_ref()); diff --git a/src/tools/clippy/clippy_lints/src/redundant_else.rs b/src/tools/clippy/clippy_lints/src/redundant_else.rs index 001686c84f8..fb434fb7450 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_else.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_else.rs @@ -111,7 +111,7 @@ impl<'ast> Visitor<'ast> for BreakVisitor { ExprKind::If(_, ref then, Some(ref els)) => self.check_block(then) && self.check_expr(els), ExprKind::If(_, _, None) // ignore loops for simplicity - | ExprKind::While(..) | ExprKind::ForLoop(..) | ExprKind::Loop(..) => false, + | ExprKind::While(..) | ExprKind::ForLoop { .. } | ExprKind::Loop(..) => false, _ => { walk_expr(self, expr); return; diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs index 8b9d9bade91..60e9d262e7e 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs @@ -554,7 +554,7 @@ fn ident_difference_expr_with_base_location( | (Closure(_), Closure(_)) | (Match(_, _), Match(_, _)) | (Loop(_, _, _), Loop(_, _, _)) - | (ForLoop(_, _, _, _), ForLoop(_, _, _, _)) + | (ForLoop { .. }, ForLoop { .. }) | (While(_, _, _), While(_, _, _)) | (If(_, _, _), If(_, _, _)) | (Let(_, _, _, _), Let(_, _, _, _)) diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index c271e498665..7fe76b946a4 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -169,9 +169,22 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Let(lp, le, _, _), Let(rp, re, _, _)) => eq_pat(lp, rp) && eq_expr(le, re), (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re), (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt), - (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => { - eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt) - }, + ( + ForLoop { + pat: lp, + iter: li, + body: lt, + label: ll, + kind: lk, + }, + ForLoop { + pat: rp, + iter: ri, + body: rt, + label: rl, + kind: rk, + }, + ) => eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt) && lk == rk, (Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll, rl) && eq_block(lt, rt), (Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb), (TryBlock(l), TryBlock(r)) => eq_block(l, r), diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 9b2bc8df1f3..c86362c427c 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -197,7 +197,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::Continue(..) | ast::ExprKind::Yield(..) | ast::ExprKind::Field(..) - | ast::ExprKind::ForLoop(..) + | ast::ExprKind::ForLoop { .. } | ast::ExprKind::Index(..) | ast::ExprKind::InlineAsm(..) | ast::ExprKind::OffsetOf(..) diff --git a/src/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs index f698f494ae5..5bf29441b54 100644 --- a/src/tools/rustfmt/src/closures.rs +++ b/src/tools/rustfmt/src/closures.rs @@ -448,7 +448,7 @@ fn is_block_closure_forced(context: &RewriteContext<'_>, expr: &ast::Expr) -> bo fn is_block_closure_forced_inner(expr: &ast::Expr, version: Version) -> bool { match expr.kind { - ast::ExprKind::If(..) | ast::ExprKind::While(..) | ast::ExprKind::ForLoop(..) => true, + ast::ExprKind::If(..) | ast::ExprKind::While(..) | ast::ExprKind::ForLoop { .. } => true, ast::ExprKind::Loop(..) if version == Version::Two => true, ast::ExprKind::AddrOf(_, _, ref expr) | ast::ExprKind::Try(ref expr) @@ -473,7 +473,7 @@ fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool { | ast::ExprKind::Block(..) | ast::ExprKind::While(..) | ast::ExprKind::Loop(..) - | ast::ExprKind::ForLoop(..) + | ast::ExprKind::ForLoop { .. } | ast::ExprKind::TryBlock(..) => false, _ => true, } diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index a68bd6694ba..7808f891336 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -3,7 +3,7 @@ use std::cmp::min; use itertools::Itertools; use rustc_ast::token::{Delimiter, Lit, LitKind}; -use rustc_ast::{ast, ptr, token}; +use rustc_ast::{ast, ptr, token, ForLoopKind}; use rustc_span::{BytePos, Span}; use crate::chains::rewrite_chain; @@ -134,7 +134,7 @@ pub(crate) fn format_expr( } ast::ExprKind::Let(ref pat, ref expr, _span, _) => rewrite_let(context, shape, pat, expr), ast::ExprKind::If(..) - | ast::ExprKind::ForLoop(..) + | ast::ExprKind::ForLoop { .. } | ast::ExprKind::Loop(..) | ast::ExprKind::While(..) => to_control_flow(expr, expr_type) .and_then(|control_flow| control_flow.rewrite(context, shape)), @@ -682,9 +682,15 @@ fn to_control_flow(expr: &ast::Expr, expr_type: ExprType) -> Option<ControlFlow< expr.span, )) } - ast::ExprKind::ForLoop(ref pat, ref cond, ref block, label) => { - Some(ControlFlow::new_for(pat, cond, block, label, expr.span)) - } + ast::ExprKind::ForLoop { + ref pat, + ref iter, + ref body, + label, + kind, + } => Some(ControlFlow::new_for( + pat, iter, body, label, expr.span, kind, + )), ast::ExprKind::Loop(ref block, label, _) => { Some(ControlFlow::new_loop(block, label, expr.span)) } @@ -771,6 +777,7 @@ impl<'a> ControlFlow<'a> { block: &'a ast::Block, label: Option<ast::Label>, span: Span, + kind: ForLoopKind, ) -> ControlFlow<'a> { ControlFlow { cond: Some(cond), @@ -778,7 +785,10 @@ impl<'a> ControlFlow<'a> { else_block: None, label, pat: Some(pat), - keyword: "for", + keyword: match kind { + ForLoopKind::For => "for", + ForLoopKind::ForAwait => "for await", + }, matcher: "", connector: " in", allow_single_line: false, @@ -1364,7 +1374,7 @@ pub(crate) fn can_be_overflowed_expr( || context.config.overflow_delimited_expr() } ast::ExprKind::If(..) - | ast::ExprKind::ForLoop(..) + | ast::ExprKind::ForLoop { .. } | ast::ExprKind::Loop(..) | ast::ExprKind::While(..) => { context.config.combine_control_expr() && context.use_block_indent() && args_len == 1 diff --git a/src/tools/rustfmt/src/matches.rs b/src/tools/rustfmt/src/matches.rs index ef509b56837..5a00984d4c0 100644 --- a/src/tools/rustfmt/src/matches.rs +++ b/src/tools/rustfmt/src/matches.rs @@ -591,7 +591,7 @@ fn can_flatten_block_around_this(body: &ast::Expr) -> bool { ast::ExprKind::If(..) => false, // We do not allow collapsing a block around expression with condition // to avoid it being cluttered with match arm. - ast::ExprKind::ForLoop(..) | ast::ExprKind::While(..) => false, + ast::ExprKind::ForLoop { .. } | ast::ExprKind::While(..) => false, ast::ExprKind::Loop(..) | ast::ExprKind::Match(..) | ast::ExprKind::Block(..) diff --git a/src/tools/rustfmt/src/overflow.rs b/src/tools/rustfmt/src/overflow.rs index d81bf24dbd1..f46583b1c89 100644 --- a/src/tools/rustfmt/src/overflow.rs +++ b/src/tools/rustfmt/src/overflow.rs @@ -409,7 +409,7 @@ impl<'a> Context<'a> { // When overflowing the expressions which consists of a control flow // expression, avoid condition to use multi line. ast::ExprKind::If(..) - | ast::ExprKind::ForLoop(..) + | ast::ExprKind::ForLoop { .. } | ast::ExprKind::Loop(..) | ast::ExprKind::While(..) | ast::ExprKind::Match(..) => { diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 7d7bbf11529..642b6603b1e 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -295,7 +295,7 @@ pub(crate) fn semicolon_for_stmt( ) -> bool { match stmt.kind { ast::StmtKind::Semi(ref expr) => match expr.kind { - ast::ExprKind::While(..) | ast::ExprKind::Loop(..) | ast::ExprKind::ForLoop(..) => { + ast::ExprKind::While(..) | ast::ExprKind::Loop(..) | ast::ExprKind::ForLoop { .. } => { false } ast::ExprKind::Break(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Ret(..) => { @@ -476,7 +476,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr | ast::ExprKind::ConstBlock(..) | ast::ExprKind::Gen(..) | ast::ExprKind::Loop(..) - | ast::ExprKind::ForLoop(..) + | ast::ExprKind::ForLoop { .. } | ast::ExprKind::TryBlock(..) | ast::ExprKind::Match(..) => repr.contains('\n'), ast::ExprKind::Paren(ref expr) diff --git a/tests/codegen/overaligned-constant.rs b/tests/codegen/overaligned-constant.rs index c94dfd85e7d..89e49738991 100644 --- a/tests/codegen/overaligned-constant.rs +++ b/tests/codegen/overaligned-constant.rs @@ -17,7 +17,7 @@ fn main() { // CHECK: [[full:%_.*]] = alloca %SmallStruct, align 8 // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[full]], ptr align 8 @0, i64 32, i1 false) // CHECK: %b.0 = load i32, ptr @0, align 4, - // CHECK: %b.1 = load i32, ptr getelementptr inbounds ({ i32, i32 }, ptr @0, i32 0, i32 1), align 4 + // CHECK: %b.1 = load i32, ptr getelementptr inbounds ({{.*}}), align 4 let mut s = S(1); s.0 = 3; diff --git a/tests/ui/async-await/feature-async-for-loop.rs b/tests/ui/async-await/feature-async-for-loop.rs new file mode 100644 index 00000000000..42247dd14b0 --- /dev/null +++ b/tests/ui/async-await/feature-async-for-loop.rs @@ -0,0 +1,23 @@ +// edition:2021 +// gate-test-async_for_loop + +#![feature(async_iter_from_iter, async_iterator)] + +fn f() { + let _ = async { + for await _i in core::async_iter::from_iter(0..3) { + //~^ ERROR `for await` loops are experimental + } + }; +} + +#[cfg(FALSE)] +fn g() { + let _ = async { + for await _i in core::async_iter::from_iter(0..3) { + //~^ ERROR `for await` loops are experimental + } + }; +} + +fn main() {} diff --git a/tests/ui/async-await/feature-async-for-loop.stderr b/tests/ui/async-await/feature-async-for-loop.stderr new file mode 100644 index 00000000000..38f75821772 --- /dev/null +++ b/tests/ui/async-await/feature-async-for-loop.stderr @@ -0,0 +1,21 @@ +error[E0658]: `for await` loops are experimental + --> $DIR/feature-async-for-loop.rs:8:13 + | +LL | for await _i in core::async_iter::from_iter(0..3) { + | ^^^^^ + | + = note: see issue #118898 <https://github.com/rust-lang/rust/issues/118898> for more information + = help: add `#![feature(async_for_loop)]` to the crate attributes to enable + +error[E0658]: `for await` loops are experimental + --> $DIR/feature-async-for-loop.rs:17:13 + | +LL | for await _i in core::async_iter::from_iter(0..3) { + | ^^^^^ + | + = note: see issue #118898 <https://github.com/rust-lang/rust/issues/118898> for more information + = help: add `#![feature(async_for_loop)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/async-await/for-await-2015.rs b/tests/ui/async-await/for-await-2015.rs new file mode 100644 index 00000000000..c1b7c016d1f --- /dev/null +++ b/tests/ui/async-await/for-await-2015.rs @@ -0,0 +1,10 @@ +// check-pass + +#![feature(async_for_loop)] + +// Make sure we don't break `for await` loops in the 2015 edition, where `await` was allowed as an +// identifier. + +fn main() { + for await in 0..3 {} +} diff --git a/tests/ui/async-await/for-await-consumes-iter.rs b/tests/ui/async-await/for-await-consumes-iter.rs new file mode 100644 index 00000000000..65bb9e88448 --- /dev/null +++ b/tests/ui/async-await/for-await-consumes-iter.rs @@ -0,0 +1,20 @@ +// edition: 2021 +#![feature(async_iterator, async_iter_from_iter, const_waker, async_for_loop, noop_waker)] + +use std::future::Future; + +// a test to make sure `for await` consumes the iterator + +async fn real_main() { + let iter = core::async_iter::from_iter(0..3); + let mut count = 0; + for await i in iter { + } + // make sure iter has been moved and we can't iterate over it again. + for await i in iter { + //~^ ERROR: use of moved value: `iter` + } +} + +fn main() { +} diff --git a/tests/ui/async-await/for-await-consumes-iter.stderr b/tests/ui/async-await/for-await-consumes-iter.stderr new file mode 100644 index 00000000000..48b2e8a51d8 --- /dev/null +++ b/tests/ui/async-await/for-await-consumes-iter.stderr @@ -0,0 +1,27 @@ +error[E0382]: use of moved value: `iter` + --> $DIR/for-await-consumes-iter.rs:14:20 + | +LL | let iter = core::async_iter::from_iter(0..3); + | ---- move occurs because `iter` has type `FromIter<std::ops::Range<i32>>`, which does not implement the `Copy` trait +LL | let mut count = 0; +LL | for await i in iter { + | ------------------- + | | | + | | value moved here + | inside of this loop +... +LL | for await i in iter { + | ^^^^ value used here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | for await i in iter.clone() { + | ++++++++ +help: borrow this binding in the pattern to avoid moving the value + | +LL | for await i in ref iter { + | +++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/async-await/for-await-passthrough.rs b/tests/ui/async-await/for-await-passthrough.rs new file mode 100644 index 00000000000..7fa133aaedc --- /dev/null +++ b/tests/ui/async-await/for-await-passthrough.rs @@ -0,0 +1,32 @@ +// run-pass +// edition: 2024 +// compile-flags: -Zunstable-options +#![feature(async_iterator, async_iter_from_iter, const_waker, async_for_loop, noop_waker, + gen_blocks)] + +use std::future::Future; + +async gen fn async_iter() -> i32 { + let iter = core::async_iter::from_iter(0..3); + for await i in iter { + yield i + 1; + } +} + +// make sure a simple for await loop works +async fn real_main() { + let mut count = 1; + for await i in async_iter() { + assert_eq!(i, count); + count += 1; + } + assert_eq!(count, 4); +} + +fn main() { + let future = real_main(); + let waker = std::task::Waker::noop(); + let mut cx = &mut core::task::Context::from_waker(&waker); + let mut future = core::pin::pin!(future); + while let core::task::Poll::Pending = future.as_mut().poll(&mut cx) {} +} diff --git a/tests/ui/async-await/for-await.rs b/tests/ui/async-await/for-await.rs new file mode 100644 index 00000000000..6345ceb0c27 --- /dev/null +++ b/tests/ui/async-await/for-await.rs @@ -0,0 +1,24 @@ +// run-pass +// edition: 2021 +#![feature(async_iterator, async_iter_from_iter, const_waker, async_for_loop, noop_waker)] + +use std::future::Future; + +// make sure a simple for await loop works +async fn real_main() { + let iter = core::async_iter::from_iter(0..3); + let mut count = 0; + for await i in iter { + assert_eq!(i, count); + count += 1; + } + assert_eq!(count, 3); +} + +fn main() { + let future = real_main(); + let waker = std::task::Waker::noop(); + let mut cx = &mut core::task::Context::from_waker(&waker); + let mut future = core::pin::pin!(future); + while let core::task::Poll::Pending = future.as_mut().poll(&mut cx) {} +} diff --git a/tests/ui/entry-point/return-ty-has-bound-vars.rs b/tests/ui/entry-point/return-ty-has-bound-vars.rs new file mode 100644 index 00000000000..0995ce06160 --- /dev/null +++ b/tests/ui/entry-point/return-ty-has-bound-vars.rs @@ -0,0 +1,3 @@ +// issue-119209 + +fn main<'a>(_: &'a i32) -> &'a () { &() } //~ERROR `main` function return type is not allowed to have generic parameters diff --git a/tests/ui/entry-point/return-ty-has-bound-vars.stderr b/tests/ui/entry-point/return-ty-has-bound-vars.stderr new file mode 100644 index 00000000000..e7aab839f31 --- /dev/null +++ b/tests/ui/entry-point/return-ty-has-bound-vars.stderr @@ -0,0 +1,9 @@ +error[E0131]: `main` function return type is not allowed to have generic parameters + --> $DIR/return-ty-has-bound-vars.rs:3:28 + | +LL | fn main<'a>(_: &'a i32) -> &'a () { &() } + | ^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0131`. diff --git a/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr b/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr index d0892fd8b09..c25e731d962 100644 --- a/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr +++ b/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr @@ -4,8 +4,8 @@ error[E0308]: mismatched types LL | assert_all::<_, &String>(id); | ^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other | - = note: expected reference `&String` - found reference `&String` + = note: expected trait `for<'a> <for<'a> fn(&'a String) -> &'a String {id} as FnMut<(&'a String,)>>` + found trait `for<'a> <for<'a> fn(&'a String) -> &'a String {id} as FnMut<(&'a String,)>>` error: aborting due to 1 previous error diff --git a/tests/ui/pattern/usefulness/impl-trait.rs b/tests/ui/pattern/usefulness/impl-trait.rs new file mode 100644 index 00000000000..6e8c0feb710 --- /dev/null +++ b/tests/ui/pattern/usefulness/impl-trait.rs @@ -0,0 +1,146 @@ +#![feature(never_type)] +#![feature(exhaustive_patterns)] +#![feature(type_alias_impl_trait)] +#![feature(non_exhaustive_omitted_patterns_lint)] +#![deny(unreachable_patterns)] +// Test that the lint traversal handles opaques correctly +#![deny(non_exhaustive_omitted_patterns)] + +fn main() {} + +#[derive(Copy, Clone)] +enum Void {} + +fn return_never_rpit(x: Void) -> impl Copy { + if false { + match return_never_rpit(x) { + _ => {} //~ ERROR unreachable + } + } + x +} +fn friend_of_return_never_rpit(x: Void) { + match return_never_rpit(x) {} + //~^ ERROR non-empty +} + +type T = impl Copy; +fn return_never_tait(x: Void) -> T { + if false { + match return_never_tait(x) { + _ => {} //~ ERROR unreachable + } + } + x +} +fn friend_of_return_never_tait(x: Void) { + match return_never_tait(x) {} + //~^ ERROR non-empty +} + +fn option_never(x: Void) -> Option<impl Copy> { + if false { + match option_never(x) { + None => {} + Some(_) => {} //~ ERROR unreachable + } + match option_never(x) { + None => {} + // FIXME: Unreachable not detected because `is_uninhabited` does not look into opaque + // types. + _ => {} + } + } + Some(x) +} + +fn option_never2(x: Void) -> impl Copy { + if false { + match option_never2(x) { + None => {} + Some(_) => {} //~ ERROR unreachable + } + match option_never2(x) { + None => {} + _ => {} //~ ERROR unreachable + } + match option_never2(x) { + None => {} + } + } + Some(x) +} + +fn inner_never(x: Void) { + type T = impl Copy; + let y: T = x; + match y { + _ => {} //~ ERROR unreachable + } +} + +// This one caused ICE https://github.com/rust-lang/rust/issues/117100. +fn inner_tuple() { + type T = impl Copy; + let foo: T = Some((1u32, 2u32)); + match foo { + _ => {} + Some((a, b)) => {} //~ ERROR unreachable + } +} + +type U = impl Copy; +fn unify_never(x: Void, u: U) -> U { + if false { + match u { + _ => {} //~ ERROR unreachable + } + } + x +} + +type V = impl Copy; +fn infer_in_match(x: Option<V>) { + match x { + None => {} + Some((a, b)) => {} + Some((mut x, mut y)) => { + //~^ ERROR unreachable + x = 42; + y = "foo"; + } + } +} + +type W = impl Copy; +#[derive(Copy, Clone)] +struct Rec<'a> { + n: u32, + w: Option<&'a W>, +} +fn recursive_opaque() -> W { + if false { + match recursive_opaque() { + // Check for the ol' ICE when the type is recursively opaque. + _ => {} + Rec { n: 0, w: Some(Rec { n: 0, w: _ }) } => {} //~ ERROR unreachable + } + } + let w: Option<&'static W> = None; + Rec { n: 0, w } +} + +type X = impl Copy; +struct SecretelyVoid(X); +fn nested_empty_opaque(x: Void) -> X { + if false { + let opaque_void = nested_empty_opaque(x); + let secretely_void = SecretelyVoid(opaque_void); + match secretely_void { + // FIXME: Unreachable not detected because `is_uninhabited` does not look into opaque + // types. + _ => {} + } + } + x +} diff --git a/tests/ui/pattern/usefulness/impl-trait.stderr b/tests/ui/pattern/usefulness/impl-trait.stderr new file mode 100644 index 00000000000..df6d43dbb3f --- /dev/null +++ b/tests/ui/pattern/usefulness/impl-trait.stderr @@ -0,0 +1,101 @@ +error: unreachable pattern + --> $DIR/impl-trait.rs:17:13 + | +LL | _ => {} + | ^ + | +note: the lint level is defined here + --> $DIR/impl-trait.rs:5:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/impl-trait.rs:31:13 + | +LL | _ => {} + | ^ + +error: unreachable pattern + --> $DIR/impl-trait.rs:45:13 + | +LL | Some(_) => {} + | ^^^^^^^ + +error: unreachable pattern + --> $DIR/impl-trait.rs:61:13 + | +LL | Some(_) => {} + | ^^^^^^^ + +error: unreachable pattern + --> $DIR/impl-trait.rs:65:13 + | +LL | _ => {} + | ^ + +error: unreachable pattern + --> $DIR/impl-trait.rs:78:9 + | +LL | _ => {} + | ^ + +error: unreachable pattern + --> $DIR/impl-trait.rs:88:9 + | +LL | _ => {} + | - matches any value +LL | Some((a, b)) => {} + | ^^^^^^^^^^^^ unreachable pattern + +error: unreachable pattern + --> $DIR/impl-trait.rs:96:13 + | +LL | _ => {} + | ^ + +error: unreachable pattern + --> $DIR/impl-trait.rs:107:9 + | +LL | Some((mut x, mut y)) => { + | ^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/impl-trait.rs:126:13 + | +LL | _ => {} + | - matches any value +LL | Rec { n: 0, w: Some(Rec { n: 0, w: _ }) } => {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern + +error[E0004]: non-exhaustive patterns: type `impl Copy` is non-empty + --> $DIR/impl-trait.rs:23:11 + | +LL | match return_never_rpit(x) {} + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: the matched value is of type `impl Copy` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown + | +LL ~ match return_never_rpit(x) { +LL + _ => todo!(), +LL + } + | + +error[E0004]: non-exhaustive patterns: type `T` is non-empty + --> $DIR/impl-trait.rs:37:11 + | +LL | match return_never_tait(x) {} + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: the matched value is of type `T` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown + | +LL ~ match return_never_tait(x) { +LL + _ => todo!(), +LL + } + | + +error: aborting due to 12 previous errors + +For more information about this error, try `rustc --explain E0004`. |
