From 7d45a75ec3c371c31744d5982283e9cf422e1894 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 17 Aug 2022 11:45:16 +0800 Subject: [PATCH 1/6] get_attr should check that no duplicates are allowed --- compiler/rustc_codegen_llvm/src/attributes.rs | 3 ++- compiler/rustc_feature/src/builtin_attrs.rs | 8 ++++++++ compiler/rustc_feature/src/lib.rs | 4 ++-- compiler/rustc_middle/src/ty/mod.rs | 6 +++++- compiler/rustc_typeck/src/check/check.rs | 2 +- src/test/ui/issues/issue-100631.rs | 8 ++++++++ src/test/ui/issues/issue-100631.stderr | 12 ++++++++++++ 7 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/issues/issue-100631.rs create mode 100644 src/test/ui/issues/issue-100631.stderr diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index aabbe8ac276d7..b38684a63e410 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -386,7 +386,8 @@ pub fn from_fn_attrs<'ll, 'tcx>( ) { let span = cx .tcx - .get_attr(instance.def_id(), sym::target_feature) + .get_attrs(instance.def_id(), sym::target_feature) + .next() .map_or_else(|| cx.tcx.def_span(instance.def_id()), |a| a.span); let msg = format!( "the target features {} must all be either enabled or disabled together", diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index ee46a364546a9..f1a44dd67d886 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -825,6 +825,14 @@ pub fn is_builtin_only_local(name: Symbol) -> bool { BUILTIN_ATTRIBUTE_MAP.get(&name).map_or(false, |attr| attr.only_local) } +pub fn is_valid_for_get_attr(name: Symbol) -> bool { + BUILTIN_ATTRIBUTE_MAP.get(&name).map_or(false, |attr| match attr.duplicates { + WarnFollowing | ErrorFollowing | ErrorPreceding | FutureWarnFollowing + | FutureWarnPreceding => true, + DuplicatesOk | WarnFollowingWordOnly => false, + }) +} + pub static BUILTIN_ATTRIBUTE_MAP: LazyLock> = LazyLock::new(|| { let mut map = FxHashMap::default(); diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index efb83052768bc..ca293338fe323 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -149,7 +149,7 @@ pub use active::{Features, ACTIVE_FEATURES, INCOMPATIBLE_FEATURES}; pub use builtin_attrs::AttributeDuplicates; pub use builtin_attrs::{ deprecated_attributes, find_gated_cfg, is_builtin_attr_name, is_builtin_only_local, - AttributeGate, AttributeTemplate, AttributeType, BuiltinAttribute, GatedCfg, - BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP, + is_valid_for_get_attr, AttributeGate, AttributeTemplate, AttributeType, BuiltinAttribute, + GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP, }; pub use removed::{REMOVED_FEATURES, STABLE_REMOVED_FEATURES}; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index a7833ab64310f..4456958c99adc 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2280,7 +2280,11 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn get_attr(self, did: DefId, attr: Symbol) -> Option<&'tcx ast::Attribute> { - self.get_attrs(did, attr).next() + if cfg!(debug_assertions) && !rustc_feature::is_valid_for_get_attr(attr) { + bug!("get_attr: unexpected called with DefId `{:?}`, attr `{:?}`", did, attr); + } else { + self.get_attrs(did, attr).next() + } } /// Determines whether an item is annotated with an attribute. diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 6e3ef15f88433..9747017ee4e09 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -1464,7 +1464,7 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L def.destructor(tcx); // force the destructor to be evaluated if vs.is_empty() { - if let Some(attr) = tcx.get_attr(def_id.to_def_id(), sym::repr) { + if let Some(attr) = tcx.get_attrs(def_id.to_def_id(), sym::repr).next() { struct_span_err!( tcx.sess, attr.span, diff --git a/src/test/ui/issues/issue-100631.rs b/src/test/ui/issues/issue-100631.rs new file mode 100644 index 0000000000000..0fefcf83fd516 --- /dev/null +++ b/src/test/ui/issues/issue-100631.rs @@ -0,0 +1,8 @@ +// issue #100631, make sure `TyCtxt::get_attr` only called by case that compiler +// can reasonably deal with multiple attributes. +// `repr` will use `TyCtxt::get_attrs` since it's `DuplicatesOk`. +#[repr(C)] //~ ERROR: unsupported representation for zero-variant enum [E0084] +#[repr(C)] +enum Foo {} + +fn main() {} diff --git a/src/test/ui/issues/issue-100631.stderr b/src/test/ui/issues/issue-100631.stderr new file mode 100644 index 0000000000000..caa5351ddc7aa --- /dev/null +++ b/src/test/ui/issues/issue-100631.stderr @@ -0,0 +1,12 @@ +error[E0084]: unsupported representation for zero-variant enum + --> $DIR/issue-100631.rs:4:1 + | +LL | #[repr(C)] + | ^^^^^^^^^^ +LL | #[repr(C)] +LL | enum Foo {} + | -------- zero-variant enum + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0084`. From 91674cc56c2ce35097cf44262096c6ecbe067194 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 3 Sep 2022 05:39:46 +0000 Subject: [PATCH 2/6] Suggest removing unnecessary prefix let in patterns --- compiler/rustc_ast/src/token.rs | 24 +++++++++++++++++++ .../locales/en-US/parser.ftl | 3 +++ .../rustc_parse/src/parser/diagnostics.rs | 8 +++++++ compiler/rustc_parse/src/parser/pat.rs | 9 ++++++- src/test/ui/parser/unnecessary-let.rs | 11 +++++++++ src/test/ui/parser/unnecessary-let.stderr | 20 ++++++++++++++++ 6 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/parser/unnecessary-let.rs create mode 100644 src/test/ui/parser/unnecessary-let.stderr diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index dd98946b4cc5e..bfc4db32d3751 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -398,6 +398,30 @@ impl Token { } } + /// Returns `true` if the token can appear at the start of an pattern. + /// + /// Shamelessly borrowed from `can_begin_expr`, only used for diagnostics right now. + pub fn can_begin_pattern(&self) -> bool { + match self.uninterpolate().kind { + Ident(name, is_raw) => + ident_can_begin_expr(name, self.span, is_raw), // value name or keyword + | OpenDelim(Delimiter::Bracket | Delimiter::Parenthesis) // tuple or array + | Literal(..) // literal + | BinOp(Minus) // unary minus + | BinOp(And) // reference + | AndAnd // double reference + // DotDotDot is no longer supported + | DotDot | DotDotDot | DotDotEq // ranges + | Lt | BinOp(Shl) // associated path + | ModSep => true, // global path + Interpolated(ref nt) => matches!(**nt, NtLiteral(..) | + NtPat(..) | + NtBlock(..) | + NtPath(..)), + _ => false, + } + } + /// Returns `true` if the token can appear at the start of a type. pub fn can_begin_type(&self) -> bool { match self.uninterpolate().kind { diff --git a/compiler/rustc_error_messages/locales/en-US/parser.ftl b/compiler/rustc_error_messages/locales/en-US/parser.ftl index 3b37a393846b2..bc17754c56ae3 100644 --- a/compiler/rustc_error_messages/locales/en-US/parser.ftl +++ b/compiler/rustc_error_messages/locales/en-US/parser.ftl @@ -150,3 +150,6 @@ parser_dotdotdot = unexpected token: `...` parser_left_arrow_operator = unexpected token: `<-` .suggestion = if you meant to write a comparison against a negative value, add a space in between `<` and `-` + +parser_remove_let = expected pattern, found `let` + .suggestion = remove the unnecessary `let` keyword diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index dd806e2130e9b..a299ee0a1046a 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -705,6 +705,14 @@ pub(crate) struct LeftArrowOperator { pub span: Span, } +#[derive(SessionDiagnostic)] +#[diag(parser::remove_let)] +pub(crate) struct RemoveLet { + #[primary_span] + #[suggestion(applicability = "machine-applicable", code = "")] + pub span: Span, +} + // SnapshotParser is used to create a snapshot of the parser // without causing duplicate errors being emitted when the `Parser` // is dropped. diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 8b3200d45fccd..6c8f3f664e253 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1,4 +1,5 @@ use super::{ForceCollect, Parser, PathStyle, TrailingToken}; +use crate::parser::diagnostics::RemoveLet; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor}; use rustc_ast::ptr::P; @@ -320,7 +321,13 @@ impl<'a> Parser<'a> { maybe_recover_from_interpolated_ty_qpath!(self, true); maybe_whole!(self, NtPat, |x| x); - let lo = self.token.span; + let mut lo = self.token.span; + + if self.token.is_keyword(kw::Let) && self.look_ahead(1, |tok| tok.can_begin_pattern()) { + self.bump(); + self.sess.emit_err(RemoveLet { span: lo }); + lo = self.token.span; + } let pat = if self.check(&token::BinOp(token::And)) || self.token.kind == token::AndAnd { self.parse_pat_deref(expected)? diff --git a/src/test/ui/parser/unnecessary-let.rs b/src/test/ui/parser/unnecessary-let.rs new file mode 100644 index 0000000000000..6279109621d70 --- /dev/null +++ b/src/test/ui/parser/unnecessary-let.rs @@ -0,0 +1,11 @@ +fn main() { + for let x of [1, 2, 3] {} + //~^ ERROR expected pattern, found `let` + //~| ERROR missing `in` in `for` loop + + match 1 { + let 1 => {} + //~^ ERROR expected pattern, found `let` + _ => {} + } +} diff --git a/src/test/ui/parser/unnecessary-let.stderr b/src/test/ui/parser/unnecessary-let.stderr new file mode 100644 index 0000000000000..952119cae3e27 --- /dev/null +++ b/src/test/ui/parser/unnecessary-let.stderr @@ -0,0 +1,20 @@ +error: expected pattern, found `let` + --> $DIR/unnecessary-let.rs:2:9 + | +LL | for let x of [1, 2, 3] {} + | ^^^ help: remove the unnecessary `let` keyword + +error: missing `in` in `for` loop + --> $DIR/unnecessary-let.rs:2:15 + | +LL | for let x of [1, 2, 3] {} + | ^^ help: try using `in` here instead + +error: expected pattern, found `let` + --> $DIR/unnecessary-let.rs:7:9 + | +LL | let 1 => {} + | ^^^ help: remove the unnecessary `let` keyword + +error: aborting due to 3 previous errors + From f5cd2230069d2029eea67cf10b85b0889d16b3cb Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sun, 4 Sep 2022 21:11:31 +0200 Subject: [PATCH 3/6] Revert "Mention rust-analyzer maintainers when `proc_macro` bridge is changed" --- triagebot.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index 89d1574726f09..89edab91fcd70 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -316,9 +316,6 @@ Examples of `T-libs-api` changes: * Changing observable runtime behavior of library APIs """ -[mentions."library/proc_macro/src/bridge"] -cc = ["@rust-lang/wg-rls-2"] - [mentions."src/librustdoc/clean/types.rs"] cc = ["@camelid"] From 3b3c7063c23d43b30077fb5f997456e60e5806a6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 4 Sep 2022 20:48:39 +0000 Subject: [PATCH 4/6] Point at type parameter in plain path expr --- .../rustc_typeck/src/check/fn_ctxt/checks.rs | 20 +++++++++---------- .../point-at-type-param-in-path-expr.rs | 6 ++++++ .../point-at-type-param-in-path-expr.stderr | 17 ++++++++++++++++ 3 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/typeck/point-at-type-param-in-path-expr.rs create mode 100644 src/test/ui/typeck/point-at-type-param-in-path-expr.stderr diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index ce0e59d062266..2f6d0b1936863 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -1780,16 +1780,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return true; } } - // Notably, we only point to params that are local to the - // item we're checking, since those are the ones we are able - // to look in the final `hir::PathSegment` for. Everything else - // would require a deeper search into the `qpath` than I think - // is worthwhile. - if let Some(param_to_point_at) = param_to_point_at - && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath) - { - return true; - } + } + // Notably, we only point to params that are local to the + // item we're checking, since those are the ones we are able + // to look in the final `hir::PathSegment` for. Everything else + // would require a deeper search into the `qpath` than I think + // is worthwhile. + if let Some(param_to_point_at) = param_to_point_at + && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath) + { + return true; } } hir::ExprKind::MethodCall(segment, args, ..) => { diff --git a/src/test/ui/typeck/point-at-type-param-in-path-expr.rs b/src/test/ui/typeck/point-at-type-param-in-path-expr.rs new file mode 100644 index 0000000000000..9a21536f9b12b --- /dev/null +++ b/src/test/ui/typeck/point-at-type-param-in-path-expr.rs @@ -0,0 +1,6 @@ +fn foo() {} + +fn main() { + let x = foo::<()>; + //~^ ERROR `()` doesn't implement `std::fmt::Display` +} diff --git a/src/test/ui/typeck/point-at-type-param-in-path-expr.stderr b/src/test/ui/typeck/point-at-type-param-in-path-expr.stderr new file mode 100644 index 0000000000000..1feaa0508bfa7 --- /dev/null +++ b/src/test/ui/typeck/point-at-type-param-in-path-expr.stderr @@ -0,0 +1,17 @@ +error[E0277]: `()` doesn't implement `std::fmt::Display` + --> $DIR/point-at-type-param-in-path-expr.rs:4:19 + | +LL | let x = foo::<()>; + | ^^ `()` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `()` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `foo` + --> $DIR/point-at-type-param-in-path-expr.rs:1:11 + | +LL | fn foo() {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From 774cadfbfa3a1b0ae3a0f8b142b556124fd552b5 Mon Sep 17 00:00:00 2001 From: joboet Date: Mon, 5 Sep 2022 09:08:07 +0200 Subject: [PATCH 5/6] std: fix cleanup for uninitialized stdout (#101375) --- library/std/src/io/stdio.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index dd4ff4952fde9..91cff3217d21b 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -607,15 +607,24 @@ pub fn stdout() -> Stdout { } } +// Flush the data and disable buffering during shutdown +// by replacing the line writer by one with zero +// buffering capacity. pub fn cleanup() { - // Flush the data and disable buffering during shutdown - // by replacing the line writer by one with zero - // buffering capacity. - // We use try_lock() instead of lock(), because someone - // might have leaked a StdoutLock, which would - // otherwise cause a deadlock here. - if let Some(lock) = STDOUT.get().and_then(ReentrantMutex::try_lock) { - *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw()); + let mut initialized = false; + let stdout = STDOUT.get_or_init(|| { + initialized = true; + ReentrantMutex::new(RefCell::new(LineWriter::with_capacity(0, stdout_raw()))) + }); + + if !initialized { + // The buffer was previously initialized, overwrite it here. + // We use try_lock() instead of lock(), because someone + // might have leaked a StdoutLock, which would + // otherwise cause a deadlock here. + if let Some(lock) = stdout.try_lock() { + *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw()); + } } } From 2d8460ef43d902f34ba2133fe38f66ee8d2fdafc Mon Sep 17 00:00:00 2001 From: est31 Date: Fri, 15 Jul 2022 19:17:10 +0200 Subject: [PATCH 6/6] Add matrix based test for documenting the let / let else temporary drop order The drop order of let and let else is supposed to be the same, and in order to ensure this, the test checks that this holds for the given list of cases. The test also ensures that we drop the temporaries of the condition before executing the else block. We made the test matrix based so it can check all the possible combinations and find out possible edge cases. --- src/test/ui/let-else/let-else-drop-order.rs | 270 ++++++++++++++++++ .../let-else/let-else-drop-order.run.stdout | 51 ++++ 2 files changed, 321 insertions(+) create mode 100644 src/test/ui/let-else/let-else-drop-order.rs create mode 100644 src/test/ui/let-else/let-else-drop-order.run.stdout diff --git a/src/test/ui/let-else/let-else-drop-order.rs b/src/test/ui/let-else/let-else-drop-order.rs new file mode 100644 index 0000000000000..0054f3d4182ec --- /dev/null +++ b/src/test/ui/let-else/let-else-drop-order.rs @@ -0,0 +1,270 @@ +// run-pass +// edition:2021 +// check-run-results +// +// Drop order tests for let else +// +// Mostly this ensures two things: +// 1. That let and let else temporary drop order is the same. +// This is a specific design request: https://github.com/rust-lang/rust/pull/93628#issuecomment-1047140316 +// 2. That the else block truly only runs after the +// temporaries have dropped. +// +// We also print some nice tables for an overview by humans. +// Changes in those tables are considered breakages, but the +// important properties 1 and 2 are also enforced by the code. +// This is important as it's easy to update the stdout file +// with a --bless and miss the impact of that change. + +#![feature(let_else)] +#![allow(irrefutable_let_patterns)] + +use std::cell::RefCell; +use std::rc::Rc; + +#[derive(Clone)] +struct DropAccountant(Rc>>>); + +impl DropAccountant { + fn new() -> Self { + Self(Default::default()) + } + fn build_droppy(&self, v: u32) -> Droppy { + Droppy(self.clone(), v) + } + fn build_droppy_enum_none(&self, _v: u32) -> ((), DroppyEnum) { + ((), DroppyEnum::None(self.clone())) + } + fn new_list(&self, s: impl ToString) { + self.0.borrow_mut().push(vec![s.to_string()]); + } + fn push(&self, s: impl ToString) { + let s = s.to_string(); + let mut accounts = self.0.borrow_mut(); + accounts.last_mut().unwrap().push(s); + } + fn print_table(&self) { + println!(); + + let accounts = self.0.borrow(); + let before_last = &accounts[accounts.len() - 2]; + let last = &accounts[accounts.len() - 1]; + let before_last = get_comma_list(before_last); + let last = get_comma_list(last); + const LINES: &[&str] = &[ + "vanilla", + "&", + "&mut", + "move", + "fn(this)", + "tuple", + "array", + "ref &", + "ref mut &mut", + ]; + let max_len = LINES.iter().map(|v| v.len()).max().unwrap(); + let max_len_before = before_last.iter().map(|v| v.len()).max().unwrap(); + let max_len_last = last.iter().map(|v| v.len()).max().unwrap(); + + println!( + "| {: Vec { + std::iter::once(sl[0].clone()) + .chain(sl[1..].chunks(2).map(|c| c.join(","))) + .collect::>() +} + +struct Droppy(DropAccountant, T); + +impl Drop for Droppy { + fn drop(&mut self) { + self.0.push("drop"); + } +} + +#[allow(dead_code)] +enum DroppyEnum { + Some(DropAccountant, T), + None(DropAccountant), +} + +impl Drop for DroppyEnum { + fn drop(&mut self) { + match self { + DroppyEnum::Some(acc, _inner) => acc, + DroppyEnum::None(acc) => acc, + } + .push("drop"); + } +} + +macro_rules! nestings_with { + ($construct:ident, $binding:pat, $exp:expr) => { + // vanilla: + $construct!($binding, $exp.1); + + // &: + $construct!(&$binding, &$exp.1); + + // &mut: + $construct!(&mut $binding, &mut ($exp.1)); + + { + // move: + let w = $exp; + $construct!( + $binding, + { + let w = w; + w + } + .1 + ); + } + + // fn(this): + $construct!($binding, std::convert::identity($exp).1); + }; +} + +macro_rules! nestings { + ($construct:ident, $binding:pat, $exp:expr) => { + nestings_with!($construct, $binding, $exp); + + // tuple: + $construct!(($binding, 77), ($exp.1, 77)); + + // array: + $construct!([$binding], [$exp.1]); + }; +} + +macro_rules! let_else { + ($acc:expr, $v:expr, $binding:pat, $build:ident) => { + let acc = $acc; + let v = $v; + + macro_rules! let_else_construct { + ($arg:pat, $exp:expr) => { + loop { + let $arg = $exp else { + acc.push("else"); + break; + }; + acc.push("body"); + break; + } + }; + } + nestings!(let_else_construct, $binding, acc.$build(v)); + // ref &: + let_else_construct!($binding, &acc.$build(v).1); + + // ref mut &mut: + let_else_construct!($binding, &mut acc.$build(v).1); + }; +} + +macro_rules! let_ { + ($acc:expr, $binding:tt) => { + let acc = $acc; + + macro_rules! let_construct { + ($arg:pat, $exp:expr) => {{ + let $arg = $exp; + acc.push("body"); + }}; + } + let v = 0; + { + nestings_with!(let_construct, $binding, acc.build_droppy(v)); + } + acc.push("n/a"); + acc.push("n/a"); + acc.push("n/a"); + acc.push("n/a"); + + // ref &: + let_construct!($binding, &acc.build_droppy(v).1); + + // ref mut &mut: + let_construct!($binding, &mut acc.build_droppy(v).1); + }; +} + +fn main() { + let acc = DropAccountant::new(); + + println!(" --- matching cases ---"); + + // Ensure that let and let else have the same behaviour + acc.new_list("let _"); + let_!(&acc, _); + acc.new_list("let else _"); + let_else!(&acc, 0, _, build_droppy); + acc.assert_equality_last_two_lists(); + acc.print_table(); + + // Ensure that let and let else have the same behaviour + acc.new_list("let _v"); + let_!(&acc, _v); + acc.new_list("let else _v"); + let_else!(&acc, 0, _v, build_droppy); + acc.assert_equality_last_two_lists(); + acc.print_table(); + + println!(); + + println!(" --- mismatching cases ---"); + + acc.new_list("let else _ mismatch"); + let_else!(&acc, 1, DroppyEnum::Some(_, _), build_droppy_enum_none); + acc.new_list("let else _v mismatch"); + let_else!(&acc, 1, DroppyEnum::Some(_, _v), build_droppy_enum_none); + acc.print_table(); + // This ensures that we always drop before visiting the else case + acc.assert_all_equal_to("drop,else"); + + acc.new_list("let else 0 mismatch"); + let_else!(&acc, 1, 0, build_droppy); + acc.new_list("let else 0 mismatch"); + let_else!(&acc, 1, 0, build_droppy); + acc.print_table(); + // This ensures that we always drop before visiting the else case + acc.assert_all_equal_to("drop,else"); +} diff --git a/src/test/ui/let-else/let-else-drop-order.run.stdout b/src/test/ui/let-else/let-else-drop-order.run.stdout new file mode 100644 index 0000000000000..01cf2f73e17c9 --- /dev/null +++ b/src/test/ui/let-else/let-else-drop-order.run.stdout @@ -0,0 +1,51 @@ + --- matching cases --- + +| construct | let _ | let else _ | +| ------------ | --------- | ---------- | +| vanilla | drop,body | drop,body | +| & | body,drop | body,drop | +| &mut | body,drop | body,drop | +| move | drop,body | drop,body | +| fn(this) | drop,body | drop,body | +| tuple | n/a,n/a | drop,body | +| array | n/a,n/a | drop,body | +| ref & | body,drop | body,drop | +| ref mut &mut | body,drop | body,drop | + +| construct | let _v | let else _v | +| ------------ | --------- | ----------- | +| vanilla | drop,body | drop,body | +| & | body,drop | body,drop | +| &mut | body,drop | body,drop | +| move | drop,body | drop,body | +| fn(this) | drop,body | drop,body | +| tuple | n/a,n/a | drop,body | +| array | n/a,n/a | drop,body | +| ref & | body,drop | body,drop | +| ref mut &mut | body,drop | body,drop | + + --- mismatching cases --- + +| construct | let else _ mismatch | let else _v mismatch | +| ------------ | ------------------- | -------------------- | +| vanilla | drop,else | drop,else | +| & | drop,else | drop,else | +| &mut | drop,else | drop,else | +| move | drop,else | drop,else | +| fn(this) | drop,else | drop,else | +| tuple | drop,else | drop,else | +| array | drop,else | drop,else | +| ref & | drop,else | drop,else | +| ref mut &mut | drop,else | drop,else | + +| construct | let else 0 mismatch | let else 0 mismatch | +| ------------ | ------------------- | ------------------- | +| vanilla | drop,else | drop,else | +| & | drop,else | drop,else | +| &mut | drop,else | drop,else | +| move | drop,else | drop,else | +| fn(this) | drop,else | drop,else | +| tuple | drop,else | drop,else | +| array | drop,else | drop,else | +| ref & | drop,else | drop,else | +| ref mut &mut | drop,else | drop,else |