diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 23432b55648ad..d3e5bfaaa1255 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -85,6 +85,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()), allow_gen_future: Some([sym::gen_future][..].into()), allow_into_future: Some([sym::into_future][..].into()), + generics_def_id_map: Default::default(), }; lctx.with_hir_id_owner(owner, |lctx| f(lctx)); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 85d6ed4f3ac8b..ef83add24bf60 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -132,6 +132,12 @@ struct LoweringContext<'a, 'hir> { allow_try_trait: Option>, allow_gen_future: Option>, allow_into_future: Option>, + + /// 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 + /// defined on the TAIT, so we have type Foo<'a1> = ... and we establish a mapping in this + /// field from the original parameter 'a to the new parameter 'a1. + generics_def_id_map: Vec>, } trait ResolverAstLoweringExt { @@ -142,12 +148,6 @@ trait ResolverAstLoweringExt { fn get_lifetime_res(&self, id: NodeId) -> Option; fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)>; fn decl_macro_kind(&self, def_id: LocalDefId) -> MacroKind; - /// Record the map from `from` local def id to `to` local def id, on `generics_def_id_map` - /// field. - fn record_def_id_remap(&mut self, from: LocalDefId, to: LocalDefId); - /// Get the previously recorded `to` local def id given the `from` local def id, obtained using - /// `generics_def_id_map` field. - fn get_remapped_def_id(&self, local_def_id: LocalDefId) -> LocalDefId; } impl ResolverAstLoweringExt for ResolverAstLowering { @@ -215,41 +215,6 @@ impl ResolverAstLoweringExt for ResolverAstLowering { fn decl_macro_kind(&self, def_id: LocalDefId) -> MacroKind { self.builtin_macro_kinds.get(&def_id).copied().unwrap_or(MacroKind::Bang) } - - /// Push a remapping into the top-most map. - /// Panics if no map has been pushed. - /// Remapping is used when creating lowering `-> impl Trait` return - /// types to create the resulting opaque type. - #[instrument(level = "debug", skip(self))] - fn record_def_id_remap(&mut self, from: LocalDefId, to: LocalDefId) { - self.generics_def_id_map.last_mut().expect("no map pushed").insert(from, to); - } - - fn get_remapped_def_id(&self, mut local_def_id: LocalDefId) -> LocalDefId { - // `generics_def_id_map` is a stack of mappings. As we go deeper in impl traits nesting we - // push new mappings so we need to try first the latest mappings, hence `iter().rev()`. - // - // Consider: - // - // `fn test<'a, 'b>() -> impl Trait<&'a u8, Ty = impl Sized + 'b> {}` - // - // We would end with a generics_def_id_map like: - // - // `[[fn#'b -> impl_trait#'b], [fn#'b -> impl_sized#'b]]` - // - // for the opaque type generated on `impl Sized + 'b`, We want the result to be: - // impl_sized#'b, so iterating forward is the wrong thing to do. - for map in self.generics_def_id_map.iter().rev() { - if let Some(r) = map.get(&local_def_id) { - debug!("def_id_remapper: remapping from `{local_def_id:?}` to `{r:?}`"); - local_def_id = *r; - } else { - debug!("def_id_remapper: no remapping for `{local_def_id:?}` found in map"); - } - } - - local_def_id - } } /// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree, @@ -522,13 +487,41 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.resolver .node_id_to_def_id .get(&node) - .map(|local_def_id| self.resolver.get_remapped_def_id(*local_def_id)) + .map(|local_def_id| self.get_remapped_def_id(*local_def_id)) } fn local_def_id(&self, node: NodeId) -> LocalDefId { self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{:?}`", node)) } + /// Get the previously recorded `to` local def id given the `from` local def id, obtained using + /// `generics_def_id_map` field. + fn get_remapped_def_id(&self, mut local_def_id: LocalDefId) -> LocalDefId { + // `generics_def_id_map` is a stack of mappings. As we go deeper in impl traits nesting we + // push new mappings so we need to try first the latest mappings, hence `iter().rev()`. + // + // Consider: + // + // `fn test<'a, 'b>() -> impl Trait<&'a u8, Ty = impl Sized + 'b> {}` + // + // We would end with a generics_def_id_map like: + // + // `[[fn#'b -> impl_trait#'b], [fn#'b -> impl_sized#'b]]` + // + // for the opaque type generated on `impl Sized + 'b`, We want the result to be: + // impl_sized#'b, so iterating forward is the wrong thing to do. + for map in self.generics_def_id_map.iter().rev() { + if let Some(r) = map.get(&local_def_id) { + debug!("def_id_remapper: remapping from `{local_def_id:?}` to `{r:?}`"); + local_def_id = *r; + } else { + debug!("def_id_remapper: no remapping for `{local_def_id:?}` found in map"); + } + } + + local_def_id + } + /// Freshen the `LoweringContext` and ready it to lower a nested item. /// The lowered item is registered into `self.children`. /// @@ -597,9 +590,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { remap: FxHashMap, f: impl FnOnce(&mut Self) -> R, ) -> R { - self.resolver.generics_def_id_map.push(remap); + self.generics_def_id_map.push(remap); let res = f(self); - self.resolver.generics_def_id_map.pop(); + self.generics_def_id_map.pop(); res } @@ -2027,7 +2020,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let name = match res { LifetimeRes::Param { param, .. } => { let p_name = ParamName::Plain(ident); - let param = self.resolver.get_remapped_def_id(param); + let param = self.get_remapped_def_id(param); hir::LifetimeName::Param(param, p_name) } 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_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 5aabb14fba884..530e252b7c077 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -6,6 +6,7 @@ use std::borrow::{Borrow, Cow}; use std::fmt::Debug; use std::hash::Hash; +use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_middle::mir; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::DefId; @@ -323,6 +324,15 @@ pub trait Machine<'mir, 'tcx>: Sized { kind: Option>, ) -> InterpResult<'tcx, Cow<'b, Allocation>>; + fn eval_inline_asm( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _template: &'tcx [InlineAsmTemplatePiece], + _operands: &[mir::InlineAsmOperand<'tcx>], + _options: InlineAsmOptions, + ) -> InterpResult<'tcx> { + throw_unsup_format!("inline assembly is not supported") + } + /// Hook for performing extra checks on a memory read access. /// /// Takes read-only access to the allocation so we can keep all the memory read diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index ea366eba77245..50a82aa0e72c9 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; +use rustc_ast::ast::InlineAsmOptions; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf}; use rustc_middle::ty::Instance; use rustc_middle::{ @@ -166,8 +167,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { terminator.kind ), - // Inline assembly can't be interpreted. - InlineAsm { .. } => throw_unsup_format!("inline assembly is not supported"), + InlineAsm { template, ref operands, options, destination, .. } => { + M::eval_inline_asm(self, template, operands, options)?; + if options.contains(InlineAsmOptions::NORETURN) { + throw_ub_format!("returned from noreturn inline assembly"); + } + self.go_to_block( + destination + .expect("InlineAsm terminators without noreturn must have a destination"), + ) + } } Ok(()) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index e359d50c4e9e5..38a02cb1d7ce9 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -823,6 +823,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 e44c9291f8483..bdaa0ee88eba1 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -151,7 +151,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 ed04e7660339e..1312a1467968a 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -178,11 +178,6 @@ pub struct ResolverAstLowering { pub label_res_map: NodeMap, /// Resolutions for lifetimes. pub lifetimes_res_map: NodeMap, - /// 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 - /// defined on the TAIT, so we have type Foo<'a1> = ... and we establish a mapping in this - /// field from the original parameter 'a to the new parameter 'a1. - pub generics_def_id_map: Vec>, /// Lifetime parameters that lowering will have to introduce. pub extra_lifetime_params_map: NodeMap>, @@ -2274,7 +2269,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_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index a15a0c298a952..aaa9ae6f32513 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -911,11 +911,6 @@ pub struct Resolver<'a> { label_res_map: NodeMap, /// Resolutions for lifetimes. lifetimes_res_map: NodeMap, - /// 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 - /// defined on the TAIT, so we have type Foo<'a1> = ... and we establish a mapping in this - /// field from the original parameter 'a to the new parameter 'a1. - generics_def_id_map: Vec>, /// Lifetime parameters that lowering will have to introduce. extra_lifetime_params_map: NodeMap>, @@ -1278,7 +1273,6 @@ impl<'a> Resolver<'a> { import_res_map: Default::default(), label_res_map: Default::default(), lifetimes_res_map: Default::default(), - generics_def_id_map: Vec::new(), extra_lifetime_params_map: Default::default(), extern_crate_map: Default::default(), reexport_map: FxHashMap::default(), @@ -1445,7 +1439,6 @@ impl<'a> Resolver<'a> { import_res_map: self.import_res_map, label_res_map: self.label_res_map, lifetimes_res_map: self.lifetimes_res_map, - generics_def_id_map: self.generics_def_id_map, extra_lifetime_params_map: self.extra_lifetime_params_map, next_node_id: self.next_node_id, node_id_to_def_id: self.node_id_to_def_id, @@ -1490,7 +1483,6 @@ impl<'a> Resolver<'a> { import_res_map: self.import_res_map.clone(), label_res_map: self.label_res_map.clone(), lifetimes_res_map: self.lifetimes_res_map.clone(), - generics_def_id_map: self.generics_def_id_map.clone(), extra_lifetime_params_map: self.extra_lifetime_params_map.clone(), next_node_id: self.next_node_id.clone(), node_id_to_def_id: self.node_id_to_def_id.clone(), diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 46135caa9bce8..43893263be138 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -1459,7 +1459,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/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 9157b83330dd6..311fcaadaa98b 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -1812,16 +1812,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, receiver, args, ..) => { 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()); + } } } diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index a463bc41db7aa..71e33fb9ed864 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -104,7 +104,8 @@ impl BorrowedFd<'_> { #[cfg(target_os = "espidf")] let cmd = libc::F_DUPFD; - let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) })?; + // Avoid using file descriptors below 3 as they are used for stdio + let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 3) })?; Ok(unsafe { OwnedFd::from_raw_fd(fd) }) } 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`. 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 | 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`. 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"]