about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-09-06 00:23:01 +0000
committerbors <bors@rust-lang.org>2022-09-06 00:23:01 +0000
commit56b27110e7f72ea01312d99f1b1e0577431cbbd0 (patch)
tree0f4b4c28ee4f9f11a39211a506d4489e44e08c1a
parent676afc514944aa618977bacf8fe28cefc443a13a (diff)
parent0d8a1f4cbf0fd6acb6e81946aa3da2c7f15ee431 (diff)
downloadrust-56b27110e7f72ea01312d99f1b1e0577431cbbd0.tar.gz
rust-56b27110e7f72ea01312d99f1b1e0577431cbbd0.zip
Auto merge of #101464 - JohnTitor:rollup-unsjgm6, r=JohnTitor
Rollup of 7 pull requests

Successful merges:

 - #99291 (Add let else drop order tests)
 - #101402 (Add a Machine hook for inline assembly)
 - #101404 (Fix cleanup for uninitialized stdout)
 - #101418 (Revert "Mention rust-analyzer maintainers when `proc_macro` bridge is changed")
 - #101425 (Point at type parameter in plain path expr)
 - #101426 (Don't duplicate file descriptors into stdio fds)
 - #101447 (Remove generics_def_id_map from the resolver.)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs1
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs83
-rw-r--r--compiler/rustc_const_eval/src/interpret/machine.rs10
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs13
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs5
-rw-r--r--compiler/rustc_resolve/src/lib.rs8
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/checks.rs20
-rw-r--r--library/std/src/io/stdio.rs25
-rw-r--r--library/std/src/os/fd/owned.rs3
-rw-r--r--src/test/ui/let-else/let-else-drop-order.rs270
-rw-r--r--src/test/ui/let-else/let-else-drop-order.run.stdout51
-rw-r--r--src/test/ui/typeck/point-at-type-param-in-path-expr.rs6
-rw-r--r--src/test/ui/typeck/point-at-type-param-in-path-expr.stderr17
-rw-r--r--triagebot.toml3
14 files changed, 433 insertions, 82 deletions
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 23432b55648..d3e5bfaaa12 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 85d6ed4f3ac..ef83add24bf 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<Lrc<[Symbol]>>,
     allow_gen_future: Option<Lrc<[Symbol]>>,
     allow_into_future: Option<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
+    /// 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<FxHashMap<LocalDefId, LocalDefId>>,
 }
 
 trait ResolverAstLoweringExt {
@@ -142,12 +148,6 @@ trait ResolverAstLoweringExt {
     fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes>;
     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<LocalDefId, LocalDefId>,
         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_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index 5aabb14fba8..530e252b7c0 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<MemoryKind<Self::MemoryKind>>,
     ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra>>>;
 
+    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 ea366eba772..50a82aa0e72 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_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index ed04e766033..8235a05fb53 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<ast::NodeId>,
     /// Resolutions for lifetimes.
     pub lifetimes_res_map: NodeMap<LifetimeRes>,
-    /// 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<FxHashMap<LocalDefId, LocalDefId>>,
     /// Lifetime parameters that lowering will have to introduce.
     pub extra_lifetime_params_map: NodeMap<Vec<(Ident, ast::NodeId, LifetimeRes)>>,
 
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index a15a0c298a9..aaa9ae6f325 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<NodeId>,
     /// Resolutions for lifetimes.
     lifetimes_res_map: NodeMap<LifetimeRes>,
-    /// 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<FxHashMap<LocalDefId, LocalDefId>>,
     /// Lifetime parameters that lowering will have to introduce.
     extra_lifetime_params_map: NodeMap<Vec<(Ident, NodeId, LifetimeRes)>>,
 
@@ -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/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
index 9157b83330d..311fcaadaa9 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 dd4ff4952fd..91cff3217d2 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 a463bc41db7..71e33fb9ed8 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/let-else/let-else-drop-order.rs b/src/test/ui/let-else/let-else-drop-order.rs
new file mode 100644
index 00000000000..0054f3d4182
--- /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<RefCell<Vec<Vec<String>>>>);
+
+impl DropAccountant {
+    fn new() -> Self {
+        Self(Default::default())
+    }
+    fn build_droppy(&self, v: u32) -> Droppy<u32> {
+        Droppy(self.clone(), v)
+    }
+    fn build_droppy_enum_none(&self, _v: u32) -> ((), DroppyEnum<u32>) {
+        ((), 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!(
+            "| {: <max_len$} | {: <max_len_before$} | {: <max_len_last$} |",
+            "construct", before_last[0], last[0]
+        );
+        println!("| {:-<max_len$} | {:-<max_len_before$} | {:-<max_len_last$} |", "", "", "");
+
+        for ((l, l_before), l_last) in
+            LINES.iter().zip(before_last[1..].iter()).zip(last[1..].iter())
+        {
+            println!(
+                "| {: <max_len$} | {: <max_len_before$} | {: <max_len_last$} |",
+                l, l_before, l_last,
+            );
+        }
+    }
+    #[track_caller]
+    fn assert_all_equal_to(&self, st: &str) {
+        let accounts = self.0.borrow();
+        let last = &accounts[accounts.len() - 1];
+        let last = get_comma_list(last);
+        for line in last[1..].iter() {
+            assert_eq!(line.trim(), st.trim());
+        }
+    }
+    #[track_caller]
+    fn assert_equality_last_two_lists(&self) {
+        let accounts = self.0.borrow();
+        let last = &accounts[accounts.len() - 1];
+        let before_last = &accounts[accounts.len() - 2];
+        for (l, b) in last[1..].iter().zip(before_last[1..].iter()) {
+            if !(l == b || l == "n/a" || b == "n/a") {
+                panic!("not equal: '{last:?}' != '{before_last:?}'");
+            }
+        }
+    }
+}
+
+fn get_comma_list(sl: &[String]) -> Vec<String> {
+    std::iter::once(sl[0].clone())
+        .chain(sl[1..].chunks(2).map(|c| c.join(",")))
+        .collect::<Vec<String>>()
+}
+
+struct Droppy<T>(DropAccountant, T);
+
+impl<T> Drop for Droppy<T> {
+    fn drop(&mut self) {
+        self.0.push("drop");
+    }
+}
+
+#[allow(dead_code)]
+enum DroppyEnum<T> {
+    Some(DropAccountant, T),
+    None(DropAccountant),
+}
+
+impl<T> Drop for DroppyEnum<T> {
+    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 00000000000..01cf2f73e17
--- /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 00000000000..9a21536f9b1
--- /dev/null
+++ b/src/test/ui/typeck/point-at-type-param-in-path-expr.rs
@@ -0,0 +1,6 @@
+fn foo<T: std::fmt::Display>() {}
+
+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 00000000000..1feaa0508bf
--- /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<T: std::fmt::Display>() {}
+   |           ^^^^^^^^^^^^^^^^^ 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 89d1574726f..89edab91fcd 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"]