mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
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
This commit is contained in:
commit
56b27110e7
@ -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));
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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(())
|
||||
|
@ -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)>>,
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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, ..) => {
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) })
|
||||
}
|
||||
|
||||
|
270
src/test/ui/let-else/let-else-drop-order.rs
Normal file
270
src/test/ui/let-else/let-else-drop-order.rs
Normal file
@ -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");
|
||||
}
|
51
src/test/ui/let-else/let-else-drop-order.run.stdout
Normal file
51
src/test/ui/let-else/let-else-drop-order.run.stdout
Normal file
@ -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 |
|
6
src/test/ui/typeck/point-at-type-param-in-path-expr.rs
Normal file
6
src/test/ui/typeck/point-at-type-param-in-path-expr.rs
Normal file
@ -0,0 +1,6 @@
|
||||
fn foo<T: std::fmt::Display>() {}
|
||||
|
||||
fn main() {
|
||||
let x = foo::<()>;
|
||||
//~^ ERROR `()` doesn't implement `std::fmt::Display`
|
||||
}
|
17
src/test/ui/typeck/point-at-type-param-in-path-expr.stderr
Normal file
17
src/test/ui/typeck/point-at-type-param-in-path-expr.stderr
Normal file
@ -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`.
|
@ -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"]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user