diff --git a/Cargo.lock b/Cargo.lock index c4501d6e574f..d5c9a5e4d0d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2590,6 +2590,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "nu-ansi-term" +version = "0.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c073d3c1930d0751774acf49e66653acecb416c3a54c6ec095a9b11caddb5a68" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -3560,6 +3569,7 @@ dependencies = [ name = "rustc_attr" version = "0.0.0" dependencies = [ + "rustc_abi", "rustc_ast", "rustc_ast_pretty", "rustc_data_structures", @@ -4773,6 +4783,8 @@ version = "0.0.0" dependencies = [ "arrayvec", "askama", + "base64", + "byteorder", "expect-test", "indexmap", "itertools 0.12.1", @@ -5358,9 +5370,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.30.7" +version = "0.30.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c385888ef380a852a16209afc8cfad22795dd8873d69c9a14d2e2088f118d18" +checksum = "4b1a378e48fb3ce3a5cf04359c456c9c98ff689bcf1c1bc6e6a31f247686f275" dependencies = [ "cfg-if", "core-foundation-sys", @@ -5778,17 +5790,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "tracing-log" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -5807,7 +5808,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", - "nu-ansi-term", + "nu-ansi-term 0.46.0", "once_cell", "parking_lot", "regex", @@ -5816,18 +5817,18 @@ dependencies = [ "thread_local", "tracing", "tracing-core", - "tracing-log 0.2.0", + "tracing-log", ] [[package]] name = "tracing-tree" -version = "0.2.5" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ec6adcab41b1391b08a308cc6302b79f8095d1673f6947c2dc65ffb028b0b2d" +checksum = "65139ecd2c3f6484c3b99bc01c77afe21e95473630747c7aca525e78b0666675" dependencies = [ - "nu-ansi-term", + "nu-ansi-term 0.49.0", "tracing-core", - "tracing-log 0.1.4", + "tracing-log", "tracing-subscriber", ] diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 4f2b9d0ef50d..7439af7aed3e 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -698,6 +698,7 @@ impl fmt::Display for AlignFromBytesError { impl Align { pub const ONE: Align = Align { pow2: 0 }; + pub const EIGHT: Align = Align { pow2: 3 }; // LLVM has a maximal supported alignment of 2^29, we inherit that. pub const MAX: Align = Align { pow2: 29 }; @@ -707,19 +708,19 @@ impl Align { } #[inline] - pub fn from_bytes(align: u64) -> Result { + pub const fn from_bytes(align: u64) -> Result { // Treat an alignment of 0 bytes like 1-byte alignment. if align == 0 { return Ok(Align::ONE); } #[cold] - fn not_power_of_2(align: u64) -> AlignFromBytesError { + const fn not_power_of_2(align: u64) -> AlignFromBytesError { AlignFromBytesError::NotPowerOfTwo(align) } #[cold] - fn too_large(align: u64) -> AlignFromBytesError { + const fn too_large(align: u64) -> AlignFromBytesError { AlignFromBytesError::TooLarge(align) } diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index aba94f4d817d..4ba298902a93 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3341,7 +3341,7 @@ impl TryFrom for ForeignItemKind { pub type ForeignItem = Item; // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index f49eb2f22c50..5060bbec4216 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -1021,7 +1021,7 @@ where } // Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 239735456ad5..f3249f3e5a8b 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -768,7 +768,7 @@ impl DelimSpacing { } // Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_attr/Cargo.toml b/compiler/rustc_attr/Cargo.toml index d33416d20038..3b24452450ab 100644 --- a/compiler/rustc_attr/Cargo.toml +++ b/compiler/rustc_attr/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start +rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 814104ec78c4..439f13e76357 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -1,5 +1,6 @@ //! Parsing and validation of builtin attributes +use rustc_abi::Align; use rustc_ast::{self as ast, attr}; use rustc_ast::{Attribute, LitKind, MetaItem, MetaItemKind, MetaItemLit, NestedMetaItem, NodeId}; use rustc_ast_pretty::pprust; @@ -919,10 +920,10 @@ pub enum ReprAttr { ReprInt(IntType), ReprRust, ReprC, - ReprPacked(u32), + ReprPacked(Align), ReprSimd, ReprTransparent, - ReprAlign(u32), + ReprAlign(Align), } #[derive(Eq, PartialEq, Debug, Copy, Clone)] @@ -968,7 +969,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec { let hint = match item.name_or_empty() { sym::Rust => Some(ReprRust), sym::C => Some(ReprC), - sym::packed => Some(ReprPacked(1)), + sym::packed => Some(ReprPacked(Align::ONE)), sym::simd => Some(ReprSimd), sym::transparent => Some(ReprTransparent), sym::align => { @@ -1209,11 +1210,17 @@ fn allow_unstable<'a>( }) } -pub fn parse_alignment(node: &ast::LitKind) -> Result { +pub fn parse_alignment(node: &ast::LitKind) -> Result { if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node { + // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first if literal.get().is_power_of_two() { - // rustc_middle::ty::layout::Align restricts align to <= 2^29 - if *literal <= 1 << 29 { Ok(literal.get() as u32) } else { Err("larger than 2^29") } + // Only possible error is larger than 2^29 + literal + .get() + .try_into() + .ok() + .and_then(|v| Align::from_bytes(v).ok()) + .ok_or("larger than 2^29") } else { Err("not a power of two") } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 26bb68003480..bd068b29c123 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -540,19 +540,23 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } + /// Suggest `map[k] = v` => `map.insert(k, v)` and the like. fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'tcx>, span: Span) { let Some(adt) = ty.ty_adt_def() else { return }; let did = adt.did(); if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did) || self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did) { - struct V<'a, 'tcx> { + /// Walks through the HIR, looking for the corresponding span for this error. + /// When it finds it, see if it corresponds to assignment operator whose LHS + /// is an index expr. + struct SuggestIndexOperatorAlternativeVisitor<'a, 'tcx> { assign_span: Span, err: &'a mut Diag<'tcx>, ty: Ty<'tcx>, suggested: bool, } - impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> { + impl<'a, 'tcx> Visitor<'tcx> for SuggestIndexOperatorAlternativeVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { hir::intravisit::walk_stmt(self, stmt); let expr = match stmt.kind { @@ -645,7 +649,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id) else { return }; let body = self.infcx.tcx.hir().body(body_id); - let mut v = V { assign_span: span, err, ty, suggested: false }; + let mut v = SuggestIndexOperatorAlternativeVisitor { + assign_span: span, + err, + ty, + suggested: false, + }; v.visit_body(body); if !v.suggested { err.help(format!( diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index b0bdf4af0975..6405364c30c1 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2279,7 +2279,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - CastKind::PointerFromExposedAddress => { + CastKind::PointerWithExposedProvenance => { let ty_from = op.ty(body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); @@ -2289,7 +2289,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { span_mirbug!( self, rvalue, - "Invalid PointerFromExposedAddress cast {:?} -> {:?}", + "Invalid PointerWithExposedProvenance cast {:?} -> {:?}", ty_from, ty ) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index ba8401393d7a..2a5bc58af3b3 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -114,6 +114,8 @@ builtin_macros_env_not_defined = environment variable `{$var}` not defined at co .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead .custom = use `std::env::var({$var_expr})` to read the variable at run time +builtin_macros_env_not_unicode = environment variable `{$var}` is not a valid Unicode string + builtin_macros_env_takes_args = `env!()` takes 1 or 2 arguments builtin_macros_expected_one_cfg_pattern = expected 1 cfg-pattern diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index bce710e5cab1..938730459437 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -11,18 +11,19 @@ use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpa use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use std::env; +use std::env::VarError; use thin_vec::thin_vec; use crate::errors; -fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Option { +fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Result { let var = var.as_str(); if let Some(value) = cx.sess.opts.logical_env.get(var) { - return Some(Symbol::intern(value)); + return Ok(Symbol::intern(value)); } // If the environment variable was not defined with the `--env-set` option, we try to retrieve it // from rustc's environment. - env::var(var).ok().as_deref().map(Symbol::intern) + Ok(Symbol::intern(&env::var(var)?)) } pub fn expand_option_env<'cx>( @@ -39,7 +40,7 @@ pub fn expand_option_env<'cx>( }; let sp = cx.with_def_site_ctxt(sp); - let value = lookup_env(cx, var); + let value = lookup_env(cx, var).ok(); cx.sess.psess.env_depinfo.borrow_mut().insert((var, value)); let e = match value { None => { @@ -108,9 +109,9 @@ pub fn expand_env<'cx>( let span = cx.with_def_site_ctxt(sp); let value = lookup_env(cx, var); - cx.sess.psess.env_depinfo.borrow_mut().insert((var, value)); + cx.sess.psess.env_depinfo.borrow_mut().insert((var, value.as_ref().ok().copied())); let e = match value { - None => { + Err(err) => { let ExprKind::Lit(token::Lit { kind: LitKind::Str | LitKind::StrRaw(..), symbol, .. }) = &var_expr.kind @@ -118,25 +119,33 @@ pub fn expand_env<'cx>( unreachable!("`expr_to_string` ensures this is a string lit") }; - let guar = if let Some(msg_from_user) = custom_msg { - cx.dcx().emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user }) - } else if is_cargo_env_var(var.as_str()) { - cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar { - span, - var: *symbol, - var_expr: var_expr.ast_deref(), - }) - } else { - cx.dcx().emit_err(errors::EnvNotDefined::CustomEnvVar { - span, - var: *symbol, - var_expr: var_expr.ast_deref(), - }) + let guar = match err { + VarError::NotPresent => { + if let Some(msg_from_user) = custom_msg { + cx.dcx() + .emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user }) + } else if is_cargo_env_var(var.as_str()) { + cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar { + span, + var: *symbol, + var_expr: var_expr.ast_deref(), + }) + } else { + cx.dcx().emit_err(errors::EnvNotDefined::CustomEnvVar { + span, + var: *symbol, + var_expr: var_expr.ast_deref(), + }) + } + } + VarError::NotUnicode(_) => { + cx.dcx().emit_err(errors::EnvNotUnicode { span, var: *symbol }) + } }; return ExpandResult::Ready(DummyResult::any(sp, guar)); } - Some(value) => cx.expr_str(span, value), + Ok(value) => cx.expr_str(span, value), }; ExpandResult::Ready(MacEager::expr(e)) } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 377aff8fb6c0..6b6647ef085c 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -458,6 +458,14 @@ pub(crate) enum EnvNotDefined<'a> { }, } +#[derive(Diagnostic)] +#[diag(builtin_macros_env_not_unicode)] +pub(crate) struct EnvNotUnicode { + #[primary_span] + pub(crate) span: Span, + pub(crate) var: Symbol, +} + #[derive(Diagnostic)] #[diag(builtin_macros_format_requires_string)] pub(crate) struct FormatRequiresString { diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 8d0b84f62dc7..249c16898ce6 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -650,7 +650,7 @@ fn codegen_stmt<'tcx>( | CastKind::FnPtrToPtr | CastKind::PtrToPtr | CastKind::PointerExposeAddress - | CastKind::PointerFromExposedAddress, + | CastKind::PointerWithExposedProvenance, ref operand, to_ty, ) => { diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index b2bc289a5b6b..4a5ef352151f 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -68,7 +68,7 @@ pub(crate) fn maybe_codegen<'tcx>( Some(CValue::by_val(ret_val, lhs.layout())) } } - BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => None, + BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne | BinOp::Cmp => None, BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => None, } } @@ -134,6 +134,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>( BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(), BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"), BinOp::Div | BinOp::Rem => unreachable!(), + BinOp::Cmp => unreachable!(), BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => unreachable!(), BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => unreachable!(), } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index 4d55a95aa9db..783ad5d1dd1f 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -965,7 +965,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }); } - sym::simd_expose_addr | sym::simd_from_exposed_addr | sym::simd_cast_ptr => { + sym::simd_expose_addr | sym::simd_with_exposed_provenance | sym::simd_cast_ptr => { intrinsic_args!(fx, args => (arg); intrinsic); ret.write_cvalue_transmute(fx, arg); } diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs index 8992f40fb903..796182418ad6 100644 --- a/compiler/rustc_codegen_cranelift/src/num.rs +++ b/compiler/rustc_codegen_cranelift/src/num.rs @@ -40,6 +40,22 @@ pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> Option { }) } +fn codegen_three_way_compare<'tcx>( + fx: &mut FunctionCx<'_, '_, 'tcx>, + signed: bool, + lhs: Value, + rhs: Value, +) -> CValue<'tcx> { + // This emits `(lhs > rhs) - (lhs < rhs)`, which is cranelift's preferred form per + // + let gt_cc = crate::num::bin_op_to_intcc(BinOp::Gt, signed).unwrap(); + let lt_cc = crate::num::bin_op_to_intcc(BinOp::Lt, signed).unwrap(); + let gt = fx.bcx.ins().icmp(gt_cc, lhs, rhs); + let lt = fx.bcx.ins().icmp(lt_cc, lhs, rhs); + let val = fx.bcx.ins().isub(gt, lt); + CValue::by_val(val, fx.layout_of(fx.tcx.ty_ordering_enum(Some(fx.mir.span)))) +} + fn codegen_compare_bin_op<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, @@ -47,6 +63,10 @@ fn codegen_compare_bin_op<'tcx>( lhs: Value, rhs: Value, ) -> CValue<'tcx> { + if bin_op == BinOp::Cmp { + return codegen_three_way_compare(fx, signed, lhs, rhs); + } + let intcc = crate::num::bin_op_to_intcc(bin_op, signed).unwrap(); let val = fx.bcx.ins().icmp(intcc, lhs, rhs); CValue::by_val(val, fx.layout_of(fx.tcx.types.bool)) @@ -59,7 +79,7 @@ pub(crate) fn codegen_binop<'tcx>( in_rhs: CValue<'tcx>, ) -> CValue<'tcx> { match bin_op { - BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => { + BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt | BinOp::Cmp => { match in_lhs.layout().ty.kind() { ty::Bool | ty::Uint(_) | ty::Int(_) | ty::Char => { let signed = type_sign(in_lhs.layout().ty); @@ -160,7 +180,7 @@ pub(crate) fn codegen_int_binop<'tcx>( } BinOp::Offset => unreachable!("Offset is not an integer operation"), // Compare binops handles by `codegen_binop`. - BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => { + BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge | BinOp::Cmp => { unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty); } }; diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index d243d7088ada..78d943192db0 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -94,6 +94,10 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { self.const_int(self.type_i32(), i as i64) } + fn const_i8(&self, i: i8) -> RValue<'gcc> { + self.const_int(self.type_i8(), i as i64) + } + fn const_u32(&self, i: u32) -> RValue<'gcc> { self.const_uint(self.type_u32(), i as u64) } diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index f9eaa0d94cbb..870e5ab32961 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -417,7 +417,7 @@ pub fn from_fn_attrs<'ll, 'tcx>( to_add.push(llvm::CreateAttrString(cx.llcx, "cmse_nonsecure_entry")); } if let Some(align) = codegen_fn_attrs.alignment { - llvm::set_alignment(llfn, align as usize); + llvm::set_alignment(llfn, align); } to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize)); diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 25cbd90460f2..568fcc3f3cf4 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -160,6 +160,10 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { self.const_int(self.type_i32(), i as i64) } + fn const_i8(&self, i: i8) -> &'ll Value { + self.const_int(self.type_i8(), i as i64) + } + fn const_u32(&self, i: u32) -> &'ll Value { self.const_uint(self.type_i32(), i as u64) } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 85277db6d538..68c1770c1d4a 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -16,6 +16,7 @@ use rustc_middle::bug; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::Instance; +use rustc_target::abi::Align; use std::cell::RefCell; @@ -23,7 +24,7 @@ pub(crate) mod ffi; pub(crate) mod map_data; pub mod mapgen; -const VAR_ALIGN_BYTES: usize = 8; +const VAR_ALIGN: Align = Align::EIGHT; /// A context object for maintaining all state needed by the coverageinfo module. pub struct CrateCoverageContext<'ll, 'tcx> { @@ -225,7 +226,7 @@ pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>( llvm::set_global_constant(llglobal, true); llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage); llvm::set_section(llglobal, &covmap_section_name); - llvm::set_alignment(llglobal, VAR_ALIGN_BYTES); + llvm::set_alignment(llglobal, VAR_ALIGN); cx.add_used_global(llglobal); } @@ -255,7 +256,7 @@ pub(crate) fn save_func_record_to_mod<'ll, 'tcx>( llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage); llvm::set_visibility(llglobal, llvm::Visibility::Hidden); llvm::set_section(llglobal, covfun_section_name); - llvm::set_alignment(llglobal, VAR_ALIGN_BYTES); + llvm::set_alignment(llglobal, VAR_ALIGN); llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name); cx.add_used_global(llglobal); } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index ab135e3ed644..e4ec7974e900 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -2139,7 +2139,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( return Ok(bx.ptrtoint(args[0].immediate(), llret_ty)); } - if name == sym::simd_from_exposed_addr { + if name == sym::simd_with_exposed_provenance { let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn); require!( in_len == out_len, diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 4f5cc575da6e..6ab1eea95976 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -11,6 +11,7 @@ pub use self::RealPredicate::*; use libc::c_uint; use rustc_data_structures::small_c_str::SmallCStr; use rustc_llvm::RustString; +use rustc_target::abi::Align; use std::cell::RefCell; use std::ffi::{CStr, CString}; use std::str::FromStr; @@ -229,9 +230,9 @@ pub fn set_visibility(llglobal: &Value, visibility: Visibility) { } } -pub fn set_alignment(llglobal: &Value, bytes: usize) { +pub fn set_alignment(llglobal: &Value, align: Align) { unsafe { - ffi::LLVMSetAlignment(llglobal, bytes as c_uint); + ffi::LLVMSetAlignment(llglobal, align.bytes() as c_uint); } } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index f7f2bfca838e..410b5d27c577 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -5,7 +5,7 @@ use crate::back::write::{ compute_per_cgu_lto_type, start_async_codegen, submit_codegened_module_to_llvm, submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm, ComputedLtoType, OngoingCodegen, }; -use crate::common::{IntPredicate, RealPredicate, TypeKind}; +use crate::common::{self, IntPredicate, RealPredicate, TypeKind}; use crate::errors; use crate::meth; use crate::mir; @@ -33,7 +33,7 @@ use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem}; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; -use rustc_session::config::{self, CrateType, EntryFnType, OutputType}; +use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType}; use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::Symbol; @@ -300,14 +300,35 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } -pub fn cast_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( +/// Returns `rhs` sufficiently masked, truncated, and/or extended so that +/// it can be used to shift `lhs`. +/// +/// Shifts in MIR are all allowed to have mismatched LHS & RHS types. +/// The shift methods in `BuilderMethods`, however, are fully homogeneous +/// (both parameters and the return type are all the same type). +/// +/// If `is_unchecked` is false, this masks the RHS to ensure it stays in-bounds, +/// as the `BuilderMethods` shifts are UB for out-of-bounds shift amounts. +/// For 32- and 64-bit types, this matches the semantics +/// of Java. (See related discussion on #1877 and #10183.) +/// +/// If `is_unchecked` is true, this does no masking, and adds sufficient `assume` +/// calls or operation flags to preserve as much freedom to optimize as possible. +pub fn build_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, lhs: Bx::Value, - rhs: Bx::Value, + mut rhs: Bx::Value, + is_unchecked: bool, ) -> Bx::Value { // Shifts may have any size int on the rhs let mut rhs_llty = bx.cx().val_ty(rhs); let mut lhs_llty = bx.cx().val_ty(lhs); + + let mask = common::shift_mask_val(bx, lhs_llty, rhs_llty, false); + if !is_unchecked { + rhs = bx.and(rhs, mask); + } + if bx.cx().type_kind(rhs_llty) == TypeKind::Vector { rhs_llty = bx.cx().element_type(rhs_llty) } @@ -317,6 +338,12 @@ pub fn cast_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let rhs_sz = bx.cx().int_width(rhs_llty); let lhs_sz = bx.cx().int_width(lhs_llty); if lhs_sz < rhs_sz { + if is_unchecked && bx.sess().opts.optimize != OptLevel::No { + // FIXME: Use `trunc nuw` once that's available + let inrange = bx.icmp(IntPredicate::IntULE, rhs, mask); + bx.assume(inrange); + } + bx.trunc(rhs, lhs_llty) } else if lhs_sz > rhs_sz { // We zero-extend even if the RHS is signed. So e.g. `(x: i32) << -1i8` will zero-extend the diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 71fca403defb..b41739867c7d 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -3,10 +3,9 @@ use rustc_hir::LangItem; use rustc_middle::mir; use rustc_middle::ty::Instance; -use rustc_middle::ty::{self, layout::TyAndLayout, Ty, TyCtxt}; +use rustc_middle::ty::{self, layout::TyAndLayout, TyCtxt}; use rustc_span::Span; -use crate::base; use crate::traits::*; #[derive(Copy, Clone)] @@ -128,44 +127,6 @@ pub fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance), instance) } -// To avoid UB from LLVM, these two functions mask RHS with an -// appropriate mask unconditionally (i.e., the fallback behavior for -// all shifts). For 32- and 64-bit types, this matches the semantics -// of Java. (See related discussion on #1877 and #10183.) - -pub fn build_masked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - lhs: Bx::Value, - rhs: Bx::Value, -) -> Bx::Value { - let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs); - // #1877, #10183: Ensure that input is always valid - let rhs = shift_mask_rhs(bx, rhs); - bx.shl(lhs, rhs) -} - -pub fn build_masked_rshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - lhs_t: Ty<'tcx>, - lhs: Bx::Value, - rhs: Bx::Value, -) -> Bx::Value { - let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs); - // #1877, #10183: Ensure that input is always valid - let rhs = shift_mask_rhs(bx, rhs); - let is_signed = lhs_t.is_signed(); - if is_signed { bx.ashr(lhs, rhs) } else { bx.lshr(lhs, rhs) } -} - -fn shift_mask_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - rhs: Bx::Value, -) -> Bx::Value { - let rhs_llty = bx.val_ty(rhs); - let shift_val = shift_mask_val(bx, rhs_llty, rhs_llty, false); - bx.and(rhs, shift_val) -} - pub fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, llty: Bx::Type, diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 0af84ff067af..6f7b98a262d5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -3,10 +3,11 @@ use super::place::PlaceRef; use super::{FunctionCx, LocalRef}; use crate::base; -use crate::common::{self, IntPredicate}; +use crate::common::IntPredicate; use crate::traits::*; use crate::MemFlags; +use rustc_hir as hir; use rustc_middle::mir; use rustc_middle::mir::Operand; use rustc_middle::ty::cast::{CastTy, IntTy}; @@ -508,7 +509,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Since int2ptr can have arbitrary integer types as input (so we have to do // sign extension and all that), it is currently best handled in the same code // path as the other integer-to-X casts. - | mir::CastKind::PointerFromExposedAddress => { + | mir::CastKind::PointerWithExposedProvenance => { assert!(bx.cx().is_backend_immediate(cast)); let ll_t_out = bx.cx().immediate_backend_type(cast); if operand.layout.abi.is_uninhabited() { @@ -860,14 +861,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.inbounds_gep(llty, lhs, &[rhs]) } } - mir::BinOp::Shl => common::build_masked_lshift(bx, lhs, rhs), - mir::BinOp::ShlUnchecked => { - let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs); + mir::BinOp::Shl | mir::BinOp::ShlUnchecked => { + let rhs = base::build_shift_expr_rhs(bx, lhs, rhs, op == mir::BinOp::ShlUnchecked); bx.shl(lhs, rhs) } - mir::BinOp::Shr => common::build_masked_rshift(bx, input_ty, lhs, rhs), - mir::BinOp::ShrUnchecked => { - let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs); + mir::BinOp::Shr | mir::BinOp::ShrUnchecked => { + let rhs = base::build_shift_expr_rhs(bx, lhs, rhs, op == mir::BinOp::ShrUnchecked); if is_signed { bx.ashr(lhs, rhs) } else { bx.lshr(lhs, rhs) } } mir::BinOp::Ne @@ -882,6 +881,35 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.icmp(base::bin_op_to_icmp_predicate(op.to_hir_binop(), is_signed), lhs, rhs) } } + mir::BinOp::Cmp => { + use std::cmp::Ordering; + debug_assert!(!is_float); + let pred = |op| base::bin_op_to_icmp_predicate(op, is_signed); + if bx.cx().tcx().sess.opts.optimize == OptLevel::No { + // FIXME: This actually generates tighter assembly, and is a classic trick + // + // However, as of 2023-11 it optimizes worse in things like derived + // `PartialOrd`, so only use it in debug for now. Once LLVM can handle it + // better (see ), it'll + // be worth trying it in optimized builds as well. + let is_gt = bx.icmp(pred(hir::BinOpKind::Gt), lhs, rhs); + let gtext = bx.zext(is_gt, bx.type_i8()); + let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs); + let ltext = bx.zext(is_lt, bx.type_i8()); + bx.unchecked_ssub(gtext, ltext) + } else { + // These operations are those expected by `tests/codegen/integer-cmp.rs`, + // from . + let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs); + let is_ne = bx.icmp(pred(hir::BinOpKind::Ne), lhs, rhs); + let ge = bx.select( + is_ne, + bx.cx().const_i8(Ordering::Greater as i8), + bx.cx().const_i8(Ordering::Equal as i8), + ); + bx.select(is_lt, bx.cx().const_i8(Ordering::Less as i8), ge) + } + } } } diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index 4dff9c7684f1..8cb17a5b37a8 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -19,6 +19,7 @@ pub trait ConstMethods<'tcx>: BackendTypes { fn const_bool(&self, val: bool) -> Self::Value; fn const_i16(&self, i: i16) -> Self::Value; fn const_i32(&self, i: i32) -> Self::Value; + fn const_i8(&self, i: i8) -> Self::Value; fn const_u32(&self, i: u32) -> Self::Value; fn const_u64(&self, i: u64) -> Self::Value; fn const_u128(&self, i: u128) -> Self::Value; diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index d6aae60c3382..f6937dc145d5 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -222,6 +222,7 @@ const_eval_mut_deref = const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind} +const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead const_eval_non_const_fmt_macro_call = cannot call non-const formatting macro in {const_eval_const_context}s diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 5c46ec799f1e..a60cedd6500d 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -25,6 +25,13 @@ pub(crate) struct DanglingPtrInFinal { pub kind: InternKind, } +#[derive(Diagnostic)] +#[diag(const_eval_nested_static_in_thread_local)] +pub(crate) struct NestedStaticInThreadLocal { + #[primary_span] + pub span: Span, +} + #[derive(LintDiagnostic)] #[diag(const_eval_mutable_ptr_in_final)] pub(crate) struct MutablePtrInFinal { diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index bbf11f169f98..e0d45f1fe114 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -40,9 +40,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.write_immediate(*res, dest)?; } - CastKind::PointerFromExposedAddress => { + CastKind::PointerWithExposedProvenance => { let src = self.read_immediate(src)?; - let res = self.pointer_from_exposed_address_cast(&src, cast_layout)?; + let res = self.pointer_with_exposed_provenance_cast(&src, cast_layout)?; self.write_immediate(*res, dest)?; } @@ -242,7 +242,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(ImmTy::from_scalar(self.cast_from_int_like(scalar, src.layout, cast_to.ty)?, cast_to)) } - pub fn pointer_from_exposed_address_cast( + pub fn pointer_with_exposed_provenance_cast( &self, src: &ImmTy<'tcx, M::Provenance>, cast_to: TyAndLayout<'tcx>, diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 58eaef65e557..d0f0190fea7e 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -18,6 +18,7 @@ use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::mir::interpret::{ConstAllocation, CtfeProvenance, InterpResult}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::TyAndLayout; @@ -27,7 +28,7 @@ use rustc_span::sym; use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy}; use crate::const_eval; -use crate::errors::{DanglingPtrInFinal, MutablePtrInFinal}; +use crate::errors::{DanglingPtrInFinal, MutablePtrInFinal, NestedStaticInThreadLocal}; pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine< 'mir, @@ -106,13 +107,21 @@ fn intern_as_new_static<'tcx>( DefKind::Static { mutability: alloc.0.mutability, nested: true }, ); tcx.set_nested_alloc_id_static(alloc_id, feed.def_id()); - feed.codegen_fn_attrs(tcx.codegen_fn_attrs(static_id).clone()); + + if tcx.is_thread_local_static(static_id.into()) { + tcx.dcx().emit_err(NestedStaticInThreadLocal { span: tcx.def_span(static_id) }); + } + + // These do not inherit the codegen attrs of the parent static allocation, since + // it doesn't make sense for them to inherit their `#[no_mangle]` and `#[link_name = ..]` + // and the like. + feed.codegen_fn_attrs(CodegenFnAttrs::new()); + feed.eval_static_initializer(Ok(alloc)); feed.generics_of(tcx.generics_of(static_id).clone()); feed.def_ident_span(tcx.def_ident_span(static_id)); feed.explicit_predicates_of(tcx.explicit_predicates_of(static_id)); - - feed.feed_hir() + feed.feed_hir(); } /// How a constant value should be interned. diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index dbc6a317640c..842fb6d204c2 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -235,6 +235,13 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { Self::from_scalar(Scalar::from_bool(b), layout) } + #[inline] + pub fn from_ordering(c: std::cmp::Ordering, tcx: TyCtxt<'tcx>) -> Self { + let ty = tcx.ty_ordering_enum(None); + let layout = tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)).unwrap(); + Self::from_scalar(Scalar::from_i8(c as i8), layout) + } + #[inline] pub fn to_const_int(self) -> ConstInt { assert!(self.layout.ty.is_integral()); @@ -785,7 +792,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 475c533077af..5665bb4999f5 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -61,6 +61,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { + fn three_way_compare(&self, lhs: T, rhs: T) -> (ImmTy<'tcx, M::Provenance>, bool) { + let res = Ord::cmp(&lhs, &rhs); + return (ImmTy::from_ordering(res, *self.tcx), false); + } + fn binary_char_op( &self, bin_op: mir::BinOp, @@ -69,6 +74,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> (ImmTy<'tcx, M::Provenance>, bool) { use rustc_middle::mir::BinOp::*; + if bin_op == Cmp { + return self.three_way_compare(l, r); + } + let res = match bin_op { Eq => l == r, Ne => l != r, @@ -231,6 +240,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let r = self.sign_extend(r, right_layout) as i128; return Ok((ImmTy::from_bool(op(&l, &r), *self.tcx), false)); } + if bin_op == Cmp { + let l = self.sign_extend(l, left_layout) as i128; + let r = self.sign_extend(r, right_layout) as i128; + return Ok(self.three_way_compare(l, r)); + } let op: Option (i128, bool)> = match bin_op { Div if r == 0 => throw_ub!(DivisionByZero), Rem if r == 0 => throw_ub!(RemainderByZero), @@ -270,6 +284,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } + if bin_op == Cmp { + return Ok(self.three_way_compare(l, r)); + } + let val = match bin_op { Eq => ImmTy::from_bool(l == r, *self.tcx), Ne => ImmTy::from_bool(l != r, *self.tcx), diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 1a2f1194f89a..e32aea39fc59 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -1058,7 +1058,7 @@ where } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index da8e28d02982..b6dcc3341473 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -547,7 +547,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => { self.check_op(ops::RawPtrToIntCast); } - Rvalue::Cast(CastKind::PointerFromExposedAddress, _, _) => { + Rvalue::Cast(CastKind::PointerWithExposedProvenance, _, _) => { // Since no pointer can ever get exposed (rejected above), this is easy to support. } diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 378b168a50c3..e1e98ebc1e9a 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -986,6 +986,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ) } } + Cmp => { + for x in [a, b] { + check_kinds!( + x, + "Cannot three-way compare non-integer type {:?}", + ty::Char | ty::Uint(..) | ty::Int(..) + ) + } + } AddUnchecked | SubUnchecked | MulUnchecked | Shl | ShlUnchecked | Shr | ShrUnchecked => { for x in [a, b] { @@ -1067,7 +1076,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // FIXME(dyn-star): make sure nothing needs to be done here. } // FIXME: Add Checks for these - CastKind::PointerFromExposedAddress + CastKind::PointerWithExposedProvenance | CastKind::PointerExposeAddress | CastKind::PointerCoercion(_) => {} CastKind::IntToInt | CastKind::IntToFloat => { diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index a8060463b69e..0c3b59a0e78e 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -19,7 +19,7 @@ pub fn binop_left_homogeneous(op: mir::BinOp) -> bool { match op { Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => true, - Eq | Ne | Lt | Le | Gt | Ge => false, + Eq | Ne | Lt | Le | Gt | Ge | Cmp => false, } } @@ -30,7 +30,7 @@ pub fn binop_right_homogeneous(op: mir::BinOp) -> bool { use rustc_middle::mir::BinOp::*; match op { Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor - | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true, + | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge | Cmp => true, Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => false, } } diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 15691804a94b..8418b4bbd470 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -684,26 +684,11 @@ where impl_stable_traits_for_trivial_type!(::std::path::Path); impl_stable_traits_for_trivial_type!(::std::path::PathBuf); -impl HashStable for ::std::collections::HashMap -where - K: ToStableHashKey + Eq, - V: HashStable, - R: BuildHasher, -{ - #[inline] - fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { - stable_hash_reduce(hcx, hasher, self.iter(), self.len(), |hasher, hcx, (key, value)| { - let key = key.to_stable_hash_key(hcx); - key.hash_stable(hcx, hasher); - value.hash_stable(hcx, hasher); - }); - } -} - -// It is not safe to implement HashStable for HashSet or any other collection type +// It is not safe to implement HashStable for HashSet, HashMap or any other collection type // with unstable but observable iteration order. // See https://github.com/rust-lang/compiler-team/issues/533 for further information. impl !HashStable for std::collections::HashSet {} +impl !HashStable for std::collections::HashMap {} impl HashStable for ::std::collections::BTreeMap where @@ -730,35 +715,6 @@ where } } -fn stable_hash_reduce( - hcx: &mut HCX, - hasher: &mut StableHasher, - mut collection: C, - length: usize, - hash_function: F, -) where - C: Iterator, - F: Fn(&mut StableHasher, &mut HCX, I), -{ - length.hash_stable(hcx, hasher); - - match length { - 1 => { - hash_function(hasher, hcx, collection.next().unwrap()); - } - _ => { - let hash = collection - .map(|value| { - let mut hasher = StableHasher::new(); - hash_function(&mut hasher, hcx, value); - hasher.finish::() - }) - .reduce(|accum, value| accum.wrapping_add(value)); - hash.hash_stable(hcx, hasher); - } - } -} - /// Controls what data we do or do not hash. /// Whenever a `HashStable` implementation caches its /// result, it needs to include `HashingControls` as part diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 7b40954e735d..c47abf5e988e 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -102,9 +102,9 @@ pub type PResult<'a, T> = Result>; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(PResult<'_, ()>, 16); -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(PResult<'_, bool>, 16); #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)] diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 30559871b4e2..cdcf67b26f80 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -48,6 +48,23 @@ impl<'a> ExtCtxt<'a> { ast::Path { span, segments, tokens: None } } + pub fn macro_call( + &self, + span: Span, + path: ast::Path, + delim: ast::token::Delimiter, + tokens: ast::tokenstream::TokenStream, + ) -> P { + P(ast::MacCall { + path, + args: P(ast::DelimArgs { + dspan: ast::tokenstream::DelimSpan { open: span, close: span }, + delim, + tokens, + }), + }) + } + pub fn ty_mt(&self, ty: P, mutbl: ast::Mutability) -> ast::MutTy { ast::MutTy { ty, mutbl } } @@ -265,6 +282,10 @@ impl<'a> ExtCtxt<'a> { self.expr(span, ast::ExprKind::Field(expr, field)) } + pub fn expr_macro_call(&self, span: Span, call: P) -> P { + self.expr(span, ast::ExprKind::MacCall(call)) + } + pub fn expr_binary( &self, sp: Span, @@ -410,16 +431,19 @@ impl<'a> ExtCtxt<'a> { self.expr(sp, ast::ExprKind::Tup(exprs)) } - pub fn expr_fail(&self, span: Span, msg: Symbol) -> P { - self.expr_call_global( - span, - [sym::std, sym::rt, sym::begin_panic].iter().map(|s| Ident::new(*s, span)).collect(), - thin_vec![self.expr_str(span, msg)], - ) - } - pub fn expr_unreachable(&self, span: Span) -> P { - self.expr_fail(span, Symbol::intern("internal error: entered unreachable code")) + self.expr_macro_call( + span, + self.macro_call( + span, + self.path_global( + span, + [sym::std, sym::unreachable].map(|s| Ident::new(s, span)).to_vec(), + ), + ast::token::Delimiter::Parenthesis, + ast::tokenstream::TokenStream::default(), + ), + ) } pub fn expr_ok(&self, sp: Span, expr: P) -> P { diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index a31be05ccc4d..9fff00ffeae1 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -266,7 +266,7 @@ struct MatcherPos { } // This type is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(MatcherPos, 16); impl MatcherPos { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index a0f86565929b..f6d63a293ba2 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3762,7 +3762,7 @@ impl<'hir> Node<'hir> { } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; // tidy-alphabetical-start diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index da5927685705..2a796ca5465c 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -226,6 +226,7 @@ language_item_table! { Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None; + OrderingEnum, sym::Ordering, ordering_enum, Target::Enum, GenericRequirement::Exact(0); PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None; diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index d5465bb5dd54..d3f51195dfb2 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -54,14 +54,20 @@ impl<'tcx> Bounds<'tcx> { span: Span, polarity: ty::PredicatePolarity, ) { - self.clauses.push(( + let clause = ( trait_ref .map_bound(|trait_ref| { ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, polarity }) }) .to_predicate(tcx), span, - )); + ); + // FIXME(-Znext-solver): We can likely remove this hack once the new trait solver lands. + if tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { + self.clauses.insert(0, clause); + } else { + self.clauses.push(clause); + } } pub fn push_projection_bound( diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 1286a724e957..a880445a27c1 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -964,7 +964,7 @@ pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { for r in attr::parse_repr_attr(tcx.sess, attr) { if let attr::ReprPacked(pack) = r && let Some(repr_pack) = repr.pack - && pack as u64 != repr_pack.bytes() + && pack != repr_pack { struct_span_code_err!( tcx.dcx(), diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index f482ae4f5fa0..00a0fca49072 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -107,6 +107,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::cttz | sym::bswap | sym::bitreverse + | sym::three_way_compare | sym::discriminant_value | sym::type_id | sym::likely @@ -418,6 +419,10 @@ pub fn check_intrinsic_type( | sym::bswap | sym::bitreverse => (1, 0, vec![param(0)], param(0)), + sym::three_way_compare => { + (1, 0, vec![param(0), param(0)], tcx.ty_ordering_enum(Some(span))) + } + sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { (1, 0, vec![param(0), param(0)], Ty::new_tup(tcx, &[param(0), tcx.types.bool])) } @@ -454,9 +459,8 @@ pub fn check_intrinsic_type( sym::unchecked_div | sym::unchecked_rem | sym::exact_div => { (1, 0, vec![param(0), param(0)], param(0)) } - sym::unchecked_shl | sym::unchecked_shr | sym::rotate_left | sym::rotate_right => { - (1, 0, vec![param(0), param(0)], param(0)) - } + sym::unchecked_shl | sym::unchecked_shr => (2, 0, vec![param(0), param(1)], param(0)), + sym::rotate_left | sym::rotate_right => (1, 0, vec![param(0), param(0)], param(0)), sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul => { (1, 0, vec![param(0), param(0)], param(0)) } @@ -624,7 +628,7 @@ pub fn check_intrinsic_type( | sym::simd_as | sym::simd_cast_ptr | sym::simd_expose_addr - | sym::simd_from_exposed_addr => (2, 0, vec![param(0)], param(1)), + | sym::simd_with_exposed_provenance => (2, 0, vec![param(0)], param(1)), sym::simd_bitmask => (2, 0, vec![param(0)], param(1)), sym::simd_select | sym::simd_select_bitmask => { (2, 0, vec![param(0), param(1), param(1)], param(1)) diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index ee3436805ca7..0b8ac9926e41 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -418,8 +418,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { { if let &hir::ClosureBinder::For { span: for_sp, .. } = binder { fn span_of_infer(ty: &hir::Ty<'_>) -> Option { - struct V; - impl<'v> Visitor<'v> for V { + /// Look for `_` anywhere in the signature of a `for<> ||` closure. + /// This is currently disallowed. + struct FindInferInClosureWithBinder; + impl<'v> Visitor<'v> for FindInferInClosureWithBinder { type Result = ControlFlow; fn visit_ty(&mut self, t: &'v hir::Ty<'v>) -> Self::Result { if matches!(t.kind, hir::TyKind::Infer) { @@ -429,7 +431,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } } } - V.visit_ty(ty).break_value() + FindInferInClosureWithBinder.visit_ty(ty).break_value() } let infer_in_rt_sp = match fn_decl.output { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index ca2e14ee3591..7a0890e50dac 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -1,6 +1,6 @@ use crate::errors::{ self, AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams, - ParenthesizedFnTraitExpansion, + ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits, }; use crate::fluent_generated as fluent; use crate::hir_ty_lowering::HirTyLowerer; @@ -8,19 +8,26 @@ use crate::traits::error_reporting::report_object_safety_error; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordMap; +use rustc_errors::MultiSpan; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, }; use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::traits::FulfillmentError; use rustc_middle::query::Key; -use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, suggest_constraining_type_param}; +use rustc_middle::ty::{AdtDef, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{Binder, TraitRef}; use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::symbol::{sym, Ident}; +use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::BytePos; use rustc_span::{Span, Symbol, DUMMY_SP}; -use rustc_trait_selection::traits::object_safety_violations_for_assoc_item; +use rustc_trait_selection::traits::{ + object_safety_violations_for_assoc_item, TraitAliasExpansionInfo, +}; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// On missing type parameters, emit an E0393 error and provide a structured suggestion using @@ -1024,6 +1031,170 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ok(()) } } + + pub fn report_prohibit_generics_error<'a>( + &self, + segments: impl Iterator> + Clone, + args_visitors: impl Iterator> + Clone, + err_extend: GenericsArgsErrExtend<'_>, + ) -> ErrorGuaranteed { + #[derive(PartialEq, Eq, Hash)] + enum ProhibitGenericsArg { + Lifetime, + Type, + Const, + Infer, + } + + let mut prohibit_args = FxIndexSet::default(); + args_visitors.for_each(|arg| { + match arg { + hir::GenericArg::Lifetime(_) => prohibit_args.insert(ProhibitGenericsArg::Lifetime), + hir::GenericArg::Type(_) => prohibit_args.insert(ProhibitGenericsArg::Type), + hir::GenericArg::Const(_) => prohibit_args.insert(ProhibitGenericsArg::Const), + hir::GenericArg::Infer(_) => prohibit_args.insert(ProhibitGenericsArg::Infer), + }; + }); + + let types_and_spans: Vec<_> = segments + .clone() + .flat_map(|segment| { + if segment.args().args.is_empty() { + None + } else { + Some(( + match segment.res { + hir::def::Res::PrimTy(ty) => { + format!("{} `{}`", segment.res.descr(), ty.name()) + } + hir::def::Res::Def(_, def_id) + if let Some(name) = self.tcx().opt_item_name(def_id) => + { + format!("{} `{name}`", segment.res.descr()) + } + hir::def::Res::Err => "this type".to_string(), + _ => segment.res.descr().to_string(), + }, + segment.ident.span, + )) + } + }) + .collect(); + let this_type = match &types_and_spans[..] { + [.., _, (last, _)] => format!( + "{} and {last}", + types_and_spans[..types_and_spans.len() - 1] + .iter() + .map(|(x, _)| x.as_str()) + .intersperse(", ") + .collect::() + ), + [(only, _)] => only.to_string(), + [] => "this type".to_string(), + }; + + let arg_spans: Vec = segments + .clone() + .flat_map(|segment| segment.args().args) + .map(|arg| arg.span()) + .collect(); + + let mut kinds = Vec::with_capacity(4); + prohibit_args.iter().for_each(|arg| match arg { + ProhibitGenericsArg::Lifetime => kinds.push("lifetime"), + ProhibitGenericsArg::Type => kinds.push("type"), + ProhibitGenericsArg::Const => kinds.push("const"), + ProhibitGenericsArg::Infer => kinds.push("generic"), + }); + + let (kind, s) = match kinds[..] { + [.., _, last] => ( + format!( + "{} and {last}", + kinds[..kinds.len() - 1] + .iter() + .map(|&x| x) + .intersperse(", ") + .collect::() + ), + "s", + ), + [only] => (only.to_string(), ""), + [] => unreachable!("expected at least one generic to prohibit"), + }; + let last_span = *arg_spans.last().unwrap(); + let span: MultiSpan = arg_spans.into(); + let mut err = struct_span_code_err!( + self.tcx().dcx(), + span, + E0109, + "{kind} arguments are not allowed on {this_type}", + ); + err.span_label(last_span, format!("{kind} argument{s} not allowed")); + for (what, span) in types_and_spans { + err.span_label(span, format!("not allowed on {what}")); + } + generics_args_err_extend(self.tcx(), segments.clone(), &mut err, err_extend); + let reported = err.emit(); + self.set_tainted_by_errors(reported); + reported + } + + pub fn report_trait_object_addition_traits_error( + &self, + regular_traits: &Vec>, + ) -> ErrorGuaranteed { + let tcx = self.tcx(); + let first_trait = ®ular_traits[0]; + let additional_trait = ®ular_traits[1]; + let mut err = struct_span_code_err!( + tcx.dcx(), + additional_trait.bottom().1, + E0225, + "only auto traits can be used as additional traits in a trait object" + ); + additional_trait.label_with_exp_info( + &mut err, + "additional non-auto trait", + "additional use", + ); + first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use"); + err.help(format!( + "consider creating a new trait with all of these as supertraits and using that \ + trait here instead: `trait NewTrait: {} {{}}`", + regular_traits + .iter() + // FIXME: This should `print_sugared`, but also needs to integrate projection bounds... + .map(|t| t.trait_ref().print_only_trait_path().to_string()) + .collect::>() + .join(" + "), + )); + err.note( + "auto-traits like `Send` and `Sync` are traits that have special properties; \ + for more information on them, visit \ + ", + ); + let reported = err.emit(); + self.set_tainted_by_errors(reported); + reported + } + + pub fn report_trait_object_with_no_traits_error( + &self, + span: Span, + trait_bounds: &Vec<(Binder<'tcx, TraitRef<'tcx>>, Span)>, + ) -> ErrorGuaranteed { + let tcx = self.tcx(); + let trait_alias_span = trait_bounds + .iter() + .map(|&(trait_ref, _)| trait_ref.def_id()) + .find(|&trait_ref| tcx.is_trait_alias(trait_ref)) + .map(|trait_ref| tcx.def_span(trait_ref)); + let reported = + tcx.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }); + self.set_tainted_by_errors(reported); + reported + } } /// Emits an error regarding forbidden type binding associations @@ -1031,7 +1202,7 @@ pub fn prohibit_assoc_item_binding( tcx: TyCtxt<'_>, span: Span, segment: Option<(&hir::PathSegment<'_>, Span)>, -) { +) -> ErrorGuaranteed { tcx.dcx().emit_err(AssocTypeBindingNotAllowed { span, fn_trait_expansion: if let Some((segment, span)) = segment @@ -1044,7 +1215,7 @@ pub fn prohibit_assoc_item_binding( } else { None }, - }); + }) } pub(crate) fn fn_trait_to_string( @@ -1099,3 +1270,208 @@ pub(crate) fn fn_trait_to_string( format!("{}<{}, Output={}>", trait_segment.ident, args, ret) } } + +/// Used for generics args error extend. +pub enum GenericsArgsErrExtend<'tcx> { + EnumVariant { + qself: &'tcx hir::Ty<'tcx>, + assoc_segment: &'tcx hir::PathSegment<'tcx>, + adt_def: AdtDef<'tcx>, + }, + OpaqueTy, + PrimTy(hir::PrimTy), + SelfTyAlias { + def_id: DefId, + span: Span, + }, + SelfTyParam(Span), + TyParam(DefId), + DefVariant, + None, +} + +fn generics_args_err_extend<'a>( + tcx: TyCtxt<'_>, + segments: impl Iterator> + Clone, + err: &mut Diag<'_>, + err_extend: GenericsArgsErrExtend<'_>, +) { + match err_extend { + GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def } => { + err.note("enum variants can't have type parameters"); + let type_name = tcx.item_name(adt_def.did()); + let msg = format!( + "you might have meant to specify type parameters on enum \ + `{type_name}`" + ); + let Some(args) = assoc_segment.args else { + return; + }; + // Get the span of the generics args *including* the leading `::`. + // We do so by stretching args.span_ext to the left by 2. Earlier + // it was done based on the end of assoc segment but that sometimes + // led to impossible spans and caused issues like #116473 + let args_span = args.span_ext.with_lo(args.span_ext.lo() - BytePos(2)); + if tcx.generics_of(adt_def.did()).count() == 0 { + // FIXME(estebank): we could also verify that the arguments being + // work for the `enum`, instead of just looking if it takes *any*. + err.span_suggestion_verbose( + args_span, + format!("{type_name} doesn't have generic parameters"), + "", + Applicability::MachineApplicable, + ); + return; + } + let Ok(snippet) = tcx.sess.source_map().span_to_snippet(args_span) else { + err.note(msg); + return; + }; + let (qself_sugg_span, is_self) = + if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { + // If the path segment already has type params, we want to overwrite + // them. + match &path.segments { + // `segment` is the previous to last element on the path, + // which would normally be the `enum` itself, while the last + // `_` `PathSegment` corresponds to the variant. + [ + .., + hir::PathSegment { + ident, args, res: Res::Def(DefKind::Enum, _), .. + }, + _, + ] => ( + // We need to include the `::` in `Type::Variant::` + // to point the span to `::`, not just ``. + ident + .span + .shrink_to_hi() + .to(args.map_or(ident.span.shrink_to_hi(), |a| a.span_ext)), + false, + ), + [segment] => { + ( + // We need to include the `::` in `Type::Variant::` + // to point the span to `::`, not just ``. + segment.ident.span.shrink_to_hi().to(segment + .args + .map_or(segment.ident.span.shrink_to_hi(), |a| a.span_ext)), + kw::SelfUpper == segment.ident.name, + ) + } + _ => { + err.note(msg); + return; + } + } + } else { + err.note(msg); + return; + }; + let suggestion = vec![ + if is_self { + // Account for people writing `Self::Variant::`, where + // `Self` is the enum, and suggest replacing `Self` with the + // appropriate type: `Type::::Variant`. + (qself.span, format!("{type_name}{snippet}")) + } else { + (qself_sugg_span, snippet) + }, + (args_span, String::new()), + ]; + err.multipart_suggestion_verbose(msg, suggestion, Applicability::MaybeIncorrect); + } + GenericsArgsErrExtend::PrimTy(prim_ty) => { + let name = prim_ty.name_str(); + for segment in segments { + if let Some(args) = segment.args { + err.span_suggestion_verbose( + segment.ident.span.shrink_to_hi().to(args.span_ext), + format!("primitive type `{name}` doesn't have generic parameters"), + "", + Applicability::MaybeIncorrect, + ); + } + } + } + GenericsArgsErrExtend::OpaqueTy => { + err.note("`impl Trait` types can't have type parameters"); + } + GenericsArgsErrExtend::DefVariant => { + err.note("enum variants can't have type parameters"); + } + GenericsArgsErrExtend::TyParam(def_id) => { + if let Some(span) = tcx.def_ident_span(def_id) { + let name = tcx.item_name(def_id); + err.span_note(span, format!("type parameter `{name}` defined here")); + } + } + GenericsArgsErrExtend::SelfTyParam(span) => { + err.span_suggestion_verbose( + span, + "the `Self` type doesn't accept type parameters", + "", + Applicability::MaybeIncorrect, + ); + } + GenericsArgsErrExtend::SelfTyAlias { def_id, span } => { + let ty = tcx.at(span).type_of(def_id).instantiate_identity(); + let span_of_impl = tcx.span_of_impl(def_id); + let def_id = match *ty.kind() { + ty::Adt(self_def, _) => self_def.did(), + _ => return, + }; + + let type_name = tcx.item_name(def_id); + let span_of_ty = tcx.def_ident_span(def_id); + let generics = tcx.generics_of(def_id).count(); + + let msg = format!("`Self` is of type `{ty}`"); + if let (Ok(i_sp), Some(t_sp)) = (span_of_impl, span_of_ty) { + let mut span: MultiSpan = vec![t_sp].into(); + span.push_span_label( + i_sp, + format!("`Self` is on type `{type_name}` in this `impl`"), + ); + let mut postfix = ""; + if generics == 0 { + postfix = ", which doesn't have generic parameters"; + } + span.push_span_label(t_sp, format!("`Self` corresponds to this type{postfix}")); + err.span_note(span, msg); + } else { + err.note(msg); + } + for segment in segments { + if let Some(args) = segment.args + && segment.ident.name == kw::SelfUpper + { + if generics == 0 { + // FIXME(estebank): we could also verify that the arguments being + // work for the `enum`, instead of just looking if it takes *any*. + err.span_suggestion_verbose( + segment.ident.span.shrink_to_hi().to(args.span_ext), + "the `Self` type doesn't accept type parameters", + "", + Applicability::MachineApplicable, + ); + return; + } else { + err.span_suggestion_verbose( + segment.ident.span, + format!( + "the `Self` type doesn't accept type parameters, use the \ + concrete type's name `{type_name}` instead if you want to \ + specify its type parameters" + ), + type_name, + Applicability::MaybeIncorrect, + ); + } + } + } + } + _ => {} + } +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 8886a78c6ecd..f726f2a7b890 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -14,7 +14,7 @@ //! trait references and bounds. mod bounds; -mod errors; +pub mod errors; pub mod generics; mod lint; mod object_safety; @@ -22,14 +22,14 @@ mod object_safety; use crate::bounds::Bounds; use crate::collect::HirPlaceholderCollector; use crate::errors::AmbiguousLifetimeBound; -use crate::hir_ty_lowering::errors::prohibit_assoc_item_binding; +use crate::hir_ty_lowering::errors::{prohibit_assoc_item_binding, GenericsArgsErrExtend}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; use crate::require_c_abi_if_c_variadic; use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::{ - codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, FatalError, MultiSpan, + codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, FatalError, }; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; @@ -46,7 +46,7 @@ use rustc_middle::ty::{ use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_span::{sym, BytePos, Span, DUMMY_SP}; +use rustc_span::{sym, Span, DUMMY_SP}; use rustc_target::spec::abi; use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::{self, ObligationCtxt}; @@ -632,7 +632,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { trait_ref: &hir::TraitRef<'tcx>, self_ty: Ty<'tcx>, ) -> ty::TraitRef<'tcx> { - self.prohibit_generic_args(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {}); + let _ = self.prohibit_generic_args( + trait_ref.path.segments.split_last().unwrap().1.iter(), + GenericsArgsErrExtend::None, + ); self.lower_mono_trait_ref( trait_ref.path.span, @@ -681,7 +684,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()); let trait_segment = trait_ref.path.segments.last().unwrap(); - self.prohibit_generic_args(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {}); + let _ = self.prohibit_generic_args( + trait_ref.path.segments.split_last().unwrap().1.iter(), + GenericsArgsErrExtend::None, + ); self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false); let (generic_args, arg_count) = self.lower_generic_args_of_path( @@ -995,8 +1001,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_ref_id: hir::HirId, span: Span, qself_ty: Ty<'tcx>, - qself: &hir::Ty<'_>, - assoc_segment: &hir::PathSegment<'tcx>, + qself: &'tcx hir::Ty<'tcx>, + assoc_segment: &'tcx hir::PathSegment<'tcx>, permit_variants: bool, ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> { debug!(%qself_ty, ?assoc_segment.ident); @@ -1020,99 +1026,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let Some(variant_def) = variant_def { if permit_variants { tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None); - self.prohibit_generic_args(slice::from_ref(assoc_segment).iter(), |err| { - err.note("enum variants can't have type parameters"); - let type_name = tcx.item_name(adt_def.did()); - let msg = format!( - "you might have meant to specify type parameters on enum \ - `{type_name}`" - ); - let Some(args) = assoc_segment.args else { - return; - }; - // Get the span of the generics args *including* the leading `::`. - // We do so by stretching args.span_ext to the left by 2. Earlier - // it was done based on the end of assoc segment but that sometimes - // led to impossible spans and caused issues like #116473 - let args_span = args.span_ext.with_lo(args.span_ext.lo() - BytePos(2)); - if tcx.generics_of(adt_def.did()).count() == 0 { - // FIXME(estebank): we could also verify that the arguments being - // work for the `enum`, instead of just looking if it takes *any*. - err.span_suggestion_verbose( - args_span, - format!("{type_name} doesn't have generic parameters"), - "", - Applicability::MachineApplicable, - ); - return; - } - let Ok(snippet) = tcx.sess.source_map().span_to_snippet(args_span) - else { - err.note(msg); - return; - }; - let (qself_sugg_span, is_self) = - if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = - &qself.kind - { - // If the path segment already has type params, we want to overwrite - // them. - match &path.segments { - // `segment` is the previous to last element on the path, - // which would normally be the `enum` itself, while the last - // `_` `PathSegment` corresponds to the variant. - [ - .., - hir::PathSegment { - ident, - args, - res: Res::Def(DefKind::Enum, _), - .. - }, - _, - ] => ( - // We need to include the `::` in `Type::Variant::` - // to point the span to `::`, not just ``. - ident.span.shrink_to_hi().to(args - .map_or(ident.span.shrink_to_hi(), |a| a.span_ext)), - false, - ), - [segment] => ( - // We need to include the `::` in `Type::Variant::` - // to point the span to `::`, not just ``. - segment.ident.span.shrink_to_hi().to(segment - .args - .map_or(segment.ident.span.shrink_to_hi(), |a| { - a.span_ext - })), - kw::SelfUpper == segment.ident.name, - ), - _ => { - err.note(msg); - return; - } - } - } else { - err.note(msg); - return; - }; - let suggestion = vec![ - if is_self { - // Account for people writing `Self::Variant::`, where - // `Self` is the enum, and suggest replacing `Self` with the - // appropriate type: `Type::::Variant`. - (qself.span, format!("{type_name}{snippet}")) - } else { - (qself_sugg_span, snippet) - }, - (args_span, String::new()), - ]; - err.multipart_suggestion_verbose( - msg, - suggestion, - Applicability::MaybeIncorrect, - ); - }); + let _ = self.prohibit_generic_args( + slice::from_ref(assoc_segment).iter(), + GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def }, + ); return Ok((qself_ty, DefKind::Variant, variant_def.def_id)); } else { variant_resolution = Some(variant_def.def_id); @@ -1624,111 +1541,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub fn prohibit_generic_args<'a>( &self, segments: impl Iterator> + Clone, - extend: impl Fn(&mut Diag<'_>), - ) -> bool { - let args = segments.clone().flat_map(|segment| segment.args().args); - - let (lt, ty, ct, inf) = - args.clone().fold((false, false, false, false), |(lt, ty, ct, inf), arg| match arg { - hir::GenericArg::Lifetime(_) => (true, ty, ct, inf), - hir::GenericArg::Type(_) => (lt, true, ct, inf), - hir::GenericArg::Const(_) => (lt, ty, true, inf), - hir::GenericArg::Infer(_) => (lt, ty, ct, true), - }); - let mut emitted = false; - if lt || ty || ct || inf { - let types_and_spans: Vec<_> = segments - .clone() - .flat_map(|segment| { - if segment.args().args.is_empty() { - None - } else { - Some(( - match segment.res { - Res::PrimTy(ty) => { - format!("{} `{}`", segment.res.descr(), ty.name()) - } - Res::Def(_, def_id) - if let Some(name) = self.tcx().opt_item_name(def_id) => - { - format!("{} `{name}`", segment.res.descr()) - } - Res::Err => "this type".to_string(), - _ => segment.res.descr().to_string(), - }, - segment.ident.span, - )) - } - }) - .collect(); - let this_type = match &types_and_spans[..] { - [.., _, (last, _)] => format!( - "{} and {last}", - types_and_spans[..types_and_spans.len() - 1] - .iter() - .map(|(x, _)| x.as_str()) - .intersperse(", ") - .collect::() - ), - [(only, _)] => only.to_string(), - [] => "this type".to_string(), - }; - - let arg_spans: Vec = args.map(|arg| arg.span()).collect(); - - let mut kinds = Vec::with_capacity(4); - if lt { - kinds.push("lifetime"); - } - if ty { - kinds.push("type"); - } - if ct { - kinds.push("const"); - } - if inf { - kinds.push("generic"); - } - let (kind, s) = match kinds[..] { - [.., _, last] => ( - format!( - "{} and {last}", - kinds[..kinds.len() - 1] - .iter() - .map(|&x| x) - .intersperse(", ") - .collect::() - ), - "s", - ), - [only] => (only.to_string(), ""), - [] => unreachable!("expected at least one generic to prohibit"), - }; - let last_span = *arg_spans.last().unwrap(); - let span: MultiSpan = arg_spans.into(); - let mut err = struct_span_code_err!( - self.tcx().dcx(), - span, - E0109, - "{kind} arguments are not allowed on {this_type}", - ); - err.span_label(last_span, format!("{kind} argument{s} not allowed")); - for (what, span) in types_and_spans { - err.span_label(span, format!("not allowed on {what}")); - } - extend(&mut err); - self.set_tainted_by_errors(err.emit()); - emitted = true; + err_extend: GenericsArgsErrExtend<'_>, + ) -> Result<(), ErrorGuaranteed> { + let args_visitors = segments.clone().flat_map(|segment| segment.args().args); + let mut result = Ok(()); + if let Some(_) = args_visitors.clone().next() { + result = Err(self.report_prohibit_generics_error( + segments.clone(), + args_visitors, + err_extend, + )); } for segment in segments { // Only emit the first error to avoid overloading the user with error messages. if let Some(b) = segment.args().bindings.first() { - prohibit_assoc_item_binding(self.tcx(), b.span, None); - return true; + return Err(prohibit_assoc_item_binding(self.tcx(), b.span, None)); } } - emitted + + result } /// Probe path segments that are semantically allowed to have generic arguments. @@ -1893,9 +1725,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Check for desugared `impl Trait`. assert!(tcx.is_type_alias_impl_trait(did)); let item_segment = path.segments.split_last().unwrap(); - self.prohibit_generic_args(item_segment.1.iter(), |err| { - err.note("`impl Trait` types can't have type parameters"); - }); + let _ = self + .prohibit_generic_args(item_segment.1.iter(), GenericsArgsErrExtend::OpaqueTy); let args = self.lower_generic_args_of_path_segment(span, did, item_segment.0); Ty::new_opaque(tcx, did, args) } @@ -1908,7 +1739,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { did, ) => { assert_eq!(opt_self_ty, None); - self.prohibit_generic_args(path.segments.split_last().unwrap().1.iter(), |_| {}); + let _ = self.prohibit_generic_args( + path.segments.split_last().unwrap().1.iter(), + GenericsArgsErrExtend::None, + ); self.lower_path_segment(span, did, path.segments.last().unwrap()) } Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => { @@ -1920,13 +1754,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.probe_generic_path_segments(path.segments, None, kind, def_id, span); let indices: FxHashSet<_> = generic_segments.iter().map(|GenericPathSegment(_, index)| index).collect(); - self.prohibit_generic_args( + let _ = self.prohibit_generic_args( path.segments.iter().enumerate().filter_map(|(index, seg)| { if !indices.contains(&index) { Some(seg) } else { None } }), - |err| { - err.note("enum variants can't have type parameters"); - }, + GenericsArgsErrExtend::DefVariant, ); let GenericPathSegment(def_id, index) = generic_segments.last().unwrap(); @@ -1934,27 +1766,25 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } Res::Def(DefKind::TyParam, def_id) => { assert_eq!(opt_self_ty, None); - self.prohibit_generic_args(path.segments.iter(), |err| { - if let Some(span) = tcx.def_ident_span(def_id) { - let name = tcx.item_name(def_id); - err.span_note(span, format!("type parameter `{name}` defined here")); - } - }); + let _ = self.prohibit_generic_args( + path.segments.iter(), + GenericsArgsErrExtend::TyParam(def_id), + ); self.lower_ty_param(hir_id) } Res::SelfTyParam { .. } => { // `Self` in trait or type alias. assert_eq!(opt_self_ty, None); - self.prohibit_generic_args(path.segments.iter(), |err| { + let _ = self.prohibit_generic_args( + path.segments.iter(), if let [hir::PathSegment { args: Some(args), ident, .. }] = &path.segments { - err.span_suggestion_verbose( + GenericsArgsErrExtend::SelfTyParam( ident.span.shrink_to_hi().to(args.span_ext), - "the `Self` type doesn't accept type parameters", - "", - Applicability::MaybeIncorrect, - ); - } - }); + ) + } else { + GenericsArgsErrExtend::None + }, + ); tcx.types.self_param } Res::SelfTyAlias { alias_to: def_id, forbid_generic, .. } => { @@ -1962,65 +1792,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assert_eq!(opt_self_ty, None); // Try to evaluate any array length constants. let ty = tcx.at(span).type_of(def_id).instantiate_identity(); - let span_of_impl = tcx.span_of_impl(def_id); - self.prohibit_generic_args(path.segments.iter(), |err| { - let def_id = match *ty.kind() { - ty::Adt(self_def, _) => self_def.did(), - _ => return, - }; - - let type_name = tcx.item_name(def_id); - let span_of_ty = tcx.def_ident_span(def_id); - let generics = tcx.generics_of(def_id).count(); - - let msg = format!("`Self` is of type `{ty}`"); - if let (Ok(i_sp), Some(t_sp)) = (span_of_impl, span_of_ty) { - let mut span: MultiSpan = vec![t_sp].into(); - span.push_span_label( - i_sp, - format!("`Self` is on type `{type_name}` in this `impl`"), - ); - let mut postfix = ""; - if generics == 0 { - postfix = ", which doesn't have generic parameters"; - } - span.push_span_label( - t_sp, - format!("`Self` corresponds to this type{postfix}"), - ); - err.span_note(span, msg); - } else { - err.note(msg); - } - for segment in path.segments { - if let Some(args) = segment.args - && segment.ident.name == kw::SelfUpper - { - if generics == 0 { - // FIXME(estebank): we could also verify that the arguments being - // work for the `enum`, instead of just looking if it takes *any*. - err.span_suggestion_verbose( - segment.ident.span.shrink_to_hi().to(args.span_ext), - "the `Self` type doesn't accept type parameters", - "", - Applicability::MachineApplicable, - ); - return; - } else { - err.span_suggestion_verbose( - segment.ident.span, - format!( - "the `Self` type doesn't accept type parameters, use the \ - concrete type's name `{type_name}` instead if you want to \ - specify its type parameters" - ), - type_name, - Applicability::MaybeIncorrect, - ); - } - } - } - }); + let _ = self.prohibit_generic_args( + path.segments.iter(), + GenericsArgsErrExtend::SelfTyAlias { def_id, span }, + ); // HACK(min_const_generics): Forbid generic `Self` types // here as we can't easily do that during nameres. // @@ -2061,7 +1836,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } Res::Def(DefKind::AssocTy, def_id) => { debug_assert!(path.segments.len() >= 2); - self.prohibit_generic_args(path.segments[..path.segments.len() - 2].iter(), |_| {}); + let _ = self.prohibit_generic_args( + path.segments[..path.segments.len() - 2].iter(), + GenericsArgsErrExtend::None, + ); // HACK: until we support ``, assume all of them are. let constness = if tcx.has_attr(tcx.parent(def_id), sym::const_trait) { ty::BoundConstness::ConstIfConst @@ -2079,19 +1857,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } Res::PrimTy(prim_ty) => { assert_eq!(opt_self_ty, None); - self.prohibit_generic_args(path.segments.iter(), |err| { - let name = prim_ty.name_str(); - for segment in path.segments { - if let Some(args) = segment.args { - err.span_suggestion_verbose( - segment.ident.span.shrink_to_hi().to(args.span_ext), - format!("primitive type `{name}` doesn't have generic parameters"), - "", - Applicability::MaybeIncorrect, - ); - } - } - }); + let _ = self.prohibit_generic_args( + path.segments.iter(), + GenericsArgsErrExtend::PrimTy(prim_ty), + ); match prim_ty { hir::PrimTy::Bool => tcx.types.bool, hir::PrimTy::Char => tcx.types.char, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs index b5b3a9131c57..d3ca35ba481e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs @@ -1,5 +1,4 @@ use crate::bounds::Bounds; -use crate::errors::TraitObjectDeclaredWithNoTraits; use crate::hir_ty_lowering::{GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::{codes::*, struct_span_code_err}; @@ -86,47 +85,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id())); if regular_traits.len() > 1 { - let first_trait = ®ular_traits[0]; - let additional_trait = ®ular_traits[1]; - let mut err = struct_span_code_err!( - tcx.dcx(), - additional_trait.bottom().1, - E0225, - "only auto traits can be used as additional traits in a trait object" - ); - additional_trait.label_with_exp_info( - &mut err, - "additional non-auto trait", - "additional use", - ); - first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use"); - err.help(format!( - "consider creating a new trait with all of these as supertraits and using that \ - trait here instead: `trait NewTrait: {} {{}}`", - regular_traits - .iter() - // FIXME: This should `print_sugared`, but also needs to integrate projection bounds... - .map(|t| t.trait_ref().print_only_trait_path().to_string()) - .collect::>() - .join(" + "), - )); - err.note( - "auto-traits like `Send` and `Sync` are traits that have special properties; \ - for more information on them, visit \ - ", - ); - self.set_tainted_by_errors(err.emit()); - } - - if regular_traits.is_empty() && auto_traits.is_empty() { - let trait_alias_span = trait_bounds - .iter() - .map(|&(trait_ref, _)| trait_ref.def_id()) - .find(|&trait_ref| tcx.is_trait_alias(trait_ref)) - .map(|trait_ref| tcx.def_span(trait_ref)); - let reported = - tcx.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }); - self.set_tainted_by_errors(reported); + let _ = self.report_trait_object_addition_traits_error(®ular_traits); + } else if regular_traits.is_empty() && auto_traits.is_empty() { + let reported = self.report_trait_object_with_no_traits_error(span, &trait_bounds); return Ty::new_error(tcx, reported); } diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index f7af438ad16c..fbdc3d1adb81 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -86,7 +86,7 @@ hir_typeck_invalid_callee = expected function, found {$ty} hir_typeck_lossy_provenance_int2ptr = strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}` .suggestion = use `.with_addr()` to adjust a valid pointer in the same allocation, to this address - .help = if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::from_exposed_addr()` instead + .help = if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead hir_typeck_lossy_provenance_ptr2int = under strict provenance it is considered bad style to cast pointer `{$expr_ty}` to integer `{$cast_ty}` diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 011607bacc6c..8d4d0833eef6 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -11,6 +11,7 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, GenericArg, Node, QPath}; +use rustc_hir_analysis::hir_ty_lowering::errors::GenericsArgsErrExtend; use rustc_hir_analysis::hir_ty_lowering::generics::{ check_generic_arg_count_for_call, lower_generic_args, }; @@ -1177,11 +1178,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let indices: FxHashSet<_> = generic_segments.iter().map(|GenericPathSegment(_, index)| index).collect(); - let generics_has_err = self.lowerer().prohibit_generic_args( + let generics_err = self.lowerer().prohibit_generic_args( segments.iter().enumerate().filter_map(|(index, seg)| { if !indices.contains(&index) || is_alias_variant_ctor { Some(seg) } else { None } }), - |_| {}, + GenericsArgsErrExtend::None, ); if let Res::Local(hid) = res { @@ -1191,7 +1192,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return (ty, res); } - if generics_has_err { + if let Err(_) = generics_err { // Don't try to infer type parameters when prohibited generic arguments were given. user_self_ty = None; } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 8ea1a88be5de..5f5ff40fb9f4 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1916,22 +1916,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx hir::Pat<'tcx>, ty: Ty<'tcx>, ) { - struct V { - pat_hir_ids: Vec, - } - - impl<'tcx> Visitor<'tcx> for V { - fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { - self.pat_hir_ids.push(p.hir_id); - hir::intravisit::walk_pat(self, p); - } - } if let Err(guar) = ty.error_reported() { + struct OverwritePatternsWithError { + pat_hir_ids: Vec, + } + impl<'tcx> Visitor<'tcx> for OverwritePatternsWithError { + fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { + self.pat_hir_ids.push(p.hir_id); + hir::intravisit::walk_pat(self, p); + } + } // Override the types everywhere with `err()` to avoid knock on errors. let err = Ty::new_error(self.tcx, guar); self.write_ty(hir_id, err); self.write_ty(pat.hir_id, err); - let mut visitor = V { pat_hir_ids: vec![] }; + let mut visitor = OverwritePatternsWithError { pat_hir_ids: vec![] }; hir::intravisit::walk_pat(&mut visitor, pat); // Mark all the subpatterns as `{type error}` as well. This allows errors for specific // subpatterns to be silenced. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 74f27cfebbd2..05e7c5b2b418 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -416,13 +416,13 @@ fn parse_never_type_options_attr( continue; } - if item.has_name(sym::diverging_block_default) && fallback.is_none() { - let mode = item.value_str().unwrap(); - match mode { + if item.has_name(sym::diverging_block_default) && block.is_none() { + let default = item.value_str().unwrap(); + match default { sym::unit => block = Some(DivergingBlockBehavior::Unit), sym::never => block = Some(DivergingBlockBehavior::Never), _ => { - tcx.dcx().span_err(item.span(), format!("unknown diverging block default: `{mode}` (supported: `unit` and `never`)")); + tcx.dcx().span_err(item.span(), format!("unknown diverging block default: `{default}` (supported: `unit` and `never`)")); } }; continue; @@ -431,7 +431,7 @@ fn parse_never_type_options_attr( tcx.dcx().span_err( item.span(), format!( - "unknown never type option: `{}` (supported: `fallback`)", + "unknown or duplicate never type option: `{}` (supported: `fallback`, `diverging_block_default`)", item.name_or_empty() ), ); diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 12f8e42c78f9..c7e563035fc3 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -400,7 +400,7 @@ enum Chunk { } // This type is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] crate::static_assert_size!(Chunk, 16); impl ChunkedBitSet { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 339c8ac10b33..3e89327d20fd 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -483,7 +483,7 @@ pub enum SubregionOrigin<'tcx> { } // `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(SubregionOrigin<'_>, 32); impl<'tcx> SubregionOrigin<'tcx> { diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index ee9ce842d00a..0444cbe2ee41 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -32,7 +32,7 @@ #[macro_use] extern crate rustc_macros; -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] #[macro_use] extern crate rustc_data_structures; #[macro_use] diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 616f5cc04564..94ad0f5b1c86 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -112,7 +112,7 @@ impl<'tcx> PolyTraitObligation<'tcx> { } // `PredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(PredicateObligation<'_>, 48); pub type PredicateObligations<'tcx> = Vec>; diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 9f09f46ea5a9..7e9d35ca6c1a 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2749,7 +2749,7 @@ declare_lint! { /// memory the pointer is allowed to read/write. Casting an integer, which /// doesn't have provenance, to a pointer requires the compiler to assign /// (guess) provenance. The compiler assigns "all exposed valid" (see the - /// docs of [`ptr::from_exposed_addr`] for more information about this + /// docs of [`ptr::with_exposed_provenance`] for more information about this /// "exposing"). This penalizes the optimiser and is not well suited for /// dynamic analysis/dynamic program verification (e.g. Miri or CHERI /// platforms). @@ -2757,11 +2757,11 @@ declare_lint! { /// It is much better to use [`ptr::with_addr`] instead to specify the /// provenance you want. If using this function is not possible because the /// code relies on exposed provenance then there is as an escape hatch - /// [`ptr::from_exposed_addr`]. + /// [`ptr::with_exposed_provenance`]. /// /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 /// [`ptr::with_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.with_addr - /// [`ptr::from_exposed_addr`]: https://doc.rust-lang.org/core/ptr/fn.from_exposed_addr.html + /// [`ptr::with_exposed_provenance`]: https://doc.rust-lang.org/core/ptr/fn.with_exposed_provenance.html pub FUZZY_PROVENANCE_CASTS, Allow, "a fuzzy integer to pointer cast is used", diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml index 6009a43e9855..3ff86f700a53 100644 --- a/compiler/rustc_log/Cargo.toml +++ b/compiler/rustc_log/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" tracing = "0.1.28" tracing-core = "=0.1.30" # FIXME(Nilstrieb) tracing has a deadlock: https://github.com/tokio-rs/tracing/issues/2635 tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } -tracing-tree = "0.2.0" +tracing-tree = "0.3.0" # tidy-alphabetical-end [dev-dependencies] diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 7d6d39a2a8a3..ec9697bbd355 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -1,6 +1,7 @@ use crate::mir::mono::Linkage; use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_span::symbol::Symbol; +use rustc_target::abi::Align; use rustc_target::spec::SanitizerSet; #[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)] @@ -42,7 +43,7 @@ pub struct CodegenFnAttrs { pub instruction_set: Option, /// The `#[repr(align(...))]` attribute. Indicates the value of which the function should be /// aligned to. - pub alignment: Option, + pub alignment: Option, } #[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 500536a9e9ed..46520d69e183 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -2,7 +2,7 @@ //! outside their scopes. This pass will also generate a set of exported items //! which are available for use externally when compiled as a library. use crate::ty::{TyCtxt, Visibility}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def::DefKind; use rustc_macros::HashStable; @@ -90,7 +90,7 @@ impl EffectiveVisibility { /// Holds a map of effective visibilities for reachable HIR nodes. #[derive(Clone, Debug)] pub struct EffectiveVisibilities { - map: FxHashMap, + map: FxIndexMap, } impl EffectiveVisibilities { @@ -130,9 +130,8 @@ impl EffectiveVisibilities { eff_vis: &EffectiveVisibility, tcx: TyCtxt<'_>, ) { - use std::collections::hash_map::Entry; match self.map.entry(def_id) { - Entry::Occupied(mut occupied) => { + IndexEntry::Occupied(mut occupied) => { let old_eff_vis = occupied.get_mut(); for l in Level::all_levels() { let vis_at_level = eff_vis.at_level(l); @@ -145,7 +144,7 @@ impl EffectiveVisibilities { } old_eff_vis } - Entry::Vacant(vacant) => vacant.insert(*eff_vis), + IndexEntry::Vacant(vacant) => vacant.insert(*eff_vis), }; } diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 2663a6b551ec..155af0620127 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -70,7 +70,7 @@ pub enum ConstValue<'tcx> { }, } -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(ConstValue<'_>, 24); impl<'tcx> ConstValue<'tcx> { diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index c86970635a55..e9be26d058b4 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -88,7 +88,7 @@ pub type EvalToConstValueResult<'tcx> = Result, ErrorHandled>; /// This is needed in `thir::pattern::lower_inline_const`. pub type EvalToValTreeResult<'tcx> = Result>, ErrorHandled>; -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(InterpErrorInfo<'_>, 8); /// Packages the kind of error we got from the const code interpreter diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 24d4a79c7d79..9f9433e483bc 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -37,7 +37,7 @@ pub enum Scalar { Ptr(Pointer, u8), } -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(Scalar, 24); // We want the `Debug` output to be readable as it is used by `derive(Debug)` for diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index e5a650c5ac4a..e046f076389b 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1881,7 +1881,7 @@ impl DefLocation { } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 731e050ca9b7..0a567e2781dc 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -276,7 +276,7 @@ pub struct ClosureOutlivesRequirement<'tcx> { } // Make sure this enum doesn't unintentionally grow -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16); /// Outlives-constraints can be categorized to determine whether and why they diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index f929a5cec253..947764307e49 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -426,7 +426,7 @@ impl<'tcx> Rvalue<'tcx> { | CastKind::FnPtrToPtr | CastKind::PtrToPtr | CastKind::PointerCoercion(_) - | CastKind::PointerFromExposedAddress + | CastKind::PointerWithExposedProvenance | CastKind::DynStar | CastKind::Transmute, _, diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 2fe63fe4cb86..eae6ef8c396a 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1312,8 +1312,8 @@ pub enum CastKind { /// See the docs on `expose_addr` for more details. PointerExposeAddress, /// An address-to-pointer cast that picks up an exposed provenance. - /// See the docs on `from_exposed_addr` for more details. - PointerFromExposedAddress, + /// See the docs on `with_exposed_provenance` for more details. + PointerWithExposedProvenance, /// Pointer related casts that are done by coercions. Note that reference-to-raw-ptr casts are /// translated into `&raw mut/const *r`, i.e., they are not actually casts. PointerCoercion(PointerCoercion), @@ -1438,12 +1438,22 @@ pub enum BinOp { Ge, /// The `>` operator (greater than) Gt, + /// The `<=>` operator (three-way comparison, like `Ord::cmp`) + /// + /// This is supported only on the integer types and `char`, always returning + /// [`rustc_hir::LangItem::OrderingEnum`] (aka [`std::cmp::Ordering`]). + /// + /// [`Rvalue::BinaryOp`]`(BinOp::Cmp, A, B)` returns + /// - `Ordering::Less` (`-1_i8`, as a Scalar) if `A < B` + /// - `Ordering::Equal` (`0_i8`, as a Scalar) if `A == B` + /// - `Ordering::Greater` (`+1_i8`, as a Scalar) if `A > B` + Cmp, /// The `ptr.offset` operator Offset, } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; // tidy-alphabetical-start diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 56a0a6233970..b86aa601ce8e 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -14,7 +14,7 @@ pub struct PlaceTy<'tcx> { } // At least on 64 bit systems, `PlaceTy` should not be larger than two or three pointers. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(PlaceTy<'_>, 16); impl<'tcx> PlaceTy<'tcx> { @@ -276,6 +276,11 @@ impl<'tcx> BinOp { &BinOp::Eq | &BinOp::Lt | &BinOp::Le | &BinOp::Ne | &BinOp::Ge | &BinOp::Gt => { tcx.types.bool } + &BinOp::Cmp => { + // these should be integer-like types of the same size. + assert_eq!(lhs_ty, rhs_ty); + tcx.ty_ordering_enum(None) + } } } } @@ -312,7 +317,8 @@ impl BinOp { BinOp::Gt => hir::BinOpKind::Gt, BinOp::Le => hir::BinOpKind::Le, BinOp::Ge => hir::BinOpKind::Ge, - BinOp::AddUnchecked + BinOp::Cmp + | BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked | BinOp::ShlUnchecked diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index e3588a7afdc1..8cb4ee7bd41e 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -339,7 +339,7 @@ macro_rules! define_callbacks { pub type Storage<'tcx> = <$($K)* as keys::Key>::Cache>; // Ensure that keys grow no larger than 64 bytes - #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] + #[cfg(all(any(target_arch = "x86_64", target_arch="aarch64"), target_pointer_width = "64"))] const _: () = { if mem::size_of::>() > 64 { panic!("{}", concat!( @@ -353,7 +353,7 @@ macro_rules! define_callbacks { }; // Ensure that values grow no larger than 64 bytes - #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] + #[cfg(all(any(target_arch = "x86_64", target_arch="aarch64"), target_pointer_width = "64"))] const _: () = { if mem::size_of::>() > 64 { panic!("{}", concat!( diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 05f6fbbbfa33..9bb38a893eb6 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -1204,7 +1204,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> { } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; // tidy-alphabetical-start diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index efea2a66bb21..ee8167919197 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -550,7 +550,7 @@ impl<'tcx> ObligationCauseCode<'tcx> { } // `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(ObligationCauseCode<'_>, 48); #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 8e9751f45294..c35524373c7a 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -193,7 +193,6 @@ pub enum SelectionCandidate<'tcx> { /// The evaluation results are ordered: /// - `EvaluatedToOk` implies `EvaluatedToOkModuloRegions` /// implies `EvaluatedToAmbig` implies `EvaluatedToAmbigStackDependent` -/// - `EvaluatedToErr` implies `EvaluatedToErrStackDependent` /// - the "union" of evaluation results is equal to their maximum - /// all the "potential success" candidates can potentially succeed, /// so they are noops when unioned with a definite error, and within @@ -219,52 +218,9 @@ pub enum EvaluationResult { /// variables. We are somewhat imprecise there, so we don't actually /// know the real result. /// - /// This can't be trivially cached for the same reason as `EvaluatedToErrStackDependent`. + /// This can't be trivially cached because the result depends on the + /// stack results. EvaluatedToAmbigStackDependent, - /// Evaluation failed because we encountered an obligation we are already - /// trying to prove on this branch. - /// - /// We know this branch can't be a part of a minimal proof-tree for - /// the "root" of our cycle, because then we could cut out the recursion - /// and maintain a valid proof tree. However, this does not mean - /// that all the obligations on this branch do not hold -- it's possible - /// that we entered this branch "speculatively", and that there - /// might be some other way to prove this obligation that does not - /// go through this cycle -- so we can't cache this as a failure. - /// - /// For example, suppose we have this: - /// - /// ```rust,ignore (pseudo-Rust) - /// pub trait Trait { fn xyz(); } - /// // This impl is "useless", but we can still have - /// // an `impl Trait for SomeUnsizedType` somewhere. - /// impl Trait for T { fn xyz() {} } - /// - /// pub fn foo() { - /// ::xyz(); - /// } - /// ``` - /// - /// When checking `foo`, we have to prove `T: Trait`. This basically - /// translates into this: - /// - /// ```plain,ignore - /// (T: Trait + Sized →_\impl T: Trait), T: Trait ⊢ T: Trait - /// ``` - /// - /// When we try to prove it, we first go the first option, which - /// recurses. This shows us that the impl is "useless" -- it won't - /// tell us that `T: Trait` unless it already implemented `Trait` - /// by some other means. However, that does not prevent `T: Trait` - /// does not hold, because of the bound (which can indeed be satisfied - /// by `SomeUnsizedType` from another crate). - // - // FIXME: when an `EvaluatedToErrStackDependent` goes past its parent root, we - // ought to convert it to an `EvaluatedToErr`, because we know - // there definitely isn't a proof tree for that obligation. Not - // doing so is still sound -- there isn't any proof tree, so the - // branch still can't be a part of a minimal one -- but does not re-enable caching. - EvaluatedToErrStackDependent, /// Evaluation failed. EvaluatedToErr, } @@ -290,13 +246,13 @@ impl EvaluationResult { | EvaluatedToAmbig | EvaluatedToAmbigStackDependent => true, - EvaluatedToErr | EvaluatedToErrStackDependent => false, + EvaluatedToErr => false, } } pub fn is_stack_dependent(self) -> bool { match self { - EvaluatedToAmbigStackDependent | EvaluatedToErrStackDependent => true, + EvaluatedToAmbigStackDependent => true, EvaluatedToOkModuloOpaqueTypes | EvaluatedToOk diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs index 50d629120ab5..9bdc679d4e5b 100644 --- a/compiler/rustc_middle/src/ty/cast.rs +++ b/compiler/rustc_middle/src/ty/cast.rs @@ -85,7 +85,7 @@ pub fn mir_cast_kind<'tcx>(from_ty: Ty<'tcx>, cast_ty: Ty<'tcx>) -> mir::CastKin (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => { mir::CastKind::PointerExposeAddress } - (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => mir::CastKind::PointerFromExposedAddress, + (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => mir::CastKind::PointerWithExposedProvenance, (_, Some(CastTy::DynStar)) => mir::CastKind::DynStar, (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => mir::CastKind::IntToInt, (Some(CastTy::FnPtr), Some(CastTy::Ptr(_))) => mir::CastKind::FnPtrToPtr, diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 3713883eb00d..49b806b8369f 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -59,7 +59,7 @@ pub struct ConstData<'tcx> { pub kind: ConstKind<'tcx>, } -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(ConstData<'_>, 40); impl<'tcx> Const<'tcx> { diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index ea02faca5f39..94e41709f5da 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -71,8 +71,8 @@ pub enum Expr<'tcx> { Cast(CastKind, Const<'tcx>, Ty<'tcx>), } -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(Expr<'_>, 24); -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(super::ConstKind<'_>, 32); diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 188cb50849dc..23881c4b124d 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -956,6 +956,13 @@ impl<'tcx> TyCtxt<'tcx> { self.get_lang_items(()) } + /// Gets a `Ty` representing the [`LangItem::OrderingEnum`] + #[track_caller] + pub fn ty_ordering_enum(self, span: Option) -> Ty<'tcx> { + let ordering_enum = self.require_lang_item(hir::LangItem::OrderingEnum, span); + self.type_of(ordering_enum).no_bound_vars().unwrap() + } + /// Obtain the given diagnostic item's `DefId`. Use `is_diagnostic_item` if you just want to /// compare against another `DefId`, since `is_diagnostic_item` is cheaper. pub fn get_diagnostic_item(self, name: Symbol) -> Option { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 99da981b9d61..191cdafacb6b 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1034,9 +1034,11 @@ impl PlaceholderLike for PlaceholderConst { } } -/// When type checking, we use the `ParamEnv` to track -/// details about the set of where-clauses that are in scope at this -/// particular point. +/// When interacting with the type system we must provide information about the +/// environment. `ParamEnv` is the type that represents this information. See the +/// [dev guide chapter][param_env_guide] for more information. +/// +/// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/param_env/param_env_summary.html #[derive(Copy, Clone, Hash, PartialEq, Eq)] pub struct ParamEnv<'tcx> { /// This packs both caller bounds and the reveal enum into one pointer. @@ -1103,8 +1105,11 @@ impl<'tcx> TypeVisitable> for ParamEnv<'tcx> { impl<'tcx> ParamEnv<'tcx> { /// Construct a trait environment suitable for contexts where /// there are no where-clauses in scope. Hidden types (like `impl - /// Trait`) are left hidden, so this is suitable for ordinary - /// type-checking. + /// Trait`) are left hidden. In majority of cases it is incorrect + /// to use an empty environment. See the [dev guide section][param_env_guide] + /// for information on what a `ParamEnv` is and how to acquire one. + /// + /// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/param_env/param_env_summary.html #[inline] pub fn empty() -> Self { Self::new(List::empty(), Reveal::UserFacing) @@ -1552,7 +1557,6 @@ impl<'tcx> TyCtxt<'tcx> { attr::ReprRust => ReprFlags::empty(), attr::ReprC => ReprFlags::IS_C, attr::ReprPacked(pack) => { - let pack = Align::from_bytes(pack as u64).unwrap(); min_pack = Some(if let Some(min_pack) = min_pack { min_pack.min(pack) } else { @@ -1584,7 +1588,7 @@ impl<'tcx> TyCtxt<'tcx> { ReprFlags::empty() } attr::ReprAlign(align) => { - max_align = max_align.max(Some(Align::from_bytes(align as u64).unwrap())); + max_align = max_align.max(Some(align)); ReprFlags::empty() } }); @@ -2164,7 +2168,7 @@ pub struct DestructuredConst<'tcx> { } // Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index b5e619f1e2a3..fb511f2275fe 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2699,7 +2699,7 @@ impl<'tcx> VarianceDiagInfo<'tcx> { } // Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index f8aaf6291312..a6b513ce7d06 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1270,9 +1270,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) { let mut split_or_candidate = false; for candidate in &mut *candidates { - if let [MatchPair { test_case: TestCase::Or { pats, .. }, .. }] = - &*candidate.match_pairs - { + if let [MatchPair { test_case: TestCase::Or { .. }, .. }] = &*candidate.match_pairs { // Split a candidate in which the only match-pair is an or-pattern into multiple // candidates. This is so that // @@ -1282,9 +1280,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // } // // only generates a single switch. - candidate.subcandidates = self.create_or_subcandidates(pats, candidate.has_guard); - let first_match_pair = candidate.match_pairs.pop().unwrap(); - candidate.or_span = Some(first_match_pair.pattern.span); + let match_pair = candidate.match_pairs.pop().unwrap(); + self.create_or_subcandidates(candidate, match_pair); split_or_candidate = true; } } @@ -1297,7 +1294,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for candidate in candidates.iter_mut() { candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate)); } - self.match_simplified_candidates( + self.match_candidates( span, scrutinee_span, start_block, @@ -1472,14 +1469,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { return; } - let match_pairs = mem::take(&mut first_candidate.match_pairs); - let (first_match_pair, remaining_match_pairs) = match_pairs.split_first().unwrap(); - let TestCase::Or { ref pats } = &first_match_pair.test_case else { unreachable!() }; - + let first_match_pair = first_candidate.match_pairs.remove(0); + let remaining_match_pairs = mem::take(&mut first_candidate.match_pairs); let remainder_start = self.cfg.start_new_block(); - let or_span = first_match_pair.pattern.span; // Test the alternatives of this or-pattern. - self.test_or_pattern(first_candidate, start_block, remainder_start, pats, or_span); + self.test_or_pattern(first_candidate, start_block, remainder_start, first_match_pair); if !remaining_match_pairs.is_empty() { // If more match pairs remain, test them after each subcandidate. @@ -1514,25 +1508,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); } - #[instrument( - skip(self, start_block, otherwise_block, or_span, candidate, pats), - level = "debug" - )] + #[instrument(skip(self, start_block, otherwise_block, candidate, match_pair), level = "debug")] fn test_or_pattern<'pat>( &mut self, candidate: &mut Candidate<'pat, 'tcx>, start_block: BasicBlock, otherwise_block: BasicBlock, - pats: &[FlatPat<'pat, 'tcx>], - or_span: Span, + match_pair: MatchPair<'pat, 'tcx>, ) { - debug!("candidate={:#?}\npats={:#?}", candidate, pats); - let mut or_candidates: Vec<_> = pats - .iter() - .cloned() - .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard)) - .collect(); - let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect(); + let or_span = match_pair.pattern.span; + self.create_or_subcandidates(candidate, match_pair); + let mut or_candidate_refs: Vec<_> = candidate.subcandidates.iter_mut().collect(); self.match_candidates( or_span, or_span, @@ -1540,30 +1526,40 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { otherwise_block, &mut or_candidate_refs, ); - candidate.subcandidates = or_candidates; - candidate.or_span = Some(or_span); self.merge_trivial_subcandidates(candidate); } - /// Try to merge all of the subcandidates of the given candidate into one. - /// This avoids exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. + /// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new + /// subcandidate. Any candidate that has been expanded that way should be passed to + /// `merge_trivial_subcandidates` after its subcandidates have been processed. + fn create_or_subcandidates<'pat>( + &mut self, + candidate: &mut Candidate<'pat, 'tcx>, + match_pair: MatchPair<'pat, 'tcx>, + ) { + let TestCase::Or { pats } = match_pair.test_case else { bug!() }; + debug!("expanding or-pattern: candidate={:#?}\npats={:#?}", candidate, pats); + candidate.or_span = Some(match_pair.pattern.span); + candidate.subcandidates = pats + .into_vec() + .into_iter() + .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard)) + .collect(); + } + + /// Try to merge all of the subcandidates of the given candidate into one. This avoids + /// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The or-pattern should have + /// been expanded with `create_or_subcandidates`. fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) { if candidate.subcandidates.is_empty() || candidate.has_guard { // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. return; } - let mut can_merge = true; - - // Not `Iterator::all` because we don't want to short-circuit. - for subcandidate in &mut candidate.subcandidates { - self.merge_trivial_subcandidates(subcandidate); - - // FIXME(or_patterns; matthewjasper) Try to be more aggressive here. - can_merge &= - subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty(); - } - + // FIXME(or_patterns; matthewjasper) Try to be more aggressive here. + let can_merge = candidate.subcandidates.iter().all(|subcandidate| { + subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty() + }); if can_merge { let any_matches = self.cfg.start_new_block(); let or_span = candidate.or_span.take().unwrap(); diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index bf1906f370b6..90f12e55ff4c 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -12,7 +12,7 @@ //! sort of test: for example, testing which variant an enum is, or //! testing a value against a constant. -use crate::build::matches::{Candidate, FlatPat, MatchPair, PatternExtraData, TestCase}; +use crate::build::matches::{MatchPair, PatternExtraData, TestCase}; use crate::build::Builder; use std::mem; @@ -66,27 +66,4 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. })); debug!(simplified = ?match_pairs, "simplify_match_pairs"); } - - /// Create a new candidate for each pattern in `pats`, and recursively simplify tje - /// single-or-pattern case. - pub(super) fn create_or_subcandidates<'pat>( - &mut self, - pats: &[FlatPat<'pat, 'tcx>], - has_guard: bool, - ) -> Vec> { - pats.iter() - .cloned() - .map(|flat_pat| { - let mut candidate = Candidate::from_flat_pat(flat_pat, has_guard); - if let [MatchPair { test_case: TestCase::Or { pats, .. }, .. }] = - &*candidate.match_pairs - { - candidate.subcandidates = self.create_or_subcandidates(pats, has_guard); - let first_match_pair = candidate.match_pairs.pop().unwrap(); - candidate.or_span = Some(first_match_pair.pattern.span); - } - candidate - }) - .collect() - } } diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index e0bbd582d88c..99dbb3422683 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -3,7 +3,7 @@ //! be a coroutine body that takes all of its upvars by-move, and which we stash //! into the `CoroutineInfo` for all coroutines returned by coroutine-closures. -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::unord::UnordSet; use rustc_hir as hir; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::{self, dump_mir, MirPass}; @@ -33,7 +33,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { return; } - let mut by_ref_fields = FxIndexSet::default(); + let mut by_ref_fields = UnordSet::default(); let by_move_upvars = Ty::new_tup_from_iter( tcx, tcx.closure_captures(coroutine_def_id).iter().enumerate().map(|(idx, capture)| { @@ -73,7 +73,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { struct MakeByMoveBody<'tcx> { tcx: TyCtxt<'tcx>, - by_ref_fields: FxIndexSet, + by_ref_fields: UnordSet, by_move_coroutine_ty: Ty<'tcx>, } @@ -89,11 +89,11 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> { location: mir::Location, ) { if place.local == ty::CAPTURE_STRUCT_LOCAL - && !place.projection.is_empty() - && let mir::ProjectionElem::Field(idx, ty) = place.projection[0] + && let Some((&mir::ProjectionElem::Field(idx, ty), projection)) = + place.projection.split_first() && self.by_ref_fields.contains(&idx) { - let (begin, end) = place.projection[1..].split_first().unwrap(); + let (begin, end) = projection.split_first().unwrap(); // FIXME(async_closures): I'm actually a bit surprised to see that we always // initially deref the by-ref upvars. If this is not actually true, then we // will at least get an ICE that explains why this isn't true :^) diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 7d4c1b9c21a6..7e8920604c17 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -90,6 +90,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul + | sym::three_way_compare | sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul @@ -109,6 +110,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { sym::wrapping_add => BinOp::Add, sym::wrapping_sub => BinOp::Sub, sym::wrapping_mul => BinOp::Mul, + sym::three_way_compare => BinOp::Cmp, sym::unchecked_add => BinOp::AddUnchecked, sym::unchecked_sub => BinOp::SubUnchecked, sym::unchecked_mul => BinOp::MulUnchecked, diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 2951897ebd69..d64cb9b12b2b 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -525,6 +525,7 @@ impl<'tcx> Validator<'_, 'tcx> { | BinOp::Lt | BinOp::Ge | BinOp::Gt + | BinOp::Cmp | BinOp::Offset | BinOp::Add | BinOp::AddUnchecked diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 9c4a6e69a3cb..2a91d69529a8 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -14,6 +14,7 @@ use rustc_middle::ty::adjustment::CustomCoerceUnsized; use rustc_middle::ty::Instance; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{self, Ty}; +use rustc_span::def_id::DefId; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::ErrorGuaranteed; @@ -57,13 +58,24 @@ fn custom_coerce_unsize_info<'tcx>( /// linkers will optimize such that dead calls to unresolved symbols are not an error, but this is /// not guaranteed. So we used this function in codegen backends to ensure we do not generate any /// unlinkable calls. +/// +/// Note that calls to LLVM intrinsics are uniquely okay because they won't make it to the linker. pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, ) -> bool { - !instance.def_id().is_local() + fn is_llvm_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + if let Some(name) = tcx.codegen_fn_attrs(def_id).link_name { + name.as_str().starts_with("llvm.") + } else { + false + } + } + + let def_id = instance.def_id(); + !def_id.is_local() && tcx.is_compiler_builtins(LOCAL_CRATE) - && tcx.codegen_fn_attrs(instance.def_id()).link_name.is_none() + && !is_llvm_intrinsic(tcx, def_id) && !should_codegen_locally(tcx, instance) } diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 63b2b47630b2..69b48bf0aff7 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -30,7 +30,7 @@ use unescape_error_reporting::{emit_unescape_error, escaped_char}; // // This assertion is in this crate, rather than in `rustc_lexer`, because that // crate cannot depend on `rustc_data_structures`. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(rustc_lexer::Token, 12); #[derive(Clone, Debug)] diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index a1dd7d6f6734..baaed5ec37b7 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -454,7 +454,7 @@ fn make_token_stream( } // Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 1971591364d3..09bc00403f38 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -179,7 +179,7 @@ pub struct Parser<'a> { // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure // it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Parser<'_>, 264); /// Stores span information about a closure. diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 2bb4b09e337c..ccda43c827c5 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -1087,7 +1087,7 @@ fn unescape_string(string: &str) -> Option { } // Assert a reasonable size for `Piece` -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] rustc_index::static_assert_size!(Piece<'_>, 16); #[cfg(test)] diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml index 6357d18b9da8..0cb47e03441b 100644 --- a/compiler/rustc_pattern_analysis/Cargo.toml +++ b/compiler/rustc_pattern_analysis/Cargo.toml @@ -24,7 +24,7 @@ tracing = "0.1" [dev-dependencies] tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "ansi"] } -tracing-tree = "0.2.0" +tracing-tree = "0.3.0" [features] default = ["rustc"] diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 95c5556410d3..1c9a9ab0f720 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -155,13 +155,13 @@ use std::iter::once; use smallvec::SmallVec; use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS}; -use rustc_index::bit_set::GrowableBitSet; +use rustc_index::bit_set::{BitSet, GrowableBitSet}; +use rustc_index::IndexVec; use self::Constructor::*; use self::MaybeInfiniteInt::*; use self::SliceKind::*; -use crate::index; use crate::PatCx; /// Whether we have seen a constructor in the column or not. @@ -920,10 +920,7 @@ pub enum ConstructorSet { Struct { empty: bool }, /// This type has the following list of constructors. If `variants` is empty and /// `non_exhaustive` is false, don't use this; use `NoConstructors` instead. - Variants { - variants: index::IdxContainer, - non_exhaustive: bool, - }, + Variants { variants: IndexVec, non_exhaustive: bool }, /// The type is `&T`. Ref, /// The type is a union. @@ -1025,7 +1022,7 @@ impl ConstructorSet { } } ConstructorSet::Variants { variants, non_exhaustive } => { - let mut seen_set = index::IdxSet::new_empty(variants.len()); + let mut seen_set = BitSet::new_empty(variants.len()); for idx in seen.iter().filter_map(|c| c.as_variant()) { seen_set.insert(idx); } diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 1a1da5c55f60..6e8843d90497 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -25,50 +25,9 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" } use std::fmt; -#[cfg(feature = "rustc")] -pub mod index { - // Faster version when the indices of variants are `0..variants.len()`. - pub use rustc_index::bit_set::BitSet as IdxSet; - pub use rustc_index::Idx; - pub use rustc_index::IndexVec as IdxContainer; -} -#[cfg(not(feature = "rustc"))] -pub mod index { - // Slower version when the indices of variants are something else. - pub trait Idx: Copy + PartialEq + Eq + std::hash::Hash {} - impl Idx for T {} - - #[derive(Debug)] - pub struct IdxContainer(pub rustc_hash::FxHashMap); - impl IdxContainer { - pub fn len(&self) -> usize { - self.0.len() - } - pub fn iter_enumerated(&self) -> impl Iterator { - self.0.iter().map(|(k, v)| (*k, v)) - } - } - - impl FromIterator for IdxContainer { - fn from_iter>(iter: T) -> Self { - Self(iter.into_iter().enumerate().collect()) - } - } - - #[derive(Debug)] - pub struct IdxSet(pub rustc_hash::FxHashSet); - impl IdxSet { - pub fn new_empty(_len: usize) -> Self { - Self(Default::default()) - } - pub fn contains(&self, elem: T) -> bool { - self.0.contains(&elem) - } - pub fn insert(&mut self, elem: T) { - self.0.insert(elem); - } - } -} +// Re-exports to avoid rustc_index version issues. +pub use rustc_index::Idx; +pub use rustc_index::IndexVec; #[cfg(feature = "rustc")] use rustc_middle::ty::Ty; @@ -96,7 +55,7 @@ pub trait PatCx: Sized + fmt::Debug { /// Errors that can abort analysis. type Error: fmt::Debug; /// The index of an enum variant. - type VariantIdx: Clone + index::Idx + fmt::Debug; + type VariantIdx: Clone + Idx + fmt::Debug; /// A string literal type StrLit: Clone + PartialEq + fmt::Debug; /// Extra data to store in a match arm. diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index b6a722da602e..9d91032c1d2c 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -268,7 +268,7 @@ impl<'tcx> Stable<'tcx> for mir::CastKind { use rustc_middle::mir::CastKind::*; match self { PointerExposeAddress => stable_mir::mir::CastKind::PointerExposeAddress, - PointerFromExposedAddress => stable_mir::mir::CastKind::PointerFromExposedAddress, + PointerWithExposedProvenance => stable_mir::mir::CastKind::PointerWithExposedProvenance, PointerCoercion(c) => stable_mir::mir::CastKind::PointerCoercion(c.stable(tables)), DynStar => stable_mir::mir::CastKind::DynStar, IntToInt => stable_mir::mir::CastKind::IntToInt, @@ -493,6 +493,7 @@ impl<'tcx> Stable<'tcx> for mir::BinOp { BinOp::Ne => stable_mir::mir::BinOp::Ne, BinOp::Ge => stable_mir::mir::BinOp::Ge, BinOp::Gt => stable_mir::mir::BinOp::Gt, + BinOp::Cmp => stable_mir::mir::BinOp::Cmp, BinOp::Offset => stable_mir::mir::BinOp::Offset, } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 998b1a5c7eaa..b1d4a63812f4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1675,7 +1675,6 @@ symbols! { simd_fmin, simd_fpow, simd_fpowi, - simd_from_exposed_addr, simd_fsin, simd_fsqrt, simd_gather, @@ -1714,6 +1713,7 @@ symbols! { simd_shuffle_generic, simd_sub, simd_trunc, + simd_with_exposed_provenance, simd_xor, since, sinf128, @@ -1813,6 +1813,7 @@ symbols! { thread, thread_local, thread_local_macro, + three_way_compare, thumb2, thumb_mode: "thumb-mode", tmm_reg, diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 5963bd7c5f16..5f5d90f359a2 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -1218,22 +1218,35 @@ pub fn typeid_for_instance<'tcx>( let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap(); let tuple_args = tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0]; - (trait_id, tuple_args) + (trait_id, Some(tuple_args)) } - ty::Coroutine(..) => ( - tcx.require_lang_item(LangItem::Coroutine, None), - instance.args.as_coroutine().resume_ty(), - ), + ty::Coroutine(..) => match tcx.coroutine_kind(instance.def_id()).unwrap() { + hir::CoroutineKind::Coroutine(..) => ( + tcx.require_lang_item(LangItem::Coroutine, None), + Some(instance.args.as_coroutine().resume_ty()), + ), + hir::CoroutineKind::Desugared(desugaring, _) => { + let lang_item = match desugaring { + hir::CoroutineDesugaring::Async => LangItem::Future, + hir::CoroutineDesugaring::AsyncGen => LangItem::AsyncIterator, + hir::CoroutineDesugaring::Gen => LangItem::Iterator, + }; + (tcx.require_lang_item(lang_item, None), None) + } + }, ty::CoroutineClosure(..) => ( tcx.require_lang_item(LangItem::FnOnce, None), - tcx.instantiate_bound_regions_with_erased( - instance.args.as_coroutine_closure().coroutine_closure_sig(), - ) - .tupled_inputs_ty, + Some( + tcx.instantiate_bound_regions_with_erased( + instance.args.as_coroutine_closure().coroutine_closure_sig(), + ) + .tupled_inputs_ty, + ), ), x => bug!("Unexpected type kind for closure-like: {x:?}"), }; - let trait_ref = ty::TraitRef::new(tcx, trait_id, [closure_ty, inputs]); + let concrete_args = tcx.mk_args_trait(closure_ty, inputs.map(Into::into)); + let trait_ref = ty::TraitRef::new(tcx, trait_id, concrete_args); let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); let abstract_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); // There should be exactly one method on this trait, and it should be the one we're diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 486afc5f8f30..706a7a310f2b 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -927,7 +927,7 @@ impl FromStr for Conv { } // Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index f694dd007036..7056288e758c 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -49,8 +49,7 @@ impl<'tcx> InferCtxt<'tcx> { /// - the parameter environment /// /// Invokes `evaluate_obligation`, so in the event that evaluating - /// `Ty: Trait` causes overflow, EvaluatedToErrStackDependent - /// (or EvaluatedToAmbigStackDependent) will be returned. + /// `Ty: Trait` causes overflow, EvaluatedToAmbigStackDependent will be returned. #[instrument(level = "debug", skip(self, params), ret)] fn type_implements_trait( &self, diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index e14fc62cd6f0..b5fb710e4cc4 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -30,7 +30,7 @@ #[macro_use] extern crate rustc_macros; -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] #[macro_use] extern crate rustc_data_structures; #[macro_use] diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index c909a0b49e24..73e94da165fb 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -6,13 +6,13 @@ use super::*; use crate::errors::UnableToConstructConstantValue; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::traits::project::ProjectAndUnifyResult; + +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; +use rustc_data_structures::unord::UnordSet; use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::{Region, RegionVid}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; - -use std::collections::hash_map::Entry; use std::collections::VecDeque; use std::iter; @@ -25,8 +25,8 @@ pub enum RegionTarget<'tcx> { #[derive(Default, Debug, Clone)] pub struct RegionDeps<'tcx> { - larger: FxIndexSet>, - smaller: FxIndexSet>, + pub larger: FxIndexSet>, + pub smaller: FxIndexSet>, } pub enum AutoTraitResult { @@ -35,17 +35,10 @@ pub enum AutoTraitResult { NegativeImpl, } -#[allow(dead_code)] -impl AutoTraitResult { - fn is_auto(&self) -> bool { - matches!(self, AutoTraitResult::PositiveImpl(_) | AutoTraitResult::NegativeImpl) - } -} - pub struct AutoTraitInfo<'cx> { pub full_user_env: ty::ParamEnv<'cx>, pub region_data: RegionConstraintData<'cx>, - pub vid_to_region: FxHashMap>, + pub vid_to_region: FxIndexMap>, } pub struct AutoTraitFinder<'tcx> { @@ -88,19 +81,12 @@ impl<'tcx> AutoTraitFinder<'tcx> { let infcx = tcx.infer_ctxt().build(); let mut selcx = SelectionContext::new(&infcx); - for polarity in [true, false] { + for polarity in [ty::PredicatePolarity::Positive, ty::PredicatePolarity::Negative] { let result = selcx.select(&Obligation::new( tcx, ObligationCause::dummy(), orig_env, - ty::TraitPredicate { - trait_ref, - polarity: if polarity { - ty::PredicatePolarity::Positive - } else { - ty::PredicatePolarity::Negative - }, - }, + ty::TraitPredicate { trait_ref, polarity }, )); if let Ok(Some(ImplSource::UserDefined(_))) = result { debug!( @@ -114,7 +100,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } let infcx = tcx.infer_ctxt().build(); - let mut fresh_preds = FxHashSet::default(); + let mut fresh_preds = FxIndexSet::default(); // Due to the way projections are handled by SelectionContext, we need to run // evaluate_predicates twice: once on the original param env, and once on the result of @@ -239,7 +225,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { ty: Ty<'tcx>, param_env: ty::ParamEnv<'tcx>, user_env: ty::ParamEnv<'tcx>, - fresh_preds: &mut FxHashSet>, + fresh_preds: &mut FxIndexSet>, ) -> Option<(ty::ParamEnv<'tcx>, ty::ParamEnv<'tcx>)> { let tcx = infcx.tcx; @@ -252,7 +238,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { let mut select = SelectionContext::new(infcx); - let mut already_visited = FxHashSet::default(); + let mut already_visited = UnordSet::new(); let mut predicates = VecDeque::new(); predicates.push_back(ty::Binder::dummy(ty::TraitPredicate { trait_ref: ty::TraitRef::new(infcx.tcx, trait_did, [ty]), @@ -473,9 +459,9 @@ impl<'tcx> AutoTraitFinder<'tcx> { fn map_vid_to_region<'cx>( &self, regions: &RegionConstraintData<'cx>, - ) -> FxHashMap> { - let mut vid_map: FxHashMap, RegionDeps<'cx>> = FxHashMap::default(); - let mut finished_map = FxHashMap::default(); + ) -> FxIndexMap> { + let mut vid_map = FxIndexMap::, RegionDeps<'cx>>::default(); + let mut finished_map = FxIndexMap::default(); for (constraint, _) in ®ions.constraints { match constraint { @@ -513,25 +499,22 @@ impl<'tcx> AutoTraitFinder<'tcx> { } while !vid_map.is_empty() { - #[allow(rustc::potential_query_instability)] - let target = *vid_map.keys().next().expect("Keys somehow empty"); - let deps = vid_map.remove(&target).expect("Entry somehow missing"); + let target = *vid_map.keys().next().unwrap(); + let deps = vid_map.swap_remove(&target).unwrap(); for smaller in deps.smaller.iter() { for larger in deps.larger.iter() { match (smaller, larger) { (&RegionTarget::Region(_), &RegionTarget::Region(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*smaller) { + if let IndexEntry::Occupied(v) = vid_map.entry(*smaller) { let smaller_deps = v.into_mut(); smaller_deps.larger.insert(*larger); - // FIXME(#120456) - is `swap_remove` correct? smaller_deps.larger.swap_remove(&target); } - if let Entry::Occupied(v) = vid_map.entry(*larger) { + if let IndexEntry::Occupied(v) = vid_map.entry(*larger) { let larger_deps = v.into_mut(); larger_deps.smaller.insert(*smaller); - // FIXME(#120456) - is `swap_remove` correct? larger_deps.smaller.swap_remove(&target); } } @@ -542,17 +525,15 @@ impl<'tcx> AutoTraitFinder<'tcx> { // Do nothing; we don't care about regions that are smaller than vids. } (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*smaller) { + if let IndexEntry::Occupied(v) = vid_map.entry(*smaller) { let smaller_deps = v.into_mut(); smaller_deps.larger.insert(*larger); - // FIXME(#120456) - is `swap_remove` correct? smaller_deps.larger.swap_remove(&target); } - if let Entry::Occupied(v) = vid_map.entry(*larger) { + if let IndexEntry::Occupied(v) = vid_map.entry(*larger) { let larger_deps = v.into_mut(); larger_deps.smaller.insert(*smaller); - // FIXME(#120456) - is `swap_remove` correct? larger_deps.smaller.swap_remove(&target); } } @@ -560,6 +541,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } } } + finished_map } @@ -588,7 +570,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { ty: Ty<'_>, nested: impl Iterator>, computed_preds: &mut FxIndexSet>, - fresh_preds: &mut FxHashSet>, + fresh_preds: &mut FxIndexSet>, predicates: &mut VecDeque>, selcx: &mut SelectionContext<'_, 'tcx>, ) -> bool { diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 2712ba194515..6e768b23ef8c 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -210,7 +210,7 @@ fn overlap<'tcx>( .intercrate(true) .with_next_trait_solver(tcx.next_trait_solver_in_coherence()) .build(); - let selcx = &mut SelectionContext::with_treat_inductive_cycle_as_ambig(&infcx); + let selcx = &mut SelectionContext::new(&infcx); if track_ambiguity_causes.is_yes() { selcx.enable_tracking_intercrate_ambiguity_causes(); } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 7a62030353d9..aef98dbad5fe 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -1128,10 +1128,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { err: &mut Diag<'_>, ) -> bool { let span = obligation.cause.span; - struct V { + /// Look for the (direct) sub-expr of `?`, and return it if it's a `.` method call. + struct FindMethodSubexprOfTry { search_span: Span, } - impl<'v> Visitor<'v> for V { + impl<'v> Visitor<'v> for FindMethodSubexprOfTry { type Result = ControlFlow<&'v hir::Expr<'v>>; fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result { if let hir::ExprKind::Match(expr, _arms, hir::MatchSource::TryDesugar(_)) = ex.kind @@ -1149,8 +1150,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) => body_id, _ => return false, }; - let ControlFlow::Break(expr) = - (V { search_span: span }).visit_body(self.tcx.hir().body(*body_id)) + let ControlFlow::Break(expr) = (FindMethodSubexprOfTry { search_span: span }) + .visit_body(self.tcx.hir().body(*body_id)) else { return false; }; diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 34c891d400e4..b5be9a2bcb35 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -72,7 +72,7 @@ pub struct PendingPredicateObligation<'tcx> { } // `PendingPredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_pointer_width = "64"))] static_assert_size!(PendingPredicateObligation<'_>, 72); impl<'tcx> FulfillmentContext<'tcx> { diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index c875d3da47e5..3ab4872fffee 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -65,7 +65,7 @@ pub use self::util::{ check_args_compatible, supertrait_def_ids, supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item, SupertraitDefIds, }; -pub use self::util::{expand_trait_aliases, TraitAliasExpander}; +pub use self::util::{expand_trait_aliases, TraitAliasExpander, TraitAliasExpansionInfo}; pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices}; pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer}; diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 6f512a1173f5..0459246553b0 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -28,7 +28,7 @@ use crate::traits::{ BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource, ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause, PolyTraitObligation, PredicateObligation, Selection, SelectionError, SignatureMismatch, TraitNotObjectSafe, - Unimplemented, + TraitObligation, Unimplemented, }; use super::BuiltinImplConditions; @@ -678,17 +678,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn_host_effect: ty::Const<'tcx>, ) -> Result>, SelectionError<'tcx>> { debug!(?obligation, "confirm_fn_pointer_candidate"); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let tcx = self.tcx(); - - let Some(self_ty) = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars()) else { - // FIXME: Ideally we'd support `for<'a> fn(&'a ()): Fn(&'a ())`, - // but we do not currently. Luckily, such a bound is not - // particularly useful, so we don't expect users to write - // them often. - return Err(SelectionError::Unimplemented); - }; - let sig = self_ty.fn_sig(tcx); let trait_ref = closure_trait_ref_and_return_type( tcx, @@ -700,7 +693,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) .map_bound(|(trait_ref, _)| trait_ref); - let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + let mut nested = + self.equate_trait_refs(obligation.with(tcx, placeholder_predicate), trait_ref)?; let cause = obligation.derived_cause(BuiltinDerivedObligation); // Confirm the `type Output: Sized;` bound that is present on `FnOnce` @@ -748,10 +742,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the args on coroutine types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { bug!("closure candidate for non-closure {:?}", obligation); }; @@ -760,15 +752,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let coroutine_sig = args.as_coroutine().sig(); - // NOTE: The self-type is a coroutine type and hence is - // in fact unparameterized (or at least does not reference any - // regions bound in the obligation). - let self_ty = obligation - .predicate - .self_ty() - .no_bound_vars() - .expect("unboxed closure type should not capture bound vars from the predicate"); - let (trait_ref, _, _) = super::util::coroutine_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), @@ -776,7 +759,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { coroutine_sig, ); - let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + let nested = self.equate_trait_refs( + obligation.with(self.tcx(), placeholder_predicate), + ty::Binder::dummy(trait_ref), + )?; debug!(?trait_ref, ?nested, "coroutine candidate obligations"); Ok(nested) @@ -786,10 +772,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the args on coroutine types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { bug!("closure candidate for non-closure {:?}", obligation); }; @@ -801,11 +785,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let (trait_ref, _) = super::util::future_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), - obligation.predicate.no_bound_vars().expect("future has no bound vars").self_ty(), + self_ty, coroutine_sig, ); - let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + let nested = self.equate_trait_refs( + obligation.with(self.tcx(), placeholder_predicate), + ty::Binder::dummy(trait_ref), + )?; debug!(?trait_ref, ?nested, "future candidate obligations"); Ok(nested) @@ -815,10 +802,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the args on coroutine types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { bug!("closure candidate for non-closure {:?}", obligation); }; @@ -830,11 +815,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let (trait_ref, _) = super::util::iterator_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), - obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(), + self_ty, gen_sig, ); - let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + let nested = self.equate_trait_refs( + obligation.with(self.tcx(), placeholder_predicate), + ty::Binder::dummy(trait_ref), + )?; debug!(?trait_ref, ?nested, "iterator candidate obligations"); Ok(nested) @@ -844,10 +832,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the args on coroutine types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { bug!("closure candidate for non-closure {:?}", obligation); }; @@ -859,11 +845,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let (trait_ref, _) = super::util::async_iterator_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), - obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(), + self_ty, gen_sig, ); - let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + let nested = self.equate_trait_refs( + obligation.with(self.tcx(), placeholder_predicate), + ty::Binder::dummy(trait_ref), + )?; debug!(?trait_ref, ?nested, "iterator candidate obligations"); Ok(nested) @@ -874,14 +863,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the args on closure types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty: Ty<'_> = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); + let trait_ref = match *self_ty.kind() { - ty::Closure(_, args) => { - self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_) - } + ty::Closure(..) => self.closure_trait_ref_unnormalized( + self_ty, + obligation.predicate.def_id(), + self.tcx().consts.true_, + ), ty::CoroutineClosure(_, args) => { args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| { ty::TraitRef::new( @@ -896,7 +886,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } }; - self.confirm_poly_trait_refs(obligation, trait_ref) + self.equate_trait_refs(obligation.with(self.tcx(), placeholder_predicate), trait_ref) } #[instrument(skip(self), level = "debug")] @@ -904,8 +894,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); + let tcx = self.tcx(); - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); let mut nested = vec![]; let (trait_ref, kind_ty) = match *self_ty.kind() { @@ -972,7 +964,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => bug!("expected callable type for AsyncFn candidate"), }; - nested.extend(self.confirm_poly_trait_refs(obligation, trait_ref)?); + nested.extend( + self.equate_trait_refs(obligation.with(tcx, placeholder_predicate), trait_ref)?, + ); let goal_kind = self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap(); @@ -1025,34 +1019,32 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// selection of the impl. Therefore, if there is a mismatch, we /// report an error to the user. #[instrument(skip(self), level = "trace")] - fn confirm_poly_trait_refs( + fn equate_trait_refs( &mut self, - obligation: &PolyTraitObligation<'tcx>, - self_ty_trait_ref: ty::PolyTraitRef<'tcx>, + obligation: TraitObligation<'tcx>, + found_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Result>, SelectionError<'tcx>> { - let obligation_trait_ref = - self.infcx.enter_forall_and_leak_universe(obligation.predicate.to_poly_trait_ref()); - let self_ty_trait_ref = self.infcx.instantiate_binder_with_fresh_vars( + let found_trait_ref = self.infcx.instantiate_binder_with_fresh_vars( obligation.cause.span, HigherRankedType, - self_ty_trait_ref, + found_trait_ref, ); // Normalize the obligation and expected trait refs together, because why not - let Normalized { obligations: nested, value: (obligation_trait_ref, expected_trait_ref) } = + let Normalized { obligations: nested, value: (obligation_trait_ref, found_trait_ref) } = ensure_sufficient_stack(|| { normalize_with_depth( self, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, - (obligation_trait_ref, self_ty_trait_ref), + (obligation.predicate.trait_ref, found_trait_ref), ) }); // needed to define opaque types for tests/ui/type-alias-impl-trait/assoc-projection-ice.rs self.infcx .at(&obligation.cause, obligation.param_env) - .eq(DefineOpaqueTypes::Yes, obligation_trait_ref, expected_trait_ref) + .eq(DefineOpaqueTypes::Yes, obligation_trait_ref, found_trait_ref) .map(|InferOk { mut obligations, .. }| { obligations.extend(nested); obligations @@ -1060,7 +1052,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map_err(|terr| { SignatureMismatch(Box::new(SignatureMismatchData { expected_trait_ref: ty::Binder::dummy(obligation_trait_ref), - found_trait_ref: ty::Binder::dummy(expected_trait_ref), + found_trait_ref: ty::Binder::dummy(found_trait_ref), terr, })) }) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 1894fbba3027..3fbe8e39d1e3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -126,8 +126,6 @@ pub struct SelectionContext<'cx, 'tcx> { /// policy. In essence, canonicalized queries need their errors propagated /// rather than immediately reported because we do not have accurate spans. query_mode: TraitQueryMode, - - treat_inductive_cycle: TreatInductiveCycleAs, } // A stack that walks back up the stack frame. @@ -208,27 +206,6 @@ enum BuiltinImplConditions<'tcx> { Ambiguous, } -#[derive(Copy, Clone)] -pub enum TreatInductiveCycleAs { - /// This is the previous behavior, where `Recur` represents an inductive - /// cycle that is known not to hold. This is not forwards-compatible with - /// coinduction, and will be deprecated. This is the default behavior - /// of the old trait solver due to back-compat reasons. - Recur, - /// This is the behavior of the new trait solver, where inductive cycles - /// are treated as ambiguous and possibly holding. - Ambig, -} - -impl From for EvaluationResult { - fn from(treat: TreatInductiveCycleAs) -> EvaluationResult { - match treat { - TreatInductiveCycleAs::Ambig => EvaluatedToAmbigStackDependent, - TreatInductiveCycleAs::Recur => EvaluatedToErrStackDependent, - } - } -} - impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> { SelectionContext { @@ -236,19 +213,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { freshener: infcx.freshener(), intercrate_ambiguity_causes: None, query_mode: TraitQueryMode::Standard, - treat_inductive_cycle: TreatInductiveCycleAs::Recur, - } - } - - pub fn with_treat_inductive_cycle_as_ambig( - infcx: &'cx InferCtxt<'tcx>, - ) -> SelectionContext<'cx, 'tcx> { - // Should be executed in a context where caching is disabled, - // otherwise the cache is poisoned with the temporary result. - assert!(infcx.intercrate); - SelectionContext { - treat_inductive_cycle: TreatInductiveCycleAs::Ambig, - ..SelectionContext::new(infcx) } } @@ -756,7 +720,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { stack.update_reached_depth(stack_arg.1); return Ok(EvaluatedToOk); } else { - return Ok(self.treat_inductive_cycle.into()); + return Ok(EvaluatedToAmbigStackDependent); } } return Ok(EvaluatedToOk); @@ -875,7 +839,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } ProjectAndUnifyResult::FailedNormalization => Ok(EvaluatedToAmbig), - ProjectAndUnifyResult::Recursive => Ok(self.treat_inductive_cycle.into()), + ProjectAndUnifyResult::Recursive => Ok(EvaluatedToAmbigStackDependent), ProjectAndUnifyResult::MismatchedProjectionTypes(_) => Ok(EvaluatedToErr), } } @@ -1180,7 +1144,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Some(EvaluatedToOk) } else { debug!("evaluate_stack --> recursive, inductive"); - Some(self.treat_inductive_cycle.into()) + Some(EvaluatedToAmbigStackDependent) } } else { None @@ -2679,26 +2643,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> { #[instrument(skip(self), level = "debug")] fn closure_trait_ref_unnormalized( &mut self, - obligation: &PolyTraitObligation<'tcx>, - args: GenericArgsRef<'tcx>, + self_ty: Ty<'tcx>, + fn_trait_def_id: DefId, fn_host_effect: ty::Const<'tcx>, ) -> ty::PolyTraitRef<'tcx> { + let ty::Closure(_, args) = *self_ty.kind() else { + bug!("expected closure, found {self_ty}"); + }; let closure_sig = args.as_closure().sig(); - debug!(?closure_sig); - - // NOTE: The self-type is an unboxed closure type and hence is - // in fact unparameterized (or at least does not reference any - // regions bound in the obligation). - let self_ty = obligation - .predicate - .self_ty() - .no_bound_vars() - .expect("unboxed closure type should not capture bound vars from the predicate"); - closure_trait_ref_and_return_type( self.tcx(), - obligation.predicate.def_id(), + fn_trait_def_id, self_ty, closure_sig, util::TupleArgumentsFlag::No, diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 2a2e53a81ed8..acbcc3918b2f 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -82,7 +82,7 @@ fn check_binop(op: mir::BinOp) -> bool { match op { Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor | BitAnd | BitOr | Shl | ShlUnchecked | Shr | ShrUnchecked | Eq | Lt | Le | Ne | Ge - | Gt => true, + | Gt | Cmp => true, Offset => false, } } diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index 7c536a3e9140..0d866f27dadf 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -329,6 +329,7 @@ pub enum BinOp { Ne, Ge, Gt, + Cmp, Offset, } @@ -368,6 +369,9 @@ impl BinOp { assert!(lhs_kind.is_primitive() || lhs_kind.is_raw_ptr() || lhs_kind.is_fn_ptr()); Ty::bool_ty() } + BinOp::Cmp => { + unimplemented!("Should cmp::Ordering be a RigidTy?"); + } } } } @@ -968,7 +972,7 @@ pub enum PointerCoercion { #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum CastKind { PointerExposeAddress, - PointerFromExposedAddress, + PointerWithExposedProvenance, PointerCoercion(PointerCoercion), DynStar, IntToInt, diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 5ff7f184a6dd..e2fc320f280a 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -2464,8 +2464,10 @@ impl VecDeque { /// /// let mut deque: VecDeque<_> = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into(); /// let num = 42; - /// let idx = deque.partition_point(|&x| x < num); - /// // The above is equivalent to `let idx = deque.binary_search(&num).unwrap_or_else(|x| x);` + /// let idx = deque.partition_point(|&x| x <= num); + /// // If `num` is unique, `s.partition_point(|&x| x < num)` (with `<`) is equivalent to + /// // `s.binary_search(&num).unwrap_or_else(|x| x)`, but using `<=` may allow `insert` + /// // to shift less elements. /// deque.insert(idx, num); /// assert_eq!(deque, &[0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]); /// ``` diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 7464df268cc1..5d552c8f15c6 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -72,9 +72,9 @@ use crate::vec::Vec; /// A UTF-8–encoded, growable string. /// -/// The `String` type is the most common string type that has ownership over the -/// contents of the string. It has a close relationship with its borrowed -/// counterpart, the primitive [`str`]. +/// `String` is the most common string type. It has ownership over the contents +/// of the string, stored in a heap-allocated buffer (see [Representation](#representation)). +/// It is closely related to its borrowed counterpart, the primitive [`str`]. /// /// # Examples /// diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index b0c5021022f7..fa218600ed92 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -376,6 +376,10 @@ pub struct AssertParamIsEq { /// ``` #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] #[stable(feature = "rust1", since = "1.0.0")] +// This is a lang item only so that `BinOp::Cmp` in MIR can return it. +// It has no special behaviour, but does require that the three variants +// `Less`/`Equal`/`Greater` remain `-1_i8`/`0_i8`/`+1_i8` respectively. +#[cfg_attr(not(bootstrap), lang = "Ordering")] #[repr(i8)] pub enum Ordering { /// An ordering where a compared value is less than another. @@ -1554,7 +1558,14 @@ mod impls { impl PartialOrd for $t { #[inline] fn partial_cmp(&self, other: &$t) -> Option { - Some(self.cmp(other)) + #[cfg(bootstrap)] + { + Some(self.cmp(other)) + } + #[cfg(not(bootstrap))] + { + Some(crate::intrinsics::three_way_compare(*self, *other)) + } } #[inline(always)] fn lt(&self, other: &$t) -> bool { (*self) < (*other) } @@ -1570,11 +1581,18 @@ mod impls { impl Ord for $t { #[inline] fn cmp(&self, other: &$t) -> Ordering { - // The order here is important to generate more optimal assembly. - // See for more info. - if *self < *other { Less } - else if *self == *other { Equal } - else { Greater } + #[cfg(bootstrap)] + { + // The order here is important to generate more optimal assembly. + // See for more info. + if *self < *other { Less } + else if *self == *other { Equal } + else { Greater } + } + #[cfg(not(bootstrap))] + { + crate::intrinsics::three_way_compare(*self, *other) + } } } )*) diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index ef0793a3e465..1c93a7b28fd3 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -752,6 +752,18 @@ pub trait BuildHasher { #[stable(since = "1.7.0", feature = "build_hasher")] pub struct BuildHasherDefault(marker::PhantomData H>); +impl BuildHasherDefault { + /// Creates a new BuildHasherDefault for Hasher `H`. + #[unstable( + feature = "build_hasher_default_const_new", + issue = "123197", + reason = "recently added" + )] + pub const fn new() -> Self { + BuildHasherDefault(marker::PhantomData) + } +} + #[stable(since = "1.9.0", feature = "core_impl_debug")] impl fmt::Debug for BuildHasherDefault { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -778,7 +790,7 @@ impl Clone for BuildHasherDefault { #[stable(since = "1.7.0", feature = "build_hasher")] impl Default for BuildHasherDefault { fn default() -> BuildHasherDefault { - BuildHasherDefault(marker::PhantomData) + Self::new() } } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 26e813346be6..b09d9fab8a76 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2146,6 +2146,18 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn bitreverse(x: T) -> T; + /// Does a three-way comparison between the two integer arguments. + /// + /// This is included as an intrinsic as it's useful to let it be one thing + /// in MIR, rather than the multiple checks and switches that make its IR + /// large and difficult to optimize. + /// + /// The stabilized version of this intrinsic is [`Ord::cmp`]. + #[cfg(not(bootstrap))] + #[rustc_const_unstable(feature = "const_three_way_compare", issue = "none")] + #[rustc_safe_intrinsic] + pub fn three_way_compare(lhs: T, rhs: T) -> crate::cmp::Ordering; + /// Performs checked integer addition. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -2224,18 +2236,20 @@ extern "rust-intrinsic" { /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_shl` method. For example, /// [`u32::checked_shl`] + #[cfg(not(bootstrap))] #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")] #[rustc_nounwind] - pub fn unchecked_shl(x: T, y: T) -> T; + pub fn unchecked_shl(x: T, y: U) -> T; /// Performs an unchecked right shift, resulting in undefined behavior when /// `y < 0` or `y >= N`, where N is the width of T in bits. /// /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_shr` method. For example, /// [`u32::checked_shr`] + #[cfg(not(bootstrap))] #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")] #[rustc_nounwind] - pub fn unchecked_shr(x: T, y: T) -> T; + pub fn unchecked_shr(x: T, y: U) -> T; /// Returns the result of an unchecked addition, resulting in /// undefined behavior when `x + y > T::MAX` or `x + y < T::MIN`. diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index 427a95f4665d..614b4b589d37 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -549,6 +549,10 @@ extern "rust-intrinsic" { /// /// `U` must be a vector of pointers, with the same length as `T`. #[rustc_nounwind] + #[cfg(not(bootstrap))] + pub fn simd_with_exposed_provenance(addr: T) -> U; + #[rustc_nounwind] + #[cfg(bootstrap)] pub fn simd_from_exposed_addr(addr: T) -> U; /// Swap bytes of each element. @@ -655,3 +659,6 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_flog(a: T) -> T; } + +#[cfg(bootstrap)] +pub use simd_from_exposed_addr as simd_with_exposed_provenance; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 574a357b44ab..6da05a1ca867 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1053,7 +1053,8 @@ pub(crate) mod builtin { /// /// If the environment variable is not defined, then a compilation error /// will be emitted. To not emit a compile error, use the [`option_env!`] - /// macro instead. + /// macro instead. A compilation error will also be emitted if the + /// environment variable is not a vaild Unicode string. /// /// # Examples /// diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 385c288db12c..1d073a6d649b 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -817,6 +817,13 @@ pub trait DiscriminantKind { /// This can be used to declare that a constant with a generic type /// will not contain interior mutability, and subsequently allow /// placing the constant behind references. +/// +/// # Safety +/// +/// This trait is a core part of the language, it is just expressed as a trait in libcore for +/// convenience. Do *not* implement it for other types. +// FIXME: Eventually this trait should become `#[rustc_deny_explicit_impl]`. +// That requires porting the impls below to native internal impls. #[lang = "freeze"] #[unstable(feature = "freeze", issue = "121675")] pub unsafe auto trait Freeze {} diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 2f5184da8853..e34e9b7fff64 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1227,10 +1227,18 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shl`. - // Any legal shift amount is losslessly representable in the self type. - unsafe { intrinsics::unchecked_shl(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) } + #[cfg(bootstrap)] + { + // For bootstrapping, just use built-in primitive shift. + // panicking is a legal manifestation of UB + self << rhs + } + #[cfg(not(bootstrap))] + { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_shl`. + unsafe { intrinsics::unchecked_shl(self, rhs) } + } } /// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is @@ -1310,10 +1318,18 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shr`. - // Any legal shift amount is losslessly representable in the self type. - unsafe { intrinsics::unchecked_shr(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) } + #[cfg(bootstrap)] + { + // For bootstrapping, just use built-in primitive shift. + // panicking is a legal manifestation of UB + self >> rhs + } + #[cfg(not(bootstrap))] + { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_shr`. + unsafe { intrinsics::unchecked_shr(self, rhs) } + } } /// Checked absolute value. Computes `self.abs()`, returning `None` if diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 9e519dad432a..9ebbb4ffe80b 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -285,17 +285,6 @@ macro_rules! widening_impl { }; } -macro_rules! conv_rhs_for_unchecked_shift { - ($SelfT:ty, $x:expr) => {{ - // If the `as` cast will truncate, ensure we still tell the backend - // that the pre-truncation value was also small. - if <$SelfT>::BITS < 32 { - intrinsics::assume($x <= (<$SelfT>::MAX as u32)); - } - $x as $SelfT - }}; -} - impl i8 { int_impl! { Self = i8, diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 3f4b5955d62c..ba6a243041ce 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1286,10 +1286,18 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shl`. - // Any legal shift amount is losslessly representable in the self type. - unsafe { intrinsics::unchecked_shl(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) } + #[cfg(bootstrap)] + { + // For bootstrapping, just use built-in primitive shift. + // panicking is a legal manifestation of UB + self << rhs + } + #[cfg(not(bootstrap))] + { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_shl`. + unsafe { intrinsics::unchecked_shl(self, rhs) } + } } /// Checked shift right. Computes `self >> rhs`, returning `None` @@ -1369,10 +1377,18 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shr`. - // Any legal shift amount is losslessly representable in the self type. - unsafe { intrinsics::unchecked_shr(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) } + #[cfg(bootstrap)] + { + // For bootstrapping, just use built-in primitive shift. + // panicking is a legal manifestation of UB + self >> rhs + } + #[cfg(not(bootstrap))] + { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_shr`. + unsafe { intrinsics::unchecked_shr(self, rhs) } + } } /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 99bd631b581d..e5713ee89df9 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -165,7 +165,7 @@ impl *const T { #[unstable(feature = "ptr_to_from_bits", issue = "91126")] #[deprecated( since = "1.67.0", - note = "replaced by the `ptr::from_exposed_addr` function, or update \ + note = "replaced by the `ptr::with_exposed_provenance` function, or update \ your code to follow the strict provenance rules using its APIs" )] #[allow(fuzzy_provenance_casts)] // this is an unstable and semi-deprecated cast function @@ -187,7 +187,7 @@ impl *const T { /// /// If using those APIs is not possible because there is no way to preserve a pointer with the /// required provenance, then Strict Provenance might not be for you. Use pointer-integer casts - /// or [`expose_addr`][pointer::expose_addr] and [`from_exposed_addr`][from_exposed_addr] + /// or [`expose_addr`][pointer::expose_addr] and [`with_exposed_provenance`][with_exposed_provenance] /// instead. However, note that this makes your code less portable and less amenable to tools /// that check for compliance with the Rust memory model. /// @@ -211,30 +211,30 @@ impl *const T { } /// Gets the "address" portion of the pointer, and 'exposes' the "provenance" part for future - /// use in [`from_exposed_addr`][]. + /// use in [`with_exposed_provenance`][]. /// /// This is equivalent to `self as usize`, which semantically discards *provenance* and /// *address-space* information. Furthermore, this (like the `as` cast) has the implicit /// side-effect of marking the provenance as 'exposed', so on platforms that support it you can - /// later call [`from_exposed_addr`][] to reconstitute the original pointer including its + /// later call [`with_exposed_provenance`][] to reconstitute the original pointer including its /// provenance. (Reconstructing address space information, if required, is your responsibility.) /// /// Using this method means that code is *not* following [Strict /// Provenance][super#strict-provenance] rules. Supporting - /// [`from_exposed_addr`][] complicates specification and reasoning and may not be supported by + /// [`with_exposed_provenance`][] complicates specification and reasoning and may not be supported by /// tools that help you to stay conformant with the Rust memory model, so it is recommended to /// use [`addr`][pointer::addr] wherever possible. /// /// On most platforms this will produce a value with the same bytes as the original pointer, /// because all the bytes are dedicated to describing the address. Platforms which need to store /// additional information in the pointer may not support this operation, since the 'expose' - /// side-effect which is required for [`from_exposed_addr`][] to work is typically not + /// side-effect which is required for [`with_exposed_provenance`][] to work is typically not /// available. /// /// It is unclear whether this method can be given a satisfying unambiguous specification. This /// API and its claimed semantics are part of [Exposed Provenance][super#exposed-provenance]. /// - /// [`from_exposed_addr`]: from_exposed_addr + /// [`with_exposed_provenance`]: with_exposed_provenance #[must_use] #[inline(always)] #[unstable(feature = "exposed_provenance", issue = "95228")] diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 56378b437e7e..28df2b04c88c 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -340,13 +340,13 @@ //! clear where a satisfying unambiguous semantics can be defined for Exposed Provenance. //! Furthermore, Exposed Provenance will not work (well) with tools like [Miri] and [CHERI]. //! -//! Exposed Provenance is provided by the [`expose_addr`] and [`from_exposed_addr`] methods, which +//! Exposed Provenance is provided by the [`expose_addr`] and [`with_exposed_provenance`] methods, which //! are meant to replace `as` casts between pointers and integers. [`expose_addr`] is a lot like //! [`addr`], but additionally adds the provenance of the pointer to a global list of 'exposed' //! provenances. (This list is purely conceptual, it exists for the purpose of specifying Rust but -//! is not materialized in actual executions, except in tools like [Miri].) [`from_exposed_addr`] +//! is not materialized in actual executions, except in tools like [Miri].) [`with_exposed_provenance`] //! can be used to construct a pointer with one of these previously 'exposed' provenances. -//! [`from_exposed_addr`] takes only `addr: usize` as arguments, so unlike in [`with_addr`] there is +//! [`with_exposed_provenance`] takes only `addr: usize` as arguments, so unlike in [`with_addr`] there is //! no indication of what the correct provenance for the returned pointer is -- and that is exactly //! what makes pointer-usize-pointer roundtrips so tricky to rigorously specify! There is no //! algorithm that decides which provenance will be used. You can think of this as "guessing" the @@ -355,10 +355,10 @@ //! there is *no* previously 'exposed' provenance that justifies the way the returned pointer will //! be used, the program has undefined behavior. //! -//! Using [`expose_addr`] or [`from_exposed_addr`] (or the `as` casts) means that code is +//! Using [`expose_addr`] or [`with_exposed_provenance`] (or the `as` casts) means that code is //! *not* following Strict Provenance rules. The goal of the Strict Provenance experiment is to //! determine how far one can get in Rust without the use of [`expose_addr`] and -//! [`from_exposed_addr`], and to encourage code to be written with Strict Provenance APIs only. +//! [`with_exposed_provenance`], and to encourage code to be written with Strict Provenance APIs only. //! Maximizing the amount of such code is a major win for avoiding specification complexity and to //! facilitate adoption of tools like [CHERI] and [Miri] that can be a big help in increasing the //! confidence in (unsafe) Rust code. @@ -375,7 +375,7 @@ //! [`addr`]: pointer::addr //! [`ptr::dangling`]: core::ptr::dangling //! [`expose_addr`]: pointer::expose_addr -//! [`from_exposed_addr`]: from_exposed_addr +//! [`with_exposed_provenance`]: with_exposed_provenance //! [Miri]: https://github.com/rust-lang/miri //! [CHERI]: https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/ //! [Strict Provenance]: https://github.com/rust-lang/rust/issues/95228 @@ -581,7 +581,7 @@ pub const fn null_mut() -> *mut T { /// little more than a usize address in disguise. /// /// This is different from `addr as *const T`, which creates a pointer that picks up a previously -/// exposed provenance. See [`from_exposed_addr`] for more details on that operation. +/// exposed provenance. See [`with_exposed_provenance`] for more details on that operation. /// /// This API and its claimed semantics are part of the Strict Provenance experiment, /// see the [module documentation][crate::ptr] for details. @@ -592,7 +592,7 @@ pub const fn null_mut() -> *mut T { pub const fn without_provenance(addr: usize) -> *const T { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. // We use transmute rather than a cast so tools like Miri can tell that this - // is *not* the same as from_exposed_addr. + // is *not* the same as with_exposed_provenance. // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that // pointer). unsafe { mem::transmute(addr) } @@ -625,7 +625,7 @@ pub const fn dangling() -> *const T { /// little more than a usize address in disguise. /// /// This is different from `addr as *mut T`, which creates a pointer that picks up a previously -/// exposed provenance. See [`from_exposed_addr_mut`] for more details on that operation. +/// exposed provenance. See [`with_exposed_provenance_mut`] for more details on that operation. /// /// This API and its claimed semantics are part of the Strict Provenance experiment, /// see the [module documentation][crate::ptr] for details. @@ -636,7 +636,7 @@ pub const fn dangling() -> *const T { pub const fn without_provenance_mut(addr: usize) -> *mut T { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. // We use transmute rather than a cast so tools like Miri can tell that this - // is *not* the same as from_exposed_addr. + // is *not* the same as with_exposed_provenance. // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that // pointer). unsafe { mem::transmute(addr) } @@ -699,7 +699,7 @@ pub const fn dangling_mut() -> *mut T { #[unstable(feature = "exposed_provenance", issue = "95228")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead -pub fn from_exposed_addr(addr: usize) -> *const T +pub fn with_exposed_provenance(addr: usize) -> *const T where T: Sized, { @@ -739,7 +739,7 @@ where #[unstable(feature = "exposed_provenance", issue = "95228")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead -pub fn from_exposed_addr_mut(addr: usize) -> *mut T +pub fn with_exposed_provenance_mut(addr: usize) -> *mut T where T: Sized, { @@ -1781,9 +1781,19 @@ pub(crate) const unsafe fn align_offset(p: *const T, a: usize) -> usiz // FIXME(#75598): Direct use of these intrinsics improves codegen significantly at opt-level <= // 1, where the method versions of these operations are not inlined. use intrinsics::{ - assume, cttz_nonzero, exact_div, mul_with_overflow, unchecked_rem, unchecked_shl, - unchecked_shr, unchecked_sub, wrapping_add, wrapping_mul, wrapping_sub, + assume, cttz_nonzero, exact_div, mul_with_overflow, unchecked_rem, unchecked_sub, + wrapping_add, wrapping_mul, wrapping_sub, }; + #[cfg(bootstrap)] + const unsafe fn unchecked_shl(value: usize, shift: usize) -> usize { + value << shift + } + #[cfg(bootstrap)] + const unsafe fn unchecked_shr(value: usize, shift: usize) -> usize { + value >> shift + } + #[cfg(not(bootstrap))] + use intrinsics::{unchecked_shl, unchecked_shr}; /// Calculate multiplicative modular inverse of `x` modulo `m`. /// diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index e9d39b2c2764..042658fa35f8 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -171,7 +171,7 @@ impl *mut T { #[unstable(feature = "ptr_to_from_bits", issue = "91126")] #[deprecated( since = "1.67.0", - note = "replaced by the `ptr::from_exposed_addr_mut` function, or \ + note = "replaced by the `ptr::with_exposed_provenance_mut` function, or \ update your code to follow the strict provenance rules using its APIs" )] #[allow(fuzzy_provenance_casts)] // this is an unstable and semi-deprecated cast function @@ -194,7 +194,7 @@ impl *mut T { /// /// If using those APIs is not possible because there is no way to preserve a pointer with the /// required provenance, then Strict Provenance might not be for you. Use pointer-integer casts - /// or [`expose_addr`][pointer::expose_addr] and [`from_exposed_addr`][from_exposed_addr] + /// or [`expose_addr`][pointer::expose_addr] and [`with_exposed_provenance`][with_exposed_provenance] /// instead. However, note that this makes your code less portable and less amenable to tools /// that check for compliance with the Rust memory model. /// @@ -218,30 +218,30 @@ impl *mut T { } /// Gets the "address" portion of the pointer, and 'exposes' the "provenance" part for future - /// use in [`from_exposed_addr`][]. + /// use in [`with_exposed_provenance`][]. /// /// This is equivalent to `self as usize`, which semantically discards *provenance* and /// *address-space* information. Furthermore, this (like the `as` cast) has the implicit /// side-effect of marking the provenance as 'exposed', so on platforms that support it you can - /// later call [`from_exposed_addr_mut`][] to reconstitute the original pointer including its + /// later call [`with_exposed_provenance_mut`][] to reconstitute the original pointer including its /// provenance. (Reconstructing address space information, if required, is your responsibility.) /// /// Using this method means that code is *not* following [Strict /// Provenance][super#strict-provenance] rules. Supporting - /// [`from_exposed_addr_mut`][] complicates specification and reasoning and may not be supported + /// [`with_exposed_provenance_mut`][] complicates specification and reasoning and may not be supported /// by tools that help you to stay conformant with the Rust memory model, so it is recommended /// to use [`addr`][pointer::addr] wherever possible. /// /// On most platforms this will produce a value with the same bytes as the original pointer, /// because all the bytes are dedicated to describing the address. Platforms which need to store /// additional information in the pointer may not support this operation, since the 'expose' - /// side-effect which is required for [`from_exposed_addr_mut`][] to work is typically not + /// side-effect which is required for [`with_exposed_provenance_mut`][] to work is typically not /// available. /// /// It is unclear whether this method can be given a satisfying unambiguous specification. This /// API and its claimed semantics are part of [Exposed Provenance][super#exposed-provenance]. /// - /// [`from_exposed_addr_mut`]: from_exposed_addr_mut + /// [`with_exposed_provenance_mut`]: with_exposed_provenance_mut #[must_use] #[inline(always)] #[unstable(feature = "exposed_provenance", issue = "95228")] diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 127a407dae5d..8d7b6165510a 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -195,6 +195,7 @@ pub unsafe trait SliceIndex: private_slice_index::Sealed { fn index_mut(self, slice: &mut T) -> &mut Self::Output; } +/// The methods `index` and `index_mut` panic if the index is out of bounds. #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for usize { @@ -328,6 +329,9 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { } } +/// The methods `index` and `index_mut` panic if: +/// - the start of the range is greater than the end of the range or +/// - the end of the range is out of bounds. #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for ops::Range { @@ -416,6 +420,7 @@ unsafe impl SliceIndex<[T]> for ops::Range { } } +/// The methods `index` and `index_mut` panic if the end of the range is out of bounds. #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for ops::RangeTo { @@ -454,6 +459,7 @@ unsafe impl SliceIndex<[T]> for ops::RangeTo { } } +/// The methods `index` and `index_mut` panic if the start of the range is out of bounds. #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for ops::RangeFrom { @@ -536,6 +542,10 @@ unsafe impl SliceIndex<[T]> for ops::RangeFull { } } +/// The methods `index` and `index_mut` panic if: +/// - the end of the range is `usize::MAX` or +/// - the start of the range is greater than the end of the range or +/// - the end of the range is out of bounds. #[stable(feature = "inclusive_range", since = "1.26.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for ops::RangeInclusive { @@ -580,6 +590,7 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { } } +/// The methods `index` and `index_mut` panic if the end of the range is out of bounds. #[stable(feature = "inclusive_range", since = "1.26.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for ops::RangeToInclusive { diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index a16005abf464..6e1ba74f72b3 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2724,8 +2724,10 @@ impl [T] { /// ``` /// let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; /// let num = 42; - /// let idx = s.partition_point(|&x| x < num); - /// // The above is equivalent to `let idx = s.binary_search(&num).unwrap_or_else(|x| x);` + /// let idx = s.partition_point(|&x| x <= num); + /// // If `num` is unique, `s.partition_point(|&x| x < num)` (with `<`) is equivalent to + /// // `s.binary_search(&num).unwrap_or_else(|x| x)`, but using `<=` will allow `insert` + /// // to shift less elements. /// s.insert(idx, num); /// assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]); /// ``` @@ -4175,7 +4177,7 @@ impl [T] { /// ``` /// let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; /// let num = 42; - /// let idx = s.partition_point(|&x| x < num); + /// let idx = s.partition_point(|&x| x <= num); /// s.insert(idx, num); /// assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]); /// ``` diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 1b43c46bda51..fe39f6c0b569 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -2,6 +2,7 @@ use crate::mem::transmute; +use crate::any::Any; use crate::fmt; use crate::marker::PhantomData; use crate::ptr; @@ -220,6 +221,12 @@ impl RawWakerVTable { } } +#[derive(Debug)] +enum ExtData<'a> { + Some(&'a mut dyn Any), + None(()), +} + /// The context of an asynchronous task. /// /// Currently, `Context` only serves to provide access to a [`&Waker`](Waker) @@ -229,6 +236,7 @@ impl RawWakerVTable { pub struct Context<'a> { waker: &'a Waker, local_waker: &'a LocalWaker, + ext: ExtData<'a>, // Ensure we future-proof against variance changes by forcing // the lifetime to be invariant (argument-position lifetimes // are contravariant while return-position lifetimes are @@ -257,6 +265,7 @@ impl<'a> Context<'a> { pub const fn waker(&self) -> &'a Waker { &self.waker } + /// Returns a reference to the [`LocalWaker`] for the current task. #[inline] #[unstable(feature = "local_waker", issue = "118959")] @@ -264,6 +273,17 @@ impl<'a> Context<'a> { pub const fn local_waker(&self) -> &'a LocalWaker { &self.local_waker } + + /// Returns a reference to the extension data for the current task. + #[inline] + #[unstable(feature = "context_ext", issue = "123392")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const fn ext(&mut self) -> &mut dyn Any { + match &mut self.ext { + ExtData::Some(data) => *data, + ExtData::None(unit) => unit, + } + } } #[stable(feature = "futures_api", since = "1.36.0")] @@ -300,6 +320,7 @@ impl fmt::Debug for Context<'_> { pub struct ContextBuilder<'a> { waker: &'a Waker, local_waker: &'a LocalWaker, + ext: ExtData<'a>, // Ensure we future-proof against variance changes by forcing // the lifetime to be invariant (argument-position lifetimes // are contravariant while return-position lifetimes are @@ -318,7 +339,39 @@ impl<'a> ContextBuilder<'a> { pub const fn from_waker(waker: &'a Waker) -> Self { // SAFETY: LocalWaker is just Waker without thread safety let local_waker = unsafe { transmute(waker) }; - Self { waker: waker, local_waker, _marker: PhantomData, _marker2: PhantomData } + Self { + waker: waker, + local_waker, + ext: ExtData::None(()), + _marker: PhantomData, + _marker2: PhantomData, + } + } + + /// Create a ContextBuilder from an existing Context. + #[inline] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[unstable(feature = "context_ext", issue = "123392")] + pub const fn from(cx: &'a mut Context<'_>) -> Self { + let ext = match &mut cx.ext { + ExtData::Some(ext) => ExtData::Some(*ext), + ExtData::None(()) => ExtData::None(()), + }; + Self { + waker: cx.waker, + local_waker: cx.local_waker, + ext, + _marker: PhantomData, + _marker2: PhantomData, + } + } + + /// This method is used to set the value for the waker on `Context`. + #[inline] + #[unstable(feature = "context_ext", issue = "123392")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const fn waker(self, waker: &'a Waker) -> Self { + Self { waker, ..self } } /// This method is used to set the value for the local waker on `Context`. @@ -329,13 +382,21 @@ impl<'a> ContextBuilder<'a> { Self { local_waker, ..self } } + /// This method is used to set the value for the extension data on `Context`. + #[inline] + #[unstable(feature = "context_ext", issue = "123392")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const fn ext(self, data: &'a mut dyn Any) -> Self { + Self { ext: ExtData::Some(data), ..self } + } + /// Builds the `Context`. #[inline] #[unstable(feature = "local_waker", issue = "118959")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn build(self) -> Context<'a> { - let ContextBuilder { waker, local_waker, _marker, _marker2 } = self; - Context { waker, local_waker, _marker, _marker2 } + let ContextBuilder { waker, local_waker, ext, _marker, _marker2 } = self; + Context { waker, local_waker, ext, _marker, _marker2 } } } diff --git a/library/core/tests/intrinsics.rs b/library/core/tests/intrinsics.rs index 740565d0df6b..eb1e1a0b9b1d 100644 --- a/library/core/tests/intrinsics.rs +++ b/library/core/tests/intrinsics.rs @@ -99,3 +99,30 @@ fn test_const_deallocate_at_runtime() { const_deallocate(core::ptr::null_mut(), 1, 1); // nop } } + +#[cfg(not(bootstrap))] +#[test] +fn test_three_way_compare_in_const_contexts() { + use core::cmp::Ordering::{self, *}; + use core::intrinsics::three_way_compare; + + const UNSIGNED_LESS: Ordering = three_way_compare(123_u16, 456); + const UNSIGNED_EQUAL: Ordering = three_way_compare(456_u16, 456); + const UNSIGNED_GREATER: Ordering = three_way_compare(789_u16, 456); + const CHAR_LESS: Ordering = three_way_compare('A', 'B'); + const CHAR_EQUAL: Ordering = three_way_compare('B', 'B'); + const CHAR_GREATER: Ordering = three_way_compare('C', 'B'); + const SIGNED_LESS: Ordering = three_way_compare(123_i64, 456); + const SIGNED_EQUAL: Ordering = three_way_compare(456_i64, 456); + const SIGNED_GREATER: Ordering = three_way_compare(789_i64, 456); + + assert_eq!(UNSIGNED_LESS, Less); + assert_eq!(UNSIGNED_EQUAL, Equal); + assert_eq!(UNSIGNED_GREATER, Greater); + assert_eq!(CHAR_LESS, Less); + assert_eq!(CHAR_EQUAL, Equal); + assert_eq!(CHAR_GREATER, Greater); + assert_eq!(SIGNED_LESS, Less); + assert_eq!(SIGNED_EQUAL, Equal); + assert_eq!(SIGNED_GREATER, Greater); +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 52d2b798c911..175c27662d08 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -22,6 +22,7 @@ #![feature(const_pointer_is_aligned)] #![feature(const_ptr_as_ref)] #![feature(const_ptr_write)] +#![cfg_attr(not(bootstrap), feature(const_three_way_compare))] #![feature(const_trait_impl)] #![feature(const_likely)] #![feature(const_location_fields)] diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs index e217d1c8c87c..4d2fe999ca6f 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -51,13 +51,13 @@ pub trait SimdConstPtr: Copy + Sealed { fn with_addr(self, addr: Self::Usize) -> Self; /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use - /// in [`Self::from_exposed_addr`]. + /// in [`Self::with_exposed_provenance`]. fn expose_addr(self) -> Self::Usize; /// Convert an address back to a pointer, picking up a previously "exposed" provenance. /// - /// Equivalent to calling [`core::ptr::from_exposed_addr`] on each element. - fn from_exposed_addr(addr: Self::Usize) -> Self; + /// Equivalent to calling [`core::ptr::with_exposed_provenance`] on each element. + fn with_exposed_provenance(addr: Self::Usize) -> Self; /// Calculates the offset from a pointer using wrapping arithmetic. /// @@ -137,9 +137,9 @@ where } #[inline] - fn from_exposed_addr(addr: Self::Usize) -> Self { + fn with_exposed_provenance(addr: Self::Usize) -> Self { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) } + unsafe { core::intrinsics::simd::simd_with_exposed_provenance(addr) } } #[inline] diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs index 5cb27af4fdeb..b3437b9c4996 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -48,13 +48,13 @@ pub trait SimdMutPtr: Copy + Sealed { fn with_addr(self, addr: Self::Usize) -> Self; /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use - /// in [`Self::from_exposed_addr`]. + /// in [`Self::with_exposed_provenance`]. fn expose_addr(self) -> Self::Usize; /// Convert an address back to a pointer, picking up a previously "exposed" provenance. /// - /// Equivalent to calling [`core::ptr::from_exposed_addr_mut`] on each element. - fn from_exposed_addr(addr: Self::Usize) -> Self; + /// Equivalent to calling [`core::ptr::with_exposed_provenance_mut`] on each element. + fn with_exposed_provenance(addr: Self::Usize) -> Self; /// Calculates the offset from a pointer using wrapping arithmetic. /// @@ -134,9 +134,9 @@ where } #[inline] - fn from_exposed_addr(addr: Self::Usize) -> Self { + fn with_exposed_provenance(addr: Self::Usize) -> Self { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) } + unsafe { core::intrinsics::simd::simd_with_exposed_provenance(addr) } } #[inline] diff --git a/library/portable-simd/crates/core_simd/tests/pointers.rs b/library/portable-simd/crates/core_simd/tests/pointers.rs index b9f32d16e01d..5984fdae2f9b 100644 --- a/library/portable-simd/crates/core_simd/tests/pointers.rs +++ b/library/portable-simd/crates/core_simd/tests/pointers.rs @@ -80,10 +80,10 @@ mod const_ptr { ); } - fn from_exposed_addr() { + fn with_exposed_provenance() { test_helpers::test_unary_elementwise( - &Simd::<*const u32, LANES>::from_exposed_addr, - &core::ptr::from_exposed_addr::, + &Simd::<*const u32, LANES>::with_exposed_provenance, + &core::ptr::with_exposed_provenance::, &|_| true, ); } @@ -103,10 +103,10 @@ mod mut_ptr { ); } - fn from_exposed_addr() { + fn with_exposed_provenance() { test_helpers::test_unary_elementwise( - &Simd::<*mut u32, LANES>::from_exposed_addr, - &core::ptr::from_exposed_addr_mut::, + &Simd::<*mut u32, LANES>::with_exposed_provenance, + &core::ptr::with_exposed_provenance_mut::, &|_| true, ); } diff --git a/library/std/src/os/xous/ffi.rs b/library/std/src/os/xous/ffi.rs index 7fe84db515c3..e9a9f5337202 100644 --- a/library/std/src/os/xous/ffi.rs +++ b/library/std/src/os/xous/ffi.rs @@ -389,7 +389,7 @@ pub(crate) unsafe fn map_memory( let result = a0; if result == SyscallResult::MemoryRange as usize { - let start = core::ptr::from_exposed_addr_mut::(a1); + let start = core::ptr::with_exposed_provenance_mut::(a1); let len = a2 / core::mem::size_of::(); let end = unsafe { start.add(len) }; Ok(unsafe { core::slice::from_raw_parts_mut(start, len) }) diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 3d576af681e0..e63b46ab7054 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -126,6 +126,9 @@ where /// Also note that unwinding into Rust code with a foreign exception (e.g. /// an exception thrown from C++ code) is undefined behavior. /// +/// Finally, be **careful in how you drop the result of this function**. +/// If it is `Err`, it contains the panic payload, and dropping that may in turn panic! +/// /// # Examples /// /// ``` diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 31dbe86b66c7..f46e1e171d25 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -21,7 +21,6 @@ use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::{PoisonError, RwLock}; use crate::sys::stdio::panic_output; use crate::sys_common::backtrace; -use crate::sys_common::thread_info; use crate::thread; #[cfg(not(test))] @@ -256,7 +255,7 @@ fn default_hook(info: &PanicInfo<'_>) { None => "Box", }, }; - let thread = thread_info::current_thread(); + let thread = thread::try_current(); let name = thread.as_ref().and_then(|t| t.name()).unwrap_or(""); let write = |err: &mut dyn crate::io::Write| { diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 335944845ae0..ff6e433ebce3 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -24,8 +24,7 @@ pub use core::panicking::{panic_display, panic_fmt}; use crate::sync::Once; use crate::sys; -use crate::sys_common::thread_info; -use crate::thread::Thread; +use crate::thread::{self, Thread}; // Prints to the "panic output", depending on the platform this may be: // - the standard error output @@ -96,13 +95,9 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { unsafe { sys::init(argc, argv, sigpipe); - let main_guard = sys::thread::guard::init(); - // Next, set up the current Thread with the guard information we just - // created. Note that this isn't necessary in general for new threads, - // but we just do this to name the main thread and to give it correct - // info about the stack bounds. + // Set up the current thread to give it the right name. let thread = Thread::new(Some(rtunwrap!(Ok, CString::new("main")))); - thread_info::set(main_guard, thread); + thread::set_current(thread); } } diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index cf45b9c23962..62c76a74cd4d 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -47,7 +47,7 @@ impl Thread { extern "C" fn thread_start(main: usize) { unsafe { // Finally, let's run some code. - Box::from_raw(ptr::from_exposed_addr::>(main).cast_mut())(); + Box::from_raw(ptr::with_exposed_provenance::>(main).cast_mut())(); // run all destructors run_dtors(); @@ -104,13 +104,3 @@ impl Thread { pub fn available_parallelism() -> io::Result> { unsafe { Ok(NonZero::new_unchecked(abi::get_processor_count())) } } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs index 814a102dd09a..1c66bd79ce4f 100644 --- a/library/std/src/sys/pal/itron/thread.rs +++ b/library/std/src/sys/pal/itron/thread.rs @@ -98,7 +98,7 @@ impl Thread { }); unsafe extern "C" fn trampoline(exinf: isize) { - let p_inner: *mut ThreadInner = crate::ptr::from_exposed_addr_mut(exinf as usize); + let p_inner: *mut ThreadInner = crate::ptr::with_exposed_provenance_mut(exinf as usize); // Safety: `ThreadInner` is alive at this point let inner = unsafe { &*p_inner }; @@ -312,16 +312,6 @@ impl Drop for Thread { } } -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} - /// Terminate and delete the specified task. /// /// This function will abort if `deleted_task` refers to the calling task. diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs index 77f68bf73348..ef07f6e6a263 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/pal/sgx/thread.rs @@ -149,13 +149,3 @@ impl Thread { pub fn available_parallelism() -> io::Result> { unsupported() } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs index b76bcf9bbb0a..fb4b74ba3c36 100644 --- a/library/std/src/sys/pal/teeos/thread.rs +++ b/library/std/src/sys/pal/teeos/thread.rs @@ -151,18 +151,6 @@ pub fn available_parallelism() -> io::Result> { )) } -// stub -pub mod guard { - use crate::ops::Range; - pub type Guard = Range; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} - fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { libc::PTHREAD_STACK_MIN.try_into().expect("Infallible") } diff --git a/library/std/src/sys/pal/uefi/thread.rs b/library/std/src/sys/pal/uefi/thread.rs index b3a4f9c53e36..ca7b1efc2699 100644 --- a/library/std/src/sys/pal/uefi/thread.rs +++ b/library/std/src/sys/pal/uefi/thread.rs @@ -52,13 +52,3 @@ pub fn available_parallelism() -> io::Result> { // UEFI is single threaded Ok(NonZero::new(1).unwrap()) } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 78a599077c75..26c49257ad00 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -11,7 +11,7 @@ pub struct Handler { impl Handler { pub unsafe fn new() -> Handler { - make_handler() + make_handler(false) } fn null() -> Handler { @@ -29,34 +29,41 @@ impl Drop for Handler { #[cfg(any( target_os = "linux", - target_os = "macos", - target_os = "dragonfly", target_os = "freebsd", target_os = "hurd", - target_os = "solaris", - target_os = "illumos", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "solaris" ))] mod imp { use super::Handler; + use crate::cell::Cell; use crate::io; use crate::mem; + use crate::ops::Range; use crate::ptr; + use crate::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; + use crate::sys::pal::unix::os; use crate::thread; - use libc::MAP_FAILED; #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::{mmap as mmap64, munmap}; + use libc::{mmap as mmap64, mprotect, munmap}; #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::{mmap64, munmap}; - use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL}; + use libc::{mmap64, mprotect, munmap}; + use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSEGV, SIG_DFL}; use libc::{sigaltstack, SS_DISABLE}; - use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SIGSEGV}; + use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; - use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; - use crate::sys::pal::unix::os::page_size; - use crate::sys_common::thread_info; + // We use a TLS variable to store the address of the guard page. While TLS + // variables are not guaranteed to be signal-safe, this works out in practice + // since we make sure to write to the variable before the signal stack is + // installed, thereby ensuring that the variable is always allocated when + // the signal handler is called. + thread_local! { + // FIXME: use `Range` once that implements `Copy`. + static GUARD: Cell<(usize, usize)> = const { Cell::new((0, 0)) }; + } // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages // (unmapped pages) at the end of every thread's stack, so if a thread ends @@ -84,12 +91,12 @@ mod imp { info: *mut libc::siginfo_t, _data: *mut libc::c_void, ) { - let guard = thread_info::stack_guard().unwrap_or(0..0); + let (start, end) = GUARD.get(); let addr = (*info).si_addr() as usize; // If the faulting address is within the guard page, then we print a // message saying so and abort. - if guard.start <= addr && addr < guard.end { + if start <= addr && addr < end { rtprintpanic!( "\nthread '{}' has overflowed its stack\n", thread::current().name().unwrap_or("") @@ -105,10 +112,17 @@ mod imp { } } + static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); static MAIN_ALTSTACK: AtomicPtr = AtomicPtr::new(ptr::null_mut()); static NEED_ALTSTACK: AtomicBool = AtomicBool::new(false); pub unsafe fn init() { + PAGE_SIZE.store(os::page_size(), Ordering::Relaxed); + + // Always write to GUARD to ensure the TLS variable is allocated. + let guard = install_main_guard().unwrap_or(0..0); + GUARD.set((guard.start, guard.end)); + let mut action: sigaction = mem::zeroed(); for &signal in &[SIGSEGV, SIGBUS] { sigaction(signal, ptr::null_mut(), &mut action); @@ -121,7 +135,7 @@ mod imp { } } - let handler = make_handler(); + let handler = make_handler(true); MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); mem::forget(handler); } @@ -150,7 +164,7 @@ mod imp { let flags = MAP_PRIVATE | MAP_ANON; let sigstack_size = sigstack_size(); - let page_size = page_size(); + let page_size = PAGE_SIZE.load(Ordering::Relaxed); let stackp = mmap64( ptr::null_mut(), @@ -172,10 +186,17 @@ mod imp { libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size } } - pub unsafe fn make_handler() -> Handler { + pub unsafe fn make_handler(main_thread: bool) -> Handler { if !NEED_ALTSTACK.load(Ordering::Relaxed) { return Handler::null(); } + + if !main_thread { + // Always write to GUARD to ensure the TLS variable is allocated. + let guard = current_guard().unwrap_or(0..0); + GUARD.set((guard.start, guard.end)); + } + let mut stack = mem::zeroed(); sigaltstack(ptr::null(), &mut stack); // Configure alternate signal stack, if one is not already set. @@ -191,7 +212,7 @@ mod imp { pub unsafe fn drop_handler(data: *mut libc::c_void) { if !data.is_null() { let sigstack_size = sigstack_size(); - let page_size = page_size(); + let page_size = PAGE_SIZE.load(Ordering::Relaxed); let stack = libc::stack_t { ss_sp: ptr::null_mut(), ss_flags: SS_DISABLE, @@ -225,25 +246,266 @@ mod imp { fn sigstack_size() -> usize { libc::SIGSTKSZ } + + #[cfg(target_os = "solaris")] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let mut current_stack: libc::stack_t = crate::mem::zeroed(); + assert_eq!(libc::stack_getbounds(&mut current_stack), 0); + Some(current_stack.ss_sp) + } + + #[cfg(target_os = "macos")] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let th = libc::pthread_self(); + let stackptr = libc::pthread_get_stackaddr_np(th); + Some(stackptr.map_addr(|addr| addr - libc::pthread_get_stacksize_np(th))) + } + + #[cfg(target_os = "openbsd")] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let mut current_stack: libc::stack_t = crate::mem::zeroed(); + assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0); + + let stack_ptr = current_stack.ss_sp; + let stackaddr = if libc::pthread_main_np() == 1 { + // main thread + stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed) + } else { + // new thread + stack_ptr.addr() - current_stack.ss_size + }; + Some(stack_ptr.with_addr(stackaddr)) + } + + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", + target_os = "hurd", + target_os = "linux", + target_os = "l4re" + ))] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let mut ret = None; + let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); + #[cfg(target_os = "freebsd")] + assert_eq!(libc::pthread_attr_init(&mut attr), 0); + #[cfg(target_os = "freebsd")] + let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); + #[cfg(not(target_os = "freebsd"))] + let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); + if e == 0 { + let mut stackaddr = crate::ptr::null_mut(); + let mut stacksize = 0; + assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0); + ret = Some(stackaddr); + } + if e == 0 || cfg!(target_os = "freebsd") { + assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); + } + ret + } + + unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> { + let page_size = PAGE_SIZE.load(Ordering::Relaxed); + let stackptr = get_stack_start()?; + let stackaddr = stackptr.addr(); + + // Ensure stackaddr is page aligned! A parent process might + // have reset RLIMIT_STACK to be non-page aligned. The + // pthread_attr_getstack() reports the usable stack area + // stackaddr < stackaddr + stacksize, so if stackaddr is not + // page-aligned, calculate the fix such that stackaddr < + // new_page_aligned_stackaddr < stackaddr + stacksize + let remainder = stackaddr % page_size; + Some(if remainder == 0 { + stackptr + } else { + stackptr.with_addr(stackaddr + page_size - remainder) + }) + } + + unsafe fn install_main_guard() -> Option> { + let page_size = PAGE_SIZE.load(Ordering::Relaxed); + if cfg!(all(target_os = "linux", not(target_env = "musl"))) { + // Linux doesn't allocate the whole stack right away, and + // the kernel has its own stack-guard mechanism to fault + // when growing too close to an existing mapping. If we map + // our own guard, then the kernel starts enforcing a rather + // large gap above that, rendering much of the possible + // stack space useless. See #43052. + // + // Instead, we'll just note where we expect rlimit to start + // faulting, so our handler can report "stack overflow", and + // trust that the kernel's own stack guard will work. + let stackptr = get_stack_start_aligned()?; + let stackaddr = stackptr.addr(); + Some(stackaddr - page_size..stackaddr) + } else if cfg!(all(target_os = "linux", target_env = "musl")) { + // For the main thread, the musl's pthread_attr_getstack + // returns the current stack size, rather than maximum size + // it can eventually grow to. It cannot be used to determine + // the position of kernel's stack guard. + None + } else if cfg!(target_os = "freebsd") { + // FreeBSD's stack autogrows, and optionally includes a guard page + // at the bottom. If we try to remap the bottom of the stack + // ourselves, FreeBSD's guard page moves upwards. So we'll just use + // the builtin guard page. + let stackptr = get_stack_start_aligned()?; + let guardaddr = stackptr.addr(); + // Technically the number of guard pages is tunable and controlled + // by the security.bsd.stack_guard_page sysctl. + // By default it is 1, checking once is enough since it is + // a boot time config value. + static PAGES: crate::sync::OnceLock = crate::sync::OnceLock::new(); + + let pages = PAGES.get_or_init(|| { + use crate::sys::weak::dlsym; + dlsym!(fn sysctlbyname(*const libc::c_char, *mut libc::c_void, *mut libc::size_t, *const libc::c_void, libc::size_t) -> libc::c_int); + let mut guard: usize = 0; + let mut size = crate::mem::size_of_val(&guard); + let oid = crate::ffi::CStr::from_bytes_with_nul( + b"security.bsd.stack_guard_page\0", + ) + .unwrap(); + match sysctlbyname.get() { + Some(fcn) => { + if fcn(oid.as_ptr(), core::ptr::addr_of_mut!(guard) as *mut _, core::ptr::addr_of_mut!(size) as *mut _, crate::ptr::null_mut(), 0) == 0 { + guard + } else { + 1 + } + }, + _ => 1, + } + }); + Some(guardaddr..guardaddr + pages * page_size) + } else if cfg!(any(target_os = "openbsd", target_os = "netbsd")) { + // OpenBSD stack already includes a guard page, and stack is + // immutable. + // NetBSD stack includes the guard page. + // + // We'll just note where we expect rlimit to start + // faulting, so our handler can report "stack overflow", and + // trust that the kernel's own stack guard will work. + let stackptr = get_stack_start_aligned()?; + let stackaddr = stackptr.addr(); + Some(stackaddr - page_size..stackaddr) + } else { + // Reallocate the last page of the stack. + // This ensures SIGBUS will be raised on + // stack overflow. + // Systems which enforce strict PAX MPROTECT do not allow + // to mprotect() a mapping with less restrictive permissions + // than the initial mmap() used, so we mmap() here with + // read/write permissions and only then mprotect() it to + // no permissions at all. See issue #50313. + let stackptr = get_stack_start_aligned()?; + let result = mmap64( + stackptr, + page_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + -1, + 0, + ); + if result != stackptr || result == MAP_FAILED { + panic!("failed to allocate a guard page: {}", io::Error::last_os_error()); + } + + let result = mprotect(stackptr, page_size, PROT_NONE); + if result != 0 { + panic!("failed to protect the guard page: {}", io::Error::last_os_error()); + } + + let guardaddr = stackptr.addr(); + + Some(guardaddr..guardaddr + page_size) + } + } + + #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))] + unsafe fn current_guard() -> Option> { + let stackptr = get_stack_start()?; + let stackaddr = stackptr.addr(); + Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr) + } + + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "l4re" + ))] + unsafe fn current_guard() -> Option> { + let mut ret = None; + let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); + #[cfg(target_os = "freebsd")] + assert_eq!(libc::pthread_attr_init(&mut attr), 0); + #[cfg(target_os = "freebsd")] + let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); + #[cfg(not(target_os = "freebsd"))] + let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); + if e == 0 { + let mut guardsize = 0; + assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0); + if guardsize == 0 { + if cfg!(all(target_os = "linux", target_env = "musl")) { + // musl versions before 1.1.19 always reported guard + // size obtained from pthread_attr_get_np as zero. + // Use page size as a fallback. + guardsize = PAGE_SIZE.load(Ordering::Relaxed); + } else { + panic!("there is no guard page"); + } + } + let mut stackptr = crate::ptr::null_mut::(); + let mut size = 0; + assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackptr, &mut size), 0); + + let stackaddr = stackptr.addr(); + ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) { + Some(stackaddr - guardsize..stackaddr) + } else if cfg!(all(target_os = "linux", target_env = "musl")) { + Some(stackaddr - guardsize..stackaddr) + } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc"))) + { + // glibc used to include the guard area within the stack, as noted in the BUGS + // section of `man pthread_attr_getguardsize`. This has been corrected starting + // with glibc 2.27, and in some distro backports, so the guard is now placed at the + // end (below) the stack. There's no easy way for us to know which we have at + // runtime, so we'll just match any fault in the range right above or below the + // stack base to call that fault a stack overflow. + Some(stackaddr - guardsize..stackaddr + guardsize) + } else { + Some(stackaddr..stackaddr + guardsize) + }; + } + if e == 0 || cfg!(target_os = "freebsd") { + assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); + } + ret + } } #[cfg(not(any( target_os = "linux", - target_os = "macos", - target_os = "dragonfly", target_os = "freebsd", target_os = "hurd", - target_os = "solaris", - target_os = "illumos", + target_os = "macos", target_os = "netbsd", target_os = "openbsd", + target_os = "solaris" )))] mod imp { pub unsafe fn init() {} pub unsafe fn cleanup() {} - pub unsafe fn make_handler() -> super::Handler { + pub unsafe fn make_handler(_main_thread: bool) -> super::Handler { super::Handler::null() } diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 4cd7c0e3059b..7d25c974ed36 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -182,8 +182,11 @@ impl Thread { if let Some(f) = pthread_setname_np.get() { #[cfg(target_os = "nto")] - let name = truncate_cstr::<{ libc::_NTO_THREAD_NAME_MAX as usize }>(name); + const THREAD_NAME_MAX: usize = libc::_NTO_THREAD_NAME_MAX as usize; + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + const THREAD_NAME_MAX: usize = 32; + let name = truncate_cstr::<{ THREAD_NAME_MAX }>(name); let res = unsafe { f(libc::pthread_self(), name.as_ptr()) }; debug_assert_eq!(res, 0); } @@ -225,9 +228,20 @@ impl Thread { // Newlib, Emscripten, and VxWorks have no way to set a thread name. } - #[cfg(target_os = "linux")] + #[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "netbsd", + target_os = "solaris", + target_os = "illumos" + ))] pub fn get_name() -> Option { + #[cfg(target_os = "linux")] const TASK_COMM_LEN: usize = 16; + #[cfg(target_os = "freebsd")] + const TASK_COMM_LEN: usize = libc::MAXCOMLEN + 1; + #[cfg(any(target_os = "netbsd", target_os = "solaris", target_os = "illumos"))] + const TASK_COMM_LEN: usize = 32; let mut name = vec![0u8; TASK_COMM_LEN]; let res = unsafe { libc::pthread_getname_np(libc::pthread_self(), name.as_mut_ptr().cast(), name.len()) @@ -252,12 +266,35 @@ impl Thread { CString::new(name).ok() } + #[cfg(target_os = "haiku")] + pub fn get_name() -> Option { + unsafe { + let mut tinfo = mem::MaybeUninit::::uninit(); + // See BeOS teams group and threads api. + // https://www.haiku-os.org/legacy-docs/bebook/TheKernelKit_ThreadsAndTeams_Overview.html + let thread_self = libc::find_thread(ptr::null_mut()); + let res = libc::get_thread_info(thread_self, tinfo.as_mut_ptr()); + if res != libc::B_OK { + return None; + } + let info = tinfo.assume_init(); + let name = + core::slice::from_raw_parts(info.name.as_ptr() as *const u8, info.name.len()); + CStr::from_bytes_until_nul(name).map(CStr::to_owned).ok() + } + } + #[cfg(not(any( target_os = "linux", + target_os = "freebsd", + target_os = "netbsd", target_os = "macos", target_os = "ios", target_os = "tvos", - target_os = "watchos" + target_os = "watchos", + target_os = "haiku", + target_os = "solaris", + target_os = "illumos" )))] pub fn get_name() -> Option { None @@ -335,6 +372,8 @@ impl Drop for Thread { target_os = "tvos", target_os = "watchos", target_os = "nto", + target_os = "solaris", + target_os = "illumos", ))] fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { let mut result = [0; MAX_WITH_NUL]; @@ -729,302 +768,6 @@ mod cgroups { } } -#[cfg(all( - not(target_os = "linux"), - not(target_os = "freebsd"), - not(target_os = "hurd"), - not(target_os = "macos"), - not(target_os = "netbsd"), - not(target_os = "openbsd"), - not(target_os = "solaris") -))] -#[cfg_attr(test, allow(dead_code))] -pub mod guard { - use crate::ops::Range; - pub type Guard = Range; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} - -#[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris" -))] -#[cfg_attr(test, allow(dead_code))] -pub mod guard { - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::{mmap as mmap64, mprotect}; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::{mmap64, mprotect}; - use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; - - use crate::io; - use crate::ops::Range; - use crate::sync::atomic::{AtomicUsize, Ordering}; - use crate::sys::os; - - // This is initialized in init() and only read from after - static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); - - pub type Guard = Range; - - #[cfg(target_os = "solaris")] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let mut current_stack: libc::stack_t = crate::mem::zeroed(); - assert_eq!(libc::stack_getbounds(&mut current_stack), 0); - Some(current_stack.ss_sp) - } - - #[cfg(target_os = "macos")] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let th = libc::pthread_self(); - let stackptr = libc::pthread_get_stackaddr_np(th); - Some(stackptr.map_addr(|addr| addr - libc::pthread_get_stacksize_np(th))) - } - - #[cfg(target_os = "openbsd")] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let mut current_stack: libc::stack_t = crate::mem::zeroed(); - assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0); - - let stack_ptr = current_stack.ss_sp; - let stackaddr = if libc::pthread_main_np() == 1 { - // main thread - stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed) - } else { - // new thread - stack_ptr.addr() - current_stack.ss_size - }; - Some(stack_ptr.with_addr(stackaddr)) - } - - #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "netbsd", - target_os = "hurd", - target_os = "linux", - target_os = "l4re" - ))] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let mut ret = None; - let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); - #[cfg(target_os = "freebsd")] - assert_eq!(libc::pthread_attr_init(&mut attr), 0); - #[cfg(target_os = "freebsd")] - let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); - #[cfg(not(target_os = "freebsd"))] - let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); - if e == 0 { - let mut stackaddr = crate::ptr::null_mut(); - let mut stacksize = 0; - assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0); - ret = Some(stackaddr); - } - if e == 0 || cfg!(target_os = "freebsd") { - assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); - } - ret - } - - // Precondition: PAGE_SIZE is initialized. - unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> { - let page_size = PAGE_SIZE.load(Ordering::Relaxed); - assert!(page_size != 0); - let stackptr = get_stack_start()?; - let stackaddr = stackptr.addr(); - - // Ensure stackaddr is page aligned! A parent process might - // have reset RLIMIT_STACK to be non-page aligned. The - // pthread_attr_getstack() reports the usable stack area - // stackaddr < stackaddr + stacksize, so if stackaddr is not - // page-aligned, calculate the fix such that stackaddr < - // new_page_aligned_stackaddr < stackaddr + stacksize - let remainder = stackaddr % page_size; - Some(if remainder == 0 { - stackptr - } else { - stackptr.with_addr(stackaddr + page_size - remainder) - }) - } - - pub unsafe fn init() -> Option { - let page_size = os::page_size(); - PAGE_SIZE.store(page_size, Ordering::Relaxed); - - if cfg!(all(target_os = "linux", not(target_env = "musl"))) { - // Linux doesn't allocate the whole stack right away, and - // the kernel has its own stack-guard mechanism to fault - // when growing too close to an existing mapping. If we map - // our own guard, then the kernel starts enforcing a rather - // large gap above that, rendering much of the possible - // stack space useless. See #43052. - // - // Instead, we'll just note where we expect rlimit to start - // faulting, so our handler can report "stack overflow", and - // trust that the kernel's own stack guard will work. - let stackptr = get_stack_start_aligned()?; - let stackaddr = stackptr.addr(); - Some(stackaddr - page_size..stackaddr) - } else if cfg!(all(target_os = "linux", target_env = "musl")) { - // For the main thread, the musl's pthread_attr_getstack - // returns the current stack size, rather than maximum size - // it can eventually grow to. It cannot be used to determine - // the position of kernel's stack guard. - None - } else if cfg!(target_os = "freebsd") { - // FreeBSD's stack autogrows, and optionally includes a guard page - // at the bottom. If we try to remap the bottom of the stack - // ourselves, FreeBSD's guard page moves upwards. So we'll just use - // the builtin guard page. - let stackptr = get_stack_start_aligned()?; - let guardaddr = stackptr.addr(); - // Technically the number of guard pages is tunable and controlled - // by the security.bsd.stack_guard_page sysctl. - // By default it is 1, checking once is enough since it is - // a boot time config value. - static LOCK: crate::sync::OnceLock = crate::sync::OnceLock::new(); - let guard = guardaddr - ..guardaddr - + *LOCK.get_or_init(|| { - use crate::sys::weak::dlsym; - dlsym!(fn sysctlbyname(*const libc::c_char, *mut libc::c_void, *mut libc::size_t, *const libc::c_void, libc::size_t) -> libc::c_int); - let mut guard: usize = 0; - let mut size = crate::mem::size_of_val(&guard); - let oid = crate::ffi::CStr::from_bytes_with_nul( - b"security.bsd.stack_guard_page\0", - ) - .unwrap(); - match sysctlbyname.get() { - Some(fcn) => { - if fcn(oid.as_ptr(), core::ptr::addr_of_mut!(guard) as *mut _, core::ptr::addr_of_mut!(size) as *mut _, crate::ptr::null_mut(), 0) == 0 { - return guard; - } - return 1; - }, - _ => { return 1; } - } - }) * page_size; - Some(guard) - } else if cfg!(any(target_os = "openbsd", target_os = "netbsd")) { - // OpenBSD stack already includes a guard page, and stack is - // immutable. - // NetBSD stack includes the guard page. - // - // We'll just note where we expect rlimit to start - // faulting, so our handler can report "stack overflow", and - // trust that the kernel's own stack guard will work. - let stackptr = get_stack_start_aligned()?; - let stackaddr = stackptr.addr(); - Some(stackaddr - page_size..stackaddr) - } else { - // Reallocate the last page of the stack. - // This ensures SIGBUS will be raised on - // stack overflow. - // Systems which enforce strict PAX MPROTECT do not allow - // to mprotect() a mapping with less restrictive permissions - // than the initial mmap() used, so we mmap() here with - // read/write permissions and only then mprotect() it to - // no permissions at all. See issue #50313. - let stackptr = get_stack_start_aligned()?; - let result = mmap64( - stackptr, - page_size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED, - -1, - 0, - ); - if result != stackptr || result == MAP_FAILED { - panic!("failed to allocate a guard page: {}", io::Error::last_os_error()); - } - - let result = mprotect(stackptr, page_size, PROT_NONE); - if result != 0 { - panic!("failed to protect the guard page: {}", io::Error::last_os_error()); - } - - let guardaddr = stackptr.addr(); - - Some(guardaddr..guardaddr + page_size) - } - } - - #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))] - pub unsafe fn current() -> Option { - let stackptr = get_stack_start()?; - let stackaddr = stackptr.addr(); - Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr) - } - - #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "l4re" - ))] - pub unsafe fn current() -> Option { - let mut ret = None; - let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); - #[cfg(target_os = "freebsd")] - assert_eq!(libc::pthread_attr_init(&mut attr), 0); - #[cfg(target_os = "freebsd")] - let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); - #[cfg(not(target_os = "freebsd"))] - let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); - if e == 0 { - let mut guardsize = 0; - assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0); - if guardsize == 0 { - if cfg!(all(target_os = "linux", target_env = "musl")) { - // musl versions before 1.1.19 always reported guard - // size obtained from pthread_attr_get_np as zero. - // Use page size as a fallback. - guardsize = PAGE_SIZE.load(Ordering::Relaxed); - } else { - panic!("there is no guard page"); - } - } - let mut stackptr = crate::ptr::null_mut::(); - let mut size = 0; - assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackptr, &mut size), 0); - - let stackaddr = stackptr.addr(); - ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) { - Some(stackaddr - guardsize..stackaddr) - } else if cfg!(all(target_os = "linux", target_env = "musl")) { - Some(stackaddr - guardsize..stackaddr) - } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc"))) - { - // glibc used to include the guard area within the stack, as noted in the BUGS - // section of `man pthread_attr_getguardsize`. This has been corrected starting - // with glibc 2.27, and in some distro backports, so the guard is now placed at the - // end (below) the stack. There's no easy way for us to know which we have at - // runtime, so we'll just match any fault in the range right above or below the - // stack base to call that fault a stack overflow. - Some(stackaddr - guardsize..stackaddr + guardsize) - } else { - Some(stackaddr..stackaddr + guardsize) - }; - } - if e == 0 || cfg!(target_os = "freebsd") { - assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); - } - ret - } -} - // glibc >= 2.15 has a __pthread_get_minstack() function that returns // PTHREAD_STACK_MIN plus bytes needed for thread-local storage. // We need that information to avoid blowing up when a small stack diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/pal/unsupported/thread.rs index b3a91ee1d4cb..d3f2fa35b929 100644 --- a/library/std/src/sys/pal/unsupported/thread.rs +++ b/library/std/src/sys/pal/unsupported/thread.rs @@ -38,13 +38,3 @@ impl Thread { pub fn available_parallelism() -> io::Result> { unsupported() } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index 4b116052f8f8..940f0c8423af 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -193,13 +193,3 @@ impl Thread { pub fn available_parallelism() -> io::Result> { unsupported() } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index 970bd9c6ce7e..80eee4e078db 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -144,14 +144,3 @@ pub fn available_parallelism() -> io::Result> { cpus => Ok(unsafe { NonZero::new_unchecked(cpus) }), } } - -#[cfg_attr(test, allow(dead_code))] -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/pal/xous/thread.rs index f95ceb7343bd..c1fd1c0d6534 100644 --- a/library/std/src/sys/pal/xous/thread.rs +++ b/library/std/src/sys/pal/xous/thread.rs @@ -140,13 +140,3 @@ pub fn available_parallelism() -> io::Result> { // We're unicore right now. Ok(unsafe { NonZero::new_unchecked(1) }) } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/xous/thread_local_key.rs b/library/std/src/sys/pal/xous/thread_local_key.rs index 2aaf46d0244a..6c29813c79df 100644 --- a/library/std/src/sys/pal/xous/thread_local_key.rs +++ b/library/std/src/sys/pal/xous/thread_local_key.rs @@ -49,7 +49,7 @@ fn tls_ptr_addr() -> *mut *mut u8 { out(reg) tp, ); } - core::ptr::from_exposed_addr_mut::<*mut u8>(tp) + core::ptr::with_exposed_provenance_mut::<*mut u8>(tp) } /// Create an area of memory that's unique per thread. This area will diff --git a/library/std/src/sys/pal/zkvm/thread_local_key.rs b/library/std/src/sys/pal/zkvm/thread_local_key.rs index 3ffe6247344e..2f67924c6182 100644 --- a/library/std/src/sys/pal/zkvm/thread_local_key.rs +++ b/library/std/src/sys/pal/zkvm/thread_local_key.rs @@ -9,13 +9,13 @@ pub unsafe fn create(_dtor: Option) -> Key { #[inline] pub unsafe fn set(key: Key, value: *mut u8) { - let key: *mut *mut u8 = core::ptr::from_exposed_addr_mut(key); + let key: *mut *mut u8 = core::ptr::with_exposed_provenance_mut(key); *key = value; } #[inline] pub unsafe fn get(key: Key) -> *mut u8 { - let key: *mut *mut u8 = core::ptr::from_exposed_addr_mut(key); + let key: *mut *mut u8 = core::ptr::with_exposed_provenance_mut(key); *key } diff --git a/library/std/src/sys/personality/dwarf/eh.rs b/library/std/src/sys/personality/dwarf/eh.rs index a78084de0fae..3f3615ea3e0a 100644 --- a/library/std/src/sys/personality/dwarf/eh.rs +++ b/library/std/src/sys/personality/dwarf/eh.rs @@ -125,7 +125,7 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result // Can never have null landing pad for sjlj -- that would have // been indicated by a -1 call site index. // FIXME(strict provenance) - let lpad = ptr::from_exposed_addr((cs_lpad + 1) as usize); + let lpad = ptr::with_exposed_provenance((cs_lpad + 1) as usize); return Ok(interpret_cs_action(action_table, cs_action_entry, lpad)); } } diff --git a/library/std/src/sys/sync/rwlock/queue.rs b/library/std/src/sys/sync/rwlock/queue.rs index dce966086b8f..d1918855797a 100644 --- a/library/std/src/sys/sync/rwlock/queue.rs +++ b/library/std/src/sys/sync/rwlock/queue.rs @@ -115,8 +115,7 @@ use crate::sync::atomic::{ AtomicBool, AtomicPtr, Ordering::{AcqRel, Acquire, Relaxed, Release}, }; -use crate::sys_common::thread_info; -use crate::thread::Thread; +use crate::thread::{self, Thread}; // Locking uses exponential backoff. `SPIN_COUNT` indicates how many times the // locking operation will be retried. @@ -203,8 +202,7 @@ impl Node { fn prepare(&mut self) { // Fall back to creating an unnamed `Thread` handle to allow locking in // TLS destructors. - self.thread - .get_or_init(|| thread_info::current_thread().unwrap_or_else(|| Thread::new(None))); + self.thread.get_or_init(|| thread::try_current().unwrap_or_else(|| Thread::new(None))); self.completed = AtomicBool::new(false); } diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 5410f135a73f..5abf201aa201 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -26,7 +26,6 @@ pub mod io; pub mod lazy_box; pub mod process; pub mod thread; -pub mod thread_info; pub mod thread_local_dtor; pub mod thread_parking; pub mod wstr; diff --git a/library/std/src/sys_common/thread_info.rs b/library/std/src/sys_common/thread_info.rs deleted file mode 100644 index ec1428ea40ec..000000000000 --- a/library/std/src/sys_common/thread_info.rs +++ /dev/null @@ -1,53 +0,0 @@ -#![allow(dead_code)] // stack_guard isn't used right now on all platforms - -use crate::cell::OnceCell; -use crate::sys; -use crate::sys::thread::guard::Guard; -use crate::thread::Thread; - -struct ThreadInfo { - stack_guard: OnceCell, - thread: OnceCell, -} - -thread_local! { - static THREAD_INFO: ThreadInfo = const { ThreadInfo { - stack_guard: OnceCell::new(), - thread: OnceCell::new() - } }; -} - -impl ThreadInfo { - fn with(f: F) -> Option - where - F: FnOnce(&Thread, &OnceCell) -> R, - { - THREAD_INFO - .try_with(move |thread_info| { - let thread = - thread_info.thread.get_or_init(|| Thread::new(sys::thread::Thread::get_name())); - f(thread, &thread_info.stack_guard) - }) - .ok() - } -} - -pub fn current_thread() -> Option { - ThreadInfo::with(|thread, _| thread.clone()) -} - -pub fn stack_guard() -> Option { - ThreadInfo::with(|_, guard| guard.get().cloned()).flatten() -} - -/// Set new thread info, panicking if it has already been initialized -#[allow(unreachable_code, unreachable_patterns)] // some platforms don't use stack_guard -pub fn set(stack_guard: Option, thread: Thread) { - THREAD_INFO.with(move |thread_info| { - rtassert!(thread_info.stack_guard.get().is_none() && thread_info.thread.get().is_none()); - if let Some(guard) = stack_guard { - thread_info.stack_guard.set(guard).unwrap(); - } - thread_info.thread.set(thread).unwrap(); - }); -} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 85de2980133d..f7eb92bc61e2 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -159,7 +159,7 @@ mod tests; use crate::any::Any; -use crate::cell::UnsafeCell; +use crate::cell::{OnceCell, UnsafeCell}; use crate::ffi::{CStr, CString}; use crate::fmt; use crate::io; @@ -174,7 +174,6 @@ use crate::str; use crate::sync::Arc; use crate::sys::thread as imp; use crate::sys_common::thread; -use crate::sys_common::thread_info; use crate::sys_common::thread_parking::Parker; use crate::sys_common::{AsInner, IntoInner}; use crate::time::{Duration, Instant}; @@ -518,12 +517,8 @@ impl Builder { crate::io::set_output_capture(output_capture); - // SAFETY: we constructed `f` initialized. let f = f.into_inner(); - // SAFETY: the stack guard passed is the one for the current thread. - // This means the current thread's stack and the new thread's stack - // are properly set and protected from each other. - thread_info::set(unsafe { imp::guard::current() }, their_thread); + set_current(their_thread); let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { crate::sys_common::backtrace::__rust_begin_short_backtrace(f) })); @@ -683,6 +678,27 @@ where Builder::new().spawn(f).expect("failed to spawn thread") } +thread_local! { + static CURRENT: OnceCell = const { OnceCell::new() }; +} + +/// Sets the thread handle for the current thread. +/// +/// Panics if the handle has been set already or when called from a TLS destructor. +pub(crate) fn set_current(thread: Thread) { + CURRENT.with(|current| current.set(thread).unwrap()); +} + +/// Gets a handle to the thread that invokes it. +/// +/// In contrast to the public `current` function, this will not panic if called +/// from inside a TLS destructor. +pub(crate) fn try_current() -> Option { + CURRENT + .try_with(|current| current.get_or_init(|| Thread::new(imp::Thread::get_name())).clone()) + .ok() +} + /// Gets a handle to the thread that invokes it. /// /// # Examples @@ -705,7 +721,7 @@ where #[must_use] #[stable(feature = "rust1", since = "1.0.0")] pub fn current() -> Thread { - thread_info::current_thread().expect( + try_current().expect( "use of std::thread::current() is not possible \ after the thread's local data has been destroyed", ) diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index 74a924d86c79..4b182a7a693d 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -150,7 +150,7 @@ fn main() { { cmd.arg("-Ztls-model=initial-exec"); } - } else if std::env::var("MIRI").is_err() { + } else { // Find any host flags that were passed by bootstrap. // The flags are stored in a RUSTC_HOST_FLAGS variable, separated by spaces. if let Ok(flags) = std::env::var("RUSTC_HOST_FLAGS") { @@ -220,6 +220,12 @@ fn main() { } } + if env::var_os("RUSTC_BOLT_LINK_FLAGS").is_some() { + if let Some("rustc_driver") = crate_name { + cmd.arg("-Clink-args=-Wl,-q"); + } + } + let is_test = args.iter().any(|a| a == "--test"); if verbose > 2 { let rust_env_vars = @@ -244,12 +250,6 @@ fn main() { eprintln!("{prefix} libdir: {libdir:?}"); } - if env::var_os("RUSTC_BOLT_LINK_FLAGS").is_some() { - if let Some("rustc_driver") = crate_name { - cmd.arg("-Clink-args=-Wl,-q"); - } - } - bin_helpers::maybe_dump(format!("stage{stage}-rustc"), &cmd); let start = Instant::now(); diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 895c43d4dda6..e03997181ee5 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -825,6 +825,7 @@ fn cp_rustc_component_to_ci_sysroot( #[derive(Debug, PartialOrd, Ord, Clone, PartialEq, Eq, Hash)] pub struct Rustc { pub target: TargetSelection, + /// The **previous** compiler used to compile this compiler. pub compiler: Compiler, /// Whether to build a subset of crates, rather than the whole compiler. /// @@ -1130,12 +1131,6 @@ pub fn rustc_cargo_env( cargo.env("CFG_DEFAULT_LINKER", s); } - if builder.config.rustc_parallel { - // keep in sync with `bootstrap/lib.rs:Build::rustc_features` - // `cfg` option for rustc, `features` option for cargo, for conditional compilation - cargo.rustflag("--cfg=parallel_compiler"); - cargo.rustdocflag("--cfg=parallel_compiler"); - } if builder.config.rust_verify_llvm_ir { cargo.env("RUSTC_VERIFY_LLVM_IR", "1"); } @@ -1518,12 +1513,9 @@ impl Step for Sysroot { run.never() } - /// Returns the sysroot for the `compiler` specified that *this build system - /// generates*. - /// - /// That is, the sysroot for the stage0 compiler is not what the compiler - /// thinks it is by default, but it's the same as the default for stages - /// 1-3. + /// Returns the sysroot that `compiler` is supposed to use. + /// For the stage0 compiler, this is stage0-sysroot (because of the initial std build). + /// For all other stages, it's the same stage directory that the compiler lives in. fn run(self, builder: &Builder<'_>) -> PathBuf { let compiler = self.compiler; let host_dir = builder.out.join(compiler.host.triple); diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index d9c7032d0db8..ecea62140a2e 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -995,9 +995,9 @@ impl Step for PlainSourceTarball { if builder.rust_info().is_managed_git_subrepository() || builder.rust_info().is_from_tarball() { - if builder.rust_info().is_managed_git_subrepository() { - // Ensure we have the submodules checked out. - builder.update_submodule(Path::new("src/tools/cargo")); + // Ensure we have all submodules from src and other directories checked out. + for submodule in builder.get_all_submodules() { + builder.update_submodule(Path::new(submodule)); } // Vendor all Cargo dependencies @@ -1028,6 +1028,20 @@ impl Step for PlainSourceTarball { builder.create(&cargo_config_dir.join("config.toml"), &config); } + // Delete extraneous directories + // FIXME: if we're managed by git, we should probably instead ask git if the given path + // is managed by it? + for entry in walkdir::WalkDir::new(tarball.image_dir()) + .follow_links(true) + .into_iter() + .filter_map(|e| e.ok()) + { + if entry.path().is_dir() && entry.path().file_name() == Some(OsStr::new("__pycache__")) + { + t!(fs::remove_dir_all(entry.path())); + } + } + tarball.bare() } } diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 3da927b5fa0f..e461e11677fa 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -274,6 +274,7 @@ impl Step for Llvm { target.to_string() }; + // If LLVM has already been built or been downloaded through download-ci-llvm, we avoid building it again. let Meta { stamp, res, out_dir, root } = match prebuilt_llvm_config(builder, target) { Ok(p) => return p, Err(m) => m, diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 61ee2fc1f6f3..7028bffea544 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -121,8 +121,6 @@ impl Step for ReplaceVersionPlaceholder { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Miri { - stage: u32, - host: TargetSelection, target: TargetSelection, } @@ -135,29 +133,35 @@ impl Step for Miri { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Miri { - stage: run.builder.top_stage, - host: run.build_triple(), - target: run.target, - }); + run.builder.ensure(Miri { target: run.target }); } fn run(self, builder: &Builder<'_>) { - let stage = self.stage; - let host = self.host; + let host = builder.build.build; let target = self.target; - let compiler = builder.compiler(stage, host); + let stage = builder.top_stage; + if stage == 0 { + eprintln!("miri cannot be run at stage 0"); + std::process::exit(1); + } - let miri = - builder.ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() }); - let miri_sysroot = test::Miri::build_miri_sysroot(builder, compiler, &miri, target); + // This compiler runs on the host, we'll just use it for the target. + let target_compiler = builder.compiler(stage, host); + // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise + // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage + // compilers, which isn't what we want. Rustdoc should be linked in the same way as the + // rustc compiler it's paired with, so it must be built with the previous stage compiler. + let host_compiler = builder.compiler(stage - 1, host); + + // Get a target sysroot for Miri. + let miri_sysroot = test::Miri::build_miri_sysroot(builder, target_compiler, target); // # Run miri. // Running it via `cargo run` as that figures out the right dylib path. // add_rustc_lib_path does not add the path that contains librustc_driver-<...>.so. let mut miri = tool::prepare_tool_cargo( builder, - compiler, + host_compiler, Mode::ToolRustc, host, "run", diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 48596af7ca02..8a79c28fc6c6 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -493,8 +493,6 @@ impl Step for RustDemangler { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Miri { - stage: u32, - host: TargetSelection, target: TargetSelection, } @@ -503,41 +501,26 @@ impl Miri { pub fn build_miri_sysroot( builder: &Builder<'_>, compiler: Compiler, - miri: &Path, target: TargetSelection, ) -> String { let miri_sysroot = builder.out.join(compiler.host.triple).join("miri-sysroot"); - let mut cargo = tool::prepare_tool_cargo( + let mut cargo = builder::Cargo::new( builder, compiler, - Mode::ToolRustc, - compiler.host, - "run", - "src/tools/miri/cargo-miri", - SourceType::InTree, - &[], + Mode::Std, + SourceType::Submodule, + target, + "miri-setup", ); - cargo.add_rustc_lib_path(builder); - cargo.arg("--").arg("miri").arg("setup"); - cargo.arg("--target").arg(target.rustc_target_arg()); // Tell `cargo miri setup` where to find the sources. cargo.env("MIRI_LIB_SRC", builder.src.join("library")); - // Tell it where to find Miri. - cargo.env("MIRI", miri); // Tell it where to put the sysroot. cargo.env("MIRI_SYSROOT", &miri_sysroot); - // Debug things. - cargo.env("RUST_BACKTRACE", "1"); let mut cargo = Command::from(cargo); - let _guard = builder.msg( - Kind::Build, - compiler.stage + 1, - "miri sysroot", - compiler.host, - compiler.host, - ); + let _guard = + builder.msg(Kind::Build, compiler.stage, "miri sysroot", compiler.host, target); builder.run(&mut cargo); // # Determine where Miri put its sysroot. @@ -574,41 +557,51 @@ impl Step for Miri { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Miri { - stage: run.builder.top_stage, - host: run.build_triple(), - target: run.target, - }); + run.builder.ensure(Miri { target: run.target }); } /// Runs `cargo test` for miri. fn run(self, builder: &Builder<'_>) { - let stage = self.stage; - let host = self.host; + let host = builder.build.build; let target = self.target; - let compiler = builder.compiler(stage, host); - // We need the stdlib for the *next* stage, as it was built with this compiler that also built Miri. - // Except if we are at stage 2, the bootstrap loop is complete and we can stick with our current stage. - let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host); + let stage = builder.top_stage; + if stage == 0 { + eprintln!("miri cannot be tested at stage 0"); + std::process::exit(1); + } - let miri = - builder.ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() }); - let _cargo_miri = builder.ensure(tool::CargoMiri { - compiler, - target: self.host, + // This compiler runs on the host, we'll just use it for the target. + let target_compiler = builder.compiler(stage, host); + // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise + // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage + // compilers, which isn't what we want. Rustdoc should be linked in the same way as the + // rustc compiler it's paired with, so it must be built with the previous stage compiler. + let host_compiler = builder.compiler(stage - 1, host); + + // Build our tools. + let miri = builder.ensure(tool::Miri { + compiler: host_compiler, + target: host, extra_features: Vec::new(), }); - // The stdlib we need might be at a different stage. And just asking for the - // sysroot does not seem to populate it, so we do that first. - builder.ensure(compile::Std::new(compiler_std, host)); - let sysroot = builder.sysroot(compiler_std); - // We also need a Miri sysroot. - let miri_sysroot = Miri::build_miri_sysroot(builder, compiler, &miri, target); + // the ui tests also assume cargo-miri has been built + builder.ensure(tool::CargoMiri { + compiler: host_compiler, + target: host, + extra_features: Vec::new(), + }); + + // We also need sysroots, for Miri and for the host (the latter for build scripts). + // This is for the tests so everything is done with the target compiler. + let miri_sysroot = Miri::build_miri_sysroot(builder, target_compiler, target); + builder.ensure(compile::Std::new(target_compiler, host)); + let sysroot = builder.sysroot(target_compiler); // # Run `cargo test`. + // This is with the Miri crate, so it uses the host compiler. let mut cargo = tool::prepare_tool_cargo( builder, - compiler, + host_compiler, Mode::ToolRustc, host, "test", @@ -616,26 +609,23 @@ impl Step for Miri { SourceType::InTree, &[], ); - let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "miri", host, target); cargo.add_rustc_lib_path(builder); + // We can NOT use `run_cargo_test` since Miri's integration tests do not use the usual test + // harness and therefore do not understand the flags added by `add_flags_and_try_run_test`. + let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", host_compiler, host, builder); + // miri tests need to know about the stage sysroot cargo.env("MIRI_SYSROOT", &miri_sysroot); cargo.env("MIRI_HOST_SYSROOT", &sysroot); cargo.env("MIRI", &miri); - if builder.config.locked_deps { - // enforce lockfiles - cargo.env("CARGO_EXTRA_FLAGS", "--locked"); - } // Set the target. cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg()); - // This can NOT be `run_cargo_test` since the Miri test runner - // does not understand the flags added by `add_flags_and_try_run_test`. - let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", compiler, target, builder); { + let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "miri", host, target); let _time = helpers::timeit(builder); builder.run(&mut cargo); } @@ -650,8 +640,14 @@ impl Step for Miri { // Optimizations can change error locations and remove UB so don't run `fail` tests. cargo.args(["tests/pass", "tests/panic"]); - let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", compiler, target, builder); { + let _guard = builder.msg_sysroot_tool( + Kind::Test, + stage, + "miri (mir-opt-level 4)", + host, + target, + ); let _time = helpers::timeit(builder); builder.run(&mut cargo); } @@ -660,42 +656,40 @@ impl Step for Miri { // # Run `cargo miri test`. // This is just a smoke test (Miri's own CI invokes this in a bunch of different ways and ensures // that we get the desired output), but that is sufficient to make sure that the libtest harness - // itself executes properly under Miri. + // itself executes properly under Miri, and that all the logic in `cargo-miri` does not explode. + // This is running the build `cargo-miri` for the given target, so we need the target compiler. let mut cargo = tool::prepare_tool_cargo( builder, - compiler, - Mode::ToolRustc, - host, - "run", - "src/tools/miri/cargo-miri", + target_compiler, + Mode::ToolStd, // it's unclear what to use here, we're not building anything just doing a smoke test! + target, + "miri-test", + "src/tools/miri/test-cargo-miri", SourceType::Submodule, &[], ); - cargo.add_rustc_lib_path(builder); - cargo.arg("--").arg("miri").arg("test"); - if builder.config.locked_deps { - cargo.arg("--locked"); + + // We're not using `prepare_cargo_test` so we have to do this ourselves. + // (We're not using that as the test-cargo-miri crate is not known to bootstrap.) + match builder.doc_tests { + DocTests::Yes => {} + DocTests::No => { + cargo.args(["--lib", "--bins", "--examples", "--tests", "--benches"]); + } + DocTests::Only => { + cargo.arg("--doc"); + } } - cargo - .arg("--manifest-path") - .arg(builder.src.join("src/tools/miri/test-cargo-miri/Cargo.toml")); - cargo.arg("--target").arg(target.rustc_target_arg()); - cargo.arg("--").args(builder.config.test_args()); - // `prepare_tool_cargo` sets RUSTDOC to the bootstrap wrapper and RUSTDOC_REAL to a dummy path as this is a "run", not a "test". - // Also, we want the rustdoc from the "next" stage for the same reason that we build a std from the next stage. - // So let's just set that here, and bypass bootstrap's RUSTDOC (just like cargo-miri already ignores bootstrap's RUSTC_WRAPPER). - cargo.env("RUSTDOC", builder.rustdoc(compiler_std)); - - // Tell `cargo miri` where to find things. + // Tell `cargo miri` where to find the sysroots. cargo.env("MIRI_SYSROOT", &miri_sysroot); cargo.env("MIRI_HOST_SYSROOT", sysroot); - cargo.env("MIRI", &miri); - // Debug things. - cargo.env("RUST_BACKTRACE", "1"); + // Finally, pass test-args and run everything. + cargo.arg("--").args(builder.config.test_args()); let mut cargo = Command::from(cargo); { + let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "cargo-miri", host, target); let _time = helpers::timeit(builder); builder.run(&mut cargo); } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index deab3fce54ca..1edf65d28b76 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -469,7 +469,7 @@ impl Step for Rustdoc { features.push("jemalloc".to_string()); } - let mut cargo = prepare_tool_cargo( + let cargo = prepare_tool_cargo( builder, build_compiler, Mode::ToolRustc, @@ -480,10 +480,6 @@ impl Step for Rustdoc { features.as_slice(), ); - if builder.config.rustc_parallel { - cargo.rustflag("--cfg=parallel_compiler"); - } - let _guard = builder.msg_tool( Mode::ToolRustc, "rustdoc", @@ -732,7 +728,7 @@ impl Step for LlvmBitcodeLinker { builder.ensure(compile::Std::new(self.compiler, self.compiler.host)); builder.ensure(compile::Rustc::new(self.compiler, self.target)); - let mut cargo = prepare_tool_cargo( + let cargo = prepare_tool_cargo( builder, self.compiler, Mode::ToolRustc, @@ -743,10 +739,6 @@ impl Step for LlvmBitcodeLinker { &self.extra_features, ); - if builder.config.rustc_parallel { - cargo.rustflag("--cfg=parallel_compiler"); - } - builder.run(&mut cargo.into()); let tool_out = builder diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 7f93fdc72ef0..c051b8183287 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -554,29 +554,7 @@ impl<'a> ShouldRun<'a> { /// /// [`path`]: ShouldRun::path pub fn paths(mut self, paths: &[&str]) -> Self { - static SUBMODULES_PATHS: OnceLock> = OnceLock::new(); - - let init_submodules_paths = |src: &PathBuf| { - let file = File::open(src.join(".gitmodules")).unwrap(); - - let mut submodules_paths = vec![]; - for line in BufReader::new(file).lines() { - if let Ok(line) = line { - let line = line.trim(); - - if line.starts_with("path") { - let actual_path = - line.split(' ').last().expect("Couldn't get value of path"); - submodules_paths.push(actual_path.to_owned()); - } - } - } - - submodules_paths - }; - - let submodules_paths = - SUBMODULES_PATHS.get_or_init(|| init_submodules_paths(&self.builder.src)); + let submodules_paths = self.builder.get_all_submodules(); self.paths.insert(PathSet::Set( paths @@ -1031,10 +1009,10 @@ impl<'a> Builder<'a> { StepDescription::run(v, self, paths); } - /// Obtain a compiler at a given stage and for a given host. Explicitly does - /// not take `Compiler` since all `Compiler` instances are meant to be - /// obtained through this function, since it ensures that they are valid - /// (i.e., built and assembled). + /// Obtain a compiler at a given stage and for a given host (i.e., this is the target that the + /// compiler will run on, *not* the target it will build code for). Explicitly does not take + /// `Compiler` since all `Compiler` instances are meant to be obtained through this function, + /// since it ensures that they are valid (i.e., built and assembled). pub fn compiler(&self, stage: u32, host: TargetSelection) -> Compiler { self.ensure(compile::Assemble { target_compiler: Compiler { stage, host } }) } @@ -1253,6 +1231,36 @@ impl<'a> Builder<'a> { cmd } + pub fn cargo_miri_cmd(&self, run_compiler: Compiler) -> Command { + assert!(run_compiler.stage > 0, "miri can not be invoked at stage 0"); + let build_compiler = self.compiler(run_compiler.stage - 1, self.build.build); + + let miri = self.ensure(tool::Miri { + compiler: build_compiler, + target: self.build.build, + extra_features: Vec::new(), + }); + let cargo_miri = self.ensure(tool::CargoMiri { + compiler: build_compiler, + target: self.build.build, + extra_features: Vec::new(), + }); + // Invoke cargo-miri, make sure we can find miri and cargo. + let mut cmd = Command::new(cargo_miri); + cmd.env("MIRI", &miri); + cmd.env("CARGO", &self.initial_cargo); + // Need to add the `run_compiler` libs. Those are the libs produces *by* `build_compiler`, + // so they match the Miri we just built. However this means they are actually living one + // stage up, i.e. we are running `stage0-tools-bin/miri` with the libraries in `stage1/lib`. + // This is an unfortunate off-by-1 caused (possibly) by the fact that Miri doesn't have an + // "assemble" step like rustc does that would cross the stage boundary. We can't use + // `add_rustc_lib_path` as that's a NOP on Windows but we do need these libraries added to + // the PATH due to the stage mismatch. + // Also see https://github.com/rust-lang/rust/pull/123192#issuecomment-2028901503. + add_dylib_path(self.rustc_lib_paths(run_compiler), &mut cmd); + cmd + } + pub fn rustdoc_cmd(&self, compiler: Compiler) -> Command { let mut cmd = Command::new(self.bootstrap_out.join("rustdoc")); cmd.env("RUSTC_STAGE", compiler.stage.to_string()) @@ -1294,20 +1302,26 @@ impl<'a> Builder<'a> { compiler: Compiler, mode: Mode, target: TargetSelection, - cmd: &str, + cmd: &str, // FIXME make this properly typed ) -> Command { - let mut cargo = if cmd == "clippy" { - self.cargo_clippy_cmd(compiler) + let mut cargo; + if cmd == "clippy" { + cargo = self.cargo_clippy_cmd(compiler); + cargo.arg(cmd); + } else if let Some(subcmd) = cmd.strip_prefix("miri-") { + cargo = self.cargo_miri_cmd(compiler); + cargo.arg("miri").arg(subcmd); } else { - Command::new(&self.initial_cargo) - }; + cargo = Command::new(&self.initial_cargo); + cargo.arg(cmd); + } // Run cargo from the source root so it can find .cargo/config. // This matters when using vendoring and the working directory is outside the repository. cargo.current_dir(&self.src); let out_dir = self.stage_out(compiler, mode); - cargo.env("CARGO_TARGET_DIR", &out_dir).arg(cmd); + cargo.env("CARGO_TARGET_DIR", &out_dir); // Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger` // from out of tree it shouldn't matter, since x.py is only used for @@ -1337,7 +1351,8 @@ impl<'a> Builder<'a> { if self.config.rust_optimize.is_release() { // FIXME: cargo bench/install do not accept `--release` - if cmd != "bench" && cmd != "install" { + // and miri doesn't want it + if cmd != "bench" && cmd != "install" && !cmd.starts_with("miri-") { cargo.arg("--release"); } } @@ -1353,14 +1368,15 @@ impl<'a> Builder<'a> { /// Cargo. This cargo will be configured to use `compiler` as the actual /// rustc compiler, its output will be scoped by `mode`'s output directory, /// it will pass the `--target` flag for the specified `target`, and will be - /// executing the Cargo command `cmd`. + /// executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for commands + /// to be run with Miri. fn cargo( &self, compiler: Compiler, mode: Mode, source_type: SourceType, target: TargetSelection, - cmd: &str, + cmd: &str, // FIXME make this properly typed ) -> Cargo { let mut cargo = self.bare_cargo(compiler, mode, target, cmd); let out_dir = self.stage_out(compiler, mode); @@ -1671,7 +1687,8 @@ impl<'a> Builder<'a> { .env("RUSTDOC", self.bootstrap_out.join("rustdoc")) .env( "RUSTDOC_REAL", - if cmd == "doc" || cmd == "rustdoc" || (cmd == "test" && want_rustdoc) { + // Make sure to handle both `test` and `miri-test` commands. + if cmd == "doc" || cmd == "rustdoc" || (cmd.ends_with("test") && want_rustdoc) { self.rustdoc(compiler) } else { PathBuf::from("/path/to/nowhere/rustdoc/not/required") @@ -2067,6 +2084,15 @@ impl<'a> Builder<'a> { rustflags.arg("-Zinline-mir"); } + if self.config.rustc_parallel + && matches!(mode, Mode::ToolRustc | Mode::Rustc | Mode::Codegen) + { + // keep in sync with `bootstrap/lib.rs:Build::rustc_features` + // `cfg` option for rustc, `features` option for cargo, for conditional compilation + rustflags.arg("--cfg=parallel_compiler"); + rustdocflags.arg("--cfg=parallel_compiler"); + } + // set rustc args passed from command line let rustc_args = self.config.cmd.rustc_args().iter().map(|s| s.to_string()).collect::>(); @@ -2151,6 +2177,37 @@ impl<'a> Builder<'a> { out } + /// Return paths of all submodules managed by git. + /// If the current checkout is not managed by git, returns an empty slice. + pub fn get_all_submodules(&self) -> &[String] { + if !self.rust_info().is_managed_git_subrepository() { + return &[]; + } + + static SUBMODULES_PATHS: OnceLock> = OnceLock::new(); + + let init_submodules_paths = |src: &PathBuf| { + let file = File::open(src.join(".gitmodules")).unwrap(); + + let mut submodules_paths = vec![]; + for line in BufReader::new(file).lines() { + if let Ok(line) = line { + let line = line.trim(); + + if line.starts_with("path") { + let actual_path = + line.split(' ').last().expect("Couldn't get value of path"); + submodules_paths.push(actual_path.to_owned()); + } + } + } + + submodules_paths + }; + + &SUBMODULES_PATHS.get_or_init(|| init_submodules_paths(&self.src)) + } + /// Ensure that a given step is built *only if it's supposed to be built by default*, returning /// its output. This will cache the step, so it's safe (and good!) to call this as often as /// needed to ensure that all dependencies are build. @@ -2300,7 +2357,7 @@ impl Cargo { mode: Mode, source_type: SourceType, target: TargetSelection, - cmd: &str, + cmd: &str, // FIXME make this properly typed ) -> Cargo { let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd); cargo.configure_linker(builder); @@ -2314,7 +2371,7 @@ impl Cargo { mode: Mode, source_type: SourceType, target: TargetSelection, - cmd: &str, + cmd: &str, // FIXME make this properly typed ) -> Cargo { builder.cargo(compiler, mode, source_type, target, cmd) } diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index d8397ab51de3..44452446eb87 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -250,6 +250,8 @@ pub enum Mode { /// directory. This is for miscellaneous sets of tools that are built /// using the bootstrap stage0 compiler in its entirety (target libraries /// and all). Typically these tools compile with stable Rust. + /// + /// Only works for stage 0. ToolBootstrap, /// Build a tool which uses the locally built std, placing output in the diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index e5aa81d83d5a..6918574814ff 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -60,7 +60,7 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ /scripts/validate-error-codes.sh && \ reuse --include-submodules lint && \ # Runs checks to ensure that there are no ES5 issues in our JS code. - es-check es6 ../src/librustdoc/html/static/js/*.js && \ + es-check es8 ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \ eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index a2b63962ba11..07feb8234926 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.16.11 \ No newline at end of file +0.17.0 \ No newline at end of file diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh index f5c426a717f7..10ae7f17db7a 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh @@ -2,6 +2,7 @@ # ignore-tidy-linelength set -eu +set -x # so one can see where we are in the script X_PY="$1" diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index 740eb7504f87..9d72fd8a55a7 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -323,6 +323,7 @@ docker \ --env GITHUB_ACTIONS \ --env GITHUB_REF \ --env GITHUB_STEP_SUMMARY="/checkout/obj/${SUMMARY_FILE}" \ + --env RUST_BACKTRACE \ --env TOOLSTATE_REPO_ACCESS_TOKEN \ --env TOOLSTATE_REPO \ --env TOOLSTATE_PUBLISH \ diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 4a4f1ae98e40..c8f5d6495703 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -553,9 +553,17 @@ Supported values for this option are: of MSVC). - `debuginfo` - debuginfo sections and debuginfo symbols from the symbol table section are stripped at link time and are not copied to the produced binary - or separate files. -- `symbols` - same as `debuginfo`, but the rest of the symbol table section is - stripped as well if the linker supports it. + or separate files. This should leave backtraces mostly-intact but may make + using a debugger like gdb or lldb ineffectual. +- `symbols` - same as `debuginfo`, but the rest of the symbol table section is stripped as well, + depending on platform support. On platforms which depend on this symbol table for backtraces, + profiling, and similar, this can affect them so negatively as to make the trace incomprehensible. + Programs which may be combined with others, such as CLI pipelines and developer tooling, + or even anything which wants crash-reporting, should usually avoid `-Cstrip=symbols`. + +Note that, at any level, removing debuginfo only necessarily impacts "friendly" introspection. +`-Cstrip` cannot be relied on as a meaningful security or obfuscation measure, as disassemblers +and decompilers can extract considerable information even in the absence of symbols. ## symbol-mangling-version diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 75d38dd20bde..aa982a445033 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -203,6 +203,7 @@ target | std | notes [`x86_64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 64-bit UEFI [^x86_32-floats-x87]: Floating-point support on `i586` targets is non-compliant: the `x87` registers and instructions used for these targets do not provide IEEE-754-compliant behavior, in particular when it comes to rounding and NaN payload bits. See [issue #114479][x86-32-float-issue]. + [wasi-rename]: https://github.com/rust-lang/compiler-team/issues/607 [Fortanix ABI]: https://edp.fortanix.com/ diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index bd0fbef998b2..9a23811ed3f9 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -9,6 +9,8 @@ path = "lib.rs" [dependencies] arrayvec = { version = "0.7", default-features = false } askama = { version = "0.12", default-features = false, features = ["config"] } +base64 = "0.21.7" +byteorder = "1.5" itertools = "0.12" indexmap = "2" minifier = "0.3.0" @@ -20,7 +22,7 @@ serde = { version = "1.0", features = ["derive"] } smallvec = "1.8.1" tempfile = "3" tracing = "0.1" -tracing-tree = "0.2.0" +tracing-tree = "0.3.0" threadpool = "1.8.1" [dependencies.tracing-subscriber] diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index fbc2c3c5af45..217f6bb550bc 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -1,747 +1,367 @@ +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; use rustc_hir as hir; -use rustc_hir::lang_items::LangItem; -use rustc_middle::ty::{Region, RegionVid, TypeFoldable}; -use rustc_trait_selection::traits::auto_trait::{self, AutoTraitResult}; +use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; +use rustc_middle::bug; +use rustc_middle::ty::{self, Region, Ty}; +use rustc_span::def_id::DefId; +use rustc_span::symbol::{kw, Symbol}; +use rustc_trait_selection::traits::auto_trait::{self, RegionTarget}; -use std::fmt::Debug; +use thin_vec::ThinVec; -use super::*; +use crate::clean::{self, simplify, Lifetime}; +use crate::clean::{ + clean_generic_param_def, clean_middle_ty, clean_predicate, clean_trait_ref_with_bindings, + clean_ty_generics, +}; +use crate::core::DocContext; -#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)] -enum RegionTarget<'tcx> { - Region(Region<'tcx>), - RegionVid(RegionVid), -} +#[instrument(level = "debug", skip(cx))] +pub(crate) fn synthesize_auto_trait_impls<'tcx>( + cx: &mut DocContext<'tcx>, + item_def_id: DefId, +) -> Vec { + let tcx = cx.tcx; + let param_env = tcx.param_env(item_def_id); + let ty = tcx.type_of(item_def_id).instantiate_identity(); -#[derive(Default, Debug, Clone)] -struct RegionDeps<'tcx> { - larger: FxHashSet>, - smaller: FxHashSet>, -} - -pub(crate) struct AutoTraitFinder<'a, 'tcx> { - pub(crate) cx: &'a mut core::DocContext<'tcx>, -} - -impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> -where - 'tcx: 'a, // should be an implied bound; rustc bug #98852. -{ - pub(crate) fn new(cx: &'a mut core::DocContext<'tcx>) -> Self { - AutoTraitFinder { cx } - } - - fn generate_for_trait( - &mut self, - ty: Ty<'tcx>, - trait_def_id: DefId, - param_env: ty::ParamEnv<'tcx>, - item_def_id: DefId, - f: &auto_trait::AutoTraitFinder<'tcx>, - // If this is set, show only negative trait implementations, not positive ones. - discard_positive_impl: bool, - ) -> Option { - let tcx = self.cx.tcx; - let trait_ref = ty::Binder::dummy(ty::TraitRef::new(tcx, trait_def_id, [ty])); - if !self.cx.generated_synthetics.insert((ty, trait_def_id)) { - debug!("get_auto_trait_impl_for({trait_ref:?}): already generated, aborting"); - return None; - } - - let result = f.find_auto_trait_generics(ty, param_env, trait_def_id, |info| { - let region_data = info.region_data; - - let names_map = tcx - .generics_of(item_def_id) - .params - .iter() - .filter_map(|param| match param.kind { - ty::GenericParamDefKind::Lifetime => Some(param.name), - _ => None, - }) - .map(|name| (name, Lifetime(name))) - .collect(); - let lifetime_predicates = Self::handle_lifetimes(®ion_data, &names_map); - let new_generics = self.param_env_to_generics( + let finder = auto_trait::AutoTraitFinder::new(tcx); + let mut auto_trait_impls: Vec<_> = cx + .auto_traits + .clone() + .into_iter() + .filter_map(|trait_def_id| { + synthesize_auto_trait_impl( + cx, + ty, + trait_def_id, + param_env, item_def_id, - info.full_user_env, - lifetime_predicates, - info.vid_to_region, - ); - - debug!( - "find_auto_trait_generics(item_def_id={:?}, trait_def_id={:?}): \ - finished with {:?}", - item_def_id, trait_def_id, new_generics - ); - - new_generics - }); - - let polarity; - let new_generics = match result { - AutoTraitResult::PositiveImpl(new_generics) => { - polarity = ty::ImplPolarity::Positive; - if discard_positive_impl { - return None; - } - new_generics - } - AutoTraitResult::NegativeImpl => { - polarity = ty::ImplPolarity::Negative; - - // For negative impls, we use the generic params, but *not* the predicates, - // from the original type. Otherwise, the displayed impl appears to be a - // conditional negative impl, when it's really unconditional. - // - // For example, consider the struct Foo(*mut T). Using - // the original predicates in our impl would cause us to generate - // `impl !Send for Foo`, which makes it appear that Foo - // implements Send where T is not copy. - // - // Instead, we generate `impl !Send for Foo`, which better - // expresses the fact that `Foo` never implements `Send`, - // regardless of the choice of `T`. - let raw_generics = clean_ty_generics( - self.cx, - tcx.generics_of(item_def_id), - ty::GenericPredicates::default(), - ); - let params = raw_generics.params; - - Generics { params, where_predicates: ThinVec::new() } - } - AutoTraitResult::ExplicitImpl => return None, - }; - - Some(Item { - name: None, - attrs: Default::default(), - item_id: ItemId::Auto { trait_: trait_def_id, for_: item_def_id }, - kind: Box::new(ImplItem(Box::new(Impl { - unsafety: hir::Unsafety::Normal, - generics: new_generics, - trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, ThinVec::new())), - for_: clean_middle_ty(ty::Binder::dummy(ty), self.cx, None, None), - items: Vec::new(), - polarity, - kind: ImplKind::Auto, - }))), - cfg: None, - inline_stmt_id: None, + &finder, + DiscardPositiveImpls::No, + ) }) + .collect(); + // We are only interested in case the type *doesn't* implement the `Sized` trait. + if !ty.is_sized(tcx, param_env) + && let Some(sized_trait_def_id) = tcx.lang_items().sized_trait() + && let Some(impl_item) = synthesize_auto_trait_impl( + cx, + ty, + sized_trait_def_id, + param_env, + item_def_id, + &finder, + DiscardPositiveImpls::Yes, + ) + { + auto_trait_impls.push(impl_item); } - - pub(crate) fn get_auto_trait_impls(&mut self, item_def_id: DefId) -> Vec { - let tcx = self.cx.tcx; - let param_env = tcx.param_env(item_def_id); - let ty = tcx.type_of(item_def_id).instantiate_identity(); - let f = auto_trait::AutoTraitFinder::new(tcx); - - debug!("get_auto_trait_impls({ty:?})"); - let auto_traits: Vec<_> = self.cx.auto_traits.to_vec(); - let mut auto_traits: Vec = auto_traits - .into_iter() - .filter_map(|trait_def_id| { - self.generate_for_trait(ty, trait_def_id, param_env, item_def_id, &f, false) - }) - .collect(); - // We are only interested in case the type *doesn't* implement the Sized trait. - if !ty.is_sized(tcx, param_env) { - // In case `#![no_core]` is used, `sized_trait` returns nothing. - if let Some(item) = tcx.lang_items().sized_trait().and_then(|sized_trait_did| { - self.generate_for_trait(ty, sized_trait_did, param_env, item_def_id, &f, true) - }) { - auto_traits.push(item); - } - } - auto_traits - } - - fn get_lifetime(region: Region<'_>, names_map: &FxHashMap) -> Lifetime { - region_name(region) - .map(|name| { - names_map - .get(&name) - .unwrap_or_else(|| panic!("Missing lifetime with name {name:?} for {region:?}")) - }) - .unwrap_or(&Lifetime::statik()) - .clone() - } - - /// This method calculates two things: Lifetime constraints of the form `'a: 'b`, - /// and region constraints of the form `RegionVid: 'a` - /// - /// This is essentially a simplified version of lexical_region_resolve. However, - /// handle_lifetimes determines what *needs be* true in order for an impl to hold. - /// lexical_region_resolve, along with much of the rest of the compiler, is concerned - /// with determining if a given set up constraints/predicates *are* met, given some - /// starting conditions (e.g., user-provided code). For this reason, it's easier - /// to perform the calculations we need on our own, rather than trying to make - /// existing inference/solver code do what we want. - fn handle_lifetimes<'cx>( - regions: &RegionConstraintData<'cx>, - names_map: &FxHashMap, - ) -> ThinVec { - // Our goal is to 'flatten' the list of constraints by eliminating - // all intermediate RegionVids. At the end, all constraints should - // be between Regions (aka region variables). This gives us the information - // we need to create the Generics. - let mut finished: FxHashMap<_, Vec<_>> = Default::default(); - - let mut vid_map: FxHashMap, RegionDeps<'_>> = Default::default(); - - // Flattening is done in two parts. First, we insert all of the constraints - // into a map. Each RegionTarget (either a RegionVid or a Region) maps - // to its smaller and larger regions. Note that 'larger' regions correspond - // to sub-regions in Rust code (e.g., in 'a: 'b, 'a is the larger region). - for (constraint, _) in ®ions.constraints { - match *constraint { - Constraint::VarSubVar(r1, r2) => { - { - let deps1 = vid_map.entry(RegionTarget::RegionVid(r1)).or_default(); - deps1.larger.insert(RegionTarget::RegionVid(r2)); - } - - let deps2 = vid_map.entry(RegionTarget::RegionVid(r2)).or_default(); - deps2.smaller.insert(RegionTarget::RegionVid(r1)); - } - Constraint::RegSubVar(region, vid) => { - let deps = vid_map.entry(RegionTarget::RegionVid(vid)).or_default(); - deps.smaller.insert(RegionTarget::Region(region)); - } - Constraint::VarSubReg(vid, region) => { - let deps = vid_map.entry(RegionTarget::RegionVid(vid)).or_default(); - deps.larger.insert(RegionTarget::Region(region)); - } - Constraint::RegSubReg(r1, r2) => { - // The constraint is already in the form that we want, so we're done with it - // Desired order is 'larger, smaller', so flip then - if region_name(r1) != region_name(r2) { - finished - .entry(region_name(r2).expect("no region_name found")) - .or_default() - .push(r1); - } - } - } - } - - // Here, we 'flatten' the map one element at a time. - // All of the element's sub and super regions are connected - // to each other. For example, if we have a graph that looks like this: - // - // (A, B) - C - (D, E) - // Where (A, B) are subregions, and (D,E) are super-regions - // - // then after deleting 'C', the graph will look like this: - // ... - A - (D, E ...) - // ... - B - (D, E, ...) - // (A, B, ...) - D - ... - // (A, B, ...) - E - ... - // - // where '...' signifies the existing sub and super regions of an entry - // When two adjacent ty::Regions are encountered, we've computed a final - // constraint, and add it to our list. Since we make sure to never re-add - // deleted items, this process will always finish. - while !vid_map.is_empty() { - let target = *vid_map.keys().next().expect("Keys somehow empty"); - let deps = vid_map.remove(&target).expect("Entry somehow missing"); - - for smaller in deps.smaller.iter() { - for larger in deps.larger.iter() { - match (smaller, larger) { - (&RegionTarget::Region(r1), &RegionTarget::Region(r2)) => { - if region_name(r1) != region_name(r2) { - finished - .entry(region_name(r2).expect("no region name found")) - .or_default() - .push(r1) // Larger, smaller - } - } - (&RegionTarget::RegionVid(_), &RegionTarget::Region(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*smaller) { - let smaller_deps = v.into_mut(); - smaller_deps.larger.insert(*larger); - smaller_deps.larger.remove(&target); - } - } - (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*larger) { - let deps = v.into_mut(); - deps.smaller.insert(*smaller); - deps.smaller.remove(&target); - } - } - (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*smaller) { - let smaller_deps = v.into_mut(); - smaller_deps.larger.insert(*larger); - smaller_deps.larger.remove(&target); - } - - if let Entry::Occupied(v) = vid_map.entry(*larger) { - let larger_deps = v.into_mut(); - larger_deps.smaller.insert(*smaller); - larger_deps.smaller.remove(&target); - } - } - } - } - } - } - - let lifetime_predicates = names_map - .iter() - .flat_map(|(name, lifetime)| { - let empty = Vec::new(); - let bounds: FxHashSet = finished - .get(name) - .unwrap_or(&empty) - .iter() - .map(|region| GenericBound::Outlives(Self::get_lifetime(*region, names_map))) - .collect(); - - if bounds.is_empty() { - return None; - } - Some(WherePredicate::RegionPredicate { - lifetime: lifetime.clone(), - bounds: bounds.into_iter().collect(), - }) - }) - .collect(); - - lifetime_predicates - } - - fn extract_for_generics(&self, pred: ty::Clause<'tcx>) -> FxHashSet { - let bound_predicate = pred.kind(); - let tcx = self.cx.tcx; - let regions = - match bound_predicate.skip_binder() { - ty::ClauseKind::Trait(poly_trait_pred) => tcx - .collect_referenced_late_bound_regions(bound_predicate.rebind(poly_trait_pred)), - ty::ClauseKind::Projection(poly_proj_pred) => tcx - .collect_referenced_late_bound_regions(bound_predicate.rebind(poly_proj_pred)), - _ => return FxHashSet::default(), - }; - - regions - .into_iter() - .filter_map(|br| { - match br { - // We only care about named late bound regions, as we need to add them - // to the 'for<>' section - ty::BrNamed(def_id, name) => Some(GenericParamDef::lifetime(def_id, name)), - _ => None, - } - }) - .collect() - } - - fn make_final_bounds( - &self, - ty_to_bounds: FxHashMap>, - ty_to_fn: FxHashMap)>, - lifetime_to_bounds: FxHashMap>, - ) -> Vec { - ty_to_bounds - .into_iter() - .flat_map(|(ty, mut bounds)| { - if let Some((ref poly_trait, ref output)) = ty_to_fn.get(&ty) { - let mut new_path = poly_trait.trait_.clone(); - let last_segment = new_path.segments.pop().expect("segments were empty"); - - let (old_input, old_output) = match last_segment.args { - GenericArgs::AngleBracketed { args, .. } => { - let types = args - .iter() - .filter_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty.clone()), - _ => None, - }) - .collect(); - (types, None) - } - GenericArgs::Parenthesized { inputs, output } => (inputs, output), - }; - - let output = output.as_ref().cloned().map(Box::new); - if old_output.is_some() && old_output != output { - panic!("Output mismatch for {ty:?} {old_output:?} {output:?}"); - } - - let new_params = GenericArgs::Parenthesized { inputs: old_input, output }; - - new_path - .segments - .push(PathSegment { name: last_segment.name, args: new_params }); - - bounds.insert(GenericBound::TraitBound( - PolyTrait { - trait_: new_path, - generic_params: poly_trait.generic_params.clone(), - }, - hir::TraitBoundModifier::None, - )); - } - if bounds.is_empty() { - return None; - } - - let mut bounds_vec = bounds.into_iter().collect(); - self.sort_where_bounds(&mut bounds_vec); - - Some(WherePredicate::BoundPredicate { - ty, - bounds: bounds_vec, - bound_params: Vec::new(), - }) - }) - .chain(lifetime_to_bounds.into_iter().filter(|(_, bounds)| !bounds.is_empty()).map( - |(lifetime, bounds)| { - let mut bounds_vec = bounds.into_iter().collect(); - self.sort_where_bounds(&mut bounds_vec); - WherePredicate::RegionPredicate { lifetime, bounds: bounds_vec } - }, - )) - .collect() - } - - /// Converts the calculated `ParamEnv` and lifetime information to a [`clean::Generics`](Generics), suitable for - /// display on the docs page. Cleaning the `Predicates` produces sub-optimal [`WherePredicate`]s, - /// so we fix them up: - /// - /// * Multiple bounds for the same type are coalesced into one: e.g., `T: Copy`, `T: Debug` - /// becomes `T: Copy + Debug` - /// * `Fn` bounds are handled specially - instead of leaving it as `T: Fn(), = - /// K`, we use the dedicated syntax `T: Fn() -> K` - /// * We explicitly add a `?Sized` bound if we didn't find any `Sized` predicates for a type - fn param_env_to_generics( - &mut self, - item_def_id: DefId, - param_env: ty::ParamEnv<'tcx>, - mut existing_predicates: ThinVec, - vid_to_region: FxHashMap>, - ) -> Generics { - debug!( - "param_env_to_generics(item_def_id={:?}, param_env={:?}, \ - existing_predicates={:?})", - item_def_id, param_env, existing_predicates - ); - - let tcx = self.cx.tcx; - - // The `Sized` trait must be handled specially, since we only display it when - // it is *not* required (i.e., '?Sized') - let sized_trait = tcx.require_lang_item(LangItem::Sized, None); - - let mut replacer = RegionReplacer { vid_to_region: &vid_to_region, tcx }; - - let orig_bounds: FxHashSet<_> = tcx.param_env(item_def_id).caller_bounds().iter().collect(); - let clean_where_predicates = param_env - .caller_bounds() - .iter() - .filter(|p| { - !orig_bounds.contains(p) - || match p.kind().skip_binder() { - ty::ClauseKind::Trait(pred) => pred.def_id() == sized_trait, - _ => false, - } - }) - .map(|p| p.fold_with(&mut replacer)); - - let raw_generics = clean_ty_generics( - self.cx, - tcx.generics_of(item_def_id), - tcx.explicit_predicates_of(item_def_id), - ); - let mut generic_params = raw_generics.params; - - debug!("param_env_to_generics({item_def_id:?}): generic_params={generic_params:?}"); - - let mut has_sized = FxHashSet::default(); - let mut ty_to_bounds: FxHashMap<_, FxHashSet<_>> = Default::default(); - let mut lifetime_to_bounds: FxHashMap<_, FxHashSet<_>> = Default::default(); - let mut ty_to_traits: FxHashMap> = Default::default(); - - let mut ty_to_fn: FxHashMap)> = Default::default(); - - // FIXME: This code shares much of the logic found in `clean_ty_generics` and - // `simplify::where_clause`. Consider deduplicating it to avoid diverging - // implementations. - // Further, the code below does not merge (partially re-sugared) bounds like - // `Tr` & `Tr` and it does not render higher-ranked parameters - // originating from equality predicates. - for p in clean_where_predicates { - let (orig_p, p) = (p, clean_predicate(p, self.cx)); - if p.is_none() { - continue; - } - let p = p.unwrap(); - match p { - WherePredicate::BoundPredicate { ty, mut bounds, .. } => { - // Writing a projection trait bound of the form - // ::Name : ?Sized - // is illegal, because ?Sized bounds can only - // be written in the (here, nonexistent) definition - // of the type. - // Therefore, we make sure that we never add a ?Sized - // bound for projections - if let Type::QPath { .. } = ty { - has_sized.insert(ty.clone()); - } - - if bounds.is_empty() { - continue; - } - - let mut for_generics = self.extract_for_generics(orig_p); - - assert!(bounds.len() == 1); - let mut b = bounds.pop().expect("bounds were empty"); - - if b.is_sized_bound(self.cx) { - has_sized.insert(ty.clone()); - } else if !b - .get_trait_path() - .and_then(|trait_| { - ty_to_traits - .get(&ty) - .map(|bounds| bounds.contains(&strip_path_generics(trait_))) - }) - .unwrap_or(false) - { - // If we've already added a projection bound for the same type, don't add - // this, as it would be a duplicate - - // Handle any 'Fn/FnOnce/FnMut' bounds specially, - // as we want to combine them with any 'Output' qpaths - // later - - let is_fn = match b { - GenericBound::TraitBound(ref mut p, _) => { - // Insert regions into the for_generics hash map first, to ensure - // that we don't end up with duplicate bounds (e.g., for<'b, 'b>) - for_generics.extend(p.generic_params.drain(..)); - p.generic_params.extend(for_generics); - self.is_fn_trait(&p.trait_) - } - _ => false, - }; - - let poly_trait = b.get_poly_trait().expect("Cannot get poly trait"); - - if is_fn { - ty_to_fn - .entry(ty.clone()) - .and_modify(|e| *e = (poly_trait.clone(), e.1.clone())) - .or_insert(((poly_trait.clone()), None)); - - ty_to_bounds.entry(ty.clone()).or_default(); - } else { - ty_to_bounds.entry(ty.clone()).or_default().insert(b.clone()); - } - } - } - WherePredicate::RegionPredicate { lifetime, bounds } => { - lifetime_to_bounds.entry(lifetime).or_default().extend(bounds); - } - WherePredicate::EqPredicate { lhs, rhs } => { - match lhs { - Type::QPath(box QPathData { - ref assoc, - ref self_type, - trait_: Some(ref trait_), - .. - }) => { - let ty = &*self_type; - let mut new_trait = trait_.clone(); - - if self.is_fn_trait(trait_) && assoc.name == sym::Output { - ty_to_fn - .entry(ty.clone()) - .and_modify(|e| { - *e = (e.0.clone(), Some(rhs.ty().unwrap().clone())) - }) - .or_insert(( - PolyTrait { - trait_: trait_.clone(), - generic_params: Vec::new(), - }, - Some(rhs.ty().unwrap().clone()), - )); - continue; - } - - let args = &mut new_trait - .segments - .last_mut() - .expect("segments were empty") - .args; - - match args { - // Convert something like ' = u8' - // to 'T: Iterator' - GenericArgs::AngleBracketed { ref mut bindings, .. } => { - bindings.push(TypeBinding { - assoc: assoc.clone(), - kind: TypeBindingKind::Equality { term: rhs }, - }); - } - GenericArgs::Parenthesized { .. } => { - existing_predicates.push(WherePredicate::EqPredicate { - lhs: lhs.clone(), - rhs, - }); - continue; // If something other than a Fn ends up - // with parentheses, leave it alone - } - } - - let bounds = ty_to_bounds.entry(ty.clone()).or_default(); - - bounds.insert(GenericBound::TraitBound( - PolyTrait { trait_: new_trait, generic_params: Vec::new() }, - hir::TraitBoundModifier::None, - )); - - // Remove any existing 'plain' bound (e.g., 'T: Iterator`) so - // that we don't see a - // duplicate bound like `T: Iterator + Iterator` - // on the docs page. - bounds.remove(&GenericBound::TraitBound( - PolyTrait { trait_: trait_.clone(), generic_params: Vec::new() }, - hir::TraitBoundModifier::None, - )); - // Avoid creating any new duplicate bounds later in the outer - // loop - ty_to_traits.entry(ty.clone()).or_default().insert(trait_.clone()); - } - _ => panic!("Unexpected LHS {lhs:?} for {item_def_id:?}"), - } - } - }; - } - - let final_bounds = self.make_final_bounds(ty_to_bounds, ty_to_fn, lifetime_to_bounds); - - existing_predicates.extend(final_bounds); - - for param in generic_params.iter_mut() { - match param.kind { - GenericParamDefKind::Type { ref mut default, ref mut bounds, .. } => { - // We never want something like `impl`. - default.take(); - let generic_ty = Type::Generic(param.name); - if !has_sized.contains(&generic_ty) { - bounds.insert(0, GenericBound::maybe_sized(self.cx)); - } - } - GenericParamDefKind::Lifetime { .. } => {} - GenericParamDefKind::Const { ref mut default, .. } => { - // We never want something like `impl` - default.take(); - } - } - } - - self.sort_where_predicates(&mut existing_predicates); - - Generics { params: generic_params, where_predicates: existing_predicates } - } - - /// Ensure that the predicates are in a consistent order. The precise - /// ordering doesn't actually matter, but it's important that - /// a given set of predicates always appears in the same order - - /// both for visual consistency between 'rustdoc' runs, and to - /// make writing tests much easier - #[inline] - fn sort_where_predicates(&self, predicates: &mut [WherePredicate]) { - // We should never have identical bounds - and if we do, - // they're visually identical as well. Therefore, using - // an unstable sort is fine. - self.unstable_debug_sort(predicates); - } - - /// Ensure that the bounds are in a consistent order. The precise - /// ordering doesn't actually matter, but it's important that - /// a given set of bounds always appears in the same order - - /// both for visual consistency between 'rustdoc' runs, and to - /// make writing tests much easier - #[inline] - fn sort_where_bounds(&self, bounds: &mut Vec) { - // We should never have identical bounds - and if we do, - // they're visually identical as well. Therefore, using - // an unstable sort is fine. - self.unstable_debug_sort(bounds); - } - - /// This might look horrendously hacky, but it's actually not that bad. - /// - /// For performance reasons, we use several different FxHashMaps - /// in the process of computing the final set of where predicates. - /// However, the iteration order of a HashMap is completely unspecified. - /// In fact, the iteration of an FxHashMap can even vary between platforms, - /// since FxHasher has different behavior for 32-bit and 64-bit platforms. - /// - /// Obviously, it's extremely undesirable for documentation rendering - /// to be dependent on the platform it's run on. Apart from being confusing - /// to end users, it makes writing tests much more difficult, as predicates - /// can appear in any order in the final result. - /// - /// To solve this problem, we sort WherePredicates and GenericBounds - /// by their Debug string. The thing to keep in mind is that we don't really - /// care what the final order is - we're synthesizing an impl or bound - /// ourselves, so any order can be considered equally valid. By sorting the - /// predicates and bounds, however, we ensure that for a given codebase, all - /// auto-trait impls always render in exactly the same way. - /// - /// Using the Debug implementation for sorting prevents us from needing to - /// write quite a bit of almost entirely useless code (e.g., how should two - /// Types be sorted relative to each other). It also allows us to solve the - /// problem for both WherePredicates and GenericBounds at the same time. This - /// approach is probably somewhat slower, but the small number of items - /// involved (impls rarely have more than a few bounds) means that it - /// shouldn't matter in practice. - fn unstable_debug_sort(&self, vec: &mut [T]) { - vec.sort_by_cached_key(|x| format!("{x:?}")) - } - - fn is_fn_trait(&self, path: &Path) -> bool { - let tcx = self.cx.tcx; - let did = path.def_id(); - did == tcx.require_lang_item(LangItem::Fn, None) - || did == tcx.require_lang_item(LangItem::FnMut, None) - || did == tcx.require_lang_item(LangItem::FnOnce, None) - } + auto_trait_impls } -fn region_name(region: Region<'_>) -> Option { +#[instrument(level = "debug", skip(cx, finder))] +fn synthesize_auto_trait_impl<'tcx>( + cx: &mut DocContext<'tcx>, + ty: Ty<'tcx>, + trait_def_id: DefId, + param_env: ty::ParamEnv<'tcx>, + item_def_id: DefId, + finder: &auto_trait::AutoTraitFinder<'tcx>, + discard_positive_impls: DiscardPositiveImpls, +) -> Option { + let tcx = cx.tcx; + let trait_ref = ty::Binder::dummy(ty::TraitRef::new(tcx, trait_def_id, [ty])); + if !cx.generated_synthetics.insert((ty, trait_def_id)) { + debug!("already generated, aborting"); + return None; + } + + let result = finder.find_auto_trait_generics(ty, param_env, trait_def_id, |info| { + clean_param_env(cx, item_def_id, info.full_user_env, info.region_data, info.vid_to_region) + }); + + let (generics, polarity) = match result { + auto_trait::AutoTraitResult::PositiveImpl(generics) => { + if let DiscardPositiveImpls::Yes = discard_positive_impls { + return None; + } + + (generics, ty::ImplPolarity::Positive) + } + auto_trait::AutoTraitResult::NegativeImpl => { + // For negative impls, we use the generic params, but *not* the predicates, + // from the original type. Otherwise, the displayed impl appears to be a + // conditional negative impl, when it's really unconditional. + // + // For example, consider the struct Foo(*mut T). Using + // the original predicates in our impl would cause us to generate + // `impl !Send for Foo`, which makes it appear that Foo + // implements Send where T is not copy. + // + // Instead, we generate `impl !Send for Foo`, which better + // expresses the fact that `Foo` never implements `Send`, + // regardless of the choice of `T`. + let mut generics = clean_ty_generics( + cx, + tcx.generics_of(item_def_id), + ty::GenericPredicates::default(), + ); + generics.where_predicates.clear(); + + (generics, ty::ImplPolarity::Negative) + } + auto_trait::AutoTraitResult::ExplicitImpl => return None, + }; + + Some(clean::Item { + name: None, + attrs: Default::default(), + item_id: clean::ItemId::Auto { trait_: trait_def_id, for_: item_def_id }, + kind: Box::new(clean::ImplItem(Box::new(clean::Impl { + unsafety: hir::Unsafety::Normal, + generics, + trait_: Some(clean_trait_ref_with_bindings(cx, trait_ref, ThinVec::new())), + for_: clean_middle_ty(ty::Binder::dummy(ty), cx, None, None), + items: Vec::new(), + polarity, + kind: clean::ImplKind::Auto, + }))), + cfg: None, + inline_stmt_id: None, + }) +} + +#[derive(Debug)] +enum DiscardPositiveImpls { + Yes, + No, +} + +#[instrument(level = "debug", skip(cx, region_data, vid_to_region))] +fn clean_param_env<'tcx>( + cx: &mut DocContext<'tcx>, + item_def_id: DefId, + param_env: ty::ParamEnv<'tcx>, + region_data: RegionConstraintData<'tcx>, + vid_to_region: FxIndexMap>, +) -> clean::Generics { + let tcx = cx.tcx; + let generics = tcx.generics_of(item_def_id); + + let params: ThinVec<_> = generics + .params + .iter() + .inspect(|param| { + if cfg!(debug_assertions) { + debug_assert!(!param.is_anonymous_lifetime() && !param.is_host_effect()); + if let ty::GenericParamDefKind::Type { synthetic, .. } = param.kind { + debug_assert!(!synthetic && param.name != kw::SelfUpper); + } + } + }) + // We're basing the generics of the synthetic auto trait impl off of the generics of the + // implementing type. Its generic parameters may have defaults, don't copy them over: + // Generic parameter defaults are meaningless in impls. + .map(|param| clean_generic_param_def(param, clean::ParamDefaults::No, cx)) + .collect(); + + // FIXME(#111101): Incorporate the explicit predicates of the item here... + let item_predicates: FxIndexSet<_> = + tcx.predicates_of(item_def_id).predicates.iter().map(|(pred, _)| pred).collect(); + let where_predicates = param_env + .caller_bounds() + .iter() + // FIXME: ...which hopefully allows us to simplify this: + .filter(|pred| { + !item_predicates.contains(pred) + || pred + .as_trait_clause() + .is_some_and(|pred| tcx.lang_items().sized_trait() == Some(pred.def_id())) + }) + .map(|pred| { + tcx.fold_regions(pred, |r, _| match *r { + // FIXME: Don't `unwrap_or`, I think we should panic if we encounter an infer var that + // we can't map to a concrete region. However, `AutoTraitFinder` *does* leak those kinds + // of `ReVar`s for some reason at the time of writing. See `rustdoc-ui/` tests. + // This is in dire need of an investigation into `AutoTraitFinder`. + ty::ReVar(vid) => vid_to_region.get(&vid).copied().unwrap_or(r), + ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r, + // FIXME(#120606): `AutoTraitFinder` can actually leak placeholder regions which feels + // incorrect. Needs investigation. + ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReErased => { + bug!("unexpected region kind: {r:?}") + } + }) + }) + .flat_map(|pred| clean_predicate(pred, cx)) + .chain(clean_region_outlives_constraints(®ion_data, generics)) + .collect(); + + let mut generics = clean::Generics { params, where_predicates }; + simplify::sized_bounds(cx, &mut generics); + generics.where_predicates = simplify::where_clauses(cx, generics.where_predicates); + generics +} + +/// Clean region outlives constraints to where-predicates. +/// +/// This is essentially a simplified version of `lexical_region_resolve`. +/// +/// However, here we determine what *needs to be* true in order for an impl to hold. +/// `lexical_region_resolve`, along with much of the rest of the compiler, is concerned +/// with determining if a given set up constraints / predicates *are* met, given some +/// starting conditions like user-provided code. +/// +/// For this reason, it's easier to perform the calculations we need on our own, +/// rather than trying to make existing inference/solver code do what we want. +fn clean_region_outlives_constraints<'tcx>( + regions: &RegionConstraintData<'tcx>, + generics: &'tcx ty::Generics, +) -> ThinVec { + // Our goal is to "flatten" the list of constraints by eliminating all intermediate + // `RegionVids` (region inference variables). At the end, all constraints should be + // between `Region`s. This gives us the information we need to create the where-predicates. + // This flattening is done in two parts. + + let mut outlives_predicates = FxIndexMap::<_, Vec<_>>::default(); + let mut map = FxIndexMap::, auto_trait::RegionDeps<'_>>::default(); + + // (1) We insert all of the constraints into a map. + // Each `RegionTarget` (a `RegionVid` or a `Region`) maps to its smaller and larger regions. + // Note that "larger" regions correspond to sub regions in the surface language. + // E.g., in `'a: 'b`, `'a` is the larger region. + for (constraint, _) in ®ions.constraints { + match *constraint { + Constraint::VarSubVar(vid1, vid2) => { + let deps1 = map.entry(RegionTarget::RegionVid(vid1)).or_default(); + deps1.larger.insert(RegionTarget::RegionVid(vid2)); + + let deps2 = map.entry(RegionTarget::RegionVid(vid2)).or_default(); + deps2.smaller.insert(RegionTarget::RegionVid(vid1)); + } + Constraint::RegSubVar(region, vid) => { + let deps = map.entry(RegionTarget::RegionVid(vid)).or_default(); + deps.smaller.insert(RegionTarget::Region(region)); + } + Constraint::VarSubReg(vid, region) => { + let deps = map.entry(RegionTarget::RegionVid(vid)).or_default(); + deps.larger.insert(RegionTarget::Region(region)); + } + Constraint::RegSubReg(r1, r2) => { + // The constraint is already in the form that we want, so we're done with it + // The desired order is [larger, smaller], so flip them. + if early_bound_region_name(r1) != early_bound_region_name(r2) { + outlives_predicates + .entry(early_bound_region_name(r2).expect("no region_name found")) + .or_default() + .push(r1); + } + } + } + } + + // (2) Here, we "flatten" the map one element at a time. All of the elements' sub and super + // regions are connected to each other. For example, if we have a graph that looks like this: + // + // (A, B) - C - (D, E) + // + // where (A, B) are sub regions, and (D,E) are super regions. + // Then, after deleting 'C', the graph will look like this: + // + // ... - A - (D, E, ...) + // ... - B - (D, E, ...) + // (A, B, ...) - D - ... + // (A, B, ...) - E - ... + // + // where '...' signifies the existing sub and super regions of an entry. When two adjacent + // `Region`s are encountered, we've computed a final constraint, and add it to our list. + // Since we make sure to never re-add deleted items, this process will always finish. + while !map.is_empty() { + let target = *map.keys().next().unwrap(); + let deps = map.swap_remove(&target).unwrap(); + + for smaller in &deps.smaller { + for larger in &deps.larger { + match (smaller, larger) { + (&RegionTarget::Region(smaller), &RegionTarget::Region(larger)) => { + if early_bound_region_name(smaller) != early_bound_region_name(larger) { + outlives_predicates + .entry( + early_bound_region_name(larger).expect("no region name found"), + ) + .or_default() + .push(smaller) + } + } + (&RegionTarget::RegionVid(_), &RegionTarget::Region(_)) => { + if let IndexEntry::Occupied(v) = map.entry(*smaller) { + let smaller_deps = v.into_mut(); + smaller_deps.larger.insert(*larger); + smaller_deps.larger.swap_remove(&target); + } + } + (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => { + if let IndexEntry::Occupied(v) = map.entry(*larger) { + let deps = v.into_mut(); + deps.smaller.insert(*smaller); + deps.smaller.swap_remove(&target); + } + } + (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => { + if let IndexEntry::Occupied(v) = map.entry(*smaller) { + let smaller_deps = v.into_mut(); + smaller_deps.larger.insert(*larger); + smaller_deps.larger.swap_remove(&target); + } + if let IndexEntry::Occupied(v) = map.entry(*larger) { + let larger_deps = v.into_mut(); + larger_deps.smaller.insert(*smaller); + larger_deps.smaller.swap_remove(&target); + } + } + } + } + } + } + + let region_params: FxIndexSet<_> = generics + .params + .iter() + .filter_map(|param| match param.kind { + ty::GenericParamDefKind::Lifetime => Some(param.name), + _ => None, + }) + .collect(); + + region_params + .iter() + .filter_map(|&name| { + let bounds: FxIndexSet<_> = outlives_predicates + .get(&name)? + .iter() + .map(|®ion| { + let lifetime = early_bound_region_name(region) + .inspect(|name| assert!(region_params.contains(name))) + .map(|name| Lifetime(name)) + .unwrap_or(Lifetime::statik()); + clean::GenericBound::Outlives(lifetime) + }) + .collect(); + if bounds.is_empty() { + return None; + } + Some(clean::WherePredicate::RegionPredicate { + lifetime: Lifetime(name), + bounds: bounds.into_iter().collect(), + }) + }) + .collect() +} + +fn early_bound_region_name(region: Region<'_>) -> Option { match *region { ty::ReEarlyParam(r) => Some(r.name), _ => None, } } - -/// Replaces all [`ty::RegionVid`]s in a type with [`ty::Region`]s, using the provided map. -struct RegionReplacer<'a, 'tcx> { - vid_to_region: &'a FxHashMap>, - tcx: TyCtxt<'tcx>, -} - -impl<'a, 'tcx> TypeFolder> for RegionReplacer<'a, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { - // These are the regions that can be seen in the AST. - ty::ReVar(vid) => self.vid_to_region.get(&vid).cloned().unwrap_or(r), - ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r, - r => bug!("unexpected region: {r:?}"), - } - } -} diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 0cdf52bfb004..a25a506d9c5b 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -21,10 +21,8 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, LOCAL_CRATE}; use rustc_hir::PredicateOrigin; use rustc_hir_analysis::lower_ty; -use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; use rustc_middle::metadata::Reexport; use rustc_middle::middle::resolve_bound_vars as rbv; -use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, AdtKind, Ty, TyCtxt}; @@ -35,9 +33,7 @@ use rustc_span::{self, ExpnKind}; use rustc_trait_selection::traits::wf::object_region_bounds; use std::borrow::Cow; -use std::collections::hash_map::Entry; use std::collections::BTreeMap; -use std::hash::Hash; use std::mem; use thin_vec::ThinVec; @@ -502,6 +498,7 @@ fn projection_to_path_segment<'tcx>( fn clean_generic_param_def<'tcx>( def: &ty::GenericParamDef, + defaults: ParamDefaults, cx: &mut DocContext<'tcx>, ) -> GenericParamDef { let (name, kind) = match def.kind { @@ -509,7 +506,9 @@ fn clean_generic_param_def<'tcx>( (def.name, GenericParamDefKind::Lifetime { outlives: ThinVec::new() }) } ty::GenericParamDefKind::Type { has_default, synthetic, .. } => { - let default = if has_default { + let default = if let ParamDefaults::Yes = defaults + && has_default + { Some(clean_middle_ty( ty::Binder::dummy(cx.tcx.type_of(def.def_id).instantiate_identity()), cx, @@ -542,11 +541,14 @@ fn clean_generic_param_def<'tcx>( Some(def.def_id), None, )), - default: match has_default { - true => Some(Box::new( + default: if let ParamDefaults::Yes = defaults + && has_default + { + Some(Box::new( cx.tcx.const_param_default(def.def_id).instantiate_identity().to_string(), - )), - false => None, + )) + } else { + None }, is_host_effect, }, @@ -556,6 +558,12 @@ fn clean_generic_param_def<'tcx>( GenericParamDef { name, def_id: def.def_id, kind } } +/// Whether to clean generic parameter defaults or not. +enum ParamDefaults { + Yes, + No, +} + fn clean_generic_param<'tcx>( cx: &mut DocContext<'tcx>, generics: Option<&hir::Generics<'tcx>>, @@ -759,34 +767,30 @@ fn clean_ty_generics<'tcx>( gens: &ty::Generics, preds: ty::GenericPredicates<'tcx>, ) -> Generics { - // Don't populate `cx.impl_trait_bounds` before `clean`ning `where` clauses, - // since `Clean for ty::Predicate` would consume them. + // Don't populate `cx.impl_trait_bounds` before cleaning where clauses, + // since `clean_predicate` would consume them. let mut impl_trait = BTreeMap::>::default(); - // Bounds in the type_params and lifetimes fields are repeated in the - // predicates field (see rustc_hir_analysis::collect::ty_generics), so remove - // them. - let stripped_params = gens + let params: ThinVec<_> = gens .params .iter() - .filter_map(|param| match param.kind { - ty::GenericParamDefKind::Lifetime if param.is_anonymous_lifetime() => None, - ty::GenericParamDefKind::Lifetime => Some(clean_generic_param_def(param, cx)), + .filter(|param| match param.kind { + ty::GenericParamDefKind::Lifetime => !param.is_anonymous_lifetime(), ty::GenericParamDefKind::Type { synthetic, .. } => { if param.name == kw::SelfUpper { - assert_eq!(param.index, 0); - return None; + debug_assert_eq!(param.index, 0); + return false; } if synthetic { impl_trait.insert(param.index, vec![]); - return None; + return false; } - Some(clean_generic_param_def(param, cx)) + true } - ty::GenericParamDefKind::Const { is_host_effect: true, .. } => None, - ty::GenericParamDefKind::Const { .. } => Some(clean_generic_param_def(param, cx)), + ty::GenericParamDefKind::Const { is_host_effect, .. } => !is_host_effect, }) - .collect::>(); + .map(|param| clean_generic_param_def(param, ParamDefaults::Yes, cx)) + .collect(); // param index -> [(trait DefId, associated type name & generics, term)] let mut impl_trait_proj = @@ -882,56 +886,13 @@ fn clean_ty_generics<'tcx>( // Now that `cx.impl_trait_bounds` is populated, we can process // remaining predicates which could contain `impl Trait`. - let mut where_predicates = - where_predicates.into_iter().flat_map(|p| clean_predicate(*p, cx)).collect::>(); + let where_predicates = + where_predicates.into_iter().flat_map(|p| clean_predicate(*p, cx)).collect(); - // In the surface language, all type parameters except `Self` have an - // implicit `Sized` bound unless removed with `?Sized`. - // However, in the list of where-predicates below, `Sized` appears like a - // normal bound: It's either present (the type is sized) or - // absent (the type might be unsized) but never *maybe* (i.e. `?Sized`). - // - // This is unsuitable for rendering. - // Thus, as a first step remove all `Sized` bounds that should be implicit. - // - // Note that associated types also have an implicit `Sized` bound but we - // don't actually know the set of associated types right here so that's - // handled when cleaning associated types. - let mut sized_params = FxHashSet::default(); - where_predicates.retain(|pred| { - if let WherePredicate::BoundPredicate { ty: Generic(g), bounds, .. } = pred - && *g != kw::SelfUpper - && bounds.iter().any(|b| b.is_sized_bound(cx)) - { - sized_params.insert(*g); - false - } else { - true - } - }); - - // As a final step, go through the type parameters again and insert a - // `?Sized` bound for each one we didn't find to be `Sized`. - for tp in &stripped_params { - if let types::GenericParamDefKind::Type { .. } = tp.kind - && !sized_params.contains(&tp.name) - { - where_predicates.push(WherePredicate::BoundPredicate { - ty: Type::Generic(tp.name), - bounds: vec![GenericBound::maybe_sized(cx)], - bound_params: Vec::new(), - }) - } - } - - // It would be nice to collect all of the bounds on a type and recombine - // them if possible, to avoid e.g., `where T: Foo, T: Bar, T: Sized, T: 'a` - // and instead see `where T: Foo + Bar + Sized + 'a` - - Generics { - params: stripped_params, - where_predicates: simplify::where_clauses(cx, where_predicates), - } + let mut generics = Generics { params, where_predicates }; + simplify::sized_bounds(cx, &mut generics); + generics.where_predicates = simplify::where_clauses(cx, generics.where_predicates); + generics } fn clean_ty_alias_inner_type<'tcx>( diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index c35fb9ec7887..5a3ccb6239a0 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -12,6 +12,7 @@ //! bounds by special casing scenarios such as these. Fun! use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::DefId; use rustc_middle::ty; use thin_vec::ThinVec; @@ -21,7 +22,7 @@ use crate::clean::GenericArgs as PP; use crate::clean::WherePredicate as WP; use crate::core::DocContext; -pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec) -> ThinVec { +pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: ThinVec) -> ThinVec { // First, partition the where clause into its separate components. // // We use `FxIndexMap` so that the insertion order is preserved to prevent messing up to @@ -128,6 +129,48 @@ fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId) .any(|did| trait_is_same_or_supertrait(cx, did, trait_)) } +pub(crate) fn sized_bounds(cx: &mut DocContext<'_>, generics: &mut clean::Generics) { + let mut sized_params = UnordSet::new(); + + // In the surface language, all type parameters except `Self` have an + // implicit `Sized` bound unless removed with `?Sized`. + // However, in the list of where-predicates below, `Sized` appears like a + // normal bound: It's either present (the type is sized) or + // absent (the type might be unsized) but never *maybe* (i.e. `?Sized`). + // + // This is unsuitable for rendering. + // Thus, as a first step remove all `Sized` bounds that should be implicit. + // + // Note that associated types also have an implicit `Sized` bound but we + // don't actually know the set of associated types right here so that + // should be handled when cleaning associated types. + generics.where_predicates.retain(|pred| { + if let WP::BoundPredicate { ty: clean::Generic(param), bounds, .. } = pred + && *param != rustc_span::symbol::kw::SelfUpper + && bounds.iter().any(|b| b.is_sized_bound(cx)) + { + sized_params.insert(*param); + false + } else { + true + } + }); + + // As a final step, go through the type parameters again and insert a + // `?Sized` bound for each one we didn't find to be `Sized`. + for param in &generics.params { + if let clean::GenericParamDefKind::Type { .. } = param.kind + && !sized_params.contains(¶m.name) + { + generics.where_predicates.push(WP::BoundPredicate { + ty: clean::Type::Generic(param.name), + bounds: vec![clean::GenericBound::maybe_sized(cx)], + bound_params: Vec::new(), + }) + } + } +} + /// Move bounds that are (likely) directly attached to generic parameters from the where-clause to /// the respective parameter. /// diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index a51f6360df2a..6793ea9f485f 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1277,13 +1277,6 @@ impl GenericBound { false } - pub(crate) fn get_poly_trait(&self) -> Option { - if let GenericBound::TraitBound(ref p, _) = *self { - return Some(p.clone()); - } - None - } - pub(crate) fn get_trait_path(&self) -> Option { if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, _) = *self { Some(trait_.clone()) diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 977b4bb45b62..d5e0e83696f3 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -1,4 +1,4 @@ -use crate::clean::auto_trait::AutoTraitFinder; +use crate::clean::auto_trait::synthesize_auto_trait_impls; use crate::clean::blanket_impl::BlanketImplFinder; use crate::clean::render_macro_matchers::render_macro_matcher; use crate::clean::{ @@ -251,15 +251,6 @@ pub(super) fn clean_middle_path<'tcx>( } } -/// Remove the generic arguments from a path. -pub(crate) fn strip_path_generics(mut path: Path) -> Path { - for ps in path.segments.iter_mut() { - ps.args = GenericArgs::AngleBracketed { args: Default::default(), bindings: ThinVec::new() } - } - - path -} - pub(crate) fn qpath_to_string(p: &hir::QPath<'_>) -> String { let segments = match *p { hir::QPath::Resolved(_, path) => &path.segments, @@ -486,6 +477,7 @@ pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type { } } +// FIXME(fmease): Update the `get_*` terminology to the `synthesize_` one. pub(crate) fn get_auto_trait_and_blanket_impls( cx: &mut DocContext<'_>, item_def_id: DefId, @@ -493,8 +485,8 @@ pub(crate) fn get_auto_trait_and_blanket_impls( let auto_impls = cx .sess() .prof - .generic_activity("get_auto_trait_impls") - .run(|| AutoTraitFinder::new(cx).get_auto_trait_impls(item_def_id)); + .generic_activity("synthesize_auto_trait_impls") + .run(|| synthesize_auto_trait_impls(cx, item_def_id)); let blanket_impls = cx .sess() .prof diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 1ddcb3d8728f..4e46f847fd76 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -184,40 +184,15 @@ pub(crate) enum RenderTypeId { impl RenderTypeId { pub fn write_to_string(&self, string: &mut String) { - // (sign, value) - let (sign, id): (bool, u32) = match &self { + let id: i32 = match &self { // 0 is a sentinel, everything else is one-indexed // concrete type - RenderTypeId::Index(idx) if *idx >= 0 => (false, (idx + 1isize).try_into().unwrap()), + RenderTypeId::Index(idx) if *idx >= 0 => (idx + 1isize).try_into().unwrap(), // generic type parameter - RenderTypeId::Index(idx) => (true, (-*idx).try_into().unwrap()), + RenderTypeId::Index(idx) => (*idx).try_into().unwrap(), _ => panic!("must convert render types to indexes before serializing"), }; - // zig-zag encoding - let value: u32 = (id << 1) | (if sign { 1 } else { 0 }); - // Self-terminating hex use capital letters for everything but the - // least significant digit, which is lowercase. For example, decimal 17 - // would be `` Aa `` if zig-zag encoding weren't used. - // - // Zig-zag encoding, however, stores the sign bit as the last bit. - // This means, in the last hexit, 1 is actually `c`, -1 is `b` - // (`a` is the imaginary -0), and, because all the bits are shifted - // by one, `` A` `` is actually 8 and `` Aa `` is -8. - // - // https://rust-lang.github.io/rustc-dev-guide/rustdoc-internals/search.html - // describes the encoding in more detail. - let mut shift: u32 = 28; - let mut mask: u32 = 0xF0_00_00_00; - while shift < 32 { - let hexit = (value & mask) >> shift; - if hexit != 0 || shift == 0 { - let hex = - char::try_from(if shift == 0 { '`' } else { '@' } as u32 + hexit).unwrap(); - string.push(hex); - } - shift = shift.wrapping_sub(4); - mask = mask >> 4; - } + search_index::encode::write_vlqhex_to_string(id, string); } } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index be2786c99eca..51f90e455001 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -1,3 +1,5 @@ +pub(crate) mod encode; + use std::collections::hash_map::Entry; use std::collections::{BTreeMap, VecDeque}; @@ -17,12 +19,46 @@ use crate::html::format::join_with_double_colon; use crate::html::markdown::short_markdown_summary; use crate::html::render::{self, IndexItem, IndexItemFunctionType, RenderType, RenderTypeId}; +use encode::{bitmap_to_string, write_vlqhex_to_string}; + +/// The serialized search description sharded version +/// +/// The `index` is a JSON-encoded list of names and other information. +/// +/// The desc has newlined descriptions, split up by size into 128KiB shards. +/// For example, `(4, "foo\nbar\nbaz\nquux")`. +/// +/// There is no single, optimal size for these shards, because it depends on +/// configuration values that we can't predict or control, such as the version +/// of HTTP used (HTTP/1.1 would work better with larger files, while HTTP/2 +/// and 3 are more agnostic), transport compression (gzip, zstd, etc), whether +/// the search query is going to produce a large number of results or a small +/// number, the bandwidth delay product of the network... +/// +/// Gzipping some standard library descriptions to guess what transport +/// compression will do, the compressed file sizes can be as small as 4.9KiB +/// or as large as 18KiB (ignoring the final 1.9KiB shard of leftovers). +/// A "reasonable" range for files is for them to be bigger than 1KiB, +/// since that's about the amount of data that can be transferred in a +/// single TCP packet, and 64KiB, the maximum amount of data that +/// TCP can transfer in a single round trip without extensions. +/// +/// [1]: https://en.wikipedia.org/wiki/Maximum_transmission_unit#MTUs_for_common_media +/// [2]: https://en.wikipedia.org/wiki/Sliding_window_protocol#Basic_concept +/// [3]: https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/description-tcp-features +pub(crate) struct SerializedSearchIndex { + pub(crate) index: String, + pub(crate) desc: Vec<(usize, String)>, +} + +const DESC_INDEX_SHARD_LEN: usize = 128 * 1024; + /// Builds the search index from the collected metadata pub(crate) fn build_index<'tcx>( krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<'tcx>, -) -> String { +) -> SerializedSearchIndex { let mut itemid_to_pathid = FxHashMap::default(); let mut primitives = FxHashMap::default(); let mut associated_types = FxHashMap::default(); @@ -319,7 +355,6 @@ pub(crate) fn build_index<'tcx>( .collect::>(); struct CrateData<'a> { - doc: String, items: Vec<&'a IndexItem>, paths: Vec<(ItemType, Vec)>, // The String is alias name and the vec is the list of the elements with this alias. @@ -328,6 +363,11 @@ pub(crate) fn build_index<'tcx>( aliases: &'a BTreeMap>, // Used when a type has more than one impl with an associated item with the same name. associated_item_disambiguators: &'a Vec<(usize, String)>, + // A list of shard lengths encoded as vlqhex. See the comment in write_vlqhex_to_string + // for information on the format. + desc_index: String, + // A list of items with no description. This is eventually turned into a bitmap. + empty_desc: Vec, } struct Paths { @@ -409,7 +449,6 @@ pub(crate) fn build_index<'tcx>( let mut names = Vec::with_capacity(self.items.len()); let mut types = String::with_capacity(self.items.len()); let mut full_paths = Vec::with_capacity(self.items.len()); - let mut descriptions = Vec::with_capacity(self.items.len()); let mut parents = Vec::with_capacity(self.items.len()); let mut functions = String::with_capacity(self.items.len()); let mut deprecated = Vec::with_capacity(self.items.len()); @@ -432,7 +471,6 @@ pub(crate) fn build_index<'tcx>( parents.push(item.parent_idx.map(|x| x + 1).unwrap_or(0)); names.push(item.name.as_str()); - descriptions.push(&item.desc); if !item.path.is_empty() { full_paths.push((index, &item.path)); @@ -444,7 +482,8 @@ pub(crate) fn build_index<'tcx>( } if item.deprecation.is_some() { - deprecated.push(index); + // bitmasks always use 1-indexing for items, with 0 as the crate itself + deprecated.push(u32::try_from(index + 1).unwrap()); } } @@ -455,17 +494,16 @@ pub(crate) fn build_index<'tcx>( let has_aliases = !self.aliases.is_empty(); let mut crate_data = serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?; - crate_data.serialize_field("doc", &self.doc)?; crate_data.serialize_field("t", &types)?; crate_data.serialize_field("n", &names)?; - // Serialize as an array of item indices and full paths crate_data.serialize_field("q", &full_paths)?; - crate_data.serialize_field("d", &descriptions)?; crate_data.serialize_field("i", &parents)?; crate_data.serialize_field("f", &functions)?; - crate_data.serialize_field("c", &deprecated)?; + crate_data.serialize_field("D", &self.desc_index)?; crate_data.serialize_field("p", &paths)?; crate_data.serialize_field("b", &self.associated_item_disambiguators)?; + crate_data.serialize_field("c", &bitmap_to_string(&deprecated))?; + crate_data.serialize_field("e", &bitmap_to_string(&self.empty_desc))?; if has_aliases { crate_data.serialize_field("a", &self.aliases)?; } @@ -473,16 +511,58 @@ pub(crate) fn build_index<'tcx>( } } - // Collect the index into a string - format!( + let (empty_desc, desc) = { + let mut empty_desc = Vec::new(); + let mut result = Vec::new(); + let mut set = String::new(); + let mut len: usize = 0; + let mut item_index: u32 = 0; + for desc in std::iter::once(&crate_doc).chain(crate_items.iter().map(|item| &item.desc)) { + if desc == "" { + empty_desc.push(item_index); + item_index += 1; + continue; + } + if set.len() >= DESC_INDEX_SHARD_LEN { + result.push((len, std::mem::replace(&mut set, String::new()))); + len = 0; + } else if len != 0 { + set.push('\n'); + } + set.push_str(&desc); + len += 1; + item_index += 1; + } + result.push((len, std::mem::replace(&mut set, String::new()))); + (empty_desc, result) + }; + + let desc_index = { + let mut desc_index = String::with_capacity(desc.len() * 4); + for &(len, _) in desc.iter() { + write_vlqhex_to_string(len.try_into().unwrap(), &mut desc_index); + } + desc_index + }; + + assert_eq!( + crate_items.len() + 1, + desc.iter().map(|(len, _)| *len).sum::() + empty_desc.len() + ); + + // The index, which is actually used to search, is JSON + // It uses `JSON.parse(..)` to actually load, since JSON + // parses faster than the full JavaScript syntax. + let index = format!( r#"["{}",{}]"#, krate.name(tcx), serde_json::to_string(&CrateData { - doc: crate_doc, items: crate_items, paths: crate_paths, aliases: &aliases, associated_item_disambiguators: &associated_item_disambiguators, + desc_index, + empty_desc, }) .expect("failed serde conversion") // All these `replace` calls are because we have to go through JS string for JSON content. @@ -490,7 +570,8 @@ pub(crate) fn build_index<'tcx>( .replace('\'', r"\'") // We need to escape double quotes for the JSON. .replace("\\\"", "\\\\\"") - ) + ); + SerializedSearchIndex { index, desc } } pub(crate) fn get_function_type_for_search<'tcx>( diff --git a/src/librustdoc/html/render/search_index/encode.rs b/src/librustdoc/html/render/search_index/encode.rs new file mode 100644 index 000000000000..54407c614c4c --- /dev/null +++ b/src/librustdoc/html/render/search_index/encode.rs @@ -0,0 +1,243 @@ +use base64::prelude::*; + +pub(crate) fn write_vlqhex_to_string(n: i32, string: &mut String) { + let (sign, magnitude): (bool, u32) = + if n >= 0 { (false, n.try_into().unwrap()) } else { (true, (-n).try_into().unwrap()) }; + // zig-zag encoding + let value: u32 = (magnitude << 1) | (if sign { 1 } else { 0 }); + // Self-terminating hex use capital letters for everything but the + // least significant digit, which is lowercase. For example, decimal 17 + // would be `` Aa `` if zig-zag encoding weren't used. + // + // Zig-zag encoding, however, stores the sign bit as the last bit. + // This means, in the last hexit, 1 is actually `c`, -1 is `b` + // (`a` is the imaginary -0), and, because all the bits are shifted + // by one, `` A` `` is actually 8 and `` Aa `` is -8. + // + // https://rust-lang.github.io/rustc-dev-guide/rustdoc-internals/search.html + // describes the encoding in more detail. + let mut shift: u32 = 28; + let mut mask: u32 = 0xF0_00_00_00; + // first skip leading zeroes + while shift < 32 { + let hexit = (value & mask) >> shift; + if hexit != 0 || shift == 0 { + break; + } + shift = shift.wrapping_sub(4); + mask = mask >> 4; + } + // now write the rest + while shift < 32 { + let hexit = (value & mask) >> shift; + let hex = char::try_from(if shift == 0 { '`' } else { '@' } as u32 + hexit).unwrap(); + string.push(hex); + shift = shift.wrapping_sub(4); + mask = mask >> 4; + } +} + +// Used during bitmap encoding +enum Container { + /// number of ones, bits + Bits(Box<[u64; 1024]>), + /// list of entries + Array(Vec), + /// list of (start, len-1) + Run(Vec<(u16, u16)>), +} +impl Container { + fn popcount(&self) -> u32 { + match self { + Container::Bits(bits) => bits.iter().copied().map(|x| x.count_ones()).sum(), + Container::Array(array) => { + array.len().try_into().expect("array can't be bigger than 2**32") + } + Container::Run(runs) => { + runs.iter().copied().map(|(_, lenm1)| u32::from(lenm1) + 1).sum() + } + } + } + fn push(&mut self, value: u16) { + match self { + Container::Bits(bits) => bits[value as usize >> 6] |= 1 << (value & 0x3F), + Container::Array(array) => { + array.push(value); + if array.len() >= 4096 { + let array = std::mem::replace(array, Vec::new()); + *self = Container::Bits(Box::new([0; 1024])); + for value in array { + self.push(value); + } + } + } + Container::Run(runs) => { + if let Some(r) = runs.last_mut() + && r.0 + r.1 + 1 == value + { + r.1 += 1; + } else { + runs.push((value, 0)); + } + } + } + } + fn try_make_run(&mut self) -> bool { + match self { + Container::Bits(bits) => { + let mut r: u64 = 0; + for (i, chunk) in bits.iter().copied().enumerate() { + let next_chunk = + i.checked_add(1).and_then(|i| bits.get(i)).copied().unwrap_or(0); + r += !chunk & u64::from((chunk << 1).count_ones()); + r += !next_chunk & u64::from((chunk >> 63).count_ones()); + } + if (2 + 4 * r) >= 8192 { + return false; + } + let bits = std::mem::replace(bits, Box::new([0; 1024])); + *self = Container::Run(Vec::new()); + for (i, bits) in bits.iter().copied().enumerate() { + if bits == 0 { + continue; + } + for j in 0..64 { + let value = (u16::try_from(i).unwrap() << 6) | j; + if bits & (1 << j) != 0 { + self.push(value); + } + } + } + true + } + Container::Array(array) if array.len() <= 5 => false, + Container::Array(array) => { + let mut r = 0; + let mut prev = None; + for value in array.iter().copied() { + if value.checked_sub(1) != prev { + r += 1; + } + prev = Some(value); + } + if 2 + 4 * r >= 2 * array.len() + 2 { + return false; + } + let array = std::mem::replace(array, Vec::new()); + *self = Container::Run(Vec::new()); + for value in array { + self.push(value); + } + true + } + Container::Run(_) => true, + } + } +} + +// checked against roaring-rs in +// https://gitlab.com/notriddle/roaring-test +pub(crate) fn write_bitmap_to_bytes( + domain: &[u32], + mut out: impl std::io::Write, +) -> std::io::Result<()> { + // https://arxiv.org/pdf/1603.06549.pdf + let mut keys = Vec::::new(); + let mut containers = Vec::::new(); + let mut key: u16; + let mut domain_iter = domain.into_iter().copied().peekable(); + let mut has_run = false; + while let Some(entry) = domain_iter.next() { + key = (entry >> 16).try_into().expect("shifted off the top 16 bits, so it should fit"); + let value: u16 = (entry & 0x00_00_FF_FF).try_into().expect("AND 16 bits, so it should fit"); + let mut container = Container::Array(vec![value]); + while let Some(entry) = domain_iter.peek().copied() { + let entry_key: u16 = + (entry >> 16).try_into().expect("shifted off the top 16 bits, so it should fit"); + if entry_key != key { + break; + } + domain_iter.next().expect("peeking just succeeded"); + container + .push((entry & 0x00_00_FF_FF).try_into().expect("AND 16 bits, so it should fit")); + } + keys.push(key); + has_run = container.try_make_run() || has_run; + containers.push(container); + } + // https://github.com/RoaringBitmap/RoaringFormatSpec + use byteorder::{WriteBytesExt, LE}; + const SERIAL_COOKIE_NO_RUNCONTAINER: u32 = 12346; + const SERIAL_COOKIE: u32 = 12347; + const NO_OFFSET_THRESHOLD: u32 = 4; + let size: u32 = containers.len().try_into().unwrap(); + let start_offset = if has_run { + out.write_u32::(SERIAL_COOKIE | ((size - 1) << 16))?; + for set in containers.chunks(8) { + let mut b = 0; + for (i, container) in set.iter().enumerate() { + if matches!(container, &Container::Run(..)) { + b |= 1 << i; + } + } + out.write_u8(b)?; + } + if size < NO_OFFSET_THRESHOLD { + 4 + 4 * size + ((size + 7) / 8) + } else { + 4 + 8 * size + ((size + 7) / 8) + } + } else { + out.write_u32::(SERIAL_COOKIE_NO_RUNCONTAINER)?; + out.write_u32::(containers.len().try_into().unwrap())?; + 4 + 4 + 4 * size + 4 * size + }; + for (&key, container) in keys.iter().zip(&containers) { + // descriptive header + let key: u32 = key.into(); + let count: u32 = container.popcount() - 1; + out.write_u32::((count << 16) | key)?; + } + if !has_run || size >= NO_OFFSET_THRESHOLD { + // offset header + let mut starting_offset = start_offset; + for container in &containers { + out.write_u32::(starting_offset)?; + starting_offset += match container { + Container::Bits(_) => 8192u32, + Container::Array(array) => u32::try_from(array.len()).unwrap() * 2, + Container::Run(runs) => 2 + u32::try_from(runs.len()).unwrap() * 4, + }; + } + } + for container in &containers { + match container { + Container::Bits(bits) => { + for chunk in bits.iter() { + out.write_u64::(*chunk)?; + } + } + Container::Array(array) => { + for value in array.iter() { + out.write_u16::(*value)?; + } + } + Container::Run(runs) => { + out.write_u16::((runs.len()).try_into().unwrap())?; + for (start, lenm1) in runs.iter().copied() { + out.write_u16::(start)?; + out.write_u16::(lenm1)?; + } + } + } + } + Ok(()) +} + +pub(crate) fn bitmap_to_string(domain: &[u32]) -> String { + let mut buf = Vec::new(); + let mut strbuf = String::new(); + write_bitmap_to_bytes(&domain, &mut buf).unwrap(); + BASE64_STANDARD.encode_string(&buf, &mut strbuf); + strbuf +} diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index fbd45b2b48ef..c806bf1cc66f 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -24,6 +24,7 @@ use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::formats::Impl; use crate::html::format::Buffer; +use crate::html::render::search_index::SerializedSearchIndex; use crate::html::render::{AssocItemLink, ImplRenderingParameters}; use crate::html::{layout, static_files}; use crate::visit::DocVisitor; @@ -46,7 +47,7 @@ use crate::{try_err, try_none}; pub(super) fn write_shared( cx: &mut Context<'_>, krate: &Crate, - search_index: String, + search_index: SerializedSearchIndex, options: &RenderOptions, ) -> Result<(), Error> { // Write out the shared files. Note that these are shared among all rustdoc @@ -312,7 +313,7 @@ pub(super) fn write_shared( let dst = cx.dst.join(&format!("search-index{}.js", cx.shared.resource_suffix)); let (mut all_indexes, mut krates) = try_err!(collect_json(&dst, krate.name(cx.tcx()).as_str()), &dst); - all_indexes.push(search_index); + all_indexes.push(search_index.index); krates.push(krate.name(cx.tcx()).to_string()); krates.sort(); @@ -335,6 +336,32 @@ else if (window.initSearch) window.initSearch(searchIndex); Ok(v.into_bytes()) })?; + let search_desc_dir = cx.dst.join(format!("search.desc/{krate}", krate = krate.name(cx.tcx()))); + if Path::new(&search_desc_dir).exists() { + try_err!(std::fs::remove_dir_all(&search_desc_dir), &search_desc_dir); + } + try_err!(std::fs::create_dir_all(&search_desc_dir), &search_desc_dir); + let kratename = krate.name(cx.tcx()).to_string(); + for (i, (_, data)) in search_index.desc.into_iter().enumerate() { + let output_filename = static_files::suffix_path( + &format!("{kratename}-desc-{i}-.js"), + &cx.shared.resource_suffix, + ); + let path = search_desc_dir.join(output_filename); + try_err!( + std::fs::write( + &path, + &format!( + r##"searchState.loadedDescShard({kratename}, {i}, {data})"##, + kratename = serde_json::to_string(&kratename).unwrap(), + data = serde_json::to_string(&data).unwrap(), + ) + .into_bytes() + ), + &path + ); + } + write_invocation_specific("crates.js", &|| { let krates = krates.iter().map(|k| format!("\"{k}\"")).join(","); Ok(format!("window.ALL_CRATES = [{krates}];").into_bytes()) diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js index 1a34530c2d16..a1e9cc6dfa14 100644 --- a/src/librustdoc/html/static/.eslintrc.js +++ b/src/librustdoc/html/static/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { }, "extends": "eslint:recommended", "parserOptions": { - "ecmaVersion": 2015, + "ecmaVersion": 8, "sourceType": "module" }, "rules": { diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index b9a769a7c6da..940b62be0c94 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -329,6 +329,30 @@ function preLoadCss(cssUrl) { search.innerHTML = "

" + searchState.loadingText + "

"; searchState.showResults(search); }, + descShards: new Map(), + loadDesc: async function({descShard, descIndex}) { + if (descShard.promise === null) { + descShard.promise = new Promise((resolve, reject) => { + // The `resolve` callback is stored in the `descShard` + // object, which is itself stored in `this.descShards` map. + // It is called in `loadedDescShard` by the + // search.desc script. + descShard.resolve = resolve; + const ds = descShard; + const fname = `${ds.crate}-desc-${ds.shard}-`; + const url = resourcePath( + `search.desc/${descShard.crate}/${fname}`, + ".js", + ); + loadScript(url, reject); + }); + } + const list = await descShard.promise; + return list[descIndex]; + }, + loadedDescShard: function(crate, shard, data) { + this.descShards.get(crate)[shard].resolve(data.split("\n")); + }, }; const toggleAllDocsId = "toggle-all-docs"; @@ -381,7 +405,7 @@ function preLoadCss(cssUrl) { window.location.replace("#" + item.id); }, 0); } - } + }, ); } } @@ -585,7 +609,7 @@ function preLoadCss(cssUrl) { const script = document .querySelector("script[data-ignore-extern-crates]"); const ignoreExternCrates = new Set( - (script ? script.getAttribute("data-ignore-extern-crates") : "").split(",") + (script ? script.getAttribute("data-ignore-extern-crates") : "").split(","), ); for (const lib of libs) { if (lib === window.currentCrate || ignoreExternCrates.has(lib)) { @@ -1098,7 +1122,7 @@ function preLoadCss(cssUrl) { } else { wrapper.style.setProperty( "--popover-arrow-offset", - (wrapperPos.right - pos.right + 4) + "px" + (wrapperPos.right - pos.right + 4) + "px", ); } wrapper.style.visibility = ""; @@ -1680,7 +1704,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm pendingSidebarResizingFrame = false; document.documentElement.style.setProperty( "--resizing-sidebar-width", - desiredSidebarSize + "px" + desiredSidebarSize + "px", ); }, 100); } diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 875ebe2fc90d..3daf1ad22ded 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -206,14 +206,14 @@ const editDistanceState = { // insertion this.current[j - 1] + 1, // substitution - this.prev[j - 1] + substitutionCost + this.prev[j - 1] + substitutionCost, ); if ((i > 1) && (j > 1) && (a[aIdx] === b[bIdx - 1]) && (a[aIdx - 1] === b[bIdx])) { // transposition this.current[j] = Math.min( this.current[j], - this.prevPrev[j - 2] + 1 + this.prevPrev[j - 2] + 1, ); } } @@ -242,6 +242,14 @@ function initSearch(rawSearchIndex) { * @type {Array} */ let searchIndex; + /** + * @type {Map} + */ + let searchIndexDeprecated; + /** + * @type {Map} + */ + let searchIndexEmptyDesc; /** * @type {Uint32Array} */ @@ -426,7 +434,7 @@ function initSearch(rawSearchIndex) { return c === "," || c === "="; } -/** + /** * Returns `true` if the given `c` character is a path separator. For example * `:` in `a::b` or a whitespace in `a b`. * @@ -856,8 +864,8 @@ function initSearch(rawSearchIndex) { parserState, parserState.userQuery.slice(start, end), generics, - isInGenerics - ) + isInGenerics, + ), ); } } @@ -1295,7 +1303,7 @@ function initSearch(rawSearchIndex) { * * @return {ResultsTable} */ - function execQuery(parsedQuery, filterCrates, currentCrate) { + async function execQuery(parsedQuery, filterCrates, currentCrate) { const results_others = new Map(), results_in_args = new Map(), results_returned = new Map(); @@ -1342,9 +1350,9 @@ function initSearch(rawSearchIndex) { * @param {Results} results * @param {boolean} isType * @param {string} preferredCrate - * @returns {[ResultObject]} + * @returns {Promise<[ResultObject]>} */ - function sortResults(results, isType, preferredCrate) { + async function sortResults(results, isType, preferredCrate) { const userQuery = parsedQuery.userQuery; const result_list = []; for (const result of results.values()) { @@ -1394,8 +1402,8 @@ function initSearch(rawSearchIndex) { } // sort deprecated items later - a = aaa.item.deprecated; - b = bbb.item.deprecated; + a = searchIndexDeprecated.get(aaa.item.crate).contains(aaa.item.bitIndex); + b = searchIndexDeprecated.get(bbb.item.crate).contains(bbb.item.bitIndex); if (a !== b) { return a - b; } @@ -1422,8 +1430,8 @@ function initSearch(rawSearchIndex) { } // sort by description (no description goes later) - a = (aaa.item.desc === ""); - b = (bbb.item.desc === ""); + a = searchIndexEmptyDesc.get(aaa.item.crate).contains(aaa.item.bitIndex); + b = searchIndexEmptyDesc.get(bbb.item.crate).contains(bbb.item.bitIndex); if (a !== b) { return a - b; } @@ -1446,7 +1454,16 @@ function initSearch(rawSearchIndex) { return 0; }); - return transformResults(result_list); + const transformed = transformResults(result_list); + const descs = await Promise.all(transformed.map(result => { + return searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ? + "" : + searchState.loadDesc(result); + })); + for (const [i, result] of transformed.entries()) { + result.desc = descs[i]; + } + return transformed; } /** @@ -1477,7 +1494,7 @@ function initSearch(rawSearchIndex) { whereClause, mgensIn, solutionCb, - unboxingDepth + unboxingDepth, ) { if (unboxingDepth >= UNBOXING_LIMIT) { return false; @@ -1524,7 +1541,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgens, - unboxingDepth + 1 + unboxingDepth + 1, )) { continue; } @@ -1541,7 +1558,7 @@ function initSearch(rawSearchIndex) { whereClause, mgensScratch, solutionCb, - unboxingDepth + 1 + unboxingDepth + 1, )) { return true; } @@ -1551,7 +1568,7 @@ function initSearch(rawSearchIndex) { whereClause, mgens ? new Map(mgens) : null, solutionCb, - unboxingDepth + 1 + unboxingDepth + 1, )) { return true; } @@ -1625,7 +1642,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgensScratch, - unboxingDepth + unboxingDepth, ); if (!solution) { return false; @@ -1638,7 +1655,7 @@ function initSearch(rawSearchIndex) { whereClause, simplifiedMgens, solutionCb, - unboxingDepth + unboxingDepth, ); if (passesUnification) { return true; @@ -1646,7 +1663,7 @@ function initSearch(rawSearchIndex) { } return false; }, - unboxingDepth + unboxingDepth, ); if (passesUnification) { return true; @@ -1663,7 +1680,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgens, - unboxingDepth + 1 + unboxingDepth + 1, )) { continue; } @@ -1689,7 +1706,7 @@ function initSearch(rawSearchIndex) { whereClause, mgensScratch, solutionCb, - unboxingDepth + 1 + unboxingDepth + 1, ); if (passesUnification) { return true; @@ -1820,7 +1837,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgensIn, - unboxingDepth + unboxingDepth, ) { if (fnType.bindings.size < queryElem.bindings.size) { return false; @@ -1849,7 +1866,7 @@ function initSearch(rawSearchIndex) { // possible solutions return false; }, - unboxingDepth + unboxingDepth, ); return newSolutions; }); @@ -1887,7 +1904,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgens, - unboxingDepth + unboxingDepth, ) { if (unboxingDepth >= UNBOXING_LIMIT) { return false; @@ -1914,7 +1931,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgensTmp, - unboxingDepth + unboxingDepth, ); } else if (fnType.generics.length > 0 || fnType.bindings.size > 0) { const simplifiedGenerics = [ @@ -1926,7 +1943,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgens, - unboxingDepth + unboxingDepth, ); } return false; @@ -1975,7 +1992,7 @@ function initSearch(rawSearchIndex) { elem, whereClause, mgens, - unboxingDepth + 1 + unboxingDepth + 1, ); } if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && @@ -1989,7 +2006,7 @@ function initSearch(rawSearchIndex) { elem, whereClause, mgens, - unboxingDepth + unboxingDepth, ); } } @@ -2007,7 +2024,7 @@ function initSearch(rawSearchIndex) { return 0; } const maxPathEditDistance = Math.floor( - contains.reduce((acc, next) => acc + next.length, 0) / 3 + contains.reduce((acc, next) => acc + next.length, 0) / 3, ); let ret_dist = maxPathEditDistance + 1; const path = ty.path.split("::"); @@ -2066,12 +2083,13 @@ function initSearch(rawSearchIndex) { crate: item.crate, name: item.name, path: item.path, - desc: item.desc, + descShard: item.descShard, + descIndex: item.descIndex, ty: item.ty, parent: item.parent, type: item.type, is_alias: true, - deprecated: item.deprecated, + bitIndex: item.bitIndex, implDisambiguator: item.implDisambiguator, }; } @@ -2192,7 +2210,7 @@ function initSearch(rawSearchIndex) { results_others, results_in_args, results_returned, - maxEditDistance + maxEditDistance, ) { if (!row || (filterCrates !== null && row.crate !== filterCrates)) { return; @@ -2204,7 +2222,7 @@ function initSearch(rawSearchIndex) { // atoms in the function not present in the query const tfpDist = compareTypeFingerprints( fullId, - parsedQuery.typeFingerprint + parsedQuery.typeFingerprint, ); if (tfpDist !== null) { const in_args = row.type && row.type.inputs @@ -2276,7 +2294,7 @@ function initSearch(rawSearchIndex) { const tfpDist = compareTypeFingerprints( row.id, - parsedQuery.typeFingerprint + parsedQuery.typeFingerprint, ); if (tfpDist === null) { return; @@ -2298,10 +2316,10 @@ function initSearch(rawSearchIndex) { row.type.where_clause, mgens, null, - 0 // unboxing depth + 0, // unboxing depth ); }, - 0 // unboxing depth + 0, // unboxing depth )) { return; } @@ -2419,7 +2437,7 @@ function initSearch(rawSearchIndex) { } return [typeNameIdMap.get(name).id, constraints]; - }) + }), ); } @@ -2446,7 +2464,7 @@ function initSearch(rawSearchIndex) { results_others, results_in_args, results_returned, - maxEditDistance + maxEditDistance, ); } } @@ -2477,10 +2495,15 @@ function initSearch(rawSearchIndex) { innerRunQuery(); } - const ret = createQueryResults( + const [sorted_in_args, sorted_returned, sorted_others] = await Promise.all([ sortResults(results_in_args, true, currentCrate), sortResults(results_returned, true, currentCrate), sortResults(results_others, false, currentCrate), + ]); + const ret = createQueryResults( + sorted_in_args, + sorted_returned, + sorted_others, parsedQuery); handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates, currentCrate); if (parsedQuery.error !== null && ret.others.length !== 0) { @@ -2581,14 +2604,14 @@ function initSearch(rawSearchIndex) { * @param {ParsedQuery} query * @param {boolean} display - True if this is the active tab */ - function addTab(array, query, display) { + async function addTab(array, query, display) { const extraClass = display ? " active" : ""; const output = document.createElement("div"); if (array.length > 0) { output.className = "search-results " + extraClass; - array.forEach(item => { + for (const item of array) { const name = item.name; const type = itemTypes[item.ty]; const longType = longItemTypes[item.ty]; @@ -2624,7 +2647,7 @@ ${item.displayPath}${name}\ link.appendChild(description); output.appendChild(link); - }); + } } else if (query.error === null) { output.className = "search-failed" + extraClass; output.innerHTML = "No results :(
" + @@ -2666,7 +2689,7 @@ ${item.displayPath}${name}\ * @param {boolean} go_to_first * @param {string} filterCrates */ - function showResults(results, go_to_first, filterCrates) { + async function showResults(results, go_to_first, filterCrates) { const search = searchState.outputElement(); if (go_to_first || (results.others.length === 1 && getSettingValue("go-to-only-result") === "true") @@ -2699,9 +2722,11 @@ ${item.displayPath}${name}\ currentResults = results.query.userQuery; - const ret_others = addTab(results.others, results.query, true); - const ret_in_args = addTab(results.in_args, results.query, false); - const ret_returned = addTab(results.returned, results.query, false); + const [ret_others, ret_in_args, ret_returned] = await Promise.all([ + addTab(results.others, results.query, true), + addTab(results.in_args, results.query, false), + addTab(results.returned, results.query, false), + ]); // Navigate to the relevant tab if the current tab is empty, like in case users search // for "-> String". If they had selected another tab previously, they have to click on @@ -2822,7 +2847,7 @@ ${item.displayPath}${name}\ * and display the results. * @param {boolean} [forced] */ - function search(forced) { + async function search(forced) { const query = parseQuery(searchState.input.value.trim()); let filterCrates = getFilterCrates(); @@ -2850,8 +2875,8 @@ ${item.displayPath}${name}\ // recent search query is added to the browser history. updateSearchHistory(buildUrl(query.original, filterCrates)); - showResults( - execQuery(query, filterCrates, window.currentCrate), + await showResults( + await execQuery(query, filterCrates, window.currentCrate), params.go_to_first, filterCrates); } @@ -2920,7 +2945,7 @@ ${item.displayPath}${name}\ pathIndex = type[PATH_INDEX_DATA]; generics = buildItemSearchTypeAll( type[GENERICS_DATA], - lowercasePaths + lowercasePaths, ); if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) { bindings = new Map(type[BINDINGS_DATA].map(binding => { @@ -3030,101 +3055,49 @@ ${item.displayPath}${name}\ * The raw function search type format is generated using serde in * librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string * - * @param {{ - * string: string, - * offset: number, - * backrefQueue: FunctionSearchType[] - * }} itemFunctionDecoder * @param {Array<{name: string, ty: number}>} lowercasePaths - * @param {Map} * * @return {null|FunctionSearchType} */ - function buildFunctionSearchType(itemFunctionDecoder, lowercasePaths) { - const c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - itemFunctionDecoder.offset += 1; - const [zero, ua, la, ob, cb] = ["0", "@", "`", "{", "}"].map(c => c.charCodeAt(0)); - // `` ` `` is used as a sentinel because it's fewer bytes than `null`, and decodes to zero - // `0` is a backref - if (c === la) { - return null; - } - // sixteen characters after "0" are backref - if (c >= zero && c < ua) { - return itemFunctionDecoder.backrefQueue[c - zero]; - } - if (c !== ob) { - throw ["Unexpected ", c, " in function: expected ", "{", "; this is a bug"]; - } - // call after consuming `{` - function decodeList() { - let c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - const ret = []; - while (c !== cb) { - ret.push(decode()); - c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); + function buildFunctionSearchTypeCallback(lowercasePaths) { + return functionSearchType => { + if (functionSearchType === 0) { + return null; } - itemFunctionDecoder.offset += 1; // eat cb - return ret; - } - // consumes and returns a list or integer - function decode() { - let n = 0; - let c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - if (c === ob) { - itemFunctionDecoder.offset += 1; - return decodeList(); - } - while (c < la) { - n = (n << 4) | (c & 0xF); - itemFunctionDecoder.offset += 1; - c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - } - // last character >= la - n = (n << 4) | (c & 0xF); - const [sign, value] = [n & 1, n >> 1]; - itemFunctionDecoder.offset += 1; - return sign ? -value : value; - } - const functionSearchType = decodeList(); - const INPUTS_DATA = 0; - const OUTPUT_DATA = 1; - let inputs, output; - if (typeof functionSearchType[INPUTS_DATA] === "number") { - inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)]; - } else { - inputs = buildItemSearchTypeAll( - functionSearchType[INPUTS_DATA], - lowercasePaths - ); - } - if (functionSearchType.length > 1) { - if (typeof functionSearchType[OUTPUT_DATA] === "number") { - output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)]; + const INPUTS_DATA = 0; + const OUTPUT_DATA = 1; + let inputs, output; + if (typeof functionSearchType[INPUTS_DATA] === "number") { + inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)]; } else { - output = buildItemSearchTypeAll( - functionSearchType[OUTPUT_DATA], - lowercasePaths + inputs = buildItemSearchTypeAll( + functionSearchType[INPUTS_DATA], + lowercasePaths, ); } - } else { - output = []; - } - const where_clause = []; - const l = functionSearchType.length; - for (let i = 2; i < l; ++i) { - where_clause.push(typeof functionSearchType[i] === "number" - ? [buildItemSearchType(functionSearchType[i], lowercasePaths)] - : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths)); - } - const ret = { - inputs, output, where_clause, + if (functionSearchType.length > 1) { + if (typeof functionSearchType[OUTPUT_DATA] === "number") { + output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)]; + } else { + output = buildItemSearchTypeAll( + functionSearchType[OUTPUT_DATA], + lowercasePaths, + ); + } + } else { + output = []; + } + const where_clause = []; + const l = functionSearchType.length; + for (let i = 2; i < l; ++i) { + where_clause.push(typeof functionSearchType[i] === "number" + ? [buildItemSearchType(functionSearchType[i], lowercasePaths)] + : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths)); + } + return { + inputs, output, where_clause, + }; }; - itemFunctionDecoder.backrefQueue.unshift(ret); - if (itemFunctionDecoder.backrefQueue.length > 16) { - itemFunctionDecoder.backrefQueue.pop(); - } - return ret; } /** @@ -3245,6 +3218,185 @@ ${item.displayPath}${name}\ return functionTypeFingerprint[(fullId * 4) + 3]; } + class VlqHexDecoder { + constructor(string, cons) { + this.string = string; + this.cons = cons; + this.offset = 0; + this.backrefQueue = []; + } + // call after consuming `{` + decodeList() { + const cb = "}".charCodeAt(0); + let c = this.string.charCodeAt(this.offset); + const ret = []; + while (c !== cb) { + ret.push(this.decode()); + c = this.string.charCodeAt(this.offset); + } + this.offset += 1; // eat cb + return ret; + } + // consumes and returns a list or integer + decode() { + const [ob, la] = ["{", "`"].map(c => c.charCodeAt(0)); + let n = 0; + let c = this.string.charCodeAt(this.offset); + if (c === ob) { + this.offset += 1; + return this.decodeList(); + } + while (c < la) { + n = (n << 4) | (c & 0xF); + this.offset += 1; + c = this.string.charCodeAt(this.offset); + } + // last character >= la + n = (n << 4) | (c & 0xF); + const [sign, value] = [n & 1, n >> 1]; + this.offset += 1; + return sign ? -value : value; + } + next() { + const c = this.string.charCodeAt(this.offset); + const [zero, ua, la] = ["0", "@", "`"].map(c => c.charCodeAt(0)); + // sixteen characters after "0" are backref + if (c >= zero && c < ua) { + this.offset += 1; + return this.backrefQueue[c - zero]; + } + // special exception: 0 doesn't use backref encoding + // it's already one character, and it's always nullish + if (c === la) { + this.offset += 1; + return this.cons(0); + } + const result = this.cons(this.decode()); + this.backrefQueue.unshift(result); + if (this.backrefQueue.length > 16) { + this.backrefQueue.pop(); + } + return result; + } + } + class RoaringBitmap { + constructor(str) { + const strdecoded = atob(str); + const u8array = new Uint8Array(strdecoded.length); + for (let j = 0; j < strdecoded.length; ++j) { + u8array[j] = strdecoded.charCodeAt(j); + } + const has_runs = u8array[0] === 0x3b; + const size = has_runs ? + ((u8array[2] | (u8array[3] << 8)) + 1) : + ((u8array[4] | (u8array[5] << 8) | (u8array[6] << 16) | (u8array[7] << 24))); + let i = has_runs ? 4 : 8; + let is_run; + if (has_runs) { + const is_run_len = Math.floor((size + 7) / 8); + is_run = u8array.slice(i, i + is_run_len); + i += is_run_len; + } else { + is_run = new Uint8Array(); + } + this.keys = []; + this.cardinalities = []; + for (let j = 0; j < size; ++j) { + this.keys.push(u8array[i] | (u8array[i + 1] << 8)); + i += 2; + this.cardinalities.push((u8array[i] | (u8array[i + 1] << 8)) + 1); + i += 2; + } + this.containers = []; + let offsets = null; + if (!has_runs || this.keys.length >= 4) { + offsets = []; + for (let j = 0; j < size; ++j) { + offsets.push(u8array[i] | (u8array[i + 1] << 8) | (u8array[i + 2] << 16) | + (u8array[i + 3] << 24)); + i += 4; + } + } + for (let j = 0; j < size; ++j) { + if (offsets && offsets[j] !== i) { + console.log(this.containers); + throw new Error(`corrupt bitmap ${j}: ${i} / ${offsets[j]}`); + } + if (is_run[j >> 3] & (1 << (j & 0x7))) { + const runcount = (u8array[i] | (u8array[i + 1] << 8)); + i += 2; + this.containers.push(new RoaringBitmapRun( + runcount, + u8array.slice(i, i + (runcount * 4)), + )); + i += runcount * 4; + } else if (this.cardinalities[j] >= 4096) { + this.containers.push(new RoaringBitmapBits(u8array.slice(i, i + 8192))); + i += 8192; + } else { + const end = this.cardinalities[j] * 2; + this.containers.push(new RoaringBitmapArray( + this.cardinalities[j], + u8array.slice(i, i + end), + )); + i += end; + } + } + } + contains(keyvalue) { + const key = keyvalue >> 16; + const value = keyvalue & 0xFFFF; + for (let i = 0; i < this.keys.length; ++i) { + if (this.keys[i] === key) { + return this.containers[i].contains(value); + } + } + return false; + } + } + + class RoaringBitmapRun { + constructor(runcount, array) { + this.runcount = runcount; + this.array = array; + } + contains(value) { + const l = this.runcount * 4; + for (let i = 0; i < l; i += 4) { + const start = this.array[i] | (this.array[i + 1] << 8); + const lenm1 = this.array[i + 2] | (this.array[i + 3] << 8); + if (value >= start && value <= (start + lenm1)) { + return true; + } + } + return false; + } + } + class RoaringBitmapArray { + constructor(cardinality, array) { + this.cardinality = cardinality; + this.array = array; + } + contains(value) { + const l = this.cardinality * 2; + for (let i = 0; i < l; i += 2) { + const start = this.array[i] | (this.array[i + 1] << 8); + if (value === start) { + return true; + } + } + return false; + } + } + class RoaringBitmapBits { + constructor(array) { + this.array = array; + } + contains(value) { + return !!(this.array[value >> 3] & (1 << (value & 7))); + } + } + /** * Convert raw search index into in-memory search index. * @@ -3252,6 +3404,8 @@ ${item.displayPath}${name}\ */ function buildIndex(rawSearchIndex) { searchIndex = []; + searchIndexDeprecated = new Map(); + searchIndexEmptyDesc = new Map(); const charA = "A".charCodeAt(0); let currentIndex = 0; let id = 0; @@ -3271,26 +3425,48 @@ ${item.displayPath}${name}\ id = 0; for (const [crate, crateCorpus] of rawSearchIndex) { + // a string representing the lengths of each description shard + // a string representing the list of function types + const itemDescShardDecoder = new VlqHexDecoder(crateCorpus.D, noop => noop); + let descShard = { + crate, + shard: 0, + start: 0, + len: itemDescShardDecoder.next(), + promise: null, + resolve: null, + }; + const descShardList = [ descShard ]; + + // Deprecated items and items with no description + searchIndexDeprecated.set(crate, new RoaringBitmap(crateCorpus.c)); + searchIndexEmptyDesc.set(crate, new RoaringBitmap(crateCorpus.e)); + let descIndex = 0; + // This object should have exactly the same set of fields as the "row" // object defined below. Your JavaScript runtime will thank you. // https://mathiasbynens.be/notes/shapes-ics const crateRow = { - crate: crate, + crate, ty: 3, // == ExternCrate name: crate, path: "", - desc: crateCorpus.doc, + descShard, + descIndex, parent: undefined, type: null, - id: id, + id, word: crate, normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""), - deprecated: null, + bitIndex: 0, implDisambiguator: null, }; id += 1; searchIndex.push(crateRow); currentIndex += 1; + if (!searchIndexEmptyDesc.get(crate).contains(0)) { + descIndex += 1; + } // a String of one character item type codes const itemTypes = crateCorpus.t; @@ -3302,19 +3478,9 @@ ${item.displayPath}${name}\ // i.e. if indices 4 and 11 are present, but 5-10 and 12-13 are not present, // 5-10 will fall back to the path for 4 and 12-13 will fall back to the path for 11 const itemPaths = new Map(crateCorpus.q); - // an array of (String) descriptions - const itemDescs = crateCorpus.d; // an array of (Number) the parent path index + 1 to `paths`, or 0 if none const itemParentIdxs = crateCorpus.i; - // a string representing the list of function types - const itemFunctionDecoder = { - string: crateCorpus.f, - offset: 0, - backrefQueue: [], - }; - // an array of (Number) indices for the deprecated items - const deprecatedItems = new Set(crateCorpus.c); - // an array of (Number) indices for the deprecated items + // a map Number, string for impl disambiguators const implDisambiguator = new Map(crateCorpus.b); // an array of [(Number) item type, // (String) name] @@ -3326,6 +3492,12 @@ ${item.displayPath}${name}\ // an array of [{name: String, ty: Number}] const lowercasePaths = []; + // a string representing the list of function types + const itemFunctionDecoder = new VlqHexDecoder( + crateCorpus.f, + buildFunctionSearchTypeCallback(lowercasePaths), + ); + // convert `rawPaths` entries into object form // generate normalizedPaths for function search mode let len = paths.length; @@ -3354,12 +3526,26 @@ ${item.displayPath}${name}\ lastPath = ""; len = itemTypes.length; for (let i = 0; i < len; ++i) { + const bitIndex = i + 1; + if (descIndex >= descShard.len && + !searchIndexEmptyDesc.get(crate).contains(bitIndex)) { + descShard = { + crate, + shard: descShard.shard + 1, + start: descShard.start + descShard.len, + len: itemDescShardDecoder.next(), + promise: null, + resolve: null, + }; + descIndex = 0; + descShardList.push(descShard); + } let word = ""; if (typeof itemNames[i] === "string") { word = itemNames[i].toLowerCase(); } const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath; - const type = buildFunctionSearchType(itemFunctionDecoder, lowercasePaths); + const type = itemFunctionDecoder.next(); if (type !== null) { if (type) { const fp = functionTypeFingerprint.subarray(id * 4, (id + 1) * 4); @@ -3380,22 +3566,26 @@ ${item.displayPath}${name}\ // This object should have exactly the same set of fields as the "crateRow" // object defined above. const row = { - crate: crate, + crate, ty: itemTypes.charCodeAt(i) - charA, name: itemNames[i], - path: path, - desc: itemDescs[i], + path, + descShard, + descIndex, parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined, type, - id: id, + id, word, normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""), - deprecated: deprecatedItems.has(i), + bitIndex, implDisambiguator: implDisambiguator.has(i) ? implDisambiguator.get(i) : null, }; id += 1; searchIndex.push(row); lastPath = row.path; + if (!searchIndexEmptyDesc.get(crate).contains(bitIndex)) { + descIndex += 1; + } } if (aliases) { @@ -3419,6 +3609,7 @@ ${item.displayPath}${name}\ } } currentIndex += itemTypes.length; + searchState.descShards.set(crate, descShardList); } // Drop the (rather large) hash table used for reusing function items TYPES_POOL = new Map(); diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index bda7b3c647e7..73c543567c07 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -211,14 +211,14 @@ function updateSidebarWidth() { if (desktopSidebarWidth && desktopSidebarWidth !== "null") { document.documentElement.style.setProperty( "--desktop-sidebar-width", - desktopSidebarWidth + "px" + desktopSidebarWidth + "px", ); } const srcSidebarWidth = getSettingValue("src-sidebar-width"); if (srcSidebarWidth && srcSidebarWidth !== "null") { document.documentElement.style.setProperty( "--src-sidebar-width", - srcSidebarWidth + "px" + srcSidebarWidth + "px", ); } } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index cabebf89becc..12b080059970 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -112,7 +112,7 @@ fn check_rvalue<'tcx>( Rvalue::Repeat(operand, _) | Rvalue::Use(operand) | Rvalue::Cast( - CastKind::PointerFromExposedAddress + CastKind::PointerWithExposedProvenance | CastKind::IntToInt | CastKind::FloatToInt | CastKind::IntToFloat diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 26e55b897080..11b777808330 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -324,7 +324,7 @@ environment variable. We first document the most relevant and most commonly used number of available CPUs is `1`. Note that this flag does not affect how miri handles threads in any way. * `-Zmiri-permissive-provenance` disables the warning for integer-to-pointer casts and - [`ptr::from_exposed_addr`](https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html). + [`ptr::with_exposed_provenance`](https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html). This will necessarily miss some bugs as those operations are not efficiently and accurately implementable in a sanitizer, but it will only miss bugs that concern memory/pointers which is subject to these operations. diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index b61bff2716c0..694720ab21f2 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -89,8 +89,12 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { let verbose = num_arg_flag("-v"); // Determine the involved architectures. - let rustc_version = VersionMeta::for_command(miri_for_host()) - .expect("failed to determine underlying rustc version of Miri"); + let rustc_version = VersionMeta::for_command(miri_for_host()).unwrap_or_else(|err| { + panic!( + "failed to determine underlying rustc version of Miri ({:?}):\n{err:?}", + miri_for_host() + ) + }); let host = &rustc_version.host; let target = get_arg_flag_value("--target"); let target = target.as_ref().unwrap_or(host); @@ -222,7 +226,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { } // Run cargo. - debug_cmd("[cargo-miri miri]", verbose, &cmd); + debug_cmd("[cargo-miri cargo]", verbose, &cmd); exec(cmd) } diff --git a/src/tools/miri/cargo-miri/src/setup.rs b/src/tools/miri/cargo-miri/src/setup.rs index a98e1fcd485e..401e9158faec 100644 --- a/src/tools/miri/cargo-miri/src/setup.rs +++ b/src/tools/miri/cargo-miri/src/setup.rs @@ -90,13 +90,13 @@ pub fn setup( let cargo_cmd = { let mut command = cargo(); // Use Miri as rustc to build a libstd compatible with us (and use the right flags). + // We set ourselves (`cargo-miri`) instead of Miri directly to be able to patch the flags + // for `libpanic_abort` (usually this is done by bootstrap but we have to do it ourselves). + // The `MIRI_CALLED_FROM_SETUP` will mean we dispatch to `phase_setup_rustc`. // However, when we are running in bootstrap, we cannot just overwrite `RUSTC`, // because we still need bootstrap to distinguish between host and target crates. // In that case we overwrite `RUSTC_REAL` instead which determines the rustc used // for target crates. - // We set ourselves (`cargo-miri`) instead of Miri directly to be able to patch the flags - // for `libpanic_abort` (usually this is done by bootstrap but we have to do it ourselves). - // The `MIRI_CALLED_FROM_SETUP` will mean we dispatch to `phase_setup_rustc`. let cargo_miri_path = std::env::current_exe().expect("current executable path invalid"); if env::var_os("RUSTC_STAGE").is_some() { assert!(env::var_os("RUSTC").is_some()); diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index e1714aa9e46b..6b8e1510a6bf 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -18,12 +18,12 @@ use reuse_pool::ReusePool; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ProvenanceMode { - /// We support `expose_addr`/`from_exposed_addr` via "wildcard" provenance. - /// However, we want on `from_exposed_addr` to alert the user of the precision loss. + /// We support `expose_addr`/`with_exposed_provenance` via "wildcard" provenance. + /// However, we want on `with_exposed_provenance` to alert the user of the precision loss. Default, /// Like `Default`, but without the warning. Permissive, - /// We error on `from_exposed_addr`, ensuring no precision loss. + /// We error on `with_exposed_provenance`, ensuring no precision loss. Strict, } diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 99d37065bace..30349c003a94 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -66,7 +66,7 @@ impl fmt::Display for TerminationInfo { Int2PtrWithStrictProvenance => write!( f, - "integer-to-pointer casts and `ptr::from_exposed_addr` are not supported with `-Zmiri-strict-provenance`" + "integer-to-pointer casts and `ptr::with_exposed_provenance` are not supported with `-Zmiri-strict-provenance`" ), StackedBorrowsUb { msg, .. } => write!(f, "{msg}"), TreeBorrowsUb { title, .. } => write!(f, "{title}"), @@ -593,7 +593,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { ( None, format!( - "This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`," + "This program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`," ), ), ( @@ -603,7 +603,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { ( None, format!( - "See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation." + "See https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation." ), ), ( @@ -615,7 +615,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { ( None, format!( - "You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics." + "You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `with_exposed_provenance` semantics." ), ), ( diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs index 6973c0e9c357..9d268f09edb5 100644 --- a/src/tools/miri/src/shims/intrinsics/simd.rs +++ b/src/tools/miri/src/shims/intrinsics/simd.rs @@ -514,7 +514,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { dest.transmute(this.machine.layouts.uint(dest.layout.size).unwrap(), this)?; this.write_int(res, &dest)?; } - "cast" | "as" | "cast_ptr" | "expose_addr" | "from_exposed_addr" => { + "cast" | "as" | "cast_ptr" | "expose_addr" | "with_exposed_provenance" => { let [op] = check_arg_count(args)?; let (op, op_len) = this.operand_to_simd(op)?; let (dest, dest_len) = this.mplace_to_simd(dest)?; @@ -525,7 +525,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let safe_cast = intrinsic_name == "as"; let ptr_cast = intrinsic_name == "cast_ptr"; let expose_cast = intrinsic_name == "expose_addr"; - let from_exposed_cast = intrinsic_name == "from_exposed_addr"; + let from_exposed_cast = intrinsic_name == "with_exposed_provenance"; for i in 0..dest_len { let op = this.read_immediate(&this.project_index(&op, i)?)?; @@ -559,7 +559,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { (ty::RawPtr(..), ty::Int(_) | ty::Uint(_)) if expose_cast => this.pointer_expose_address_cast(&op, dest.layout)?, (ty::Int(_) | ty::Uint(_), ty::RawPtr(..)) if from_exposed_cast => - this.pointer_from_exposed_address_cast(&op, dest.layout)?, + this.pointer_with_exposed_provenance_cast(&op, dest.layout)?, // Error otherwise _ => throw_unsup_format!( diff --git a/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs b/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs index 20fd33069989..f89378fcb3c4 100644 --- a/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs +++ b/src/tools/miri/tests/fail/provenance/ptr_int_unexposed.rs @@ -7,6 +7,6 @@ fn main() { let x_usize: usize = x_ptr.addr(); // Cast back an address that did *not* get exposed. - let ptr = std::ptr::from_exposed_addr::(x_usize); + let ptr = std::ptr::with_exposed_provenance::(x_usize); assert_eq!(unsafe { *ptr }, 3); //~ ERROR: is a dangling pointer } diff --git a/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs b/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs index 106cf4d804b4..d7b54f640f65 100644 --- a/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs +++ b/src/tools/miri/tests/fail/provenance/strict_provenance_cast.rs @@ -3,5 +3,5 @@ fn main() { let addr = &0 as *const i32 as usize; - let _ptr = std::ptr::from_exposed_addr::(addr); //~ ERROR: integer-to-pointer casts and `ptr::from_exposed_addr` are not supported + let _ptr = std::ptr::with_exposed_provenance::(addr); //~ ERROR: integer-to-pointer casts and `ptr::with_exposed_provenance` are not supported } diff --git a/src/tools/miri/tests/fail/provenance/strict_provenance_cast.stderr b/src/tools/miri/tests/fail/provenance/strict_provenance_cast.stderr index a110ed4ebb21..8c61b66ac469 100644 --- a/src/tools/miri/tests/fail/provenance/strict_provenance_cast.stderr +++ b/src/tools/miri/tests/fail/provenance/strict_provenance_cast.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: integer-to-pointer casts and `ptr::from_exposed_addr` are not supported with `-Zmiri-strict-provenance` +error: unsupported operation: integer-to-pointer casts and `ptr::with_exposed_provenance` are not supported with `-Zmiri-strict-provenance` --> $DIR/strict_provenance_cast.rs:LL:CC | -LL | let _ptr = std::ptr::from_exposed_addr::(addr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer casts and `ptr::from_exposed_addr` are not supported with `-Zmiri-strict-provenance` +LL | let _ptr = std::ptr::with_exposed_provenance::(addr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer casts and `ptr::with_exposed_provenance` are not supported with `-Zmiri-strict-provenance` | = help: use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead = note: BACKTRACE: diff --git a/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs b/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs index b0e4cceb98f3..aa05649d550c 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs +++ b/src/tools/miri/tests/fail/stacked_borrows/exposed_only_ro.rs @@ -7,6 +7,6 @@ fn main() { let mut x = 0; let _fool = &mut x as *mut i32; // this would have fooled the old untagged pointer logic let addr = (&x as *const i32).expose_addr(); - let ptr = std::ptr::from_exposed_addr_mut::(addr); + let ptr = std::ptr::with_exposed_provenance_mut::(addr); unsafe { *ptr = 0 }; //~ ERROR: /write access using .* no exposed tags have suitable permission in the borrow stack/ } diff --git a/src/tools/miri/tests/pass/box.stack.stderr b/src/tools/miri/tests/pass/box.stack.stderr index f6e208cea9a8..1a4d52ee3146 100644 --- a/src/tools/miri/tests/pass/box.stack.stderr +++ b/src/tools/miri/tests/pass/box.stack.stderr @@ -4,11 +4,11 @@ warning: integer-to-pointer cast LL | let r2 = ((r as usize) + 0) as *mut i32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast | - = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`, + = help: This program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, = help: which means that Miri might miss pointer bugs in this program. - = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation. + = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation. = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead. - = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics. + = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `with_exposed_provenance` semantics. = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning. = note: BACKTRACE: = note: inside `into_raw` at $DIR/box.rs:LL:CC diff --git a/src/tools/miri/tests/pass/extern_types.stack.stderr b/src/tools/miri/tests/pass/extern_types.stack.stderr index 2e18f6930589..275d718129b4 100644 --- a/src/tools/miri/tests/pass/extern_types.stack.stderr +++ b/src/tools/miri/tests/pass/extern_types.stack.stderr @@ -4,11 +4,11 @@ warning: integer-to-pointer cast LL | let x: &Foo = unsafe { &*(16 as *const Foo) }; | ^^^^^^^^^^^^^^^^^^ integer-to-pointer cast | - = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`, + = help: This program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, = help: which means that Miri might miss pointer bugs in this program. - = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation. + = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation. = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead. - = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics. + = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `with_exposed_provenance` semantics. = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning. = note: BACKTRACE: = note: inside `main` at $DIR/extern_types.rs:LL:CC diff --git a/src/tools/miri/tests/pass/portable-simd-ptrs.rs b/src/tools/miri/tests/pass/portable-simd-ptrs.rs index 70ba5636c600..3b2d221bd8ea 100644 --- a/src/tools/miri/tests/pass/portable-simd-ptrs.rs +++ b/src/tools/miri/tests/pass/portable-simd-ptrs.rs @@ -8,5 +8,5 @@ fn main() { // Pointer casts let _val: Simd<*const u8, 4> = Simd::<*const i32, 4>::splat(ptr::null()).cast(); let addrs = Simd::<*const i32, 4>::splat(ptr::null()).expose_addr(); - let _ptrs = Simd::<*const i32, 4>::from_exposed_addr(addrs); + let _ptrs = Simd::<*const i32, 4>::with_exposed_provenance(addrs); } diff --git a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs index d8d57679e6b3..8555de986f06 100644 --- a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs +++ b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs @@ -12,7 +12,7 @@ fn ptr_roundtrip_out_of_bounds() { let x_usize = x_ptr.wrapping_offset(128).expose_addr(); - let ptr = ptr::from_exposed_addr::(x_usize).wrapping_offset(-128); + let ptr = ptr::with_exposed_provenance::(x_usize).wrapping_offset(-128); assert_eq!(unsafe { *ptr }, 3); } @@ -27,7 +27,7 @@ fn ptr_roundtrip_confusion() { let x_usize = x_ptr.expose_addr(); let y_usize = y_ptr.expose_addr(); - let ptr = ptr::from_exposed_addr::(y_usize); + let ptr = ptr::with_exposed_provenance::(y_usize); let ptr = ptr.with_addr(x_usize); assert_eq!(unsafe { *ptr }, 0); } @@ -39,7 +39,7 @@ fn ptr_roundtrip_imperfect() { let x_usize = x_ptr.expose_addr() + 128; - let ptr = ptr::from_exposed_addr::(x_usize).wrapping_offset(-128); + let ptr = ptr::with_exposed_provenance::(x_usize).wrapping_offset(-128); assert_eq!(unsafe { *ptr }, 3); } @@ -51,7 +51,7 @@ fn ptr_roundtrip_null() { let null = x_null_ptr.expose_addr(); assert_eq!(null, 0); - let x_null_ptr_copy = ptr::from_exposed_addr::(null); // just a roundtrip, so has provenance of x (angelically) + let x_null_ptr_copy = ptr::with_exposed_provenance::(null); // just a roundtrip, so has provenance of x (angelically) let x_ptr_copy = x_null_ptr_copy.with_addr(x_ptr.addr()); // addr of x and provenance of x assert_eq!(unsafe { *x_ptr_copy }, 42); } diff --git a/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs b/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs index e467356dd04b..5622bf186545 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs +++ b/src/tools/miri/tests/pass/stacked-borrows/int-to-ptr.rs @@ -39,7 +39,7 @@ fn example(variant: bool) { // 4 is the "obvious" choice (topmost tag, what we used to do with untagged pointers). // And indeed if `variant == true` it is the only possible choice. // But if `variant == false` then 2 is the only possible choice! - let x_wildcard = ptr::from_exposed_addr_mut::(x_raw2_addr); + let x_wildcard = ptr::with_exposed_provenance_mut::(x_raw2_addr); if variant { // If we picked 2, this will invalidate 3. diff --git a/src/tools/miri/tests/pass/stacked-borrows/issue-miri-2389.stderr b/src/tools/miri/tests/pass/stacked-borrows/issue-miri-2389.stderr index f3ba052ae513..7cbfad3942b3 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/issue-miri-2389.stderr +++ b/src/tools/miri/tests/pass/stacked-borrows/issue-miri-2389.stderr @@ -4,11 +4,11 @@ warning: integer-to-pointer cast LL | let wildcard = &root0 as *const Cell as usize as *const Cell; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast | - = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`, + = help: This program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, = help: which means that Miri might miss pointer bugs in this program. - = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation. + = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation. = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead. - = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics. + = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `with_exposed_provenance` semantics. = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning. = note: BACKTRACE: = note: inside `main` at $DIR/issue-miri-2389.rs:LL:CC diff --git a/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs b/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs index 5bb4e879c3e4..6e177a6e4abb 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs +++ b/src/tools/miri/tests/pass/stacked-borrows/unknown-bottom-gc.rs @@ -9,7 +9,7 @@ fn main() { // Expose the allocation and use the exposed pointer, creating an unknown bottom unsafe { - let p: *mut u8 = ptr::from_exposed_addr::(ptr.expose_addr()) as *mut u8; + let p: *mut u8 = ptr::with_exposed_provenance::(ptr.expose_addr()) as *mut u8; *p = 1; } diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 7975677286d3..48fa2bbf1ac4 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -19,7 +19,11 @@ pub fn tmp_dir() -> PathBuf { } fn handle_failed_output(cmd: &str, output: Output, caller_line_number: u32) -> ! { - eprintln!("command failed at line {caller_line_number}"); + if output.status.success() { + eprintln!("command incorrectly succeeded at line {caller_line_number}"); + } else { + eprintln!("command failed at line {caller_line_number}"); + } eprintln!("{cmd}"); eprintln!("output status: `{}`", output.status); eprintln!("=== STDOUT ===\n{}\n\n", String::from_utf8(output.stdout).unwrap()); diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs index d0ab8df42d28..50ff0d26bbb8 100644 --- a/src/tools/run-make-support/src/rustc.rs +++ b/src/tools/run-make-support/src/rustc.rs @@ -1,4 +1,5 @@ use std::env; +use std::ffi::OsStr; use std::path::Path; use std::process::{Command, Output}; @@ -104,6 +105,20 @@ impl Rustc { self } + /// Specify the crate type. + pub fn crate_type(&mut self, crate_type: &str) -> &mut Self { + self.cmd.arg("--crate-type"); + self.cmd.arg(crate_type); + self + } + + /// Specify the edition year. + pub fn edition(&mut self, edition: &str) -> &mut Self { + self.cmd.arg("--edition"); + self.cmd.arg(edition); + self + } + /// Generic command arguments provider. Use `.arg("-Zname")` over `.arg("-Z").arg("arg")`. /// This method will panic if a plain `-Z` or `-C` is passed, or if `-Z ` or `-C ` /// is passed (note the space). @@ -119,6 +134,11 @@ impl Rustc { self } + pub fn env(&mut self, name: impl AsRef, value: impl AsRef) -> &mut Self { + self.cmd.env(name, value); + self + } + // Command inspection, output and running helper methods /// Get the [`Output`][std::process::Output] of the finished `rustc` process. @@ -139,6 +159,18 @@ impl Rustc { output } + #[track_caller] + pub fn run_fail(&mut self) -> Output { + let caller_location = std::panic::Location::caller(); + let caller_line_number = caller_location.line(); + + let output = self.cmd.output().unwrap(); + if output.status.success() { + handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number); + } + output + } + /// Inspect what the underlying [`Command`] is up to the current construction. pub fn inspect(&mut self, f: impl FnOnce(&Command)) -> &mut Self { f(&self.cmd); diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 2d8946520d5e..08ad10c29718 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -71,7 +71,7 @@ jobs: run: echo "::add-matcher::.github/rust.json" - name: Cache Dependencies - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 + uses: Swatinem/rust-cache@640a22190e7a783d4c409684cea558f081f92012 with: key: ${{ env.RUST_CHANNEL }} @@ -140,7 +140,7 @@ jobs: rustup target add ${{ env.targets }} ${{ env.targets_ide }} - name: Cache Dependencies - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 + uses: Swatinem/rust-cache@640a22190e7a783d4c409684cea558f081f92012 - name: Check run: | diff --git a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml index 862373ec1cce..34ca53e2e53a 100644 --- a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml +++ b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml @@ -32,4 +32,5 @@ jobs: git config --global user.name "GitHub Action" # Remove r-a crates from the workspaces so we don't auto-publish them as well sed -i 's/ "crates\/\*"//' ./Cargo.toml + sed -i 's/ "xtask\/"//' ./Cargo.toml cargo workspaces publish --yes --exact --from-git --no-git-commit --allow-dirty diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml index dc0a6c2d91f4..11014338d72f 100644 --- a/src/tools/rust-analyzer/.github/workflows/release.yaml +++ b/src/tools/rust-analyzer/.github/workflows/release.yaml @@ -36,6 +36,7 @@ jobs: - os: ubuntu-20.04 target: x86_64-unknown-linux-gnu code-target: linux-x64 + container: rockylinux:8 - os: ubuntu-20.04 target: aarch64-unknown-linux-gnu code-target: linux-arm64 @@ -58,10 +59,18 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: ${{ env.FETCH_DEPTH }} + - name: Install toolchain dependencies + if: matrix.container == 'rockylinux:8' + shell: bash + run: | + dnf install -y gcc + curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- --profile minimal --default-toolchain none -y + echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH + - name: Install Rust toolchain run: | rustup update --no-self-update stable @@ -69,9 +78,9 @@ jobs: rustup component add rust-src - name: Install Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: 16 + node-version: 18 - name: Update apt repositories if: matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'arm-unknown-linux-gnueabihf' @@ -181,7 +190,7 @@ jobs: - name: Install Nodejs uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 - run: echo "TAG=$(date --iso -u)" >> $GITHUB_ENV if: github.ref == 'refs/heads/release' diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 68ed32391b7f..c7cf4479b330 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -594,6 +594,7 @@ dependencies = [ "rustc-hash", "scoped-tls", "smallvec", + "span", "stdx", "syntax", "test-fixture", @@ -637,6 +638,7 @@ dependencies = [ "pulldown-cmark", "pulldown-cmark-to-cmark", "smallvec", + "span", "stdx", "syntax", "test-fixture", @@ -732,6 +734,7 @@ dependencies = [ "ide-db", "itertools", "once_cell", + "paths", "serde_json", "stdx", "syntax", @@ -931,6 +934,7 @@ dependencies = [ "hir-expand", "ide-db", "itertools", + "paths", "proc-macro-api", "project-model", "span", @@ -1225,6 +1229,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "paths" version = "0.0.0" +dependencies = [ + "camino", +] [[package]] name = "percent-encoding" @@ -1375,6 +1382,7 @@ dependencies = [ "semver", "serde", "serde_json", + "span", "stdx", "toolchain", "tracing", @@ -1432,9 +1440,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ae52e2d5b08762c9464b541345f519b8719d57b643b73632bade43ecece9dc" +checksum = "b8709df2a746f055316bc0c62bd30948695a25e734863bf6e1f9755403e010ab" dependencies = [ "bitflags 2.4.2", "ra-ap-rustc_index", @@ -1443,9 +1451,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd7e10c7853fe79443d46e1d2d8ab09fe99926118e59653fb8b480d5045f126" +checksum = "9ad68bacffb87dcdbb23a3ce11261375078aaa06b85d348c49f39ffd5510dc20" dependencies = [ "arrayvec", "ra-ap-rustc_index_macros", @@ -1454,9 +1462,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47f1d1c589be6c9a9e852fadee0e60329c0f862e87442ac2fe5adae30663cc76" +checksum = "8782aaf3a113837c533dfb1c45df91cd17e1fdd1d2f9a20c2e0d1976025c4f1f" dependencies = [ "proc-macro2", "quote", @@ -1466,9 +1474,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa852373a757b4c723bbdc96ced7f575cad68a1e266e45fee12bc4c69a482d80" +checksum = "aab683fc8579d09eb72033bd5dc9ba6d701aa9645b5fed087ef19af71184dff3" dependencies = [ "unicode-properties", "unicode-xid", @@ -1476,9 +1484,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2afe3c49accd95a53ac4d72ae13bafc7d115bdd80c8cd56ab09e6fc68f482210" +checksum = "0bcf9ff5edbf784b67b8ad5e03a068f1300fcc24062c0d476b3018965135d933" dependencies = [ "ra-ap-rustc_index", "ra-ap-rustc_lexer", @@ -1486,9 +1494,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1253da23515d80c377a3998731e0ec3794997b62b989fd47db73efbde6a0bd7c" +checksum = "d63d1e1d5b2a13273cee1a10011147418f40e12b70f70578ce1dee0f1cafc334" dependencies = [ "ra-ap-rustc_index", "rustc-hash", @@ -1598,6 +1606,7 @@ dependencies = [ "oorandom", "parking_lot", "parser", + "paths", "proc-macro-api", "profile", "project-model", @@ -1869,20 +1878,16 @@ dependencies = [ "itertools", "once_cell", "parser", - "proc-macro2", - "quote", "ra-ap-rustc_lexer", "rayon", "rowan", "rustc-hash", "smol_str", - "sourcegen", "stdx", "test-utils", "text-edit", "tracing", "triomphe", - "ungrammar", ] [[package]] @@ -2024,6 +2029,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" name = "toolchain" version = "0.0.0" dependencies = [ + "camino", "home", ] @@ -2109,7 +2115,6 @@ name = "tt" version = "0.0.0" dependencies = [ "smol_str", - "span", "stdx", "text-size", ] @@ -2438,8 +2443,12 @@ version = "0.1.0" dependencies = [ "anyhow", "flate2", + "itertools", + "proc-macro2", + "quote", "stdx", "time", + "ungrammar", "write-json", "xflags", "xshell", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 0679522efd66..d9343d2b9632 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -84,11 +84,11 @@ tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.42.0", default-features = false } -ra-ap-rustc_parse_format = { version = "0.42.0", default-features = false } -ra-ap-rustc_index = { version = "0.42.0", default-features = false } -ra-ap-rustc_abi = { version = "0.42.0", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.42.0", default-features = false } +ra-ap-rustc_lexer = { version = "0.44.0", default-features = false } +ra-ap-rustc_parse_format = { version = "0.44.0", default-features = false } +ra-ap-rustc_index = { version = "0.44.0", default-features = false } +ra-ap-rustc_abi = { version = "0.44.0", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.44.0", default-features = false } # local crates that aren't published to crates.io. These should not have versions. sourcegen = { path = "./crates/sourcegen" } @@ -105,6 +105,7 @@ anyhow = "1.0.75" arrayvec = "0.7.4" bitflags = "2.4.1" cargo_metadata = "0.18.1" +camino = "1.1.6" chalk-solve = { version = "0.96.0", default-features = false } chalk-ir = "0.96.0" chalk-recursive = { version = "0.96.0", default-features = false } diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index b243b37b77b9..27eb05cd4dc5 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -6,11 +6,12 @@ //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how //! actual IO is done and lowered to input. -use std::{fmt, mem, ops, str::FromStr}; +use std::{fmt, mem, ops}; use cfg::CfgOptions; use la_arena::{Arena, Idx, RawIdx}; use rustc_hash::{FxHashMap, FxHashSet}; +use span::Edition; use syntax::SmolStr; use triomphe::Arc; use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath}; @@ -293,42 +294,11 @@ pub struct CrateData { pub is_proc_macro: bool, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Edition { - Edition2015, - Edition2018, - Edition2021, - Edition2024, -} - -impl Edition { - pub const CURRENT: Edition = Edition::Edition2021; - pub const DEFAULT: Edition = Edition::Edition2015; -} - #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct Env { entries: FxHashMap, } -impl Env { - pub fn new_for_test_fixture() -> Self { - Env { - entries: FxHashMap::from_iter([( - String::from("__ra_is_test_fixture"), - String::from("__ra_is_test_fixture"), - )]), - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum DependencyKind { - Normal, - Dev, - Build, -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Dependency { pub crate_id: CrateId, @@ -530,13 +500,6 @@ impl CrateGraph { } } - // FIXME: this only finds one crate with the given root; we could have multiple - pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option { - let (crate_id, _) = - self.arena.iter().find(|(_crate_id, data)| data.root_file_id == file_id)?; - Some(crate_id) - } - pub fn sort_deps(&mut self) { self.arena .iter_mut() @@ -653,6 +616,10 @@ impl CrateGraph { } id_map } + + pub fn shrink_to_fit(&mut self) { + self.arena.shrink_to_fit(); + } } impl ops::Index for CrateGraph { @@ -670,32 +637,6 @@ impl CrateData { } } -impl FromStr for Edition { - type Err = ParseEditionError; - - fn from_str(s: &str) -> Result { - let res = match s { - "2015" => Edition::Edition2015, - "2018" => Edition::Edition2018, - "2021" => Edition::Edition2021, - "2024" => Edition::Edition2024, - _ => return Err(ParseEditionError { invalid_input: s.to_owned() }), - }; - Ok(res) - } -} - -impl fmt::Display for Edition { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match self { - Edition::Edition2015 => "2015", - Edition::Edition2018 => "2018", - Edition::Edition2021 => "2021", - Edition::Edition2024 => "2024", - }) - } -} - impl Extend<(String, String)> for Env { fn extend>(&mut self, iter: T) { self.entries.extend(iter); @@ -722,19 +663,6 @@ impl Env { } } -#[derive(Debug)] -pub struct ParseEditionError { - invalid_input: String, -} - -impl fmt::Display for ParseEditionError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "invalid edition: {:?}", self.invalid_input) - } -} - -impl std::error::Error for ParseEditionError {} - #[derive(Debug)] pub struct CyclicDependenciesError { path: Vec<(CrateId, Option)>, diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index 5dcb580723fa..785ff9ceffad 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -14,9 +14,9 @@ use triomphe::Arc; pub use crate::{ change::FileChange, input::{ - CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, - DependencyKind, Edition, Env, LangCrateOrigin, ProcMacroPaths, ReleaseChannel, SourceRoot, - SourceRootId, TargetLayoutLoadResult, + CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env, + LangCrateOrigin, ProcMacroPaths, ReleaseChannel, SourceRoot, SourceRootId, + TargetLayoutLoadResult, }, }; pub use salsa::{self, Cancelled}; diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs index f8efb5202220..4ee86954acd9 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs +++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs @@ -8,10 +8,10 @@ #![warn(rust_2018_idioms, unused_lifetimes)] -use std::{fmt, io, path::PathBuf, process::Command, time::Duration}; +use std::{fmt, io, process::Command, time::Duration}; use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::Deserialize; @@ -53,7 +53,7 @@ pub enum FlycheckConfig { extra_args: Vec, extra_env: FxHashMap, ansi_color_output: bool, - target_dir: Option, + target_dir: Option, }, CustomCommand { command: String, @@ -363,7 +363,7 @@ impl FlycheckActor { }); cmd.arg("--manifest-path"); - cmd.arg(self.root.join("Cargo.toml").as_os_str()); + cmd.arg(self.root.join("Cargo.toml")); for target in target_triples { cmd.args(["--target", target.as_str()]); diff --git a/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs b/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs index 31378716b3ec..9f761c9ead18 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs @@ -55,13 +55,16 @@ pub struct CargoTestHandle { } // Example of a cargo test command: -// cargo test -- module::func -Z unstable-options --format=json +// cargo test --workspace --no-fail-fast -- module::func -Z unstable-options --format=json impl CargoTestHandle { pub fn new(path: Option<&str>) -> std::io::Result { let mut cmd = Command::new(Tool::Cargo.path()); cmd.env("RUSTC_BOOTSTRAP", "1"); cmd.arg("test"); + cmd.arg("--workspace"); + // --no-fail-fast is needed to ensure that all requested tests will run + cmd.arg("--no-fail-fast"); cmd.arg("--"); if let Some(path) = path { cmd.arg(path); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index 21536098b82d..fa7730f302ec 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -148,12 +148,12 @@ impl Attrs { } } - pub fn lang(&self) -> Option<&SmolStr> { + pub fn lang(&self) -> Option<&str> { self.by_key("lang").string_value() } pub fn lang_item(&self) -> Option { - self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it)) + self.by_key("lang").string_value().and_then(LangItem::from_str) } pub fn has_doc_hidden(&self) -> bool { @@ -178,7 +178,7 @@ impl Attrs { self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec()) } - pub fn export_name(&self) -> Option<&SmolStr> { + pub fn export_name(&self) -> Option<&str> { self.by_key("export_name").string_value() } @@ -565,7 +565,7 @@ impl<'attr> AttrQuery<'attr> { self.attrs().filter_map(|attr| attr.token_tree_value()) } - pub fn string_value(self) -> Option<&'attr SmolStr> { + pub fn string_value(self) -> Option<&'attr str> { self.attrs().find_map(|attr| attr.string_value()) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index b815c9b73ef8..da790f11516d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -453,8 +453,8 @@ impl ProcMacroData { ( def.name, match def.kind { - ProcMacroKind::CustomDerive { helpers } => Some(helpers), - ProcMacroKind::FnLike | ProcMacroKind::Attr => None, + ProcMacroKind::Derive { helpers } => Some(helpers), + ProcMacroKind::Bang | ProcMacroKind::Attr => None, }, ) } else { @@ -484,10 +484,11 @@ impl ExternCrateDeclData { let extern_crate = &item_tree[loc.id.value]; let name = extern_crate.name.clone(); + let krate = loc.container.krate(); let crate_id = if name == hir_expand::name![self] { - Some(loc.container.krate()) + Some(krate) } else { - db.crate_def_map(loc.container.krate()) + db.crate_def_map(krate) .extern_prelude() .find(|&(prelude_name, ..)| *prelude_name == name) .map(|(_, (root, _))| root.krate()) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs index 1d2c7c3a55fd..4638b3771973 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs @@ -22,8 +22,8 @@ use crate::{ lower::LowerCtx, nameres::{DefMap, MacroSubNs}, type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef}, - AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LocalTypeOrConstParamId, Lookup, - TypeOrConstParamId, TypeParamId, + AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LifetimeParamId, + LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId, }; /// Data about a generic type parameter (to a function, struct, impl, ...). @@ -102,6 +102,52 @@ impl TypeOrConstParamData { impl_from!(TypeParamData, ConstParamData for TypeOrConstParamData); +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub enum GenericParamData { + TypeParamData(TypeParamData), + ConstParamData(ConstParamData), + LifetimeParamData(LifetimeParamData), +} + +impl GenericParamData { + pub fn name(&self) -> Option<&Name> { + match self { + GenericParamData::TypeParamData(it) => it.name.as_ref(), + GenericParamData::ConstParamData(it) => Some(&it.name), + GenericParamData::LifetimeParamData(it) => Some(&it.name), + } + } + + pub fn type_param(&self) -> Option<&TypeParamData> { + match self { + GenericParamData::TypeParamData(it) => Some(it), + _ => None, + } + } + + pub fn const_param(&self) -> Option<&ConstParamData> { + match self { + GenericParamData::ConstParamData(it) => Some(it), + _ => None, + } + } + + pub fn lifetime_param(&self) -> Option<&LifetimeParamData> { + match self { + GenericParamData::LifetimeParamData(it) => Some(it), + _ => None, + } + } +} + +impl_from!(TypeParamData, ConstParamData, LifetimeParamData for GenericParamData); + +pub enum GenericParamDataRef<'a> { + TypeParamData(&'a TypeParamData), + ConstParamData(&'a ConstParamData), + LifetimeParamData(&'a LifetimeParamData), +} + /// Data about the generic parameters of a function, struct, impl, etc. #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct GenericParams { @@ -358,6 +404,15 @@ impl GenericParamsCollector { } impl GenericParams { + /// Number of Generic parameters (type_or_consts + lifetimes) + pub fn len(&self) -> usize { + self.type_or_consts.len() + self.lifetimes.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + /// Iterator of type_or_consts field pub fn iter( &self, @@ -365,6 +420,13 @@ impl GenericParams { self.type_or_consts.iter() } + /// Iterator of lifetimes field + pub fn iter_lt( + &self, + ) -> impl DoubleEndedIterator, &LifetimeParamData)> { + self.lifetimes.iter() + } + pub(crate) fn generic_params_query( db: &dyn DefDatabase, def: GenericDefId, @@ -507,4 +569,18 @@ impl GenericParams { .then(|| id) }) } + + pub fn find_lifetime_by_name( + &self, + name: &Name, + parent: GenericDefId, + ) -> Option { + self.lifetimes.iter().find_map(|(id, p)| { + if &p.name == name { + Some(LifetimeParamId { local_id: id, parent }) + } else { + None + } + }) + } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs index 953bf6b85d64..0c84057950bd 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs @@ -526,7 +526,7 @@ impl Printer<'_> { } fn print_generic_params(&mut self, params: &GenericParams) { - if params.type_or_consts.is_empty() && params.lifetimes.is_empty() { + if params.is_empty() { return; } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 7d98f6cfe88c..3a07c678428e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -192,7 +192,7 @@ impl LangItems { pub(crate) fn lang_attr(db: &dyn DefDatabase, item: AttrDefId) -> Option { let attrs = db.attrs(item); - attrs.by_key("lang").string_value().and_then(|it| LangItem::from_str(it)) + attrs.by_key("lang").string_value().and_then(LangItem::from_str) } pub(crate) fn notable_traits_in_deps( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 828842de7e8a..46898ce542d6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -73,7 +73,7 @@ use std::{ use base_db::{ impl_intern_key, salsa::{self, impl_intern_value_trivial}, - CrateId, Edition, + CrateId, }; use hir_expand::{ builtin_attr_macro::BuiltinAttrExpander, @@ -90,7 +90,7 @@ use hir_expand::{ use item_tree::ExternBlock; use la_arena::Idx; use nameres::DefMap; -use span::{AstIdNode, FileAstId, FileId, SyntaxContextId}; +use span::{AstIdNode, Edition, FileAstId, FileId, SyntaxContextId}; use stdx::impl_from; use syntax::{ast, AstNode}; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs index 965f329acb9e..c5c26e26bc0e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -1449,6 +1449,7 @@ ok!(); #[test] fn test_new_std_matches() { check( + //- edition:2021 r#" macro_rules! matches { ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => { @@ -1480,6 +1481,90 @@ fn main() { ); } +#[test] +fn test_hygienic_pat() { + check( + r#" +//- /new.rs crate:new deps:old edition:2015 +old::make!(); +fn main() { + matches!(0, 0 | 1 if true); +} +//- /old.rs crate:old edition:2021 +#[macro_export] +macro_rules! make { + () => { + macro_rules! matches { + ($expression:expr, $pattern:pat if $guard:expr ) => { + match $expression { + $pattern if $guard => true, + _ => false + } + }; + } + } +} + "#, + expect![[r#" +macro_rules !matches { + ($expression: expr, $pattern: pat if $guard: expr) = > { + match $expression { + $pattern if $guard = > true , _ = > false + } + } + ; +} +fn main() { + match 0 { + 0|1 if true =>true , _=>false + }; +} +"#]], + ); + check( + r#" +//- /new.rs crate:new deps:old edition:2021 +old::make!(); +fn main() { + matches/*+errors*/!(0, 0 | 1 if true); +} +//- /old.rs crate:old edition:2015 +#[macro_export] +macro_rules! make { + () => { + macro_rules! matches { + ($expression:expr, $pattern:pat if $guard:expr ) => { + match $expression { + $pattern if $guard => true, + _ => false + } + }; + } + } +} + "#, + expect![[r#" +macro_rules !matches { + ($expression: expr, $pattern: pat if $guard: expr) = > { + match $expression { + $pattern if $guard = > true , _ = > false + } + } + ; +} +fn main() { + /* error: unexpected token in input *//* parse error: expected expression */ +/* parse error: expected FAT_ARROW */ +/* parse error: expected `,` */ +/* parse error: expected pattern */ +match 0 { + 0 if $guard=>true , _=>false + }; +} +"#]], + ); +} + #[test] fn test_dollar_crate_lhs_is_not_meta() { check( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index b56dee3efb55..a528c4cc6972 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -59,14 +59,14 @@ mod tests; use std::ops::Deref; -use base_db::{CrateId, Edition, FileId}; +use base_db::{CrateId, FileId}; use hir_expand::{ name::Name, proc_macro::ProcMacroKind, ErasedAstId, HirFileId, InFile, MacroCallId, MacroDefId, }; use itertools::Itertools; use la_arena::Arena; use rustc_hash::{FxHashMap, FxHashSet}; -use span::{FileAstId, ROOT_ERASED_FILE_AST_ID}; +use span::{Edition, FileAstId, ROOT_ERASED_FILE_AST_ID}; use stdx::format_to; use syntax::{ast, SmolStr}; use triomphe::Arc; @@ -737,7 +737,7 @@ impl MacroSubNs { MacroId::ProcMacroId(it) => { return match it.lookup(db).kind { ProcMacroKind::CustomDerive | ProcMacroKind::Attr => Self::Attr, - ProcMacroKind::FuncLike => Self::Bang, + ProcMacroKind::Bang => Self::Bang, }; } }; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs index 662c80edf324..eb7f4c05ae21 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs @@ -136,6 +136,7 @@ pub(super) fn derive_macro_as_call_id( call_site: SyntaxContextId, krate: CrateId, resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>, + derive_macro_id: MacroCallId, ) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> { let (macro_id, def_id) = resolver(item_attr.path.clone()) .filter(|(_, def_id)| def_id.is_derive()) @@ -147,6 +148,7 @@ pub(super) fn derive_macro_as_call_id( ast_id: item_attr.ast_id, derive_index: derive_pos, derive_attr_index, + derive_macro_id, }, call_site, ); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 3d026447fb74..ae8f028e488d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -5,7 +5,7 @@ use std::{cmp::Ordering, iter, mem, ops::Not}; -use base_db::{CrateId, Dependency, Edition, FileId}; +use base_db::{CrateId, Dependency, FileId}; use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_expand::{ @@ -22,9 +22,9 @@ use itertools::{izip, Itertools}; use la_arena::Idx; use limit::Limit; use rustc_hash::{FxHashMap, FxHashSet}; -use span::{ErasedFileAstId, FileAstId, Span, SyntaxContextId}; +use span::{Edition, ErasedFileAstId, FileAstId, Span, SyntaxContextId}; use stdx::always; -use syntax::{ast, SmolStr}; +use syntax::ast; use triomphe::Arc; use crate::{ @@ -237,6 +237,8 @@ enum MacroDirectiveKind { derive_attr: AttrId, derive_pos: usize, ctxt: SyntaxContextId, + /// The "parent" macro it is resolved to. + derive_macro_id: MacroCallId, }, Attr { ast_id: AstIdWithPath, @@ -312,7 +314,7 @@ impl DefCollector<'_> { } } () if *attr_name == hir_expand::name![crate_type] => { - if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) { + if let Some("proc-macro") = attr.string_value() { self.is_proc_macro = true; } } @@ -602,7 +604,7 @@ impl DefCollector<'_> { .intern(self.db); self.define_proc_macro(def.name.clone(), proc_macro_id); let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap(); - if let ProcMacroKind::CustomDerive { helpers } = def.kind { + if let ProcMacroKind::Derive { helpers } = def.kind { crate_data.exported_derives.insert(self.db.macro_def(proc_macro_id.into()), helpers); } crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id); @@ -1146,7 +1148,13 @@ impl DefCollector<'_> { return Resolved::Yes; } } - MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: call_site } => { + MacroDirectiveKind::Derive { + ast_id, + derive_attr, + derive_pos, + ctxt: call_site, + derive_macro_id, + } => { let id = derive_macro_as_call_id( self.db, ast_id, @@ -1155,6 +1163,7 @@ impl DefCollector<'_> { *call_site, self.def_map.krate, resolver, + *derive_macro_id, ); if let Ok((macro_id, def_id, call_id)) = id { @@ -1224,6 +1233,8 @@ impl DefCollector<'_> { _ => return Resolved::No, }; + let call_id = + attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def); if let MacroDefId { kind: MacroDefKind::BuiltInAttr( @@ -1252,6 +1263,7 @@ impl DefCollector<'_> { return recollect_without(self); } }; + let ast_id = ast_id.with_value(ast_adt_id); match attr.parse_path_comma_token_tree(self.db.upcast()) { @@ -1267,6 +1279,7 @@ impl DefCollector<'_> { derive_attr: attr.id, derive_pos: idx, ctxt: call_site.ctx, + derive_macro_id: call_id, }, container: directive.container, }); @@ -1301,10 +1314,6 @@ impl DefCollector<'_> { return recollect_without(self); } - // Not resolved to a derive helper or the derive attribute, so try to treat as a normal attribute. - let call_id = - attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def); - // Skip #[test]/#[bench] expansion, which would merely result in more memory usage // due to duplicating functions into macro expansions if matches!( @@ -1460,13 +1469,20 @@ impl DefCollector<'_> { )); } } - MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: _ } => { + MacroDirectiveKind::Derive { + ast_id, + derive_attr, + derive_pos, + derive_macro_id, + .. + } => { self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( directive.module_id, MacroCallKind::Derive { ast_id: ast_id.ast_id, derive_attr_index: *derive_attr, derive_index: *derive_pos as u32, + derive_macro_id: *derive_macro_id, }, ast_id.path.clone(), )); @@ -1902,7 +1918,7 @@ impl ModCollector<'_, '_> { } fn collect_module(&mut self, module_id: FileItemTreeId, attrs: &Attrs) { - let path_attr = attrs.by_key("path").string_value().map(SmolStr::as_str); + let path_attr = attrs.by_key("path").string_value(); let is_macro_use = attrs.by_key("macro_use").exists(); let module = &self.item_tree[module_id]; match &module.kind { @@ -2146,7 +2162,7 @@ impl ModCollector<'_, '_> { Some(it) => { // FIXME: a hacky way to create a Name from string. name = tt::Ident { - text: it.clone(), + text: it.into(), span: Span { range: syntax::TextRange::empty(syntax::TextSize::new(0)), anchor: span::SpanAnchor { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index 9e53b0372830..ee29b89f3d3b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -10,8 +10,8 @@ //! //! `ReachedFixedPoint` signals about this. -use base_db::Edition; use hir_expand::{name::Name, Lookup}; +use span::Edition; use triomphe::Arc; use crate::{ diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs index c126fdac1c62..5052708dc939 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs @@ -13,18 +13,16 @@ pub struct ProcMacroDef { #[derive(Debug, PartialEq, Eq)] pub enum ProcMacroKind { - CustomDerive { helpers: Box<[Name]> }, - FnLike, + Derive { helpers: Box<[Name]> }, + Bang, Attr, } impl ProcMacroKind { pub(super) fn to_basedb_kind(&self) -> hir_expand::proc_macro::ProcMacroKind { match self { - ProcMacroKind::CustomDerive { .. } => { - hir_expand::proc_macro::ProcMacroKind::CustomDerive - } - ProcMacroKind::FnLike => hir_expand::proc_macro::ProcMacroKind::FuncLike, + ProcMacroKind::Derive { .. } => hir_expand::proc_macro::ProcMacroKind::CustomDerive, + ProcMacroKind::Bang => hir_expand::proc_macro::ProcMacroKind::Bang, ProcMacroKind::Attr => hir_expand::proc_macro::ProcMacroKind::Attr, } } @@ -34,13 +32,13 @@ impl Attrs { #[rustfmt::skip] pub fn parse_proc_macro_decl(&self, func_name: &Name) -> Option { if self.is_proc_macro() { - Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::FnLike }) + Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Bang }) } else if self.is_proc_macro_attribute() { Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr }) } else if self.by_key("proc_macro_derive").exists() { let derive = self.by_key("proc_macro_derive").tt_values().next()?; let def = parse_macro_name_and_helper_attrs(&derive.token_trees) - .map(|(name, helpers)| ProcMacroDef { name, kind: ProcMacroKind::CustomDerive { helpers } }); + .map(|(name, helpers)| ProcMacroDef { name, kind: ProcMacroKind::Derive { helpers } }); if def.is_none() { tracing::trace!("malformed `#[proc_macro_derive]`: {}", derive); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 226d6f513f5d..fadab858aa14 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -24,6 +24,7 @@ use crate::{ nameres::{DefMap, MacroSubNs}, path::{ModPath, Path, PathKind}, per_ns::PerNs, + type_ref::LifetimeRef, visibility::{RawVisibility, Visibility}, AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, @@ -120,6 +121,12 @@ pub enum ValueNs { GenericParam(ConstParamId), } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum LifetimeNs { + Static, + LifetimeParam(LifetimeParamId), +} + impl Resolver { /// Resolve known trait from std, like `std::futures::Future` pub fn resolve_known_trait(&self, db: &dyn DefDatabase, path: &ModPath) -> Option { @@ -418,6 +425,19 @@ impl Resolver { self.resolve_path_as_macro(db, path, expected_macro_kind).map(|(it, _)| db.macro_def(it)) } + pub fn resolve_lifetime(&self, lifetime: &LifetimeRef) -> Option { + if lifetime.name == name::known::STATIC_LIFETIME { + return Some(LifetimeNs::Static); + } + + self.scopes().find_map(|scope| match scope { + Scope::GenericParams { def, params } => { + params.find_lifetime_by_name(&lifetime.name, *def).map(LifetimeNs::LifetimeParam) + } + _ => None, + }) + } + /// Returns a set of names available in the current scope. /// /// Note that this is a somewhat fuzzy concept -- internally, the compiler diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index af3ecdcd5e32..f1540498f266 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -8,8 +8,8 @@ use intern::Interned; use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct}; use smallvec::{smallvec, SmallVec}; use span::{Span, SyntaxContextId}; -use syntax::{ast, match_ast, AstNode, AstToken, SmolStr, SyntaxNode}; -use triomphe::Arc; +use syntax::{ast, format_smolstr, match_ast, AstNode, AstToken, SmolStr, SyntaxNode}; +use triomphe::ThinArc; use crate::{ db::ExpandDatabase, @@ -22,8 +22,7 @@ use crate::{ /// Syntactical attributes, without filtering of `cfg_attr`s. #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct RawAttrs { - // FIXME: Make this a ThinArc - entries: Option>, + entries: Option>, } impl ops::Deref for RawAttrs { @@ -31,7 +30,7 @@ impl ops::Deref for RawAttrs { fn deref(&self) -> &[Attr] { match &self.entries { - Some(it) => it, + Some(it) => &it.slice, None => &[], } } @@ -45,20 +44,34 @@ impl RawAttrs { owner: &dyn ast::HasAttrs, span_map: SpanMapRef<'_>, ) -> Self { - let entries = collect_attrs(owner).filter_map(|(id, attr)| match attr { - Either::Left(attr) => { - attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id)) - } - Either::Right(comment) => comment.doc_comment().map(|doc| Attr { - id, - input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))), - path: Interned::new(ModPath::from(crate::name!(doc))), - ctxt: span_map.span_for_range(comment.syntax().text_range()).ctx, - }), - }); - let entries: Arc<[Attr]> = Arc::from_iter(entries); + let entries: Vec<_> = collect_attrs(owner) + .filter_map(|(id, attr)| match attr { + Either::Left(attr) => { + attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id)) + } + Either::Right(comment) => comment.doc_comment().map(|doc| { + let span = span_map.span_for_range(comment.syntax().text_range()); + Attr { + id, + input: Some(Interned::new(AttrInput::Literal(tt::Literal { + // FIXME: Escape quotes from comment content + text: SmolStr::new(format_smolstr!("\"{doc}\"",)), + span, + }))), + path: Interned::new(ModPath::from(crate::name!(doc))), + ctxt: span.ctx, + } + }), + }) + .collect(); - Self { entries: if entries.is_empty() { None } else { Some(entries) } } + let entries = if entries.is_empty() { + None + } else { + Some(ThinArc::from_header_and_iter((), entries.into_iter())) + }; + + RawAttrs { entries } } pub fn from_attrs_owner( @@ -75,16 +88,20 @@ impl RawAttrs { (None, entries @ Some(_)) => Self { entries }, (Some(entries), None) => Self { entries: Some(entries.clone()) }, (Some(a), Some(b)) => { - let last_ast_index = a.last().map_or(0, |it| it.id.ast_index() + 1) as u32; - Self { - entries: Some(Arc::from_iter(a.iter().cloned().chain(b.iter().map(|it| { + let last_ast_index = a.slice.last().map_or(0, |it| it.id.ast_index() + 1) as u32; + let items = a + .slice + .iter() + .cloned() + .chain(b.slice.iter().map(|it| { let mut it = it.clone(); it.id.id = (it.id.ast_index() as u32 + last_ast_index) | (it.id.cfg_attr_index().unwrap_or(0) as u32) << AttrId::AST_INDEX_BITS; it - })))), - } + })) + .collect::>(); + Self { entries: Some(ThinArc::from_header_and_iter((), items.into_iter())) } } } } @@ -100,41 +117,47 @@ impl RawAttrs { } let crate_graph = db.crate_graph(); - let new_attrs = Arc::from_iter(self.iter().flat_map(|attr| -> SmallVec<[_; 1]> { - let is_cfg_attr = - attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]); - if !is_cfg_attr { - return smallvec![attr.clone()]; - } + let new_attrs = + self.iter() + .flat_map(|attr| -> SmallVec<[_; 1]> { + let is_cfg_attr = + attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]); + if !is_cfg_attr { + return smallvec![attr.clone()]; + } - let subtree = match attr.token_tree_value() { - Some(it) => it, - _ => return smallvec![attr.clone()], - }; + let subtree = match attr.token_tree_value() { + Some(it) => it, + _ => return smallvec![attr.clone()], + }; - let (cfg, parts) = match parse_cfg_attr_input(subtree) { - Some(it) => it, - None => return smallvec![attr.clone()], - }; - let index = attr.id; - let attrs = parts - .enumerate() - .take(1 << AttrId::CFG_ATTR_BITS) - .filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx))); + let (cfg, parts) = match parse_cfg_attr_input(subtree) { + Some(it) => it, + None => return smallvec![attr.clone()], + }; + let index = attr.id; + let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map( + |(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)), + ); - let cfg_options = &crate_graph[krate].cfg_options; - let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) }; - let cfg = CfgExpr::parse(&cfg); - if cfg_options.check(&cfg) == Some(false) { - smallvec![] - } else { - cov_mark::hit!(cfg_attr_active); + let cfg_options = &crate_graph[krate].cfg_options; + let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) }; + let cfg = CfgExpr::parse(&cfg); + if cfg_options.check(&cfg) == Some(false) { + smallvec![] + } else { + cov_mark::hit!(cfg_attr_active); - attrs.collect() - } - })); - - RawAttrs { entries: Some(new_attrs) } + attrs.collect() + } + }) + .collect::>(); + let entries = if new_attrs.is_empty() { + None + } else { + Some(ThinArc::from_header_and_iter((), new_attrs.into_iter())) + }; + RawAttrs { entries } } } @@ -179,8 +202,7 @@ pub struct Attr { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum AttrInput { /// `#[attr = "string"]` - // FIXME: This is losing span - Literal(SmolStr), + Literal(tt::Literal), /// `#[attr(subtree)]` TokenTree(Box), } @@ -188,7 +210,7 @@ pub enum AttrInput { impl fmt::Display for AttrInput { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - AttrInput::Literal(lit) => write!(f, " = \"{}\"", lit.escape_debug()), + AttrInput::Literal(lit) => write!(f, " = {lit}"), AttrInput::TokenTree(tt) => tt.fmt(f), } } @@ -208,11 +230,10 @@ impl Attr { })?); let span = span_map.span_for_range(range); let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { - let value = match lit.kind() { - ast::LiteralKind::String(string) => string.value()?.into(), - _ => lit.syntax().first_token()?.text().trim_matches('"').into(), - }; - Some(Interned::new(AttrInput::Literal(value))) + Some(Interned::new(AttrInput::Literal(tt::Literal { + text: lit.token().text().into(), + span, + }))) } else if let Some(tt) = ast.token_tree() { let tree = syntax_node_to_token_tree(tt.syntax(), span_map, span); Some(Interned::new(AttrInput::TokenTree(Box::new(tree)))) @@ -245,9 +266,8 @@ impl Attr { } Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))) => { let input = match input.get(1) { - Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text, .. }))) => { - //FIXME the trimming here isn't quite right, raw strings are not handled - Some(Interned::new(AttrInput::Literal(text.trim_matches('"').into()))) + Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => { + Some(Interned::new(AttrInput::Literal(lit.clone()))) } _ => None, }; @@ -265,9 +285,14 @@ impl Attr { impl Attr { /// #[path = "string"] - pub fn string_value(&self) -> Option<&SmolStr> { + pub fn string_value(&self) -> Option<&str> { match self.input.as_deref()? { - AttrInput::Literal(it) => Some(it), + AttrInput::Literal(it) => match it.text.strip_prefix('r') { + Some(it) => it.trim_matches('#'), + None => it.text.as_str(), + } + .strip_prefix('"')? + .strip_suffix('"'), _ => None, } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs index 9fb6a0b2346b..fd3e4e7a4dba 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs @@ -1,11 +1,11 @@ //! Builtin macro -use base_db::{AnchoredPath, Edition, FileId}; +use base_db::{AnchoredPath, FileId}; use cfg::CfgExpr; use either::Either; use itertools::Itertools; use mbe::{parse_exprs_with_sep, parse_to_token_tree}; -use span::{Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; +use span::{Edition, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; use syntax::ast::{self, AstToken}; use crate::{ diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs index c74c13a6fd3c..db3558a84e9e 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs @@ -10,7 +10,7 @@ use syntax::{ use tracing::{debug, warn}; use tt::SmolStr; -use crate::{db::ExpandDatabase, MacroCallKind, MacroCallLoc}; +use crate::{db::ExpandDatabase, proc_macro::ProcMacroKind, MacroCallLoc, MacroDefKind}; fn check_cfg_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option { if !attr.simple_name().as_deref().map(|v| v == "cfg")? { @@ -139,7 +139,7 @@ fn process_enum( 'variant: for variant in variants.variants() { for attr in variant.attrs() { if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() { - // Rustc does not strip the attribute if it is enabled. So we will will leave it + // Rustc does not strip the attribute if it is enabled. So we will leave it debug!("censoring type {:?}", variant.syntax()); remove.insert(variant.syntax().clone().into()); // We need to remove the , as well @@ -180,7 +180,13 @@ pub(crate) fn process_cfg_attrs( db: &dyn ExpandDatabase, ) -> Option> { // FIXME: #[cfg_eval] is not implemented. But it is not stable yet - if !matches!(loc.kind, MacroCallKind::Derive { .. }) { + let is_derive = match loc.def.kind { + MacroDefKind::BuiltInDerive(..) + | MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _) => true, + MacroDefKind::BuiltInAttr(expander, _) => expander.is_derive(), + _ => false, + }; + if !is_derive { return None; } let mut remove = FxHashSet::default(); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index ec68f2f96e5e..5461c1c49a32 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -24,7 +24,8 @@ use crate::{ HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, MacroFileId, }; - +/// This is just to ensure the types of smart_macro_arg and macro_arg are the same +type MacroArgResult = (Arc, SyntaxFixupUndoInfo, Span); /// Total limit on the number of tokens produced by any macro invocation. /// /// If an invocation produces more tokens than this limit, it will not be stored in the database and @@ -98,7 +99,13 @@ pub trait ExpandDatabase: SourceDatabase { /// Lowers syntactic macro call to a token tree representation. That's a firewall /// query, only typing in the macro call itself changes the returned /// subtree. - fn macro_arg(&self, id: MacroCallId) -> (Arc, SyntaxFixupUndoInfo, Span); + fn macro_arg(&self, id: MacroCallId) -> MacroArgResult; + #[salsa::transparent] + fn macro_arg_considering_derives( + &self, + id: MacroCallId, + kind: &MacroCallKind, + ) -> MacroArgResult; /// Fetches the expander for this macro. #[salsa::transparent] #[salsa::invoke(TokenExpander::macro_expander)] @@ -144,7 +151,7 @@ pub fn expand_speculative( let span_map = RealSpanMap::absolute(FileId::BOGUS); let span_map = SpanMapRef::RealSpanMap(&span_map); - let (_, _, span) = db.macro_arg(actual_macro_call); + let (_, _, span) = db.macro_arg_considering_derives(actual_macro_call, &loc.kind); // Build the subtree and token mapping for the speculative args let (mut tt, undo_info) = match loc.kind { @@ -339,12 +346,24 @@ pub(crate) fn parse_with_map( } } -// FIXME: for derive attributes, this will return separate copies of the same structures! Though -// they may differ in spans due to differing call sites... -fn macro_arg( +/// This resolves the [MacroCallId] to check if it is a derive macro if so get the [macro_arg] for the derive. +/// Other wise return the [macro_arg] for the macro_call_id. +/// +/// This is not connected to the database so it does not cached the result. However, the inner [macro_arg] query is +fn macro_arg_considering_derives( db: &dyn ExpandDatabase, id: MacroCallId, -) -> (Arc, SyntaxFixupUndoInfo, Span) { + kind: &MacroCallKind, +) -> MacroArgResult { + match kind { + // Get the macro arg for the derive macro + MacroCallKind::Derive { derive_macro_id, .. } => db.macro_arg(*derive_macro_id), + // Normal macro arg + _ => db.macro_arg(id), + } +} + +fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { let loc = db.lookup_intern_macro_call(id); if let MacroCallLoc { @@ -414,29 +433,30 @@ fn macro_arg( } return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span); } - MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { - let node = ast_id.to_ptr(db).to_node(&root); - let censor_derive_input = censor_derive_input(derive_attr_index, &node); - let item_node = node.into(); - let attr_source = attr_source(derive_attr_index, &item_node); - // FIXME: This is wrong, this should point to the path of the derive attribute` - let span = - map.span_for_range(attr_source.as_ref().and_then(|it| it.path()).map_or_else( - || item_node.syntax().text_range(), - |it| it.syntax().text_range(), - )); - (censor_derive_input, item_node, span) + // MacroCallKind::Derive should not be here. As we are getting the argument for the derive macro + MacroCallKind::Derive { .. } => { + unreachable!("`ExpandDatabase::macro_arg` called with `MacroCallKind::Derive`") } MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { let node = ast_id.to_ptr(db).to_node(&root); let attr_source = attr_source(invoc_attr_index, &node); + let span = map.span_for_range( attr_source .as_ref() .and_then(|it| it.path()) .map_or_else(|| node.syntax().text_range(), |it| it.syntax().text_range()), ); - (attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span) + // If derive attribute we need to censor the derive input + if matches!(loc.def.kind, MacroDefKind::BuiltInAttr(expander, ..) if expander.is_derive()) + && ast::Adt::can_cast(node.syntax().kind()) + { + let adt = ast::Adt::cast(node.syntax().clone()).unwrap(); + let censor_derive_input = censor_derive_input(invoc_attr_index, &adt); + (censor_derive_input, node, span) + } else { + (attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span) + } } }; @@ -526,7 +546,8 @@ fn macro_expand( let (ExpandResult { value: tt, err }, span) = match loc.def.kind { MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc), _ => { - let (macro_arg, undo_info, span) = db.macro_arg(macro_call_id); + let (macro_arg, undo_info, span) = + db.macro_arg_considering_derives(macro_call_id, &loc.kind); let arg = &*macro_arg; let res = @@ -603,7 +624,7 @@ fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId) -> Span { fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult> { let loc = db.lookup_intern_macro_call(id); - let (macro_arg, undo_info, span) = db.macro_arg(id); + let (macro_arg, undo_info, span) = db.macro_arg_considering_derives(id, &loc.kind); let (expander, ast) = match loc.def.kind { MacroDefKind::ProcMacro(expander, _, ast) => (expander, ast), diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index 33643c02724f..9a0b218e6d1d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -1,8 +1,8 @@ //! Compiled declarative macro expanders (`macro_rules!`` and `macro`) use std::sync::OnceLock; -use base_db::{CrateId, Edition, VersionReq}; -use span::{MacroCallId, Span}; +use base_db::{CrateId, VersionReq}; +use span::{MacroCallId, Span, SyntaxContextId}; use syntax::{ast, AstNode}; use triomphe::Arc; @@ -10,13 +10,13 @@ use crate::{ attrs::RawAttrs, db::ExpandDatabase, hygiene::{apply_mark, Transparency}, - tt, AstId, ExpandError, ExpandResult, + tt, AstId, ExpandError, ExpandResult, Lookup, }; /// Old-style `macro_rules` or the new macros 2.0 #[derive(Debug, Clone, Eq, PartialEq)] pub struct DeclarativeMacroExpander { - pub mac: mbe::DeclarativeMacro, + pub mac: mbe::DeclarativeMacro, pub transparency: Transparency, } @@ -94,8 +94,6 @@ impl DeclarativeMacroExpander { def_crate: CrateId, id: AstId, ) -> Arc { - let crate_data = &db.crate_graph()[def_crate]; - let is_2021 = crate_data.edition >= Edition::Edition2021; let (root, map) = crate::db::parse_with_map(db, id.file_id); let root = root.syntax_node(); @@ -133,6 +131,16 @@ impl DeclarativeMacroExpander { ) }); + let edition = |ctx: SyntaxContextId| { + let crate_graph = db.crate_graph(); + if ctx.is_root() { + crate_graph[def_crate].edition + } else { + let data = db.lookup_intern_syntax_context(ctx); + // UNWRAP-SAFETY: Only the root context has no outer expansion + crate_graph[data.outer_expn.unwrap().lookup(db).def.krate].edition + } + }; let (mac, transparency) = match id.to_ptr(db).to_node(&root) { ast::Macro::MacroRules(macro_rules) => ( match macro_rules.token_tree() { @@ -145,12 +153,11 @@ impl DeclarativeMacroExpander { ), ); - mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021, new_meta_vars) + mbe::DeclarativeMacro::parse_macro_rules(&tt, edition, new_meta_vars) } - None => mbe::DeclarativeMacro::from_err( - mbe::ParseError::Expected("expected a token tree".into()), - is_2021, - ), + None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected( + "expected a token tree".into(), + )), }, transparency(¯o_rules).unwrap_or(Transparency::SemiTransparent), ), @@ -163,12 +170,11 @@ impl DeclarativeMacroExpander { map.span_for_range(macro_def.macro_token().unwrap().text_range()), ); - mbe::DeclarativeMacro::parse_macro2(&tt, is_2021, new_meta_vars) + mbe::DeclarativeMacro::parse_macro2(&tt, edition, new_meta_vars) } - None => mbe::DeclarativeMacro::from_err( - mbe::ParseError::Expected("expected a token tree".into()), - is_2021, - ), + None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected( + "expected a token tree".into(), + )), }, transparency(¯o_def).unwrap_or(Transparency::Opaque), ), diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 5d4f7dc1462a..db8bbeccef8f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -30,10 +30,11 @@ use triomphe::Arc; use std::{fmt, hash::Hash}; -use base_db::{salsa::impl_intern_value_trivial, CrateId, Edition, FileId}; +use base_db::{salsa::impl_intern_value_trivial, CrateId, FileId}; use either::Either; use span::{ - ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData, SyntaxContextId, + Edition, ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData, + SyntaxContextId, }; use syntax::{ ast::{self, AstNode}, @@ -53,11 +54,9 @@ use crate::{ pub use crate::files::{AstId, ErasedAstId, InFile, InMacroFile, InRealFile}; -pub use mbe::ValueResult; +pub use mbe::{DeclarativeMacro, ValueResult}; pub use span::{HirFileId, MacroCallId, MacroFileId}; -pub type DeclarativeMacro = ::mbe::DeclarativeMacro; - pub mod tt { pub use span::Span; pub use tt::{DelimiterKind, Spacing}; @@ -201,7 +200,7 @@ pub struct EagerCallInfo { /// Call id of the eager macro's input file (this is the macro file for its fully expanded input). arg_id: MacroCallId, error: Option, - /// TODO: Doc + /// The call site span of the eager macro span: Span, } @@ -212,7 +211,7 @@ pub enum MacroCallKind { expand_to: ExpandTo, /// Some if this is a macro call for an eager macro. Note that this is `None` /// for the eager input macro file. - // FIXME: This is being interned, subtrees can vary quickly differ just slightly causing + // FIXME: This is being interned, subtrees can vary quickly differing just slightly causing // leakage problems here eager: Option>, }, @@ -225,6 +224,9 @@ pub enum MacroCallKind { derive_attr_index: AttrId, /// Index of the derive macro in the derive attribute derive_index: u32, + /// The "parent" macro call. + /// We will resolve the same token tree for all derive macros in the same derive attribute. + derive_macro_id: MacroCallId, }, Attr { ast_id: AstId, @@ -484,7 +486,7 @@ impl MacroDefId { matches!( self.kind, MacroDefKind::BuiltIn(..) - | MacroDefKind::ProcMacro(_, ProcMacroKind::FuncLike, _) + | MacroDefKind::ProcMacro(_, ProcMacroKind::Bang, _) | MacroDefKind::BuiltInEager(..) | MacroDefKind::Declarative(..) ) @@ -806,7 +808,8 @@ impl ExpansionInfo { let (parse, exp_map) = db.parse_macro_expansion(macro_file).value; let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() }; - let (macro_arg, _, _) = db.macro_arg(macro_file.macro_call_id); + let (macro_arg, _, _) = + db.macro_arg_considering_derives(macro_file.macro_call_id, &loc.kind); let def = loc.def.ast_id().left().and_then(|id| { let def_tt = match id.to_node(db) { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index fc186d2c26d3..46f8c2b9d8c4 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -225,6 +225,26 @@ fn convert_path( let mut segments = path.segments(); let segment = &segments.next()?; + let handle_super_kw = &mut |init_deg| { + let mut deg = init_deg; + let mut next_segment = None; + for segment in segments.by_ref() { + match segment.kind()? { + ast::PathSegmentKind::SuperKw => deg += 1, + ast::PathSegmentKind::Name(name) => { + next_segment = Some(name.as_name()); + break; + } + ast::PathSegmentKind::Type { .. } + | ast::PathSegmentKind::SelfTypeKw + | ast::PathSegmentKind::SelfKw + | ast::PathSegmentKind::CrateKw => return None, + } + } + + Some(ModPath::from_segments(PathKind::Super(deg), next_segment)) + }; + let mut mod_path = match segment.kind()? { ast::PathSegmentKind::Name(name_ref) => { if name_ref.text() == "$crate" { @@ -245,26 +265,8 @@ fn convert_path( ModPath::from_segments(PathKind::Plain, Some(known::SELF_TYPE)) } ast::PathSegmentKind::CrateKw => ModPath::from_segments(PathKind::Crate, iter::empty()), - ast::PathSegmentKind::SelfKw => ModPath::from_segments(PathKind::Super(0), iter::empty()), - ast::PathSegmentKind::SuperKw => { - let mut deg = 1; - let mut next_segment = None; - for segment in segments.by_ref() { - match segment.kind()? { - ast::PathSegmentKind::SuperKw => deg += 1, - ast::PathSegmentKind::Name(name) => { - next_segment = Some(name.as_name()); - break; - } - ast::PathSegmentKind::Type { .. } - | ast::PathSegmentKind::SelfTypeKw - | ast::PathSegmentKind::SelfKw - | ast::PathSegmentKind::CrateKw => return None, - } - } - - ModPath::from_segments(PathKind::Super(deg), next_segment) - } + ast::PathSegmentKind::SelfKw => handle_super_kw(0)?, + ast::PathSegmentKind::SuperKw => handle_super_kw(1)?, ast::PathSegmentKind::Type { .. } => { // not allowed in imports return None; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs index ca6fc0afe2d7..abed16fecdeb 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs @@ -23,7 +23,7 @@ impl ProcMacroId { #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] pub enum ProcMacroKind { CustomDerive, - FuncLike, + Bang, Attr, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 3cfedcdcb4dc..bf473740166c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -47,13 +47,14 @@ hir-expand.workspace = true base-db.workspace = true syntax.workspace = true limit.workspace = true +span.workspace = true [dev-dependencies] expect-test = "1.4.0" tracing.workspace = true tracing-subscriber.workspace = true tracing-tree.workspace = true -project-model = { path = "../project-model" } +project-model.workspace = true # local deps test-utils.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs index c485c9b2e80b..cb118a368486 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs @@ -9,21 +9,21 @@ use chalk_ir::{ AdtId, DebruijnIndex, Scalar, }; use hir_def::{ - builtin_type::BuiltinType, generics::TypeOrConstParamData, ConstParamId, DefWithBodyId, - GenericDefId, TraitId, TypeAliasId, + builtin_type::BuiltinType, DefWithBodyId, GenericDefId, GenericParamId, TraitId, TypeAliasId, }; use smallvec::SmallVec; use crate::{ - consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive, - to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig, - GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, - TyKind, + consteval::unknown_const_as_generic, db::HirDatabase, error_lifetime, + infer::unify::InferenceTable, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, + Binders, BoundVar, CallableSig, GenericArg, GenericArgData, Interner, ProjectionTy, + Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind, }; #[derive(Debug, Clone, PartialEq, Eq)] pub enum ParamKind { Type, + Lifetime, Const(Ty), } @@ -107,6 +107,9 @@ impl TyBuilder { ParamKind::Const(ty) => { BoundVar::new(debruijn, idx).to_const(Interner, ty.clone()).cast(Interner) } + ParamKind::Lifetime => { + BoundVar::new(debruijn, idx).to_lifetime(Interner).cast(Interner) + } }); this.vec.extend(filler.take(this.remaining()).casted(Interner)); assert_eq!(this.remaining(), 0); @@ -119,6 +122,7 @@ impl TyBuilder { let filler = this.param_kinds[this.vec.len()..].iter().map(|x| match x { ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }); this.vec.extend(filler.casted(Interner)); assert_eq!(this.remaining(), 0); @@ -130,6 +134,7 @@ impl TyBuilder { self.fill(|x| match x { ParamKind::Type => table.new_type_var().cast(Interner), ParamKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner), + ParamKind::Lifetime => table.new_lifetime_var().cast(Interner), }) } @@ -142,7 +147,8 @@ impl TyBuilder { fn assert_match_kind(&self, a: &chalk_ir::GenericArg, e: &ParamKind) { match (a.data(Interner), e) { (GenericArgData::Ty(_), ParamKind::Type) - | (GenericArgData::Const(_), ParamKind::Const(_)) => (), + | (GenericArgData::Const(_), ParamKind::Const(_)) + | (GenericArgData::Lifetime(_), ParamKind::Lifetime) => (), _ => panic!("Mismatched kinds: {a:?}, {:?}, {:?}", self.vec, self.param_kinds), } } @@ -201,10 +207,11 @@ impl TyBuilder<()> { Substitution::from_iter( Interner, params.iter_id().map(|id| match id { - either::Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner), - either::Either::Right(id) => { + GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner), + GenericParamId::ConstParamId(id) => { unknown_const_as_generic(db.const_param_ty(id)).cast(Interner) } + GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), }), ) } @@ -219,11 +226,10 @@ impl TyBuilder<()> { assert!(generics.parent_generics().is_some() == parent_subst.is_some()); let params = generics .iter_self() - .map(|(id, data)| match data { - TypeOrConstParamData::TypeParamData(_) => ParamKind::Type, - TypeOrConstParamData::ConstParamData(_) => { - ParamKind::Const(db.const_param_ty(ConstParamId::from_unchecked(id))) - } + .map(|(id, _data)| match id { + GenericParamId::TypeParamId(_) => ParamKind::Type, + GenericParamId::ConstParamId(id) => ParamKind::Const(db.const_param_ty(id)), + GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime, }) .collect(); TyBuilder::new((), params, parent_subst) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index e678a2fee132..46612242b090 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -272,6 +272,19 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { }; chalk_ir::Binders::new(binders, bound) } + crate::ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + let datas = self + .db + .type_alias_impl_traits(alias) + .expect("impl trait id without impl traits"); + let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders(); + let data = &datas.impl_traits[idx]; + let bound = OpaqueTyDatumBound { + bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()), + where_clauses: chalk_ir::Binders::empty(Interner, vec![]), + }; + chalk_ir::Binders::new(binders, bound) + } crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => { if let Some((future_trait, future_output)) = self .db diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index 795a5996912b..d1aebeff261c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -268,6 +268,13 @@ impl TyExt for Ty { data.substitute(Interner, &subst).into_value_and_skipped_binders().0 }) } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + db.type_alias_impl_traits(alias).map(|it| { + let data = + (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + data.substitute(Interner, &subst).into_value_and_skipped_binders().0 + }) + } } } TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { @@ -280,6 +287,13 @@ impl TyExt for Ty { data.substitute(Interner, &opaque_ty.substitution) }) } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + db.type_alias_impl_traits(alias).map(|it| { + let data = + (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + data.substitute(Interner, &opaque_ty.substitution) + }) + } // It always has an parameter for Future::Output type. ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(), }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 98384c47490a..d1ffd5046c3f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -2825,3 +2825,30 @@ fn unsized_local() { |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::UnsizedTemporary(_))), ); } + +#[test] +fn recursive_adt() { + check_fail( + r#" + //- minicore: coerce_unsized, index, slice + pub enum TagTree { + Leaf, + Choice(&'static [TagTree]), + } + const GOAL: TagTree = { + const TAG_TREE: TagTree = TagTree::Choice(&[ + { + const VARIANT_TAG_TREE: TagTree = TagTree::Choice( + &[ + TagTree::Leaf, + ], + ); + VARIANT_TAG_TREE + }, + ]); + TAG_TREE + }; + "#, + |e| matches!(e, ConstEvalError::MirEvalError(MirEvalError::StackOverflow)), + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 28c497989fe9..90bf46b5056c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -11,7 +11,7 @@ use base_db::{ use hir_def::{ db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, - LifetimeParamId, LocalFieldId, StaticId, TypeOrConstParamId, VariantId, + LifetimeParamId, LocalFieldId, StaticId, TypeAliasId, TypeOrConstParamId, VariantId, }; use la_arena::ArenaMap; use smallvec::SmallVec; @@ -23,9 +23,9 @@ use crate::{ layout::{Layout, LayoutError}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, - Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, - Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, - TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, + Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, ImplTraits, + InferenceResult, Interner, PolyFnSig, QuantifiedWhereClause, Substitution, TraitEnvironment, + TraitRef, Ty, TyDefId, ValueTyDefId, }; use hir_expand::name::Name; @@ -132,10 +132,10 @@ pub trait HirDatabase: DefDatabase + Upcast { fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig; #[salsa::invoke(crate::lower::return_type_impl_traits)] - fn return_type_impl_traits( - &self, - def: FunctionId, - ) -> Option>>; + fn return_type_impl_traits(&self, def: FunctionId) -> Option>>; + + #[salsa::invoke(crate::lower::type_alias_impl_traits)] + fn type_alias_impl_traits(&self, def: TypeAliasId) -> Option>>; #[salsa::invoke(crate::lower::generic_predicates_for_param_query)] #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 67cfbc294df7..20b0da441daa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -11,7 +11,6 @@ use hir_def::{ItemContainerId, Lookup}; use hir_expand::name; use itertools::Itertools; use rustc_hash::FxHashSet; -use rustc_pattern_analysis::usefulness::{compute_match_usefulness, ValidityConstraint}; use syntax::{ast, AstNode}; use tracing::debug; use triomphe::Arc; @@ -234,13 +233,7 @@ impl ExprValidator { return; } - let report = match compute_match_usefulness( - &cx, - m_arms.as_slice(), - scrut_ty.clone(), - ValidityConstraint::ValidOnly, - None, - ) { + let report = match cx.compute_match_usefulness(m_arms.as_slice(), scrut_ty.clone()) { Ok(report) => report, Err(()) => return, }; @@ -282,13 +275,7 @@ impl ExprValidator { continue; } - let report = match compute_match_usefulness( - &cx, - &[match_arm], - ty.clone(), - ValidityConstraint::ValidOnly, - None, - ) { + let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) { Ok(v) => v, Err(e) => { debug!(?e, "match usefulness error"); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index ca0584287965..f45beb4c92bf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -8,7 +8,8 @@ use rustc_hash::FxHashMap; use rustc_pattern_analysis::{ constructor::{Constructor, ConstructorSet, VariantVisibility}, index::IdxContainer, - Captures, PrivateUninhabitedField, TypeCx, + usefulness::{compute_match_usefulness, PlaceValidity, UsefulnessReport}, + Captures, PatCx, PrivateUninhabitedField, }; use smallvec::{smallvec, SmallVec}; use stdx::never; @@ -59,6 +60,18 @@ impl<'p> MatchCheckCtx<'p> { Self { module, body, db, exhaustive_patterns, min_exhaustive_patterns } } + pub(crate) fn compute_match_usefulness( + &self, + arms: &[MatchArm<'p>], + scrut_ty: Ty, + ) -> Result, ()> { + // FIXME: Determine place validity correctly. For now, err on the safe side. + let place_validity = PlaceValidity::MaybeInvalid; + // Measured to take ~100ms on modern hardware. + let complexity_limit = Some(500000); + compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit) + } + fn is_uninhabited(&self, ty: &Ty) -> bool { is_ty_uninhabited_from(ty, self.module, self.db) } @@ -107,15 +120,17 @@ impl<'p> MatchCheckCtx<'p> { } pub(crate) fn lower_pat(&self, pat: &Pat) -> DeconstructedPat<'p> { - let singleton = |pat| vec![pat]; + let singleton = |pat: DeconstructedPat<'p>| vec![pat.at_index(0)]; let ctor; - let fields: Vec<_>; + let mut fields: Vec<_>; + let arity; match pat.kind.as_ref() { PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat), PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { ctor = Wildcard; fields = Vec::new(); + arity = 0; } PatKind::Deref { subpattern } => { ctor = match pat.ty.kind(Interner) { @@ -128,23 +143,22 @@ impl<'p> MatchCheckCtx<'p> { } }; fields = singleton(self.lower_pat(subpattern)); + arity = 1; } PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { + fields = subpatterns + .iter() + .map(|pat| { + let idx: u32 = pat.field.into_raw().into(); + self.lower_pat(&pat.pattern).at_index(idx as usize) + }) + .collect(); match pat.ty.kind(Interner) { TyKind::Tuple(_, substs) => { ctor = Struct; - let mut wilds: Vec<_> = substs - .iter(Interner) - .map(|arg| arg.assert_ty_ref(Interner).clone()) - .map(DeconstructedPat::wildcard) - .collect(); - for pat in subpatterns { - let idx: u32 = pat.field.into_raw().into(); - wilds[idx as usize] = self.lower_pat(&pat.pattern); - } - fields = wilds + arity = substs.len(Interner); } - TyKind::Adt(adt, substs) if is_box(self.db, adt.0) => { + TyKind::Adt(adt, _) if is_box(self.db, adt.0) => { // The only legal patterns of type `Box` (outside `std`) are `_` and box // patterns. If we're here we can assume this is a box pattern. // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_, @@ -157,16 +171,9 @@ impl<'p> MatchCheckCtx<'p> { // normally or through box-patterns. We'll have to figure out a proper // solution when we introduce generalized deref patterns. Also need to // prevent mixing of those two options. - let pat = - subpatterns.iter().find(|pat| pat.field.into_raw() == 0u32.into()); - let field = if let Some(pat) = pat { - self.lower_pat(&pat.pattern) - } else { - let ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); - DeconstructedPat::wildcard(ty) - }; + fields.retain(|ipat| ipat.idx == 0); ctor = Struct; - fields = singleton(field); + arity = 1; } &TyKind::Adt(adt, _) => { ctor = match pat.kind.as_ref() { @@ -181,37 +188,33 @@ impl<'p> MatchCheckCtx<'p> { } }; let variant = Self::variant_id_for_adt(&ctor, adt.0).unwrap(); - // Fill a vec with wildcards, then place the fields we have at the right - // index. - let mut wilds: Vec<_> = self - .list_variant_fields(&pat.ty, variant) - .map(|(_, ty)| ty) - .map(DeconstructedPat::wildcard) - .collect(); - for pat in subpatterns { - let field_id: u32 = pat.field.into_raw().into(); - wilds[field_id as usize] = self.lower_pat(&pat.pattern); - } - fields = wilds; + arity = variant.variant_data(self.db.upcast()).fields().len(); } _ => { never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty); ctor = Wildcard; - fields = Vec::new(); + fields.clear(); + arity = 0; } } } &PatKind::LiteralBool { value } => { ctor = Bool(value); fields = Vec::new(); + arity = 0; } PatKind::Or { pats } => { ctor = Or; - fields = pats.iter().map(|pat| self.lower_pat(pat)).collect(); + fields = pats + .iter() + .enumerate() + .map(|(i, pat)| self.lower_pat(pat).at_index(i)) + .collect(); + arity = pats.len(); } } let data = PatData { db: self.db }; - DeconstructedPat::new(ctor, fields, pat.ty.clone(), data) + DeconstructedPat::new(ctor, fields, arity, pat.ty.clone(), data) } pub(crate) fn hoist_witness_pat(&self, pat: &WitnessPat<'p>) -> Pat { @@ -271,7 +274,7 @@ impl<'p> MatchCheckCtx<'p> { } } -impl<'p> TypeCx for MatchCheckCtx<'p> { +impl<'p> PatCx for MatchCheckCtx<'p> { type Error = (); type Ty = Ty; type VariantIdx = EnumVariantId; @@ -453,7 +456,7 @@ impl<'p> TypeCx for MatchCheckCtx<'p> { let variant = pat.ty().as_adt().and_then(|(adt, _)| Self::variant_id_for_adt(pat.ctor(), adt)); - let db = pat.data().unwrap().db; + let db = pat.data().db; if let Some(variant) = variant { match variant { VariantId::EnumVariantId(v) => { @@ -475,7 +478,6 @@ impl<'p> TypeCx for MatchCheckCtx<'p> { } fn complexity_exceeded(&self) -> Result<(), Self::Error> { - // FIXME(Nadrieril): make use of the complexity counter. Err(()) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 20964f5acbd0..8740ae6797cb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -938,18 +938,32 @@ impl HirDisplay for Ty { f.end_location_link(); if parameters.len(Interner) > 0 { let generics = generics(db.upcast(), def.into()); - let (parent_params, self_param, type_params, const_params, _impl_trait_params) = - generics.provenance_split(); - let total_len = parent_params + self_param + type_params + const_params; + let ( + parent_params, + self_param, + type_params, + const_params, + _impl_trait_params, + lifetime_params, + ) = generics.provenance_split(); + let total_len = + parent_params + self_param + type_params + const_params + lifetime_params; // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? if total_len > 0 { - // `parameters` are in the order of fn's params (including impl traits), + // `parameters` are in the order of fn's params (including impl traits), fn's lifetimes // parent's params (those from enclosing impl or trait, if any). let parameters = parameters.as_slice(Interner); let fn_params_len = self_param + type_params + const_params; + // This will give slice till last type or const let fn_params = parameters.get(..fn_params_len); + let fn_lt_params = + parameters.get(fn_params_len..(fn_params_len + lifetime_params)); let parent_params = parameters.get(parameters.len() - parent_params..); - let params = parent_params.into_iter().chain(fn_params).flatten(); + let params = parent_params + .into_iter() + .chain(fn_lt_params) + .chain(fn_params) + .flatten(); write!(f, "<")?; f.write_joined(params, ", ")?; write!(f, ">")?; @@ -1063,6 +1077,20 @@ impl HirDisplay for Ty { )?; // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + let datas = + db.type_alias_impl_traits(alias).expect("impl trait id without data"); + let data = + (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + let bounds = data.substitute(Interner, ¶meters); + let krate = alias.krate(db.upcast()); + write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + bounds.skip_binders(), + SizedByDefault::Sized { anchor: krate }, + )?; + } ImplTraitId::AsyncBlockTypeImplTrait(body, ..) => { let future_trait = db .lang_item(body.module(db.upcast()).krate(), LangItem::Future) @@ -1228,6 +1256,20 @@ impl HirDisplay for Ty { SizedByDefault::Sized { anchor: krate }, )?; } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + let datas = + db.type_alias_impl_traits(alias).expect("impl trait id without data"); + let data = + (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + let bounds = data.substitute(Interner, &opaque_ty.substitution); + let krate = alias.krate(db.upcast()); + write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + bounds.skip_binders(), + SizedByDefault::Sized { anchor: krate }, + )?; + } ImplTraitId::AsyncBlockTypeImplTrait(..) => { write!(f, "{{async block}}")?; } @@ -1280,8 +1322,17 @@ fn hir_fmt_generics( generic_def: Option, ) -> Result<(), HirDisplayError> { let db = f.db; - let lifetime_args_count = generic_def.map_or(0, |g| db.generic_params(g).lifetimes.len()); - if parameters.len(Interner) + lifetime_args_count > 0 { + if parameters.len(Interner) > 0 { + use std::cmp::Ordering; + let param_compare = + |a: &GenericArg, b: &GenericArg| match (a.data(Interner), b.data(Interner)) { + (crate::GenericArgData::Lifetime(_), crate::GenericArgData::Lifetime(_)) => { + Ordering::Equal + } + (crate::GenericArgData::Lifetime(_), _) => Ordering::Less, + (_, crate::GenericArgData::Lifetime(_)) => Ordering::Less, + (_, _) => Ordering::Equal, + }; let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() { match generic_def .map(|generic_def_id| db.generic_defaults(generic_def_id)) @@ -1307,6 +1358,11 @@ fn hir_fmt_generics( return true; } } + if parameter.lifetime(Interner).map(|it| it.data(Interner)) + == Some(&crate::LifetimeData::Static) + { + return true; + } let default_parameter = match default_parameters.get(i) { Some(it) => it, None => return true, @@ -1327,16 +1383,12 @@ fn hir_fmt_generics( } else { parameters.as_slice(Interner) }; - if !parameters_to_write.is_empty() || lifetime_args_count != 0 { + //FIXME: Should handle the ordering of lifetimes when creating substitutions + let mut parameters_to_write = parameters_to_write.to_vec(); + parameters_to_write.sort_by(param_compare); + if !parameters_to_write.is_empty() { write!(f, "<")?; let mut first = true; - for _ in 0..lifetime_args_count { - if !first { - write!(f, ", ")?; - } - first = false; - write!(f, "'_")?; - } for generic_arg in parameters_to_write { if !first { write!(f, ", ")?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 34ba17f145e0..be3b50e1411d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -25,8 +25,11 @@ pub(crate) mod unify; use std::{convert::identity, iter, ops::Index}; use chalk_ir::{ - cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety, - Scalar, TyKind, TypeFlags, Variance, + cast::Cast, + fold::TypeFoldable, + interner::HasInterner, + visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, + DebruijnIndex, Mutability, Safety, Scalar, TyKind, TypeFlags, Variance, }; use either::Either; use hir_def::{ @@ -39,7 +42,7 @@ use hir_def::{ layout::Integer, path::{ModPath, Path}, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, - type_ref::TypeRef, + type_ref::{LifetimeRef, TypeRef}, AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId, }; @@ -53,14 +56,14 @@ use triomphe::Arc; use crate::{ db::HirDatabase, fold_tys, - infer::coerce::CoerceMany, + infer::{coerce::CoerceMany, unify::InferenceTable}, lower::ImplTraitLoweringMode, static_lifetime, to_assoc_type_id, traits::FnTrait, utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder}, AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId, - InEnvironment, Interner, Lifetime, ProjectionTy, RpitId, Substitution, TraitEnvironment, - TraitRef, Ty, TyBuilder, TyExt, + ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution, + TraitEnvironment, Ty, TyBuilder, TyExt, }; // This lint has a false positive here. See the link below for details. @@ -422,7 +425,7 @@ pub struct InferenceResult { /// unresolved or missing subpatterns or subpatterns of mismatched types. pub type_of_pat: ArenaMap, pub type_of_binding: ArenaMap, - pub type_of_rpit: ArenaMap, + pub type_of_rpit: ArenaMap, /// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop. pub type_of_for_iterator: FxHashMap, type_mismatches: FxHashMap, @@ -752,7 +755,12 @@ impl<'a> InferenceContext<'a> { } fn collect_const(&mut self, data: &ConstData) { - self.return_ty = self.make_ty(&data.type_ref); + let return_ty = self.make_ty(&data.type_ref); + + // Constants might be associated items that define ATPITs. + self.insert_atpit_coercion_table(iter::once(&return_ty)); + + self.return_ty = return_ty; } fn collect_static(&mut self, data: &StaticData) { @@ -785,11 +793,13 @@ impl<'a> InferenceContext<'a> { self.write_binding_ty(self_param, ty); } } + let mut params_and_ret_tys = Vec::new(); for (ty, pat) in param_tys.zip(&*self.body.params) { let ty = self.insert_type_vars(ty); let ty = self.normalize_associated_types_in(ty); self.infer_top_pat(*pat, &ty); + params_and_ret_tys.push(ty); } let return_ty = &*data.ret_type; @@ -801,8 +811,11 @@ impl<'a> InferenceContext<'a> { let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) { // RPIT opaque types use substitution of their parent function. let fn_placeholders = TyBuilder::placeholder_subst(self.db, func); - let result = - self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders); + let result = self.insert_inference_vars_for_impl_trait( + return_ty, + rpits.clone(), + fn_placeholders, + ); let rpits = rpits.skip_binders(); for (id, _) in rpits.impl_traits.iter() { if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) { @@ -817,13 +830,19 @@ impl<'a> InferenceContext<'a> { self.return_ty = self.normalize_associated_types_in(return_ty); self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); + + // Functions might be associated items that define ATPITs. + // To define an ATPITs, that ATPIT must appear in the function's signatures. + // So, it suffices to check for params and return types. + params_and_ret_tys.push(self.return_ty.clone()); + self.insert_atpit_coercion_table(params_and_ret_tys.iter()); } - fn insert_inference_vars_for_rpit( + fn insert_inference_vars_for_impl_trait( &mut self, t: T, - rpits: Arc>, - fn_placeholders: Substitution, + rpits: Arc>, + placeholders: Substitution, ) -> T where T: crate::HasInterner + crate::TypeFoldable, @@ -837,6 +856,7 @@ impl<'a> InferenceContext<'a> { }; let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) { ImplTraitId::ReturnTypeImplTrait(_, idx) => idx, + ImplTraitId::AssociatedTypeImplTrait(_, idx) => idx, _ => unreachable!(), }; let bounds = @@ -844,15 +864,14 @@ impl<'a> InferenceContext<'a> { let var = self.table.new_type_var(); let var_subst = Substitution::from1(Interner, var.clone()); for bound in bounds { - let predicate = - bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders); + let predicate = bound.map(|it| it.cloned()).substitute(Interner, &placeholders); let (var_predicate, binders) = predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders(); always!(binders.is_empty(Interner)); // quantified where clauses not yet handled - let var_predicate = self.insert_inference_vars_for_rpit( + let var_predicate = self.insert_inference_vars_for_impl_trait( var_predicate, rpits.clone(), - fn_placeholders.clone(), + placeholders.clone(), ); self.push_obligation(var_predicate.cast(Interner)); } @@ -863,6 +882,106 @@ impl<'a> InferenceContext<'a> { ) } + /// The coercion of a non-inference var into an opaque type should fail, + /// but not in the defining sites of the ATPITs. + /// In such cases, we insert an proxy inference var for each ATPIT, + /// and coerce into it instead of ATPIT itself. + /// + /// The inference var stretagy is effective because; + /// + /// - It can still unify types that coerced into ATPIT + /// - We are pushing `impl Trait` bounds into it + /// + /// This function inserts a map that maps the opaque type to that proxy inference var. + fn insert_atpit_coercion_table<'b>(&mut self, tys: impl Iterator) { + struct OpaqueTyCollector<'a, 'b> { + table: &'b mut InferenceTable<'a>, + opaque_tys: FxHashMap, + } + + impl<'a, 'b> TypeVisitor for OpaqueTyCollector<'a, 'b> { + type BreakTy = (); + + fn as_dyn(&mut self) -> &mut dyn TypeVisitor { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn visit_ty( + &mut self, + ty: &chalk_ir::Ty, + outer_binder: DebruijnIndex, + ) -> std::ops::ControlFlow { + let ty = self.table.resolve_ty_shallow(ty); + + if let TyKind::OpaqueType(id, _) = ty.kind(Interner) { + self.opaque_tys.insert(*id, ty.clone()); + } + + ty.super_visit_with(self, outer_binder) + } + } + + // Early return if this is not happening inside the impl block + let impl_id = if let Some(impl_id) = self.resolver.impl_def() { + impl_id + } else { + return; + }; + + let assoc_tys: FxHashSet<_> = self + .db + .impl_data(impl_id) + .items + .iter() + .filter_map(|item| match item { + AssocItemId::TypeAliasId(alias) => Some(*alias), + _ => None, + }) + .collect(); + if assoc_tys.is_empty() { + return; + } + + let mut collector = + OpaqueTyCollector { table: &mut self.table, opaque_tys: FxHashMap::default() }; + for ty in tys { + ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST); + } + let atpit_coercion_table: FxHashMap<_, _> = collector + .opaque_tys + .into_iter() + .filter_map(|(opaque_ty_id, ty)| { + if let ImplTraitId::AssociatedTypeImplTrait(alias_id, _) = + self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) + { + if assoc_tys.contains(&alias_id) { + let atpits = self + .db + .type_alias_impl_traits(alias_id) + .expect("Marked as ATPIT but no impl traits!"); + let alias_placeholders = TyBuilder::placeholder_subst(self.db, alias_id); + let ty = self.insert_inference_vars_for_impl_trait( + ty, + atpits, + alias_placeholders, + ); + return Some((opaque_ty_id, ty)); + } + } + + None + }) + .collect(); + + if !atpit_coercion_table.is_empty() { + self.table.atpit_coercion_table = Some(atpit_coercion_table); + } + } + fn infer_body(&mut self) { match self.return_coercion { Some(_) => self.infer_return(self.body.body_expr), @@ -918,6 +1037,12 @@ impl<'a> InferenceContext<'a> { self.result.standard_types.unknown.clone() } + fn make_lifetime(&mut self, lifetime_ref: &LifetimeRef) -> Lifetime { + let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into()); + let lt = ctx.lower_lifetime(lifetime_ref); + self.insert_type_vars(lt) + } + /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { self.table.insert_type_vars_shallow(ty) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index ff6de61ba649..cfbbc9dd6c04 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -276,6 +276,23 @@ impl InferenceTable<'_> { return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]); } + // If we are coercing into an ATPIT, coerce into its proxy inference var, instead. + let mut to_ty = to_ty; + let _to; + if let Some(atpit_table) = &self.atpit_coercion_table { + if let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) { + if !matches!( + from_ty.kind(Interner), + TyKind::InferenceVar(..) | TyKind::OpaqueType(..) + ) { + if let Some(ty) = atpit_table.get(opaque_ty_id) { + _to = ty.clone(); + to_ty = &_to; + } + } + } + } + // Consider coercing the subtype to a DST if let Ok(ret) = self.try_coerce_unsized(&from_ty, to_ty) { return Ok(ret); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index a3dab1fd9d54..35d59679355a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -8,13 +8,12 @@ use std::{ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKind}; use either::Either; use hir_def::{ - generics::TypeOrConstParamData, hir::{ ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp, }, lang_item::{LangItem, LangItemTarget}, - path::{GenericArg, GenericArgs, Path}, - BlockId, ConstParamId, FieldId, ItemContainerId, Lookup, TupleFieldId, TupleId, + path::{GenericArgs, Path}, + BlockId, FieldId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId, }; use hir_expand::name::{name, Name}; use stdx::always; @@ -1816,10 +1815,17 @@ impl InferenceContext<'_> { def_generics: Generics, generic_args: Option<&GenericArgs>, ) -> Substitution { - let (parent_params, self_params, type_params, const_params, impl_trait_params) = - def_generics.provenance_split(); + let ( + parent_params, + self_params, + type_params, + const_params, + impl_trait_params, + lifetime_params, + ) = def_generics.provenance_split(); assert_eq!(self_params, 0); // method shouldn't have another Self param - let total_len = parent_params + type_params + const_params + impl_trait_params; + let total_len = + parent_params + type_params + const_params + impl_trait_params + lifetime_params; let mut substs = Vec::with_capacity(total_len); // handle provided arguments @@ -1828,8 +1834,7 @@ impl InferenceContext<'_> { for (arg, kind_id) in generic_args .args .iter() - .filter(|arg| !matches!(arg, GenericArg::Lifetime(_))) - .take(type_params + const_params) + .take(type_params + const_params + lifetime_params) .zip(def_generics.iter_id()) { if let Some(g) = generic_arg_to_chalk( @@ -1850,6 +1855,7 @@ impl InferenceContext<'_> { DebruijnIndex::INNERMOST, ) }, + |this, lt_ref| this.make_lifetime(lt_ref), ) { substs.push(g); } @@ -1858,16 +1864,17 @@ impl InferenceContext<'_> { // Handle everything else as unknown. This also handles generic arguments for the method's // parent (impl or trait), which should come after those for the method. - for (id, data) in def_generics.iter().skip(substs.len()) { - match data { - TypeOrConstParamData::TypeParamData(_) => { + for (id, _data) in def_generics.iter().skip(substs.len()) { + match id { + GenericParamId::TypeParamId(_) => { substs.push(self.table.new_type_var().cast(Interner)) } - TypeOrConstParamData::ConstParamData(_) => substs.push( - self.table - .new_const_var(self.db.const_param_ty(ConstParamId::from_unchecked(id))) - .cast(Interner), - ), + GenericParamId::ConstParamId(id) => { + substs.push(self.table.new_const_var(self.db.const_param_ty(id)).cast(Interner)) + } + GenericParamId::LifetimeParamId(_) => { + substs.push(self.table.new_lifetime_var().cast(Interner)) + } } } assert_eq!(substs.len(), total_len); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 8f537bb448b9..9a1835b625b3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -11,15 +11,15 @@ use stdx::never; use crate::{ builder::ParamKind, - consteval, + consteval, error_lifetime, method_resolution::{self, VisibleFromModule}, to_chalk_trait_id, utils::generics, - InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, - ValueTyDefId, + InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, + TyKind, ValueTyDefId, }; -use super::{ExprOrPatId, InferenceContext, TraitRef}; +use super::{ExprOrPatId, InferenceContext}; impl InferenceContext<'_> { pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option { @@ -111,6 +111,7 @@ impl InferenceContext<'_> { it.next().unwrap_or_else(|| match x { ParamKind::Type => self.result.standard_types.unknown.clone().cast(Interner), ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }) }) .build(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index be7547f9bae5..afb89fe1e5b1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -10,16 +10,18 @@ use chalk_solve::infer::ParameterEnaVariableExt; use either::Either; use ena::unify::UnifyKey; use hir_expand::name; +use rustc_hash::FxHashMap; use smallvec::SmallVec; use triomphe::Arc; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ - consteval::unknown_const, db::HirDatabase, fold_tys_and_consts, static_lifetime, - to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, - DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment, - InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, - Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, + consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, + static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, + Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, + Guidance, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, + ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, + TyKind, VariableKind, WhereClause, }; impl InferenceContext<'_> { @@ -239,6 +241,7 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable; pub(crate) struct InferenceTable<'a> { pub(crate) db: &'a dyn HirDatabase, pub(crate) trait_env: Arc, + pub(crate) atpit_coercion_table: Option>, var_unification_table: ChalkInferenceTable, type_variable_table: SmallVec<[TypeVariableFlags; 16]>, pending_obligations: Vec>>, @@ -258,6 +261,7 @@ impl<'a> InferenceTable<'a> { InferenceTable { db, trait_env, + atpit_coercion_table: None, var_unification_table: ChalkInferenceTable::new(), type_variable_table: SmallVec::new(), pending_obligations: Vec::new(), @@ -803,6 +807,7 @@ impl<'a> InferenceTable<'a> { .fill(|it| { let arg = match it { ParamKind::Type => self.new_type_var(), + ParamKind::Lifetime => unreachable!("Tuple with lifetime parameter"), ParamKind::Const(_) => unreachable!("Tuple with const parameter"), }; arg_tys.push(arg.clone()); @@ -857,11 +862,16 @@ impl<'a> InferenceTable<'a> { where T: HasInterner + TypeFoldable, { - fold_tys_and_consts( + fold_generic_args( ty, - |it, _| match it { - Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)), - Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)), + |arg, _| match arg { + GenericArgData::Ty(ty) => GenericArgData::Ty(self.insert_type_vars_shallow(ty)), + // FIXME: insert lifetime vars once LifetimeData::InferenceVar + // and specific error variant for lifetimes start being constructed + GenericArgData::Lifetime(lt) => GenericArgData::Lifetime(lt), + GenericArgData::Const(c) => { + GenericArgData::Const(self.insert_const_vars_shallow(c)) + } }, DebruijnIndex::INNERMOST, ) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 9655981cc9cc..dd949e26c2ab 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -389,6 +389,9 @@ pub fn layout_of_ty_query( let infer = db.infer(func.into()); return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env); } + crate::ImplTraitId::AssociatedTypeImplTrait(..) => { + return Err(LayoutError::NotImplemented); + } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { return Err(LayoutError::NotImplemented) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index ec97bdc2c434..ba64f5c8d7ea 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -15,7 +15,8 @@ extern crate rustc_abi; #[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_abi as rustc_abi; -// No need to use the in-tree one. +// Use the crates.io version unconditionally until the API settles enough that we can switch to +// using the in-tree one. extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis; mod builder; @@ -89,8 +90,8 @@ pub use lower::{ }; pub use mapping::{ from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, - lt_from_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, to_foreign_def_id, - to_placeholder_idx, + lt_from_placeholder_idx, lt_to_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, + to_foreign_def_id, to_placeholder_idx, }; pub use method_resolution::check_orphan_rules; pub use traits::TraitEnvironment; @@ -334,11 +335,23 @@ pub(crate) fn make_binders_with_count>( generics: &Generics, value: T, ) -> Binders { - let it = generics.iter_id().take(count).map(|id| match id { - Either::Left(_) => None, - Either::Right(id) => Some(db.const_param_ty(id)), - }); - crate::make_type_and_const_binders(it, value) + let it = generics.iter_id().take(count); + + Binders::new( + VariableKinds::from_iter( + Interner, + it.map(|x| match x { + hir_def::GenericParamId::ConstParamId(id) => { + chalk_ir::VariableKind::Const(db.const_param_ty(id)) + } + hir_def::GenericParamId::TypeParamId(_) => { + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) + } + hir_def::GenericParamId::LifetimeParamId(_) => chalk_ir::VariableKind::Lifetime, + }), + ), + value, + ) } pub(crate) fn make_binders>( @@ -584,29 +597,34 @@ impl TypeFoldable for CallableSig { #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum ImplTraitId { - ReturnTypeImplTrait(hir_def::FunctionId, RpitId), + ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx), + AssociatedTypeImplTrait(hir_def::TypeAliasId, ImplTraitIdx), AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId), } impl_intern_value_trivial!(ImplTraitId); #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct ReturnTypeImplTraits { - pub(crate) impl_traits: Arena, +pub struct ImplTraits { + pub(crate) impl_traits: Arena, } -has_interner!(ReturnTypeImplTraits); +has_interner!(ImplTraits); #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct ReturnTypeImplTrait { +pub struct ImplTrait { pub(crate) bounds: Binders>, } -pub type RpitId = Idx; +pub type ImplTraitIdx = Idx; pub fn static_lifetime() -> Lifetime { LifetimeData::Static.intern(Interner) } +pub fn error_lifetime() -> Lifetime { + static_lifetime() +} + pub(crate) fn fold_free_vars + TypeFoldable>( t: T, for_ty: impl FnMut(BoundVar, DebruijnIndex) -> Ty, @@ -696,6 +714,55 @@ pub(crate) fn fold_tys_and_consts + TypeFold t.fold_with(&mut TyFolder(f), binders) } +pub(crate) fn fold_generic_args + TypeFoldable>( + t: T, + f: impl FnMut(GenericArgData, DebruijnIndex) -> GenericArgData, + binders: DebruijnIndex, +) -> T { + use chalk_ir::fold::{TypeFolder, TypeSuperFoldable}; + #[derive(chalk_derive::FallibleTypeFolder)] + #[has_interner(Interner)] + struct TyFolder GenericArgData>(F); + impl GenericArgData> TypeFolder + for TyFolder + { + fn as_dyn(&mut self) -> &mut dyn TypeFolder { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Ty { + let ty = ty.super_fold_with(self.as_dyn(), outer_binder); + self.0(GenericArgData::Ty(ty), outer_binder) + .intern(Interner) + .ty(Interner) + .unwrap() + .clone() + } + + fn fold_const(&mut self, c: Const, outer_binder: DebruijnIndex) -> Const { + self.0(GenericArgData::Const(c), outer_binder) + .intern(Interner) + .constant(Interner) + .unwrap() + .clone() + } + + fn fold_lifetime(&mut self, lt: Lifetime, outer_binder: DebruijnIndex) -> Lifetime { + let lt = lt.super_fold_with(self.as_dyn(), outer_binder); + self.0(GenericArgData::Lifetime(lt), outer_binder) + .intern(Interner) + .lifetime(Interner) + .unwrap() + .clone() + } + } + t.fold_with(&mut TyFolder(f), binders) +} + /// 'Canonicalizes' the `t` by replacing any errors with new variables. Also /// ensures there are no unbound variables or inference variables anywhere in /// the `t`. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index dac20f225971..25ccc84c13c8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -24,17 +24,20 @@ use hir_def::{ data::adt::StructKind, expander::Expander, generics::{ - TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, + GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate, + WherePredicateTypeTarget, }, lang_item::LangItem, nameres::MacroSubNs, path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments}, - resolver::{HasResolver, Resolver, TypeNs}, - type_ref::{ConstRef, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef}, + resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, + type_ref::{ + ConstRef, LifetimeRef, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef, + }, AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, - GenericDefId, HasModule, ImplId, InTypeConstLoc, ItemContainerId, LocalFieldId, Lookup, - ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, - TypeParamId, UnionId, VariantId, + GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstLoc, ItemContainerId, LocalFieldId, + Lookup, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, + UnionId, VariantId, }; use hir_expand::{name::Name, ExpandResult}; use intern::Interned; @@ -52,18 +55,18 @@ use crate::{ unknown_const_as_generic, }, db::HirDatabase, - make_binders, - mapping::{from_chalk_trait_id, ToChalk}, + error_lifetime, make_binders, + mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk}, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, - utils::Generics, utils::{ - all_super_trait_refs, associated_type_by_name_including_super_traits, generics, + all_super_trait_refs, associated_type_by_name_including_super_traits, generics, Generics, InTypeConstIdMetadata, }, AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy, - FnAbi, FnPointer, FnSig, FnSubst, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy, - QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits, - Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, + FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, + LifetimeData, ParamKind, PolyFnSig, ProjectionTy, QuantifiedWhereClause, + QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, + TyKind, WhereClause, }; #[derive(Debug)] @@ -76,7 +79,7 @@ enum ImplTraitLoweringState { /// we're grouping the mutable data (the counter and this field) together /// with the immutable context (the references to the DB and resolver). /// Splitting this up would be a possible fix. - Opaque(RefCell>), + Opaque(RefCell>), Param(Cell), Variable(Cell), Disallowed, @@ -275,9 +278,11 @@ impl<'a> TyLoweringContext<'a> { let inner_ty = self.lower_ty(inner); TyKind::Slice(inner_ty).intern(Interner) } - TypeRef::Reference(inner, _, mutability) => { + TypeRef::Reference(inner, lifetime, mutability) => { let inner_ty = self.lower_ty(inner); - let lifetime = static_lifetime(); + // FIXME: It should infer the eldided lifetimes instead of stubbing with static + let lifetime = + lifetime.as_ref().map_or_else(static_lifetime, |lr| self.lower_lifetime(lr)); TyKind::Ref(lower_to_chalk_mutability(*mutability), lifetime, inner_ty) .intern(Interner) } @@ -301,15 +306,18 @@ impl<'a> TyLoweringContext<'a> { TypeRef::ImplTrait(bounds) => { match &self.impl_trait_mode { ImplTraitLoweringState::Opaque(opaque_type_data) => { - let func = match self.resolver.generic_def() { - Some(GenericDefId::FunctionId(f)) => f, - _ => panic!("opaque impl trait lowering in non-function"), + let origin = match self.resolver.generic_def() { + Some(GenericDefId::FunctionId(it)) => Either::Left(it), + Some(GenericDefId::TypeAliasId(it)) => Either::Right(it), + _ => panic!( + "opaque impl trait lowering must be in function or type alias" + ), }; // this dance is to make sure the data is in the right // place even if we encounter more opaque types while // lowering the bounds - let idx = opaque_type_data.borrow_mut().alloc(ReturnTypeImplTrait { + let idx = opaque_type_data.borrow_mut().alloc(ImplTrait { bounds: crate::make_single_type_binders(Vec::new()), }); // We don't want to lower the bounds inside the binders @@ -323,13 +331,17 @@ impl<'a> TyLoweringContext<'a> { // away instead of two. let actual_opaque_type_data = self .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { - ctx.lower_impl_trait(bounds, func) + ctx.lower_impl_trait(bounds, self.resolver.krate()) }); opaque_type_data.borrow_mut()[idx] = actual_opaque_type_data; - let impl_trait_id = ImplTraitId::ReturnTypeImplTrait(func, idx); + let impl_trait_id = origin.either( + |f| ImplTraitId::ReturnTypeImplTrait(f, idx), + |a| ImplTraitId::AssociatedTypeImplTrait(a, idx), + ); let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); - let generics = generics(self.db.upcast(), func.into()); + let generics = + generics(self.db.upcast(), origin.either(|f| f.into(), |a| a.into())); let parameters = generics.bound_vars_subst(self.db, self.in_binders); TyKind::OpaqueType(opaque_ty_id, parameters).intern(Interner) } @@ -344,13 +356,18 @@ impl<'a> TyLoweringContext<'a> { .filter(|(_, data)| { matches!( data, - TypeOrConstParamData::TypeParamData(data) + GenericParamDataRef::TypeParamData(data) if data.provenance == TypeParamProvenance::ArgumentImplTrait ) }) .nth(idx as usize) .map_or(TyKind::Error, |(id, _)| { - TyKind::Placeholder(to_placeholder_idx(self.db, id)) + if let GenericParamId::TypeParamId(id) = id { + TyKind::Placeholder(to_placeholder_idx(self.db, id.into())) + } else { + // we just filtered them out + unreachable!("Unexpected lifetime or const argument"); + } }); param.intern(Interner) } else { @@ -367,11 +384,12 @@ impl<'a> TyLoweringContext<'a> { list_params, const_params, _impl_trait_params, + _lifetime_params, ) = if let Some(def) = self.resolver.generic_def() { let generics = generics(self.db.upcast(), def); generics.provenance_split() } else { - (0, 0, 0, 0, 0) + (0, 0, 0, 0, 0, 0) }; TyKind::BoundVar(BoundVar::new( self.in_binders, @@ -808,9 +826,16 @@ impl<'a> TyLoweringContext<'a> { return Substitution::empty(Interner); }; let def_generics = generics(self.db.upcast(), def); - let (parent_params, self_params, type_params, const_params, impl_trait_params) = - def_generics.provenance_split(); - let item_len = self_params + type_params + const_params + impl_trait_params; + let ( + parent_params, + self_params, + type_params, + const_params, + impl_trait_params, + lifetime_params, + ) = def_generics.provenance_split(); + let item_len = + self_params + type_params + const_params + impl_trait_params + lifetime_params; let total_len = parent_params + item_len; let ty_error = TyKind::Error.intern(Interner).cast(Interner); @@ -825,7 +850,10 @@ impl<'a> TyLoweringContext<'a> { .take(self_params) { if let Some(id) = def_generic_iter.next() { - assert!(id.is_left()); + assert!(matches!( + id, + GenericParamId::TypeParamId(_) | GenericParamId::LifetimeParamId(_) + )); substs.push(x); } } @@ -858,6 +886,7 @@ impl<'a> TyLoweringContext<'a> { &mut (), |_, type_ref| self.lower_ty(type_ref), |_, const_ref, ty| self.lower_const(const_ref, ty), + |_, lifetime_ref| self.lower_lifetime(lifetime_ref), ) { had_explicit_args = true; substs.push(x); @@ -867,15 +896,45 @@ impl<'a> TyLoweringContext<'a> { } } } + + for arg in generic_args + .args + .iter() + .filter(|arg| matches!(arg, GenericArg::Lifetime(_))) + .take(lifetime_params) + { + // Taking into the fact that def_generic_iter will always have lifetimes at the end + // Should have some test cases tho to test this behaviour more properly + if let Some(id) = def_generic_iter.next() { + if let Some(x) = generic_arg_to_chalk( + self.db, + id, + arg, + &mut (), + |_, type_ref| self.lower_ty(type_ref), + |_, const_ref, ty| self.lower_const(const_ref, ty), + |_, lifetime_ref| self.lower_lifetime(lifetime_ref), + ) { + had_explicit_args = true; + substs.push(x); + } else { + // Never return a None explicitly + never!("Unexpected None by generic_arg_to_chalk"); + } + } + } } else { fill_self_params(); } // These params include those of parent. let remaining_params: SmallVec<[_; 2]> = def_generic_iter - .map(|eid| match eid { - Either::Left(_) => ty_error.clone(), - Either::Right(x) => unknown_const_as_generic(self.db.const_param_ty(x)), + .map(|id| match id { + GenericParamId::ConstParamId(x) => { + unknown_const_as_generic(self.db.const_param_ty(x)) + } + GenericParamId::TypeParamId(_) => ty_error.clone(), + GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), }) .collect(); assert_eq!(remaining_params.len() + substs.len(), total_len); @@ -1107,8 +1166,12 @@ impl<'a> TyLoweringContext<'a> { binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), ); if let Some(type_ref) = &binding.type_ref { - if let (TypeRef::ImplTrait(bounds), ImplTraitLoweringState::Disallowed) = - (type_ref, &self.impl_trait_mode) + if let ( + TypeRef::ImplTrait(bounds), + ImplTraitLoweringState::Param(_) + | ImplTraitLoweringState::Variable(_) + | ImplTraitLoweringState::Disallowed, + ) = (type_ref, &self.impl_trait_mode) { for bound in bounds { predicates.extend( @@ -1270,11 +1333,7 @@ impl<'a> TyLoweringContext<'a> { } } - fn lower_impl_trait( - &self, - bounds: &[Interned], - func: FunctionId, - ) -> ReturnTypeImplTrait { + fn lower_impl_trait(&self, bounds: &[Interned], krate: CrateId) -> ImplTrait { cov_mark::hit!(lower_rpit); let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner); let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { @@ -1284,7 +1343,6 @@ impl<'a> TyLoweringContext<'a> { .collect(); if !ctx.unsized_types.borrow().contains(&self_ty) { - let krate = func.krate(ctx.db.upcast()); let sized_trait = ctx .db .lang_item(krate, LangItem::Sized) @@ -1301,7 +1359,34 @@ impl<'a> TyLoweringContext<'a> { } predicates }); - ReturnTypeImplTrait { bounds: crate::make_single_type_binders(predicates) } + ImplTrait { bounds: crate::make_single_type_binders(predicates) } + } + + pub fn lower_lifetime(&self, lifetime: &LifetimeRef) -> Lifetime { + match self.resolver.resolve_lifetime(lifetime) { + Some(resolution) => match resolution { + LifetimeNs::Static => static_lifetime(), + LifetimeNs::LifetimeParam(id) => match self.type_param_mode { + ParamLoweringMode::Placeholder => { + LifetimeData::Placeholder(lt_to_placeholder_idx(self.db, id)) + } + ParamLoweringMode::Variable => { + let generics = generics( + self.db.upcast(), + self.resolver.generic_def().expect("generics in scope"), + ); + let idx = match generics.lifetime_idx(id) { + None => return error_lifetime(), + Some(idx) => idx, + }; + + LifetimeData::BoundVar(BoundVar::new(self.in_binders, idx)) + } + } + .intern(Interner), + }, + None => error_lifetime(), + } } } @@ -1685,7 +1770,7 @@ pub(crate) fn generic_defaults_query( let defaults = Arc::from_iter(generic_params.iter().enumerate().map(|(idx, (id, p))| { match p { - TypeOrConstParamData::TypeParamData(p) => { + GenericParamDataRef::TypeParamData(p) => { let mut ty = p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t)); // Each default can only refer to previous parameters. @@ -1694,13 +1779,13 @@ pub(crate) fn generic_defaults_query( ty = fallback_bound_vars(ty, idx, parent_start_idx); crate::make_binders(db, &generic_params, ty.cast(Interner)) } - TypeOrConstParamData::ConstParamData(p) => { + GenericParamDataRef::ConstParamData(p) => { + let GenericParamId::ConstParamId(id) = id else { + unreachable!("Unexpected lifetime or type argument") + }; + let mut val = p.default.as_ref().map_or_else( - || { - unknown_const_as_generic( - db.const_param_ty(ConstParamId::from_unchecked(id)), - ) - }, + || unknown_const_as_generic(db.const_param_ty(id)), |c| { let c = ctx.lower_const(c, ctx.lower_ty(&p.ty)); c.cast(Interner) @@ -1710,6 +1795,10 @@ pub(crate) fn generic_defaults_query( val = fallback_bound_vars(val, idx, parent_start_idx); make_binders(db, &generic_params, val) } + GenericParamDataRef::LifetimeParamData(_) => { + // using static because it requires defaults + make_binders(db, &generic_params, static_lifetime().cast(Interner)) + } } })); @@ -1726,8 +1815,9 @@ pub(crate) fn generic_defaults_recover( // we still need one default per parameter let defaults = Arc::from_iter(generic_params.iter_id().map(|id| { let val = match id { - Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner), - Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)), + GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner), + GenericParamId::ConstParamId(id) => unknown_const_as_generic(db.const_param_ty(id)), + GenericParamId::LifetimeParamId(_) => static_lifetime().cast(Interner), }; crate::make_binders(db, &generic_params, val) })); @@ -1869,6 +1959,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders { let generics = generics(db.upcast(), t.into()); let resolver = t.resolver(db.upcast()); let ctx = TyLoweringContext::new(db, &resolver, t.into()) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) .with_type_param_mode(ParamLoweringMode::Variable); let type_alias_data = db.type_alias_data(t); if type_alias_data.is_extern { @@ -2029,7 +2120,7 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option< pub(crate) fn return_type_impl_traits( db: &dyn HirDatabase, def: hir_def::FunctionId, -) -> Option>> { +) -> Option>> { // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe let data = db.function_data(def); let resolver = def.resolver(db.upcast()); @@ -2038,7 +2129,7 @@ pub(crate) fn return_type_impl_traits( .with_type_param_mode(ParamLoweringMode::Variable); let _ret = ctx_ret.lower_ty(&data.ret_type); let generics = generics(db.upcast(), def.into()); - let return_type_impl_traits = ReturnTypeImplTraits { + let return_type_impl_traits = ImplTraits { impl_traits: match ctx_ret.impl_trait_mode { ImplTraitLoweringState::Opaque(x) => x.into_inner(), _ => unreachable!(), @@ -2051,6 +2142,32 @@ pub(crate) fn return_type_impl_traits( } } +pub(crate) fn type_alias_impl_traits( + db: &dyn HirDatabase, + def: hir_def::TypeAliasId, +) -> Option>> { + let data = db.type_alias_data(def); + let resolver = def.resolver(db.upcast()); + let ctx = TyLoweringContext::new(db, &resolver, def.into()) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) + .with_type_param_mode(ParamLoweringMode::Variable); + if let Some(type_ref) = &data.type_ref { + let _ty = ctx.lower_ty(type_ref); + } + let generics = generics(db.upcast(), def.into()); + let type_alias_impl_traits = ImplTraits { + impl_traits: match ctx.impl_trait_mode { + ImplTraitLoweringState::Opaque(x) => x.into_inner(), + _ => unreachable!(), + }, + }; + if type_alias_impl_traits.impl_traits.is_empty() { + None + } else { + Some(Arc::new(make_binders(db, &generics, type_alias_impl_traits))) + } +} + pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mutability { match m { hir_def::type_ref::Mutability::Shared => Mutability::Not, @@ -2064,23 +2181,29 @@ pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mut /// Returns `Some` of the lowered generic arg. `None` if the provided arg is a lifetime. pub(crate) fn generic_arg_to_chalk<'a, T>( db: &dyn HirDatabase, - kind_id: Either, + kind_id: GenericParamId, arg: &'a GenericArg, this: &mut T, for_type: impl FnOnce(&mut T, &TypeRef) -> Ty + 'a, for_const: impl FnOnce(&mut T, &ConstRef, Ty) -> Const + 'a, + for_lifetime: impl FnOnce(&mut T, &LifetimeRef) -> Lifetime + 'a, ) -> Option { let kind = match kind_id { - Either::Left(_) => ParamKind::Type, - Either::Right(id) => { + GenericParamId::TypeParamId(_) => ParamKind::Type, + GenericParamId::ConstParamId(id) => { let ty = db.const_param_ty(id); ParamKind::Const(ty) } + GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime, }; Some(match (arg, kind) { (GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, type_ref).cast(Interner), (GenericArg::Const(c), ParamKind::Const(c_ty)) => for_const(this, c, c_ty).cast(Interner), + (GenericArg::Lifetime(lifetime_ref), ParamKind::Lifetime) => { + for_lifetime(this, lifetime_ref).cast(Interner) + } (GenericArg::Const(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner), + (GenericArg::Lifetime(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner), (GenericArg::Type(t), ParamKind::Const(c_ty)) => { // We want to recover simple idents, which parser detects them // as types. Maybe here is not the best place to do it, but @@ -2096,7 +2219,9 @@ pub(crate) fn generic_arg_to_chalk<'a, T>( } unknown_const_as_generic(c_ty) } - (GenericArg::Lifetime(_), _) => return None, + (GenericArg::Lifetime(_), ParamKind::Const(c_ty)) => unknown_const_as_generic(c_ty), + (GenericArg::Type(_), ParamKind::Lifetime) => error_lifetime().cast(Interner), + (GenericArg::Const(_), ParamKind::Lifetime) => error_lifetime().cast(Interner), }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs index fba760974f24..c61d82771429 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs @@ -151,6 +151,14 @@ pub fn lt_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> L db.lookup_intern_lifetime_param_id(interned_id) } +pub fn lt_to_placeholder_idx(db: &dyn HirDatabase, id: LifetimeParamId) -> PlaceholderIndex { + let interned_id = db.intern_lifetime_param_id(id); + PlaceholderIndex { + ui: chalk_ir::UniverseIndex::ROOT, + idx: salsa::InternKey::as_intern_id(&interned_id).as_usize(), + } +} + pub fn to_chalk_trait_id(id: TraitId) -> ChalkTraitId { chalk_ir::TraitId(salsa::InternKey::as_intern_id(&id)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index a679a114b4b9..73b07df56f7e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -4,7 +4,7 @@ //! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs. use std::ops::ControlFlow; -use base_db::{CrateId, Edition}; +use base_db::CrateId; use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause}; use hir_def::{ data::{adt::StructFlags, ImplData}, @@ -15,6 +15,7 @@ use hir_def::{ use hir_expand::name::Name; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{smallvec, SmallVec}; +use span::Edition; use stdx::never; use triomphe::Arc; @@ -643,7 +644,7 @@ pub fn is_dyn_method( let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else { return None; }; - let trait_params = db.generic_params(trait_id.into()).type_or_consts.len(); + let trait_params = db.generic_params(trait_id.into()).len(); let fn_params = fn_subst.len(Interner) - trait_params; let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), @@ -685,7 +686,7 @@ pub(crate) fn lookup_impl_method_query( let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else { return (func, fn_subst); }; - let trait_params = db.generic_params(trait_id.into()).type_or_consts.len(); + let trait_params = db.generic_params(trait_id.into()).len(); let fn_params = fn_subst.len(Interner) - trait_params; let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), @@ -966,7 +967,7 @@ pub fn iterate_method_candidates_dyn( // the methods by autoderef order of *receiver types*, not *self // types*. - let mut table = InferenceTable::new(db, env.clone()); + let mut table = InferenceTable::new(db, env); let ty = table.instantiate_canonical(ty.clone()); let deref_chain = autoderef_method_receiver(&mut table, ty); @@ -1044,7 +1045,7 @@ fn iterate_method_candidates_with_autoref( let ref_muted = Canonical { value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone()) .intern(Interner), - binders: receiver_ty.binders.clone(), + binders: receiver_ty.binders, }; iterate_method_candidates_by_receiver(ref_muted, first_adjustment.with_autoref(Mutability::Mut)) @@ -1060,7 +1061,7 @@ fn iterate_method_candidates_by_receiver( name: Option<&Name>, mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { - let receiver_ty = table.instantiate_canonical(receiver_ty.clone()); + let receiver_ty = table.instantiate_canonical(receiver_ty); // We're looking for methods with *receiver* type receiver_ty. These could // be found in any of the derefs of receiver_ty, so we have to go through // that, including raw derefs. @@ -1456,7 +1457,7 @@ fn is_valid_trait_method_candidate( if let Some(receiver_ty) = receiver_ty { check_that!(data.has_self_param()); - let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone())) + let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst)) .fill_with_inference_vars(table) .build(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index fd98141af63e..045ffb418c8d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -1931,7 +1931,11 @@ impl Evaluator<'_> { ty: &Ty, locals: &Locals, mm: &mut ComplexMemoryMap, + stack_depth_limit: usize, ) -> Result<()> { + if stack_depth_limit.checked_sub(1).is_none() { + return Err(MirEvalError::StackOverflow); + } match ty.kind(Interner) { TyKind::Ref(_, _, t) => { let size = this.size_align_of(t, locals)?; @@ -1970,7 +1974,14 @@ impl Evaluator<'_> { if let Some(ty) = check_inner { for i in 0..count { let offset = element_size * i; - rec(this, &b[offset..offset + element_size], ty, locals, mm)?; + rec( + this, + &b[offset..offset + element_size], + ty, + locals, + mm, + stack_depth_limit - 1, + )?; } } } @@ -1984,7 +1995,14 @@ impl Evaluator<'_> { let size = this.size_of_sized(inner, locals, "inner of array")?; for i in 0..len { let offset = i * size; - rec(this, &bytes[offset..offset + size], inner, locals, mm)?; + rec( + this, + &bytes[offset..offset + size], + inner, + locals, + mm, + stack_depth_limit - 1, + )?; } } chalk_ir::TyKind::Tuple(_, subst) => { @@ -1993,7 +2011,14 @@ impl Evaluator<'_> { let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument let offset = layout.fields.offset(id).bytes_usize(); let size = this.layout(ty)?.size.bytes_usize(); - rec(this, &bytes[offset..offset + size], ty, locals, mm)?; + rec( + this, + &bytes[offset..offset + size], + ty, + locals, + mm, + stack_depth_limit - 1, + )?; } } chalk_ir::TyKind::Adt(adt, subst) => match adt.0 { @@ -2008,7 +2033,14 @@ impl Evaluator<'_> { .bytes_usize(); let ty = &field_types[f].clone().substitute(Interner, subst); let size = this.layout(ty)?.size.bytes_usize(); - rec(this, &bytes[offset..offset + size], ty, locals, mm)?; + rec( + this, + &bytes[offset..offset + size], + ty, + locals, + mm, + stack_depth_limit - 1, + )?; } } AdtId::EnumId(e) => { @@ -2027,7 +2059,14 @@ impl Evaluator<'_> { l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize(); let ty = &field_types[f].clone().substitute(Interner, subst); let size = this.layout(ty)?.size.bytes_usize(); - rec(this, &bytes[offset..offset + size], ty, locals, mm)?; + rec( + this, + &bytes[offset..offset + size], + ty, + locals, + mm, + stack_depth_limit - 1, + )?; } } } @@ -2038,7 +2077,7 @@ impl Evaluator<'_> { Ok(()) } let mut mm = ComplexMemoryMap::default(); - rec(self, bytes, ty, locals, &mut mm)?; + rec(self, bytes, ty, locals, &mut mm, self.stack_depth_limit - 1)?; Ok(mm) } @@ -2317,7 +2356,7 @@ impl Evaluator<'_> { fn exec_fn_with_args( &mut self, - def: FunctionId, + mut def: FunctionId, args: &[IntervalAndTy], generic_args: Substitution, locals: &Locals, @@ -2335,6 +2374,9 @@ impl Evaluator<'_> { )? { return Ok(None); } + if let Some(redirect_def) = self.detect_and_redirect_special_function(def)? { + def = redirect_def; + } let arg_bytes = args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval)); match self.get_mir_or_dyn_index(def, generic_args.clone(), locals, span)? { MirOrDynIndex::Dyn(self_ty_idx) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 628a1fe2d283..d4d669182f2e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -13,7 +13,7 @@ use crate::mir::eval::{ name, pad16, static_lifetime, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, HasModule, HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy, IntervalOrOwned, ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan, - ModPath, Mutability, Result, Substitution, Ty, TyBuilder, TyExt, + Mutability, Result, Substitution, Ty, TyBuilder, TyExt, }; mod simd; @@ -158,6 +158,25 @@ impl Evaluator<'_> { Ok(false) } + pub(super) fn detect_and_redirect_special_function( + &mut self, + def: FunctionId, + ) -> Result> { + // `PanicFmt` is redirected to `ConstPanicFmt` + if let Some(LangItem::PanicFmt) = self.db.lang_attr(def.into()) { + let resolver = + self.db.crate_def_map(self.crate_id).crate_root().resolver(self.db.upcast()); + + let Some(hir_def::lang_item::LangItemTarget::Function(const_panic_fmt)) = + self.db.lang_item(resolver.krate(), LangItem::ConstPanicFmt) + else { + not_supported!("const_panic_fmt lang item not found or not a function"); + }; + return Ok(Some(const_panic_fmt)); + } + Ok(None) + } + /// Clone has special impls for tuples and function pointers fn exec_clone( &mut self, @@ -291,9 +310,14 @@ impl Evaluator<'_> { use LangItem::*; let candidate = self.db.lang_attr(def.into())?; // We want to execute these functions with special logic - if [PanicFmt, BeginPanic, SliceLen, DropInPlace].contains(&candidate) { + // `PanicFmt` is not detected here as it's redirected later. + if [BeginPanic, SliceLen, DropInPlace].contains(&candidate) { return Some(candidate); } + if self.db.attrs(def.into()).by_key("rustc_const_panic_str").exists() { + // `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE. + return Some(LangItem::BeginPanic); + } None } @@ -309,43 +333,6 @@ impl Evaluator<'_> { let mut args = args.iter(); match it { BeginPanic => Err(MirEvalError::Panic("".to_owned())), - PanicFmt => { - let message = (|| { - let resolver = self - .db - .crate_def_map(self.crate_id) - .crate_root() - .resolver(self.db.upcast()); - let Some(format_fn) = resolver.resolve_path_in_value_ns_fully( - self.db.upcast(), - &hir_def::path::Path::from_known_path_with_no_generic( - ModPath::from_segments( - hir_expand::mod_path::PathKind::Abs, - [name![std], name![fmt], name![format]], - ), - ), - ) else { - not_supported!("std::fmt::format not found"); - }; - let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else { - not_supported!("std::fmt::format is not a function") - }; - let interval = self.interpret_mir( - self.db - .mir_body(format_fn.into()) - .map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, - args.map(|x| IntervalOrOwned::Owned(x.clone())), - )?; - let message_string = interval.get(self)?; - let addr = - Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?; - let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]); - Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?) - .into_owned()) - })() - .unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}")); - Err(MirEvalError::Panic(message)) - } SliceLen => { let arg = args.next().ok_or(MirEvalError::InternalError( "argument of <[T]>::len() is not provided".into(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index d2e413f0a33a..d6557c3a8160 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -82,6 +82,9 @@ impl FallibleTypeFolder for Filler<'_> { }; filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder) } + crate::ImplTraitId::AssociatedTypeImplTrait(..) => { + not_supported!("associated type impl trait"); + } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { not_supported!("async block impl trait"); } @@ -181,8 +184,16 @@ impl Filler<'_> { self.generics .as_ref() .and_then(|it| it.iter().nth(b.index)) - .unwrap() - .0, + .and_then(|(id, _)| match id { + hir_def::GenericParamId::ConstParamId(id) => { + Some(hir_def::TypeOrConstParamId::from(id)) + } + hir_def::GenericParamId::TypeParamId(id) => { + Some(hir_def::TypeOrConstParamId::from(id)) + } + _ => None, + }) + .unwrap(), self.subst.clone(), ) })? diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index d699067b5a64..2a46becbfda9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -298,7 +298,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { if let Some(syntax_ptr) = body_source_map.self_param_syntax() { let root = db.parse_or_expand(syntax_ptr.file_id); let node = syntax_ptr.map(|ptr| ptr.to_node(&root).syntax().clone()); - types.push((node.clone(), ty)); + types.push((node, ty)); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs index e75b037e38d3..506926749960 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs @@ -85,7 +85,7 @@ fn render_dyn_for_ty() { trait Foo<'a> {} fn foo(foo: &dyn for<'a> Foo<'a>) {} - // ^^^ &dyn Foo + // ^^^ &dyn Foo<'static> "#, ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 963b4a2aba05..80d5a0ae001b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -1109,7 +1109,7 @@ fn var_args() { #[lang = "va_list"] pub struct VaListImpl<'f>; fn my_fn(foo: ...) {} - //^^^ VaListImpl<'_> + //^^^ VaListImpl<'static> "#, ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 9a8ebd07d015..8565b60210ba 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -896,13 +896,13 @@ fn flush(&self) { "#, expect![[r#" 123..127 'self': &Mutex - 150..152 '{}': MutexGuard<'_, T> + 150..152 '{}': MutexGuard<'static, T> 234..238 'self': &{unknown} 240..290 '{ ...()); }': () 250..251 'w': &Mutex 276..287 '*(w.lock())': BufWriter 278..279 'w': &Mutex - 278..286 'w.lock()': MutexGuard<'_, BufWriter> + 278..286 'w.lock()': MutexGuard<'static, BufWriter> "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 917e9f440852..f39404593e5d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -3092,7 +3092,7 @@ fn main() { 389..394 'boxed': Box> 389..406 'boxed....nner()': &i32 416..421 'good1': &i32 - 424..438 'Foo::get_inner': fn get_inner(&Box>) -> &i32 + 424..438 'Foo::get_inner': fn get_inner(&Box>) -> &i32 424..446 'Foo::g...boxed)': &i32 439..445 '&boxed': &Box> 440..445 'boxed': Box> @@ -3100,7 +3100,7 @@ fn main() { 464..469 'boxed': Box> 464..480 'boxed....self()': &Foo 490..495 'good2': &Foo - 498..511 'Foo::get_self': fn get_self(&Box>) -> &Foo + 498..511 'Foo::get_self': fn get_self(&Box>) -> &Foo 498..519 'Foo::g...boxed)': &Foo 512..518 '&boxed': &Box> 513..518 'boxed': Box> @@ -3659,7 +3659,7 @@ fn main() { let are = "are"; let count = 10; builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!"); - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'_> + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'static> } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index b80cfe18e4cf..759af18c98bf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -1278,6 +1278,40 @@ fn bar() { ); } +#[test] +fn argument_assoc_impl_trait() { + check_infer( + r#" +trait Outer { + type Item; +} + +trait Inner { } + +fn foo>(baz: T) { +} + +impl Outer for usize { + type Item = usize; +} + +impl Inner for usize {} + +fn main() { + foo(2); +} +"#, + expect![[r#" + 85..88 'baz': T + 93..96 '{ }': () + 182..197 '{ foo(2); }': () + 188..191 'foo': fn foo(usize) + 188..194 'foo(2)': () + 192..193 '2': usize + "#]], + ); +} + #[test] fn simple_return_pos_impl_trait() { cov_mark::check!(lower_rpit); @@ -4655,3 +4689,78 @@ fn f() { "#, ); } + +#[test] +fn associated_type_impl_trait() { + check_types( + r#" +trait Foo {} +struct S1; +impl Foo for S1 {} + +trait Bar { + type Item; + fn bar(&self) -> Self::Item; +} +struct S2; +impl Bar for S2 { + type Item = impl Foo; + fn bar(&self) -> Self::Item { + S1 + } +} + +fn test() { + let x = S2.bar(); + //^ impl Foo + ?Sized +} + "#, + ); +} + +#[test] +fn associated_type_impl_traits_complex() { + check_types( + r#" +struct Unary(T); +struct Binary(T, U); + +trait Foo {} +struct S1; +impl Foo for S1 {} + +trait Bar { + type Item; + fn bar(&self) -> Unary; +} +struct S2; +impl Bar for S2 { + type Item = Unary; + fn bar(&self) -> Unary<::Item> { + Unary(Unary(S1)) + } +} + +trait Baz { + type Target1; + type Target2; + fn baz(&self) -> Binary; +} +struct S3; +impl Baz for S3 { + type Target1 = impl Foo; + type Target2 = Unary; + fn baz(&self) -> Binary { + Binary(S1, Unary(S2)) + } +} + +fn test() { + let x = S3.baz(); + //^ Binary> + let y = x.1.0.bar(); + //^ Unary> +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 8bd57820d2cd..afd4d1f271df 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -9,18 +9,18 @@ use chalk_ir::{ fold::{FallibleTypeFolder, Shift}, BoundVar, DebruijnIndex, }; -use either::Either; use hir_def::{ db::DefDatabase, generics::{ - GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate, - WherePredicateTypeTarget, + GenericParamDataRef, GenericParams, LifetimeParamData, TypeOrConstParamData, + TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, }, lang_item::LangItem, resolver::{HasResolver, TypeNs}, type_ref::{TraitBoundModifier, TypeRef}, - ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, ItemContainerId, Lookup, - OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, + ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, GenericParamId, ItemContainerId, + LifetimeParamId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId, + TypeParamId, }; use hir_expand::name::Name; use intern::Interned; @@ -270,64 +270,130 @@ pub(crate) struct Generics { } impl Generics { - pub(crate) fn iter_id(&self) -> impl Iterator> + '_ { - self.iter().map(|(id, data)| match data { - TypeOrConstParamData::TypeParamData(_) => Either::Left(TypeParamId::from_unchecked(id)), - TypeOrConstParamData::ConstParamData(_) => { - Either::Right(ConstParamId::from_unchecked(id)) - } - }) + pub(crate) fn iter_id(&self) -> impl Iterator + '_ { + self.iter().map(|(id, _)| id) } /// Iterator over types and const params of self, then parent. pub(crate) fn iter<'a>( &'a self, - ) -> impl DoubleEndedIterator + 'a { - let to_toc_id = |it: &'a Generics| { - move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p) + ) -> impl DoubleEndedIterator)> + 'a { + let from_toc_id = |it: &'a Generics| { + move |(local_id, p): (_, &'a TypeOrConstParamData)| { + let id = TypeOrConstParamId { parent: it.def, local_id }; + match p { + TypeOrConstParamData::TypeParamData(p) => ( + GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)), + GenericParamDataRef::TypeParamData(p), + ), + TypeOrConstParamData::ConstParamData(p) => ( + GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)), + GenericParamDataRef::ConstParamData(p), + ), + } + } }; - self.params.iter().map(to_toc_id(self)).chain(self.iter_parent()) + + let from_lt_id = |it: &'a Generics| { + move |(local_id, p): (_, &'a LifetimeParamData)| { + ( + GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }), + GenericParamDataRef::LifetimeParamData(p), + ) + } + }; + + let lt_iter = self.params.iter_lt().map(from_lt_id(self)); + self.params.iter().map(from_toc_id(self)).chain(lt_iter).chain(self.iter_parent()) } /// Iterate over types and const params without parent params. pub(crate) fn iter_self<'a>( &'a self, - ) -> impl DoubleEndedIterator + 'a { - let to_toc_id = |it: &'a Generics| { - move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p) + ) -> impl DoubleEndedIterator)> + 'a { + let from_toc_id = |it: &'a Generics| { + move |(local_id, p): (_, &'a TypeOrConstParamData)| { + let id = TypeOrConstParamId { parent: it.def, local_id }; + match p { + TypeOrConstParamData::TypeParamData(p) => ( + GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)), + GenericParamDataRef::TypeParamData(p), + ), + TypeOrConstParamData::ConstParamData(p) => ( + GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)), + GenericParamDataRef::ConstParamData(p), + ), + } + } }; - self.params.iter().map(to_toc_id(self)) + + let from_lt_id = |it: &'a Generics| { + move |(local_id, p): (_, &'a LifetimeParamData)| { + ( + GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }), + GenericParamDataRef::LifetimeParamData(p), + ) + } + }; + + self.params.iter().map(from_toc_id(self)).chain(self.params.iter_lt().map(from_lt_id(self))) } /// Iterator over types and const params of parent. - pub(crate) fn iter_parent( - &self, - ) -> impl DoubleEndedIterator { + #[allow(clippy::needless_lifetimes)] + pub(crate) fn iter_parent<'a>( + &'a self, + ) -> impl DoubleEndedIterator)> + 'a { self.parent_generics().into_iter().flat_map(|it| { - let to_toc_id = - move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p); - it.params.iter().map(to_toc_id) + let from_toc_id = move |(local_id, p): (_, &'a TypeOrConstParamData)| { + let id = TypeOrConstParamId { parent: it.def, local_id }; + match p { + TypeOrConstParamData::TypeParamData(p) => ( + GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)), + GenericParamDataRef::TypeParamData(p), + ), + TypeOrConstParamData::ConstParamData(p) => ( + GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)), + GenericParamDataRef::ConstParamData(p), + ), + } + }; + + let from_lt_id = move |(local_id, p): (_, &'a LifetimeParamData)| { + ( + GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }), + GenericParamDataRef::LifetimeParamData(p), + ) + }; + let lt_iter = it.params.iter_lt().map(from_lt_id); + it.params.iter().map(from_toc_id).chain(lt_iter) }) } /// Returns total number of generic parameters in scope, including those from parent. pub(crate) fn len(&self) -> usize { let parent = self.parent_generics().map_or(0, Generics::len); - let child = self.params.type_or_consts.len(); + let child = self.params.len(); parent + child } - /// Returns numbers of generic parameters excluding those from parent. + /// Returns numbers of generic parameters and lifetimes excluding those from parent. pub(crate) fn len_self(&self) -> usize { + self.params.len() + } + + /// Returns number of generic parameter excluding those from parent + fn len_params(&self) -> usize { self.params.type_or_consts.len() } - /// (parent total, self param, type param list, const param list, impl trait) - pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize) { + /// (parent total, self param, type params, const params, impl trait list, lifetimes) + pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize, usize) { let mut self_params = 0; let mut type_params = 0; let mut impl_trait_params = 0; let mut const_params = 0; + let mut lifetime_params = 0; self.params.iter().for_each(|(_, data)| match data { TypeOrConstParamData::TypeParamData(p) => match p.provenance { TypeParamProvenance::TypeParamList => type_params += 1, @@ -337,8 +403,10 @@ impl Generics { TypeOrConstParamData::ConstParamData(_) => const_params += 1, }); + self.params.iter_lt().for_each(|(_, _)| lifetime_params += 1); + let parent_len = self.parent_generics().map_or(0, Generics::len); - (parent_len, self_params, type_params, const_params, impl_trait_params) + (parent_len, self_params, type_params, const_params, impl_trait_params, lifetime_params) } pub(crate) fn param_idx(&self, param: TypeOrConstParamId) -> Option { @@ -358,6 +426,26 @@ impl Generics { } } + pub(crate) fn lifetime_idx(&self, lifetime: LifetimeParamId) -> Option { + Some(self.find_lifetime(lifetime)?.0) + } + + fn find_lifetime(&self, lifetime: LifetimeParamId) -> Option<(usize, &LifetimeParamData)> { + if lifetime.parent == self.def { + let (idx, (_local_id, data)) = self + .params + .iter_lt() + .enumerate() + .find(|(_, (idx, _))| *idx == lifetime.local_id)?; + + Some((self.len_params() + idx, data)) + } else { + self.parent_generics() + .and_then(|g| g.find_lifetime(lifetime)) + .map(|(idx, data)| (self.len_self() + idx, data)) + } + } + pub(crate) fn parent_generics(&self) -> Option<&Generics> { self.parent_generics.as_deref() } @@ -371,10 +459,15 @@ impl Generics { Substitution::from_iter( Interner, self.iter_id().enumerate().map(|(idx, id)| match id { - Either::Left(_) => BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner), - Either::Right(id) => BoundVar::new(debruijn, idx) + GenericParamId::ConstParamId(id) => BoundVar::new(debruijn, idx) .to_const(Interner, db.const_param_ty(id)) .cast(Interner), + GenericParamId::TypeParamId(_) => { + BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner) + } + GenericParamId::LifetimeParamId(_) => { + BoundVar::new(debruijn, idx).to_lifetime(Interner).cast(Interner) + } }), ) } @@ -384,12 +477,15 @@ impl Generics { Substitution::from_iter( Interner, self.iter_id().map(|id| match id { - Either::Left(id) => { + GenericParamId::TypeParamId(id) => { crate::to_placeholder_idx(db, id.into()).to_ty(Interner).cast(Interner) } - Either::Right(id) => crate::to_placeholder_idx(db, id.into()) + GenericParamId::ConstParamId(id) => crate::to_placeholder_idx(db, id.into()) .to_const(Interner, db.const_param_ty(id)) .cast(Interner), + GenericParamId::LifetimeParamId(id) => { + crate::lt_to_placeholder_idx(db, id).to_lifetime(Interner).cast(Interner) + } }), ) } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index c5d44c11f2c1..23c6b078b969 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -186,18 +186,29 @@ impl HirDisplay for Struct { } StructKind::Record => { let has_where_clause = write_where_clause(def_id, f)?; - let fields = self.fields(f.db); - f.write_char(if !has_where_clause { ' ' } else { '\n' })?; - if fields.is_empty() { - f.write_str("{}")?; - } else { - f.write_str("{\n")?; - for field in self.fields(f.db) { - f.write_str(" ")?; - field.hir_fmt(f)?; - f.write_str(",\n")?; + if let Some(limit) = f.entity_limit { + let fields = self.fields(f.db); + let count = fields.len().min(limit); + f.write_char(if !has_where_clause { ' ' } else { '\n' })?; + if count == 0 { + if fields.is_empty() { + f.write_str("{}")?; + } else { + f.write_str("{ /* … */ }")?; + } + } else { + f.write_str(" {\n")?; + for field in &fields[..count] { + f.write_str(" ")?; + field.hir_fmt(f)?; + f.write_str(",\n")?; + } + + if fields.len() > count { + f.write_str(" /* … */\n")?; + } + f.write_str("}")?; } - f.write_str("}")?; } } StructKind::Unit => _ = write_where_clause(def_id, f)?, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index b922aa8e46db..106056c2fc3a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -38,7 +38,7 @@ mod display; use std::{iter, mem::discriminant, ops::ControlFlow}; use arrayvec::ArrayVec; -use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId}; +use base_db::{CrateDisplayName, CrateId, CrateOrigin, FileId}; use either::Either; use hir_def::{ body::{BodyDiagnostic, SyntheticSyntax}, @@ -65,7 +65,7 @@ use hir_ty::{ consteval::{try_const_usize, unknown_const_as_generic, ConstExt}, db::InternedClosure, diagnostics::BodyValidationDiagnostic, - known_const_to_ast, + error_lifetime, known_const_to_ast, layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, mir::{interpret_mir, MutBorrowKind}, @@ -79,6 +79,7 @@ use hir_ty::{ use itertools::Itertools; use nameres::diagnostics::DefDiagnosticKind; use rustc_hash::FxHashSet; +use span::Edition; use stdx::{impl_from, never}; use syntax::{ ast::{self, HasAttrs as _, HasName}, @@ -971,7 +972,7 @@ fn precise_macro_call_location( MacroKind::ProcMacro, ) } - MacroCallKind::Derive { ast_id, derive_attr_index, derive_index } => { + MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => { let node = ast_id.to_node(db.upcast()); // Compute the precise location of the macro name's token in the derive // list. @@ -1099,13 +1100,14 @@ impl Field { VariantDef::Union(it) => it.id.into(), VariantDef::Variant(it) => it.parent_enum(db).id.into(), }; - let mut generics = generics.map(|it| it.ty.clone()); + let mut generics = generics.map(|it| it.ty); let substs = TyBuilder::subst_for_def(db, def_id, None) .fill(|x| match x { ParamKind::Type => { generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner) } ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }) .build(); let ty = db.field_types(var_id)[self.id].clone().substitute(Interner, &substs); @@ -1416,7 +1418,7 @@ impl Adt { } pub fn layout(self, db: &dyn HirDatabase) -> Result { - if db.generic_params(self.into()).iter().count() != 0 { + if !db.generic_params(self.into()).is_empty() { return Err(LayoutError::HasPlaceholder); } let krate = self.krate(db).id; @@ -1440,13 +1442,14 @@ impl Adt { /// the greatest API, FIXME find a better one. pub fn ty_with_args(self, db: &dyn HirDatabase, args: impl Iterator) -> Type { let id = AdtId::from(self); - let mut it = args.map(|t| t.ty.clone()); + let mut it = args.map(|t| t.ty); let ty = TyBuilder::def_ty(db, id.into(), None) .fill(|x| { let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); match x { ParamKind::Type => r.cast(Interner), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), } }) .build(); @@ -1859,12 +1862,13 @@ impl Function { ItemContainerId::TraitId(it) => Some(it.into()), ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, }; - let mut generics = generics.map(|it| it.ty.clone()); + let mut generics = generics.map(|it| it.ty); let mut filler = |x: &_| match x { ParamKind::Type => { generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner) } ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }; let parent_substs = @@ -1954,7 +1958,7 @@ impl Function { ItemContainerId::TraitId(it) => Some(it.into()), ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, }; - let mut generics = generics.map(|it| it.ty.clone()); + let mut generics = generics.map(|it| it.ty); let parent_substs = parent_id.map(|id| { TyBuilder::subst_for_def(db, id, None) .fill(|x| match x { @@ -1963,6 +1967,7 @@ impl Function { .unwrap_or_else(|| TyKind::Error.intern(Interner)) .cast(Interner), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }) .build() }); @@ -2007,8 +2012,7 @@ impl Function { } let data = db.function_data(self.id); - data.name.to_smol_str() == "main" - || data.attrs.export_name().map(core::ops::Deref::deref) == Some("main") + data.name.to_smol_str() == "main" || data.attrs.export_name() == Some("main") } /// Does this function have the ignore attribute? @@ -2215,12 +2219,13 @@ impl SelfParam { } }; - let mut generics = generics.map(|it| it.ty.clone()); + let mut generics = generics.map(|it| it.ty); let mut filler = |x: &_| match x { ParamKind::Type => { generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner) } ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }; let parent_substs = TyBuilder::subst_for_def(db, parent_id, None).fill(&mut filler).build(); @@ -2592,7 +2597,7 @@ impl Macro { }, MacroId::ProcMacroId(it) => match it.lookup(db.upcast()).kind { ProcMacroKind::CustomDerive => MacroKind::Derive, - ProcMacroKind::FuncLike => MacroKind::ProcMacro, + ProcMacroKind::Bang => MacroKind::ProcMacro, ProcMacroKind::Attr => MacroKind::Attr, }, } @@ -3628,16 +3633,41 @@ impl Impl { .filter(filter), ); } + + if let Some(block) = + ty.adt_id(Interner).and_then(|def| def.0.module(db.upcast()).containing_block()) + { + if let Some(inherent_impls) = db.inherent_impls_in_block(block) { + all.extend( + inherent_impls.for_self_ty(&ty).iter().cloned().map(Self::from).filter(filter), + ); + } + if let Some(trait_impls) = db.trait_impls_in_block(block) { + all.extend( + trait_impls + .for_self_ty_without_blanket_impls(fp) + .map(Self::from) + .filter(filter), + ); + } + } + all } pub fn all_for_trait(db: &dyn HirDatabase, trait_: Trait) -> Vec { - let krate = trait_.module(db).krate(); + let module = trait_.module(db); + let krate = module.krate(); let mut all = Vec::new(); for Crate { id } in krate.transitive_reverse_dependencies(db) { let impls = db.trait_impls_in_crate(id); all.extend(impls.for_trait(trait_.id).map(Self::from)) } + if let Some(block) = module.id.containing_block() { + if let Some(trait_impls) = db.trait_impls_in_block(block) { + all.extend(trait_impls.for_trait(trait_.id).map(Self::from)); + } + } all } @@ -3683,7 +3713,7 @@ impl Impl { let macro_file = src.file_id.macro_file()?; let loc = macro_file.macro_call_id.lookup(db.upcast()); let (derive_attr, derive_index) = match loc.kind { - MacroCallKind::Derive { ast_id, derive_attr_index, derive_index } => { + MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => { let module_id = self.id.lookup(db.upcast()).container; ( db.crate_def_map(module_id.krate())[module_id.local_id] @@ -4114,6 +4144,7 @@ impl Type { // FIXME: this code is not covered in tests. unknown_const_as_generic(ty.clone()) } + ParamKind::Lifetime => error_lifetime().cast(Interner), } }) .build(); @@ -4144,6 +4175,7 @@ impl Type { match it { ParamKind::Type => args.next().unwrap().ty.clone().cast(Interner), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), } }) .build(); diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs index 102e0ca4c3d7..63b2a2506f89 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs @@ -177,7 +177,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( // Note that we need special case for 0 param constructors because of multi cartesian // product let variant_exprs: Vec = if param_exprs.is_empty() { - vec![Expr::Variant { variant, generics: generics.clone(), params: Vec::new() }] + vec![Expr::Variant { variant, generics, params: Vec::new() }] } else { param_exprs .into_iter() @@ -462,7 +462,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>( /// # Impl method tactic /// -/// Attempts to to call methods on types from lookup table. +/// Attempts to call methods on types from lookup table. /// This includes both functions from direct impl blocks as well as functions from traits. /// Methods defined in impl blocks that are generic and methods that are themselves have /// generics are ignored for performance reasons. diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index d111005c2ec4..65ce3e822c5b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -5617,7 +5617,7 @@ fn func(i: Struct<'_, T>) { fun_name(i); } -fn $0fun_name(i: Struct<'_, T>) { +fn $0fun_name(i: Struct<'static, T>) { foo(i); } "#, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs index 38f40b8d58b4..2150003bc143 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -614,7 +614,7 @@ struct Foo<'a, T> { } impl<'a, T> Foo<'a, T> { - $0fn bar(self, mut b: Vec<&'a Bar<'_, T>>) -> &'a Bar<'_, T> { + $0fn bar(self, mut b: Vec<&'a Bar<'a, T>>) -> &'a Bar<'a, T> { self.field.bar(b) } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 24a1f9492e22..a4f092cc498e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -961,11 +961,11 @@ struct Foo { field: i32 } impl Foo { fn foo(&self) { $0 } }"#, expect![[r#" fd self.field i32 + me self.foo() fn(&self) lc self &Foo sp Self Foo st Foo Foo bt u32 u32 - me self.foo() fn(&self) "#]], ); check( @@ -975,11 +975,11 @@ struct Foo(i32); impl Foo { fn foo(&mut self) { $0 } }"#, expect![[r#" fd self.0 i32 + me self.foo() fn(&mut self) lc self &mut Foo sp Self Foo st Foo Foo bt u32 u32 - me self.foo() fn(&mut self) "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 794678415022..c48672e80aca 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -186,11 +186,11 @@ fn add_function_impl( if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." } ); - let completion_kind = if func.has_self_param(ctx.db) { - CompletionItemKind::Method + let completion_kind = CompletionItemKind::SymbolKind(if func.has_self_param(ctx.db) { + SymbolKind::Method } else { - CompletionItemKind::SymbolKind(SymbolKind::Function) - }; + SymbolKind::Function + }); let mut item = CompletionItem::new(completion_kind, replacement_range, label); item.lookup_by(format!("fn {}", fn_name.display(ctx.db))) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs index ed32a5db23e7..1322c05e30e9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs @@ -75,8 +75,8 @@ impl Future for A {} fn foo(a: A) { a.$0 } "#, expect![[r#" - kw await expr.await me into_future() (as IntoFuture) fn(self) -> ::IntoFuture + kw await expr.await sn box Box::new(expr) sn call function(expr) sn dbg dbg!(expr) @@ -102,8 +102,8 @@ fn foo() { } "#, expect![[r#" - kw await expr.await me into_future() (use core::future::IntoFuture) fn(self) -> ::IntoFuture + kw await expr.await sn box Box::new(expr) sn call function(expr) sn dbg dbg!(expr) @@ -131,8 +131,8 @@ impl IntoFuture for A {} fn foo(a: A) { a.$0 } "#, expect![[r#" - kw await expr.await me into_future() (as IntoFuture) fn(self) -> ::IntoFuture + kw await expr.await sn box Box::new(expr) sn call function(expr) sn dbg dbg!(expr) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index aa22155feffe..995e3f482532 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -540,7 +540,7 @@ impl CompletionContext<'_> { /// Whether the given trait is an operator trait or not. pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool { match trait_.attrs(self.db).lang() { - Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()), + Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang), None => false, } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index 357060817c7b..b9a2c383bdd1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -342,7 +342,6 @@ pub enum CompletionItemKind { BuiltinType, InferredType, Keyword, - Method, Snippet, UnresolvedReference, Expression, @@ -369,6 +368,7 @@ impl CompletionItemKind { SymbolKind::LifetimeParam => "lt", SymbolKind::Local => "lc", SymbolKind::Macro => "ma", + SymbolKind::Method => "me", SymbolKind::ProcMacro => "pm", SymbolKind::Module => "md", SymbolKind::SelfParam => "sp", @@ -388,7 +388,6 @@ impl CompletionItemKind { CompletionItemKind::BuiltinType => "bt", CompletionItemKind::InferredType => "it", CompletionItemKind::Keyword => "kw", - CompletionItemKind::Method => "me", CompletionItemKind::Snippet => "sn", CompletionItemKind::UnresolvedReference => "??", CompletionItemKind::Expression => "ex", diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 6d1a5a0bc529..ca0424809ede 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -312,7 +312,7 @@ pub(crate) fn render_expr( None => ctx.source_range(), }; - let mut item = CompletionItem::new(CompletionItemKind::Expression, source_range, label.clone()); + let mut item = CompletionItem::new(CompletionItemKind::Expression, source_range, label); let snippet = format!( "{}$0", @@ -677,10 +677,11 @@ mod tests { #[track_caller] fn check_function_relevance(ra_fixture: &str, expect: Expect) { - let actual: Vec<_> = do_completion(ra_fixture, CompletionItemKind::Method) - .into_iter() - .map(|item| (item.detail.unwrap_or_default(), item.relevance.function)) - .collect(); + let actual: Vec<_> = + do_completion(ra_fixture, CompletionItemKind::SymbolKind(SymbolKind::Method)) + .into_iter() + .map(|item| (item.detail.unwrap_or_default(), item.relevance.function)) + .collect(); expect.assert_debug_eq(&actual); } @@ -1392,7 +1393,10 @@ impl S { /// Method docs fn bar(self) { self.$0 } }"#, - &[CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Field)], + &[ + CompletionItemKind::SymbolKind(SymbolKind::Method), + CompletionItemKind::SymbolKind(SymbolKind::Field), + ], expect![[r#" [ CompletionItem { @@ -1400,7 +1404,9 @@ impl S { source_range: 94..94, delete: 94..94, insert: "bar()$0", - kind: Method, + kind: SymbolKind( + Method, + ), lookup: "bar", detail: "fn(self)", documentation: Documentation( @@ -1520,7 +1526,7 @@ impl S { } fn foo(s: S) { s.$0 } "#, - CompletionItemKind::Method, + CompletionItemKind::SymbolKind(SymbolKind::Method), expect![[r#" [ CompletionItem { @@ -1528,7 +1534,9 @@ fn foo(s: S) { s.$0 } source_range: 81..81, delete: 81..81, insert: "the_method()$0", - kind: Method, + kind: SymbolKind( + Method, + ), lookup: "the_method", detail: "fn(&self)", relevance: CompletionRelevance { @@ -2408,7 +2416,10 @@ impl Foo { fn baz(&self) -> u32 { 0 } } fn foo(f: Foo) { let _: &u32 = f.b$0 } "#, - &[CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Field)], + &[ + CompletionItemKind::SymbolKind(SymbolKind::Method), + CompletionItemKind::SymbolKind(SymbolKind::Field), + ], expect![[r#" [ CompletionItem { @@ -2416,7 +2427,9 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } source_range: 109..110, delete: 109..110, insert: "baz()$0", - kind: Method, + kind: SymbolKind( + Method, + ), lookup: "baz", detail: "fn(&self) -> u32", relevance: CompletionRelevance { @@ -2631,7 +2644,7 @@ fn main() { let _: bool = (9 > 2).not$0; } "#, - &[CompletionItemKind::Snippet, CompletionItemKind::Method], + &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)], expect![[r#" sn not [snippet] me not() (use ops::Not) [type_could_unify+requires_import] @@ -2664,7 +2677,7 @@ fn main() { S.$0 } "#, - &[CompletionItemKind::Snippet, CompletionItemKind::Method], + &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)], expect![[r#" me f() [] sn ref [] @@ -2907,7 +2920,7 @@ fn main() { } "#, &[ - CompletionItemKind::Method, + CompletionItemKind::SymbolKind(SymbolKind::Method), CompletionItemKind::SymbolKind(SymbolKind::Field), CompletionItemKind::SymbolKind(SymbolKind::Function), ], @@ -2918,7 +2931,9 @@ fn main() { source_range: 193..193, delete: 193..193, insert: "flush()$0", - kind: Method, + kind: SymbolKind( + Method, + ), lookup: "flush", detail: "fn(&self)", relevance: CompletionRelevance { @@ -2941,7 +2956,9 @@ fn main() { source_range: 193..193, delete: 193..193, insert: "write()$0", - kind: Method, + kind: SymbolKind( + Method, + ), lookup: "write", detail: "fn(&self)", relevance: CompletionRelevance { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index cf9fe1ab3072..1634b0a92061 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -68,11 +68,11 @@ fn render( }; let has_self_param = func.self_param(db).is_some(); let mut item = CompletionItem::new( - if has_self_param { - CompletionItemKind::Method + CompletionItemKind::SymbolKind(if has_self_param { + SymbolKind::Method } else { - CompletionItemKind::SymbolKind(SymbolKind::Function) - }, + SymbolKind::Function + }), ctx.source_range(), call.clone(), ); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 7749fac40b9d..a653314233d3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -127,6 +127,7 @@ impl Unit { en Enum Enum fn function() fn() fn local_func() fn() + me self.foo() fn(self) lc self Unit ma makro!(…) macro_rules! makro md module @@ -166,7 +167,6 @@ impl Unit { kw use kw while kw while let - me self.foo() fn(self) sn macro_rules sn pd sn ppd diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs index 46a3e97d3e92..3718dff56e88 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs @@ -19,7 +19,7 @@ struct Foo<'lt, T, const C: usize> where $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module - st Foo<…> Foo<'_, {unknown}, _> + st Foo<…> Foo<'static, {unknown}, _> st Record Record st Tuple Tuple st Unit Unit @@ -92,7 +92,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module - st Foo<…> Foo<'_, {unknown}, _> + st Foo<…> Foo<'static, {unknown}, _> st Record Record st Tuple Tuple st Unit Unit diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs index ff32eccfbff4..69d8fe91040b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs @@ -1,6 +1,7 @@ //! Tests that don't fit into a specific category. use expect_test::{expect, Expect}; +use ide_db::SymbolKind; use crate::{ tests::{ @@ -316,15 +317,15 @@ trait Sub: Super { fn foo() { T::$0 } "#, expect![[r#" - ct C2 (as Sub) const C2: () - ct CONST (as Super) const CONST: u8 - fn func() (as Super) fn() - fn subfunc() (as Sub) fn() - ta SubTy (as Sub) type SubTy - ta Ty (as Super) type Ty - me method(…) (as Super) fn(&self) - me submethod(…) (as Sub) fn(&self) - "#]], + ct C2 (as Sub) const C2: () + ct CONST (as Super) const CONST: u8 + fn func() (as Super) fn() + fn subfunc() (as Sub) fn() + me method(…) (as Super) fn(&self) + me submethod(…) (as Sub) fn(&self) + ta SubTy (as Sub) type SubTy + ta Ty (as Super) type Ty + "#]], ); } @@ -356,15 +357,15 @@ impl Sub for Wrap { } "#, expect![[r#" - ct C2 (as Sub) const C2: () - ct CONST (as Super) const CONST: u8 - fn func() (as Super) fn() - fn subfunc() (as Sub) fn() - ta SubTy (as Sub) type SubTy - ta Ty (as Super) type Ty - me method(…) (as Super) fn(&self) - me submethod(…) (as Sub) fn(&self) - "#]], + ct C2 (as Sub) const C2: () + ct CONST (as Super) const CONST: u8 + fn func() (as Super) fn() + fn subfunc() (as Sub) fn() + me method(…) (as Super) fn(&self) + me submethod(…) (as Sub) fn(&self) + ta SubTy (as Sub) type SubTy + ta Ty (as Super) type Ty + "#]], ); } @@ -555,10 +556,10 @@ impl Foo { } "#, expect![[r#" - ev Bar Bar - ev Baz Baz - me foo(…) fn(self) - "#]], + me foo(…) fn(self) + ev Bar Bar + ev Baz Baz + "#]], ); } @@ -1399,7 +1400,7 @@ fn main() { bar.b$0 } "#, - CompletionItemKind::Method, + CompletionItemKind::SymbolKind(SymbolKind::Method), expect!("const fn(&'foo mut self, &Foo) -> !"), expect!("pub const fn baz<'foo>(&'foo mut self, x: &'foo Foo) -> !"), ); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index db4ac9381ced..97709728656d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs @@ -20,8 +20,8 @@ struct Foo<'lt, T, const C: usize> { en Enum Enum ma makro!(…) macro_rules! makro md module - sp Self Foo<'_, {unknown}, _> - st Foo<…> Foo<'_, {unknown}, _> + sp Self Foo<'static, {unknown}, _> + st Foo<…> Foo<'static, {unknown}, _> st Record Record st Tuple Tuple st Unit Unit @@ -45,8 +45,8 @@ struct Foo<'lt, T, const C: usize>(f$0); en Enum Enum ma makro!(…) macro_rules! makro md module - sp Self Foo<'_, {unknown}, _> - st Foo<…> Foo<'_, {unknown}, _> + sp Self Foo<'static, {unknown}, _> + st Foo<…> Foo<'static, {unknown}, _> st Record Record st Tuple Tuple st Unit Unit diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 0d5a93f7b8e5..357209ceb0b4 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -346,6 +346,7 @@ pub enum SymbolKind { Enum, Field, Function, + Method, Impl, Label, LifetimeParam, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml index 8ccea99e9e13..edd05009332e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml @@ -26,6 +26,7 @@ text-edit.workspace = true cfg.workspace = true hir.workspace = true ide-db.workspace = true +paths.workspace = true [dev-dependencies] expect-test = "1.4.0" diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 67daa172b27e..045154614f80 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -23,6 +23,7 @@ mod tests { }, DiagnosticsConfig, }; + use test_utils::skip_slow_tests; #[track_caller] fn check_diagnostics_no_bails(ra_fixture: &str) { @@ -1004,6 +1005,32 @@ fn f() { ); } + #[test] + fn exponential_match() { + if skip_slow_tests() { + return; + } + // Constructs a match where match checking takes exponential time. Ensures we bail early. + use std::fmt::Write; + let struct_arity = 50; + let mut code = String::new(); + write!(code, "struct BigStruct {{").unwrap(); + for i in 0..struct_arity { + write!(code, " field{i}: bool,").unwrap(); + } + write!(code, "}}").unwrap(); + write!(code, "fn big_match(s: BigStruct) {{").unwrap(); + write!(code, " match s {{").unwrap(); + for i in 0..struct_arity { + write!(code, " BigStruct {{ field{i}: true, ..}} => {{}},").unwrap(); + write!(code, " BigStruct {{ field{i}: false, ..}} => {{}},").unwrap(); + } + write!(code, " _ => {{}},").unwrap(); + write!(code, " }}").unwrap(); + write!(code, "}}").unwrap(); + check_diagnostics_no_bails(&code); + } + mod rust_unstable { use super::*; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 34a0038295fd..00352266ddbd 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -7,7 +7,11 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: need-mut // // This diagnostic is triggered on mutating an immutable variable. -pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagnostic { +pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option { + if d.span.file_id.macro_file().is_some() { + // FIXME: Our infra can't handle allow from within macro expansions rn + return None; + } let fixes = (|| { if d.local.is_ref(ctx.sema.db) { // There is no simple way to add `mut` to `ref x` and `ref mut x` @@ -29,24 +33,30 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagno use_range, )]) })(); - Diagnostic::new_with_syntax_node_ptr( - ctx, - // FIXME: `E0384` is not the only error that this diagnostic handles - DiagnosticCode::RustcHardError("E0384"), - format!( - "cannot mutate immutable variable `{}`", - d.local.name(ctx.sema.db).display(ctx.sema.db) - ), - d.span, + Some( + Diagnostic::new_with_syntax_node_ptr( + ctx, + // FIXME: `E0384` is not the only error that this diagnostic handles + DiagnosticCode::RustcHardError("E0384"), + format!( + "cannot mutate immutable variable `{}`", + d.local.name(ctx.sema.db).display(ctx.sema.db) + ), + d.span, + ) + .with_fixes(fixes), ) - .with_fixes(fixes) } // Diagnostic: unused-mut // // This diagnostic is triggered when a mutable variable isn't actually mutated. -pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Diagnostic { +pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Option { let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); + if ast.file_id.macro_file().is_some() { + // FIXME: Our infra can't handle allow from within macro expansions rn + return None; + } let fixes = (|| { let file_id = ast.file_id.file_id()?; let mut edit_builder = TextEdit::builder(); @@ -70,14 +80,16 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Di )]) })(); let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); - Diagnostic::new_with_syntax_node_ptr( - ctx, - DiagnosticCode::RustcLint("unused_mut"), - "variable does not need to be mutable", - ast, + Some( + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcLint("unused_mut"), + "variable does not need to be mutable", + ast, + ) + .experimental() // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive. + .with_fixes(fixes), ) - .experimental() // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive. - .with_fixes(fixes) } pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs index 7a040e46e338..d831878044d3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs @@ -12,7 +12,12 @@ use crate::{adjusted_display_range, fix, Diagnostic, DiagnosticCode, Diagnostics pub(crate) fn remove_trailing_return( ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn, -) -> Diagnostic { +) -> Option { + if d.return_expr.file_id.macro_file().is_some() { + // FIXME: Our infra can't handle allow from within macro expansions rn + return None; + } + let display_range = adjusted_display_range(ctx, d.return_expr, &|return_expr| { return_expr .syntax() @@ -20,12 +25,14 @@ pub(crate) fn remove_trailing_return( .and_then(ast::ExprStmt::cast) .map(|stmt| stmt.syntax().text_range()) }); - Diagnostic::new( - DiagnosticCode::Clippy("needless_return"), - "replace return ; with ", - display_range, + Some( + Diagnostic::new( + DiagnosticCode::Clippy("needless_return"), + "replace return ; with ", + display_range, + ) + .with_fixes(fixes(ctx, d)), ) - .with_fixes(fixes(ctx, d)) } fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn) -> Option> { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs index f68e69823856..448df1ca163a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs @@ -21,23 +21,30 @@ use crate::{ pub(crate) fn remove_unnecessary_else( ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse, -) -> Diagnostic { +) -> Option { + if d.if_expr.file_id.macro_file().is_some() { + // FIXME: Our infra can't handle allow from within macro expansions rn + return None; + } + let display_range = adjusted_display_range(ctx, d.if_expr, &|if_expr| { if_expr.else_token().as_ref().map(SyntaxToken::text_range) }); - Diagnostic::new( - DiagnosticCode::Ra("remove-unnecessary-else", Severity::WeakWarning), - "remove unnecessary else block", - display_range, + Some( + Diagnostic::new( + DiagnosticCode::Ra("remove-unnecessary-else", Severity::WeakWarning), + "remove unnecessary else block", + display_range, + ) + .experimental() + .with_fixes(fixes(ctx, d)), ) - .experimental() - .with_fixes(fixes(ctx, d)) } fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse) -> Option> { let root = ctx.sema.db.parse_or_expand(d.if_expr.file_id); let if_expr = d.if_expr.value.to_node(&root); - let if_expr = ctx.sema.original_ast_node(if_expr.clone())?; + let if_expr = ctx.sema.original_ast_node(if_expr)?; let mut indent = IndentLevel::from_node(if_expr.syntax()); let has_parent_if_expr = if_expr.syntax().parent().and_then(ast::IfExpr::cast).is_some(); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs index becc24ab21ec..b9327f85567c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -8,6 +8,7 @@ use ide_db::{ source_change::SourceChange, RootDatabase, }; +use paths::Utf8Component; use syntax::{ ast::{self, edit::IndentLevel, HasModuleItem, HasName}, AstNode, TextRange, @@ -84,10 +85,10 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option> { // try resolving the relative difference of the paths as inline modules let mut current = root_module; - for ele in rel.as_ref().components() { + for ele in rel.as_utf8_path().components() { let seg = match ele { - std::path::Component::Normal(seg) => seg.to_str()?, - std::path::Component::RootDir => continue, + Utf8Component::Normal(seg) => seg, + Utf8Component::RootDir => continue, // shouldn't occur _ => continue 'crates, }; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs index a9e1d07d7c52..cd251faab9ad 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -14,18 +14,24 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; pub(crate) fn unused_variables( ctx: &DiagnosticsContext<'_>, d: &hir::UnusedVariable, -) -> Diagnostic { +) -> Option { let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); + if ast.file_id.macro_file().is_some() { + // FIXME: Our infra can't handle allow from within macro expansions rn + return None; + } let diagnostic_range = ctx.sema.diagnostics_display_range(ast); let var_name = d.local.primary_source(ctx.sema.db).syntax().to_string(); - Diagnostic::new_with_syntax_node_ptr( - ctx, - DiagnosticCode::RustcLint("unused_variables"), - "unused variable", - ast, + Some( + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcLint("unused_variables"), + "unused variable", + ast, + ) + .with_fixes(fixes(&var_name, diagnostic_range, ast.file_id.is_macro())) + .experimental(), ) - .with_fixes(fixes(&var_name, diagnostic_range, ast.file_id.is_macro())) - .experimental() } fn fixes(var_name: &String, diagnostic_range: FileRange, is_in_marco: bool) -> Option> { @@ -47,7 +53,7 @@ fn fixes(var_name: &String, diagnostic_range: FileRange, is_in_marco: bool) -> O #[cfg(test)] mod tests { - use crate::tests::{check_diagnostics, check_fix, check_no_fix}; + use crate::tests::{check_diagnostics, check_fix}; #[test] fn unused_variables_simple() { @@ -193,7 +199,7 @@ fn main() { #[test] fn no_fix_for_marco() { - check_no_fix( + check_diagnostics( r#" macro_rules! my_macro { () => { @@ -202,7 +208,7 @@ macro_rules! my_macro { } fn main() { - $0my_macro!(); + my_macro!(); } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 0df6f0e0373c..270cf844c65b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -330,7 +330,6 @@ pub fn diagnostics( } for diag in diags { - #[rustfmt::skip] let d = match diag { AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) { @@ -361,7 +360,10 @@ pub fn diagnostics( AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d), AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d), AnyDiagnostic::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d), - AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d), + AnyDiagnostic::NeedMut(d) => match handlers::mutability_errors::need_mut(&ctx, &d) { + Some(it) => it, + None => continue, + }, AnyDiagnostic::NonExhaustiveLet(d) => handlers::non_exhaustive_let::non_exhaustive_let(&ctx, &d), AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d), AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d), @@ -385,12 +387,24 @@ pub fn diagnostics( AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d), AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d), AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled), - AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d), - AnyDiagnostic::UnusedVariable(d) => handlers::unused_variables::unused_variables(&ctx, &d), + AnyDiagnostic::UnusedMut(d) => match handlers::mutability_errors::unused_mut(&ctx, &d) { + Some(it) => it, + None => continue, + }, + AnyDiagnostic::UnusedVariable(d) => match handlers::unused_variables::unused_variables(&ctx, &d) { + Some(it) => it, + None => continue, + }, AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d), AnyDiagnostic::MismatchedTupleStructPatArgCount(d) => handlers::mismatched_arg_count::mismatched_tuple_struct_pat_arg_count(&ctx, &d), - AnyDiagnostic::RemoveTrailingReturn(d) => handlers::remove_trailing_return::remove_trailing_return(&ctx, &d), - AnyDiagnostic::RemoveUnnecessaryElse(d) => handlers::remove_unnecessary_else::remove_unnecessary_else(&ctx, &d), + AnyDiagnostic::RemoveTrailingReturn(d) => match handlers::remove_trailing_return::remove_trailing_return(&ctx, &d) { + Some(it) => it, + None => continue, + }, + AnyDiagnostic::RemoveUnnecessaryElse(d) => match handlers::remove_unnecessary_else::remove_unnecessary_else(&ctx, &d) { + Some(it) => it, + None => continue, + }, }; res.push(d) } @@ -399,9 +413,9 @@ pub fn diagnostics( .iter_mut() .filter_map(|it| { Some(( - it.main_node - .map(|ptr| ptr.map(|node| node.to_node(&ctx.sema.parse_or_expand(ptr.file_id)))) - .clone()?, + it.main_node.map(|ptr| { + ptr.map(|node| node.to_node(&ctx.sema.parse_or_expand(ptr.file_id))) + })?, it, )) }) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs index dcaa21208923..bb5c2b791392 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs @@ -283,6 +283,10 @@ fn test_disabled_diagnostics() { #[test] fn minicore_smoke_test() { + if test_utils::skip_slow_tests() { + return; + } + fn check(minicore: MiniCore) { let source = minicore.source_code(); let mut config = DiagnosticsConfig::test_sample(); diff --git a/src/tools/rust-analyzer/crates/ide/Cargo.toml b/src/tools/rust-analyzer/crates/ide/Cargo.toml index aca7d613e11f..a535015a27f0 100644 --- a/src/tools/rust-analyzer/crates/ide/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide/Cargo.toml @@ -36,6 +36,7 @@ ide-ssr.workspace = true profile.workspace = true stdx.workspace = true syntax.workspace = true +span.workspace = true text-edit.workspace = true # ide should depend only on the top-level `hir` package. if you need # something from some `hir-xxx` subpackage, reexport the API via `hir`. diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index d10bdca50d81..64b4ccc5bd81 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -5,8 +5,6 @@ mod tests; mod intra_doc_links; -use std::ffi::OsStr; - use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; use pulldown_cmark_to_cmark::{cmark_resume_with_options, Options as CMarkOptions}; use stdx::format_to; @@ -134,8 +132,8 @@ pub(crate) fn remove_links(markdown: &str) -> String { pub(crate) fn external_docs( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, - target_dir: Option<&OsStr>, - sysroot: Option<&OsStr>, + target_dir: Option<&str>, + sysroot: Option<&str>, ) -> Option { let sema = &Semantics::new(db); let file = sema.parse(file_id).syntax().clone(); @@ -331,8 +329,8 @@ fn broken_link_clone_cb(link: BrokenLink<'_>) -> Option<(CowStr<'_>, CowStr<'_>) fn get_doc_links( db: &RootDatabase, def: Definition, - target_dir: Option<&OsStr>, - sysroot: Option<&OsStr>, + target_dir: Option<&str>, + sysroot: Option<&str>, ) -> DocumentationLinks { let join_url = |base_url: Option, path: &str| -> Option { base_url.and_then(|url| url.join(path).ok()) @@ -479,15 +477,13 @@ fn map_links<'e>( fn get_doc_base_urls( db: &RootDatabase, def: Definition, - target_dir: Option<&OsStr>, - sysroot: Option<&OsStr>, + target_dir: Option<&str>, + sysroot: Option<&str>, ) -> (Option, Option) { let local_doc = target_dir - .and_then(|path| path.to_str()) .and_then(|path| Url::parse(&format!("file:///{path}/")).ok()) .and_then(|it| it.join("doc/").ok()); let system_doc = sysroot - .and_then(|it| it.to_str()) .map(|sysroot| format!("file:///{sysroot}/share/doc/rust/html/")) .and_then(|it| Url::parse(&it).ok()); diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index 60e8d29a7163..ac44908a9021 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -1,4 +1,4 @@ -use std::{ffi::OsStr, iter}; +use std::iter; use expect_test::{expect, Expect}; use hir::Semantics; @@ -18,10 +18,10 @@ use crate::{ fn check_external_docs( ra_fixture: &str, - target_dir: Option<&OsStr>, + target_dir: Option<&str>, expect_web_url: Option, expect_local_url: Option, - sysroot: Option<&OsStr>, + sysroot: Option<&str>, ) { let (analysis, position) = fixture::position(ra_fixture); let links = analysis.external_docs(position, target_dir, sysroot).unwrap(); @@ -127,10 +127,10 @@ fn external_docs_doc_builtin_type() { //- /main.rs crate:foo let x: u3$02 = 0; "#, - Some(OsStr::new("/home/user/project")), + Some("/home/user/project"), Some(expect![[r#"https://doc.rust-lang.org/nightly/core/primitive.u32.html"#]]), Some(expect![[r#"file:///sysroot/share/doc/rust/html/core/primitive.u32.html"#]]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } @@ -143,10 +143,10 @@ use foo$0::Foo; //- /lib.rs crate:foo pub struct Foo; "#, - Some(OsStr::new("/home/user/project")), + Some("/home/user/project"), Some(expect![[r#"https://docs.rs/foo/*/foo/index.html"#]]), Some(expect![[r#"file:///home/user/project/doc/foo/index.html"#]]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } @@ -157,10 +157,10 @@ fn external_docs_doc_url_std_crate() { //- /main.rs crate:std use self$0; "#, - Some(OsStr::new("/home/user/project")), + Some("/home/user/project"), Some(expect!["https://doc.rust-lang.org/stable/std/index.html"]), Some(expect!["file:///sysroot/share/doc/rust/html/std/index.html"]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } @@ -171,10 +171,10 @@ fn external_docs_doc_url_struct() { //- /main.rs crate:foo pub struct Fo$0o; "#, - Some(OsStr::new("/home/user/project")), + Some("/home/user/project"), Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]), Some(expect![[r#"file:///home/user/project/doc/foo/struct.Foo.html"#]]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } @@ -185,10 +185,10 @@ fn external_docs_doc_url_windows_backslash_path() { //- /main.rs crate:foo pub struct Fo$0o; "#, - Some(OsStr::new(r"C:\Users\user\project")), + Some(r"C:\Users\user\project"), Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]), Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } @@ -199,10 +199,10 @@ fn external_docs_doc_url_windows_slash_path() { //- /main.rs crate:foo pub struct Fo$0o; "#, - Some(OsStr::new(r"C:/Users/user/project")), + Some("C:/Users/user/project"), Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]), Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index 0e790e14205f..813691540f92 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -134,15 +134,22 @@ fn structure_node(node: &SyntaxNode) -> Option { if let Some(type_param_list) = it.generic_param_list() { collapse_ws(type_param_list.syntax(), &mut detail); } - if let Some(param_list) = it.param_list() { + let has_self_param = if let Some(param_list) = it.param_list() { collapse_ws(param_list.syntax(), &mut detail); - } + param_list.self_param().is_some() + } else { + false + }; if let Some(ret_type) = it.ret_type() { detail.push(' '); collapse_ws(ret_type.syntax(), &mut detail); } - decl_with_detail(&it, Some(detail), StructureNodeKind::SymbolKind(SymbolKind::Function)) + decl_with_detail(&it, Some(detail), StructureNodeKind::SymbolKind(if has_self_param { + SymbolKind::Method + } else { + SymbolKind::Function + })) }, ast::Struct(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Struct)), ast::Union(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Union)), diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs index 8a12cbacccc7..76e5e9dd9288 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -337,6 +337,77 @@ impl Tr for S { const C: usize = 4; //^ } +"#, + ); + } + + #[test] + fn goto_adt_implementation_inside_block() { + check( + r#" +//- minicore: copy, derive +trait Bar {} + +fn test() { + #[derive(Copy)] + //^^^^^^^^^^^^^^^ + struct Foo$0; + + impl Foo {} + //^^^ + + trait Baz {} + + impl Bar for Foo {} + //^^^ + + impl Baz for Foo {} + //^^^ +} +"#, + ); + } + + #[test] + fn goto_trait_implementation_inside_block() { + check( + r#" +struct Bar; + +fn test() { + trait Foo$0 {} + + struct Baz; + + impl Foo for Bar {} + //^^^ + + impl Foo for Baz {} + //^^^ +} +"#, + ); + check( + r#" +struct Bar; + +fn test() { + trait Foo { + fn foo$0() {} + } + + struct Baz; + + impl Foo for Bar { + fn foo() {} + //^^^ + } + + impl Foo for Baz { + fn foo() {} + //^^^ + } +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 8f4c629b5813..822751c0e4ce 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -33,6 +33,7 @@ pub struct HoverConfig { pub keywords: bool, pub format: HoverDocFormat, pub max_trait_assoc_items_count: Option, + pub max_struct_field_count: Option, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 63777d491050..abedbff831a5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -410,6 +410,9 @@ pub(super) fn definition( Definition::Trait(trait_) => { trait_.display_limited(db, config.max_trait_assoc_items_count).to_string() } + Definition::Adt(Adt::Struct(struct_)) => { + struct_.display_limited(db, config.max_struct_field_count).to_string() + } _ => def.label(db), }; let docs = def.docs(db, famous_defs); diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 4451e31870f2..08925fcdff58 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -18,6 +18,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { format: HoverDocFormat::Markdown, keywords: true, max_trait_assoc_items_count: None, + max_struct_field_count: None, }; fn check_hover_no_result(ra_fixture: &str) { @@ -49,6 +50,28 @@ fn check(ra_fixture: &str, expect: Expect) { expect.assert_eq(&actual) } +#[track_caller] +fn check_hover_struct_limit(count: usize, ra_fixture: &str, expect: Expect) { + let (analysis, position) = fixture::position(ra_fixture); + let hover = analysis + .hover( + &HoverConfig { + links_in_hover: true, + max_struct_field_count: Some(count), + ..HOVER_BASE_CONFIG + }, + FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, + ) + .unwrap() + .unwrap(); + + let content = analysis.db.file_text(position.file_id); + let hovered_element = &content[hover.range]; + + let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); + expect.assert_eq(&actual) +} + #[track_caller] fn check_assoc_count(count: usize, ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); @@ -853,9 +876,7 @@ struct Foo$0 { field: u32 } ```rust // size = 4, align = 4 - struct Foo { - field: u32, - } + struct Foo ``` "#]], ); @@ -875,8 +896,74 @@ struct Foo$0 where u32: Copy { field: u32 } struct Foo where u32: Copy, - { - field: u32, + ``` + "#]], + ); +} + +#[test] +fn hover_record_struct_limit() { + check_hover_struct_limit( + 3, + r#" + struct Foo$0 { a: u32, b: i32, c: i32 } + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 12 (0xC), align = 4 + struct Foo { + a: u32, + b: i32, + c: i32, + } + ``` + "#]], + ); + check_hover_struct_limit( + 3, + r#" + struct Foo$0 { a: u32 } + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + struct Foo { + a: u32, + } + ``` + "#]], + ); + check_hover_struct_limit( + 3, + r#" + struct Foo$0 { a: u32, b: i32, c: i32, d: u32 } + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 16 (0x10), align = 4 + struct Foo { + a: u32, + b: i32, + c: i32, + /* … */ } ``` "#]], @@ -1344,9 +1431,7 @@ impl Thing { ``` ```rust - struct Thing { - x: u32, - } + struct Thing ``` "#]], ); @@ -1365,9 +1450,7 @@ impl Thing { ``` ```rust - struct Thing { - x: u32, - } + struct Thing ``` "#]], ); @@ -2599,7 +2682,7 @@ fn main() { let s$0t = S{ f1:0 }; } focus_range: 7..8, name: "S", kind: Struct, - description: "struct S {\n f1: u32,\n}", + description: "struct S", }, }, ], @@ -2645,7 +2728,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; } focus_range: 24..25, name: "S", kind: Struct, - description: "struct S {\n f1: T,\n}", + description: "struct S", }, }, ], @@ -2704,7 +2787,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; } focus_range: 24..25, name: "S", kind: Struct, - description: "struct S {\n f1: T,\n}", + description: "struct S", }, }, ], @@ -2957,7 +3040,7 @@ fn main() { let s$0t = foo(); } focus_range: 39..41, name: "S1", kind: Struct, - description: "struct S1 {}", + description: "struct S1", }, }, HoverGotoTypeData { @@ -2970,7 +3053,7 @@ fn main() { let s$0t = foo(); } focus_range: 52..54, name: "S2", kind: Struct, - description: "struct S2 {}", + description: "struct S2", }, }, ], @@ -3061,7 +3144,7 @@ fn foo(ar$0g: &impl Foo + Bar) {} focus_range: 36..37, name: "S", kind: Struct, - description: "struct S {}", + description: "struct S", }, }, ], @@ -3161,7 +3244,7 @@ fn foo(ar$0g: &impl Foo) {} focus_range: 23..24, name: "S", kind: Struct, - description: "struct S {}", + description: "struct S", }, }, ], @@ -3198,7 +3281,7 @@ fn main() { let s$0t = foo(); } focus_range: 49..50, name: "B", kind: Struct, - description: "struct B {}", + description: "struct B", }, }, HoverGotoTypeData { @@ -3287,7 +3370,7 @@ fn foo(ar$0g: &dyn Foo) {} focus_range: 23..24, name: "S", kind: Struct, - description: "struct S {}", + description: "struct S", }, }, ], @@ -3322,7 +3405,7 @@ fn foo(a$0rg: &impl ImplTrait>>>) {} focus_range: 50..51, name: "B", kind: Struct, - description: "struct B {}", + description: "struct B", }, }, HoverGotoTypeData { @@ -3361,7 +3444,7 @@ fn foo(a$0rg: &impl ImplTrait>>>) {} focus_range: 65..66, name: "S", kind: Struct, - description: "struct S {}", + description: "struct S", }, }, ], @@ -5105,6 +5188,32 @@ fn foo(e: E) { ); } +#[test] +fn hover_const_value() { + check( + r#" +pub enum AA { + BB, +} +const CONST: AA = AA::BB; +pub fn the_function() -> AA { + CON$0ST +} +"#, + expect![[r#" + *CONST* + + ```rust + test + ``` + + ```rust + const CONST: AA = BB + ``` + "#]], + ); +} + #[test] fn array_repeat_exp() { check( @@ -7747,3 +7856,25 @@ impl Iterator for S { "#]], ); } + +#[test] +fn hover_lifetime_regression_16963() { + check( + r#" +struct Pedro$0<'a> { + hola: &'a str +} +"#, + expect![[r#" + *Pedro* + + ```rust + test + ``` + + ```rust + struct Pedro<'a> + ``` + "#]], + ) +} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 8311e770b4b4..dda38ce77e0c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -1,5 +1,6 @@ use std::{ fmt::{self, Write}, + hash::{BuildHasher, BuildHasherDefault}, mem::take, }; @@ -8,7 +9,7 @@ use hir::{ known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef, ModuleDefId, Semantics, }; -use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase}; +use ide_db::{base_db::FileRange, famous_defs::FamousDefs, FxHasher, RootDatabase}; use itertools::Itertools; use smallvec::{smallvec, SmallVec}; use stdx::never; @@ -116,7 +117,7 @@ pub enum AdjustmentHintsMode { PreferPostfix, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum InlayKind { Adjustment, BindingMode, @@ -132,7 +133,7 @@ pub enum InlayKind { RangeExclusive, } -#[derive(Debug)] +#[derive(Debug, Hash)] pub enum InlayHintPosition { Before, After, @@ -151,13 +152,23 @@ pub struct InlayHint { pub label: InlayHintLabel, /// Text edit to apply when "accepting" this inlay hint. pub text_edit: Option, - pub needs_resolve: bool, +} + +impl std::hash::Hash for InlayHint { + fn hash(&self, state: &mut H) { + self.range.hash(state); + self.position.hash(state); + self.pad_left.hash(state); + self.pad_right.hash(state); + self.kind.hash(state); + self.label.hash(state); + self.text_edit.is_some().hash(state); + } } impl InlayHint { fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { - needs_resolve: false, range, kind, label: InlayHintLabel::from(")"), @@ -167,9 +178,9 @@ impl InlayHint { pad_right: false, } } + fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { - needs_resolve: false, range, kind, label: InlayHintLabel::from("("), @@ -179,15 +190,19 @@ impl InlayHint { pad_right: false, } } + + pub fn needs_resolve(&self) -> bool { + self.text_edit.is_some() || self.label.needs_resolve() + } } -#[derive(Debug)] +#[derive(Debug, Hash)] pub enum InlayTooltip { String(String), Markdown(String), } -#[derive(Default)] +#[derive(Default, Hash)] pub struct InlayHintLabel { pub parts: SmallVec<[InlayHintLabelPart; 1]>, } @@ -265,6 +280,7 @@ impl fmt::Debug for InlayHintLabel { } } +#[derive(Hash)] pub struct InlayHintLabelPart { pub text: String, /// Source location represented by this label part. The client will use this to fetch the part's @@ -313,9 +329,7 @@ impl fmt::Write for InlayHintLabelBuilder<'_> { impl HirWrite for InlayHintLabelBuilder<'_> { fn start_location_link(&mut self, def: ModuleDefId) { - if self.location.is_some() { - never!("location link is already started"); - } + never!(self.location.is_some(), "location link is already started"); self.make_new_part(); let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; let location = location.call_site(); @@ -425,11 +439,6 @@ fn ty_to_text_edit( Some(builder.finish()) } -pub enum RangeLimit { - Fixed(TextRange), - NearestParent(TextSize), -} - // Feature: Inlay Hints // // rust-analyzer shows additional information inline with the source code. @@ -451,7 +460,7 @@ pub enum RangeLimit { pub(crate) fn inlay_hints( db: &RootDatabase, file_id: FileId, - range_limit: Option, + range_limit: Option, config: &InlayHintsConfig, ) -> Vec { let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered(); @@ -466,31 +475,13 @@ pub(crate) fn inlay_hints( let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); match range_limit { - Some(RangeLimit::Fixed(range)) => match file.covering_element(range) { + Some(range) => match file.covering_element(range) { NodeOrToken::Token(_) => return acc, NodeOrToken::Node(n) => n .descendants() .filter(|descendant| range.intersect(descendant.text_range()).is_some()) .for_each(hints), }, - Some(RangeLimit::NearestParent(position)) => { - match file.token_at_offset(position).left_biased() { - Some(token) => { - if let Some(parent_block) = - token.parent_ancestors().find_map(ast::BlockExpr::cast) - { - parent_block.syntax().descendants().for_each(hints) - } else if let Some(parent_item) = - token.parent_ancestors().find_map(ast::Item::cast) - { - parent_item.syntax().descendants().for_each(hints) - } else { - return acc; - } - } - None => return acc, - } - } None => file.descendants().for_each(hints), }; } @@ -498,6 +489,39 @@ pub(crate) fn inlay_hints( acc } +pub(crate) fn inlay_hints_resolve( + db: &RootDatabase, + file_id: FileId, + position: TextSize, + hash: u64, + config: &InlayHintsConfig, +) -> Option { + let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered(); + let sema = Semantics::new(db); + let file = sema.parse(file_id); + let file = file.syntax(); + + let scope = sema.scope(file)?; + let famous_defs = FamousDefs(&sema, scope.krate()); + let mut acc = Vec::new(); + + let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); + match file.token_at_offset(position).left_biased() { + Some(token) => { + if let Some(parent_block) = token.parent_ancestors().find_map(ast::BlockExpr::cast) { + parent_block.syntax().descendants().for_each(hints) + } else if let Some(parent_item) = token.parent_ancestors().find_map(ast::Item::cast) { + parent_item.syntax().descendants().for_each(hints) + } else { + return None; + } + } + None => return None, + } + + acc.into_iter().find(|hint| BuildHasherDefault::::default().hash_one(hint) == hash) +} + fn hints( hints: &mut Vec, famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 631807d99a7e..20128a286f26 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -147,7 +147,6 @@ pub(super) fn hints( None, ); acc.push(InlayHint { - needs_resolve: label.needs_resolve(), range: expr.syntax().text_range(), pad_left: false, pad_right: false, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 45b51e355703..07b9f9cc1fff 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -99,7 +99,6 @@ pub(super) fn hints( None => pat.syntax().text_range(), }; acc.push(InlayHint { - needs_resolve: label.needs_resolve() || text_edit.is_some(), range: match type_ascriptable { Some(Some(t)) => text_range.cover(t.text_range()), _ => text_range, @@ -177,11 +176,7 @@ mod tests { use syntax::{TextRange, TextSize}; use test_utils::extract_annotations; - use crate::{ - fixture, - inlay_hints::{InlayHintsConfig, RangeLimit}, - ClosureReturnTypeHints, - }; + use crate::{fixture, inlay_hints::InlayHintsConfig, ClosureReturnTypeHints}; use crate::inlay_hints::tests::{ check, check_edit, check_no_edit, check_with_config, DISABLED_CONFIG, TEST_CONFIG, @@ -404,7 +399,7 @@ fn main() { .inlay_hints( &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, file_id, - Some(RangeLimit::Fixed(TextRange::new(TextSize::from(500), TextSize::from(600)))), + Some(TextRange::new(TextSize::from(500), TextSize::from(600))), ) .unwrap(); let actual = diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs index 35504ffa7859..f27390ee898b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs @@ -50,7 +50,6 @@ pub(super) fn hints( _ => return, }; acc.push(InlayHint { - needs_resolve: false, range, kind: InlayKind::BindingMode, label: r.into(), @@ -69,7 +68,6 @@ pub(super) fn hints( hir::BindingMode::Ref(Mutability::Shared) => "ref", }; acc.push(InlayHint { - needs_resolve: false, range: pat.syntax().text_range(), kind: InlayKind::BindingMode, label: bm.into(), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index b6063978e923..d86487d4b41b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -59,7 +59,6 @@ pub(super) fn hints( } let label = label_of_ty(famous_defs, config, &ty)?; acc.push(InlayHint { - needs_resolve: label.needs_resolve(), range: expr.syntax().text_range(), kind: InlayKind::Chaining, label, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index 2b68538c198c..2cefd5acdc2e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -109,7 +109,6 @@ pub(super) fn hints( let linked_location = name_range.map(|range| FileRange { file_id, range }); acc.push(InlayHint { - needs_resolve: linked_location.is_some(), range: closing_token.text_range(), kind: InlayKind::ClosingBrace, label: InlayHintLabel::simple(label, None, linked_location), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index 2f8b959516df..f1b524e08809 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -32,7 +32,6 @@ pub(super) fn hints( let range = closure.syntax().first_token()?.prev_token()?.text_range(); let range = TextRange::new(range.end() - TextSize::from(1), range.end()); acc.push(InlayHint { - needs_resolve: false, range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from("move"), @@ -45,7 +44,6 @@ pub(super) fn hints( } }; acc.push(InlayHint { - needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from("("), @@ -79,7 +77,6 @@ pub(super) fn hints( }), ); acc.push(InlayHint { - needs_resolve: label.needs_resolve(), range: move_kw_range, kind: InlayKind::ClosureCapture, label, @@ -91,7 +88,6 @@ pub(super) fn hints( if idx != last { acc.push(InlayHint { - needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from(", "), @@ -103,7 +99,6 @@ pub(super) fn hints( } } acc.push(InlayHint { - needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from(")"), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs index 204967cd7ca8..3b41db0f13d0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs @@ -64,7 +64,6 @@ pub(super) fn hints( }; acc.push(InlayHint { - needs_resolve: label.needs_resolve() || text_edit.is_some(), range: param_list.syntax().text_range(), kind: InlayKind::Type, label, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs index 06cce147d2a1..202954100fb9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs @@ -79,7 +79,6 @@ fn variant_hints( None, ); acc.push(InlayHint { - needs_resolve: label.needs_resolve(), range: match eq_token { Some(t) => range.cover(t.text_range()), _ => range, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs index 6e5f23bed09f..d3666754e2be 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs @@ -22,7 +22,6 @@ pub(super) fn hints( } let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint { - needs_resolve: false, range: t.text_range(), kind: InlayKind::Lifetime, label: label.into(), @@ -184,7 +183,6 @@ pub(super) fn hints( let angle_tok = gpl.l_angle_token()?; let is_empty = gpl.generic_params().next().is_none(); acc.push(InlayHint { - needs_resolve: false, range: angle_tok.text_range(), kind: InlayKind::Lifetime, label: format!( @@ -200,7 +198,6 @@ pub(super) fn hints( }); } (None, allocated_lifetimes) => acc.push(InlayHint { - needs_resolve: false, range: func.name()?.syntax().text_range(), kind: InlayKind::GenericParamList, label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs index 5ba4e514e1f0..31f0c7903745 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs @@ -105,7 +105,6 @@ pub(super) fn hints( pad_left: true, pad_right: true, kind: InlayKind::Drop, - needs_resolve: label.needs_resolve(), label, text_edit: None, }) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs index f18e6421cbcb..42223ddf580e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs @@ -31,7 +31,6 @@ pub(super) fn hints( if ty.lifetime().is_none() { let t = ty.amp_token()?; acc.push(InlayHint { - needs_resolve: false, range: t.text_range(), kind: InlayKind::Lifetime, label: "'static".into(), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index 418fc002a8be..96e845b2f323 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -57,7 +57,6 @@ pub(super) fn hints( let label = InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location); InlayHint { - needs_resolve: label.needs_resolve(), range, kind: InlayKind::Parameter, label, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs index c4b0c199fc23..bfb92838857c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs @@ -30,7 +30,6 @@ fn inlay_hint(token: SyntaxToken) -> InlayHint { kind: crate::InlayKind::RangeExclusive, label: crate::InlayHintLabel::from("<"), text_edit: None, - needs_resolve: false, } } diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 6955e14a10a1..ad48d803895f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -58,8 +58,6 @@ mod view_item_tree; mod view_memory_layout; mod view_mir; -use std::ffi::OsStr; - use cfg::CfgOptions; use fetch_crates::CrateInfo; use hir::ChangeWithProcMacros; @@ -90,7 +88,7 @@ pub use crate::{ inlay_hints::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, - InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, RangeLimit, + InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, }, join_lines::JoinLinesConfig, markup::Markup, @@ -121,8 +119,8 @@ pub use ide_completion::{ }; pub use ide_db::{ base_db::{ - Cancelled, CrateGraph, CrateId, Edition, FileChange, FileId, FilePosition, FileRange, - SourceRoot, SourceRootId, + Cancelled, CrateGraph, CrateId, FileChange, FileId, FilePosition, FileRange, SourceRoot, + SourceRootId, }, documentation::Documentation, label::Label, @@ -137,6 +135,7 @@ pub use ide_diagnostics::{ Diagnostic, DiagnosticCode, DiagnosticsConfig, ExprFillDefaultMode, Severity, }; pub use ide_ssr::SsrError; +pub use span::Edition; pub use syntax::{TextRange, TextSize}; pub use text_edit::{Indel, TextEdit}; @@ -354,6 +353,10 @@ impl Analysis { self.with_db(|db| test_explorer::discover_tests_in_crate(db, crate_id)) } + pub fn discover_tests_in_file(&self, file_id: FileId) -> Cancellable> { + self.with_db(|db| test_explorer::discover_tests_in_file(db, file_id)) + } + /// Renders the crate graph to GraphViz "dot" syntax. pub fn view_crate_graph(&self, full: bool) -> Cancellable> { self.with_db(|db| view_crate_graph::view_crate_graph(db, full)) @@ -415,10 +418,19 @@ impl Analysis { &self, config: &InlayHintsConfig, file_id: FileId, - range: Option, + range: Option, ) -> Cancellable> { self.with_db(|db| inlay_hints::inlay_hints(db, file_id, range, config)) } + pub fn inlay_hints_resolve( + &self, + config: &InlayHintsConfig, + file_id: FileId, + position: TextSize, + hash: u64, + ) -> Cancellable> { + self.with_db(|db| inlay_hints::inlay_hints_resolve(db, file_id, position, hash, config)) + } /// Returns the set of folding ranges. pub fn folding_ranges(&self, file_id: FileId) -> Cancellable> { @@ -502,8 +514,8 @@ impl Analysis { pub fn external_docs( &self, position: FilePosition, - target_dir: Option<&OsStr>, - sysroot: Option<&OsStr>, + target_dir: Option<&str>, + sysroot: Option<&str>, ) -> Cancellable { self.with_db(|db| { doc_links::external_docs(db, position, target_dir, sysroot).unwrap_or_default() diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index fe063081f799..3fef16df25e3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -167,6 +167,7 @@ impl StaticIndex<'_> { keywords: true, format: crate::HoverDocFormat::Markdown, max_trait_assoc_items_count: None, + max_struct_field_count: None, }; let tokens = tokens.filter(|token| { matches!( diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs index c3d85e38936d..8e7767c8e5d7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/status.rs +++ b/src/tools/rust-analyzer/crates/ide/src/status.rs @@ -10,7 +10,7 @@ use ide_db::{ debug::{DebugQueryTable, TableEntry}, Query, QueryTable, }, - CrateData, FileId, FileTextQuery, ParseQuery, SourceDatabase, SourceRootId, + CompressedFileTextQuery, CrateData, FileId, ParseQuery, SourceDatabase, SourceRootId, }, symbol_index::ModuleSymbolsQuery, }; @@ -38,7 +38,7 @@ use triomphe::Arc; pub(crate) fn status(db: &RootDatabase, file_id: Option) -> String { let mut buf = String::new(); - format_to!(buf, "{}\n", collect_query(FileTextQuery.in_db(db))); + format_to!(buf, "{}\n", collect_query(CompressedFileTextQuery.in_db(db))); format_to!(buf, "{}\n", collect_query(ParseQuery.in_db(db))); format_to!(buf, "{}\n", collect_query(ParseMacroExpansionQuery.in_db(db))); format_to!(buf, "{}\n", collect_query(LibrarySymbolsQuery.in_db(db))); @@ -160,7 +160,7 @@ impl QueryCollect for ParseMacroExpansionQuery { type Collector = SyntaxTreeStats; } -impl QueryCollect for FileTextQuery { +impl QueryCollect for CompressedFileTextQuery { type Collector = FilesStats; } @@ -188,8 +188,8 @@ impl fmt::Display for FilesStats { } } -impl StatCollect> for FilesStats { - fn collect_entry(&mut self, _: FileId, value: Option>) { +impl StatCollect> for FilesStats { + fn collect_entry(&mut self, _: FileId, value: Option>) { self.total += 1; self.size += value.unwrap().len(); } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index 96c7c4755942..e7346cbb9925 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -178,6 +178,23 @@ fn keyword( T![do] | T![yeet] if parent_matches::(&token) => h | HlMod::ControlFlow, T![for] if parent_matches::(&token) => h | HlMod::ControlFlow, T![unsafe] => h | HlMod::Unsafe, + T![const] + if token.parent().map_or(false, |it| { + matches!( + it.kind(), + SyntaxKind::CONST + | SyntaxKind::FN + | SyntaxKind::IMPL + | SyntaxKind::BLOCK_EXPR + | SyntaxKind::CLOSURE_EXPR + | SyntaxKind::FN_PTR_TYPE + | SyntaxKind::TYPE_BOUND + | SyntaxKind::CONST_BLOCK_PAT + ) + }) => + { + h | HlMod::Const + } T![true] | T![false] => HlTag::BoolLiteral.into(), // crate is handled just as a token if it's in an `extern crate` T![crate] if parent_matches::(&token) => h, @@ -377,14 +394,17 @@ pub(super) fn highlight_def( if let Some(item) = func.as_assoc_item(db) { h |= HlMod::Associated; match func.self_param(db) { - Some(sp) => match sp.access(db) { - hir::Access::Exclusive => { - h |= HlMod::Mutable; - h |= HlMod::Reference; + Some(sp) => { + h.tag = HlTag::Symbol(SymbolKind::Method); + match sp.access(db) { + hir::Access::Exclusive => { + h |= HlMod::Mutable; + h |= HlMod::Reference; + } + hir::Access::Shared => h |= HlMod::Reference, + hir::Access::Owned => h |= HlMod::Consuming, } - hir::Access::Shared => h |= HlMod::Reference, - hir::Access::Owned => h |= HlMod::Consuming, - }, + } None => h |= HlMod::Static, } @@ -406,6 +426,9 @@ pub(super) fn highlight_def( if func.is_async(db) { h |= HlMod::Async; } + if func.is_const(db) { + h |= HlMod::Const; + } h } @@ -420,10 +443,11 @@ pub(super) fn highlight_def( } Definition::Variant(_) => Highlight::new(HlTag::Symbol(SymbolKind::Variant)), Definition::Const(konst) => { - let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const)); + let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const)) | HlMod::Const; if let Some(item) = konst.as_assoc_item(db) { h |= HlMod::Associated; + h |= HlMod::Static; match item.container(db) { hir::AssocItemContainer::Impl(i) => { if i.trait_(db).is_some() { @@ -445,6 +469,7 @@ pub(super) fn highlight_def( if let Some(item) = type_.as_assoc_item(db) { h |= HlMod::Associated; + h |= HlMod::Static; match item.container(db) { hir::AssocItemContainer::Impl(i) => { if i.trait_(db).is_some() { @@ -474,7 +499,7 @@ pub(super) fn highlight_def( Definition::GenericParam(it) => match it { hir::GenericParam::TypeParam(_) => Highlight::new(HlTag::Symbol(SymbolKind::TypeParam)), hir::GenericParam::ConstParam(_) => { - Highlight::new(HlTag::Symbol(SymbolKind::ConstParam)) + Highlight::new(HlTag::Symbol(SymbolKind::ConstParam)) | HlMod::Const } hir::GenericParam::LifetimeParam(_) => { Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)) @@ -550,8 +575,7 @@ fn highlight_method_call( ) -> Option { let func = sema.resolve_method_call(method_call)?; - let mut h = SymbolKind::Function.into(); - h |= HlMod::Associated; + let mut h = SymbolKind::Method.into(); if func.is_unsafe_to_call(sema.db) || sema.is_unsafe_method_call(method_call) { h |= HlMod::Unsafe; @@ -647,7 +671,7 @@ fn highlight_name_ref_by_syntax( match parent.kind() { METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent) .and_then(|it| highlight_method_call(sema, krate, &it)) - .unwrap_or_else(|| SymbolKind::Function.into()), + .unwrap_or_else(|| SymbolKind::Method.into()), FIELD_EXPR => { let h = HlTag::Symbol(SymbolKind::Field); let is_union = ast::FieldExpr::cast(parent) diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs index a6dca0541e5f..e754b702deed 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs @@ -109,6 +109,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs index 5163a0de4177..5c7a463ccdc9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs @@ -46,7 +46,7 @@ pub enum HlTag { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] #[repr(u8)] pub enum HlMod { - /// Used for items in traits and impls. + /// Used for associated items. Associated = 0, /// Used with keywords like `async` and `await`. Async, @@ -54,6 +54,8 @@ pub enum HlMod { Attribute, /// Callable item or value. Callable, + /// Constant operation. + Const, /// Value that is being consumed in a function call Consuming, /// Used with keywords like `if` and `break`. @@ -82,7 +84,7 @@ pub enum HlMod { Public, /// Immutable reference. Reference, - /// Used for associated functions. + /// Used for associated items, except Methods. (Some languages call these static members) Static, /// Used for items in traits and trait impls. Trait, @@ -147,6 +149,7 @@ impl HlTag { SymbolKind::LifetimeParam => "lifetime", SymbolKind::Local => "variable", SymbolKind::Macro => "macro", + SymbolKind::Method => "method", SymbolKind::ProcMacro => "proc_macro", SymbolKind::Module => "module", SymbolKind::SelfParam => "self_keyword", @@ -211,6 +214,7 @@ impl HlMod { HlMod::Async, HlMod::Attribute, HlMod::Callable, + HlMod::Const, HlMod::Consuming, HlMod::ControlFlow, HlMod::CrateRoot, @@ -237,6 +241,7 @@ impl HlMod { HlMod::Attribute => "attribute", HlMod::Callable => "callable", HlMod::Consuming => "consuming", + HlMod::Const => "const", HlMod::ControlFlow => "control", HlMod::CrateRoot => "crate_root", HlMod::DefaultLibrary => "default_library", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html index 6994cb3d5c54..9c7f03fc1587 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -50,15 +51,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd impl foo { pub fn is_static() {} - pub fn is_not_static(&self) {} + pub fn is_not_static(&self) {} } trait t { fn t_is_static() {} - fn t_is_not_static(&self) {} + fn t_is_not_static(&self) {} } impl t for foo { pub fn is_static() {} - pub fn is_not_static(&self) {} + pub fn is_not_static(&self) {} } \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html index dc2d103b5815..de902b5137d0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -47,7 +48,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
#[allow(dead_code)]
 #[rustfmt::skip]
 #[proc_macros::identity]
-#[derive(Copy)]
+#[derive(Default)]
 /// This is a doc comment
 // This is a normal comment
 /// This is a doc comment
@@ -57,4 +58,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 // This is another normal comment
 #[derive(Copy, Unresolved)]
 // The reason for these being here is to test AttrIds
-struct Foo;
\ No newline at end of file +enum Foo { + #[default] + Bar +} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html index 093cc2358a67..eed3968a9068 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -58,7 +59,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd foo!(Bar); fn func() { mod inner { - struct Innerest<const C: usize> { field: [(); {C}] } + struct Innerest<const C: usize> { field: [(); {C}] } } } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html new file mode 100644 index 000000000000..cd6ffc2c3ae3 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html @@ -0,0 +1,83 @@ + + +
macro_rules! id {
+    ($($tt:tt)*) => {
+        $($tt)*
+    };
+}
+const CONST_ITEM: *const () = &raw const ();
+const fn const_fn<const CONST_PARAM: ()>(const {}: const fn()) where (): const ConstTrait {
+    CONST_ITEM;
+    CONST_PARAM;
+    const {
+        const || {}
+    }
+    id!(
+        CONST_ITEM;
+        CONST_PARAM;
+        const {
+            const || {}
+        };
+        &raw const ();
+        const
+    );
+}
+trait ConstTrait {
+    const ASSOC_CONST: () = ();
+    const fn assoc_const_fn() {}
+}
+impl const ConstTrait for () {
+    const ASSOC_CONST: () = ();
+    const fn assoc_const_fn() {}
+}
+
+macro_rules! unsafe_deref {
+    () => {
+        *(&() as *const ())
+    };
+}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html index 154b823fffb1..63e1560b9210 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -47,7 +48,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
extern crate foo;
 use core::iter;
 
-pub const NINETY_TWO: u8 = 92;
+pub const NINETY_TWO: u8 = 92;
 
 use foo as foooo;
 
@@ -56,13 +57,13 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 }
 
 mod bar {
-    pub(in super) const FORTY_TWO: u8 = 42;
+    pub(in super) const FORTY_TWO: u8 = 42;
 
     mod baz {
-        use super::super::NINETY_TWO;
+        use super::super::NINETY_TWO;
         use crate::foooo::Point;
 
-        pub(in super::super) const TWENTY_NINE: u8 = 29;
+        pub(in super::super) const TWENTY_NINE: u8 = 29;
     }
 }
 
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html index 58613bf15105..366895ce7ec7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -48,5 +49,5 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd fn main() { let foo = Some(92); - let nums = iter::repeat(foo.unwrap()); + let nums = iter::repeat(foo.unwrap()); } \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 34274932afc1..b2ca6e1ca4b6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -71,7 +72,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd // KILLER WHALE /// Ishmael."; /// ``` - pub const bar: bool = true; + pub const bar: bool = true; /// Constructs a new `Foo`. /// @@ -81,7 +82,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd /// # #![allow(unused_mut)] /// let mut foo: Foo = Foo::new(); /// ``` - pub const fn new() -> Foo { + pub const fn new() -> Foo { Foo { bar: true } } @@ -109,25 +110,25 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd /// ``` /// /// ```rust,no_run - /// let foobar = Foo::new().bar(); + /// let foobar = Foo::new().bar(); /// ``` /// /// ~~~rust,no_run /// // code block with tilde. - /// let foobar = Foo::new().bar(); + /// let foobar = Foo::new().bar(); /// ~~~ /// /// ``` /// // functions - /// fn foo<T, const X: usize>(arg: i32) { - /// let x: T = X; + /// fn foo<T, const X: usize>(arg: i32) { + /// let x: T = X; /// } /// ``` /// /// ```sh /// echo 1 /// ``` - pub fn foo(&self) -> bool { + pub fn foo(&self) -> bool { true } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html index 729e4791f557..129b287e52f2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index 066fcfb1dfe6..7ba1194d675f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -63,25 +64,25 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } trait Bar { - fn bar(&self) -> i32; + fn bar(&self) -> i32; } impl Bar for Foo { - fn bar(&self) -> i32 { + fn bar(&self) -> i32 { self.x } } impl Foo { - fn baz(mut self, f: Foo) -> i32 { - f.baz(self) + fn baz(mut self, f: Foo) -> i32 { + f.baz(self) } - fn qux(&mut self) { + fn qux(&mut self) { self.x = 0; } - fn quop(&self) -> i32 { + fn quop(&self) -> i32 { self.x } } @@ -94,15 +95,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } impl FooCopy { - fn baz(self, f: FooCopy) -> u32 { - f.baz(self) + fn baz(self, f: FooCopy) -> u32 { + f.baz(self) } - fn qux(&mut self) { + fn qux(&mut self) { self.x = 0; } - fn quop(&self) -> u32 { + fn quop(&self) -> u32 { self.x } } @@ -119,9 +120,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd loop {} } -fn const_param<const FOO: usize>() -> usize { - const_param::<{ FOO }>(); - FOO +fn const_param<const FOO: usize>() -> usize { + const_param::<{ FOO }>(); + FOO } use ops::Fn; @@ -148,17 +149,17 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd let mut foo = Foo { x, y: x }; let foo2 = Foo { x, y: x }; - foo.quop(); - foo.qux(); - foo.baz(foo2); + foo.quop(); + foo.qux(); + foo.baz(foo2); let mut copy = FooCopy { x }; - copy.quop(); - copy.qux(); - copy.baz(copy); + copy.quop(); + copy.qux(); + copy.baz(copy); let a = |x| x; - let bar = Foo::baz; + let bar = Foo::baz; let baz = (-42,); let baz = -baz.0; @@ -178,7 +179,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd use Option::*; impl<T> Option<T> { - fn and<U>(self, other: Option<U>) -> Option<(T, U)> { + fn and<U>(self, other: Option<U>) -> Option<(T, U)> { match other { None => unimplemented!(), Nope => Nope, @@ -200,12 +201,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd fn use_foo_items() { let bob = foo::Person { name: "Bob", - age: foo::consts::NUMBER, + age: foo::consts::NUMBER, }; let control_flow = foo::identity(foo::ControlFlow::Continue); - if control_flow.should_die() { + if control_flow.should_die() { foo::die!(); } } @@ -213,23 +214,23 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd pub enum Bool { True, False } impl Bool { - pub const fn to_primitive(self) -> bool { + pub const fn to_primitive(self) -> bool { true } } -const USAGE_OF_BOOL:bool = Bool::True.to_primitive(); +const USAGE_OF_BOOL:bool = Bool::True.to_primitive(); trait Baz { - type Qux; + type Qux; } fn baz<T>(t: T) where T: Baz, - <T as Baz>::Qux: Bar {} + <T as Baz>::Qux: Bar {} fn gp_shadows_trait<Baz: Bar>() { - Baz::bar; + Baz::bar; } \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index 58a147dd80a4..6c3fbcfcf41e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html index 22ae5c82a4b5..7064a877070c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html index af7799965937..8428b8158003 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index 32ac6a94d868..573b3d4bd527 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html index ef8a48ca1c1d..947d1bf1e357 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html index a2ded15fd1ba..0fe2b6f274d0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html index d123ee049765..c60b6ab27bba 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html index 4429e5d933ac..5d51b1497895 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index b6458fa7ca09..7a07d17b2718 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -171,9 +172,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd in(reg) i, ); - const CONSTANT: () = (): + const CONSTANT: () = (): let mut m = (); format_args!(concat!("{}"), "{}"); - format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); + format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); reuse_twice!("{backslash}"); } \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 49b588baa58e..15d6be6334c6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -65,7 +66,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd struct Struct { field: i32 } impl Struct { - unsafe fn unsafe_method(&self) {} + unsafe fn unsafe_method(&self) {} } #[repr(packed)] @@ -80,11 +81,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd fn unsafe_trait_bound<T: UnsafeTrait>(_: T) {} trait DoTheAutoref { - fn calls_autoref(&self); + fn calls_autoref(&self); } impl DoTheAutoref for u16 { - fn calls_autoref(&self) {} + fn calls_autoref(&self) {} } fn main() { @@ -106,7 +107,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd Union { b: 0 } => (), Union { a } => (), } - Struct { field: 0 }.unsafe_method(); + Struct { field: 0 }.unsafe_method(); // unsafe deref *x; @@ -123,6 +124,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd let Packed { a: ref _a } = packed; // unsafe auto ref of packed field - packed.a.calls_autoref(); + packed.a.calls_autoref(); } } \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index 6fed7d783e81..c2990fd76eac 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -22,11 +22,11 @@ fn attributes() { check_highlighting( r#" //- proc_macros: identity -//- minicore: derive, copy +//- minicore: derive, copy, default #[allow(dead_code)] #[rustfmt::skip] #[proc_macros::identity] -#[derive(Copy)] +#[derive(Default)] /// This is a doc comment // This is a normal comment /// This is a doc comment @@ -36,7 +36,10 @@ fn attributes() { // This is another normal comment #[derive(Copy, Unresolved)] // The reason for these being here is to test AttrIds -struct Foo; +enum Foo { + #[default] + Bar +} "#, expect_file!["./test_data/highlight_attributes.html"], false, @@ -629,6 +632,52 @@ fn main() { ); } +#[test] +fn test_const_highlighting() { + check_highlighting( + r#" +macro_rules! id { + ($($tt:tt)*) => { + $($tt)* + }; +} +const CONST_ITEM: *const () = &raw const (); +const fn const_fn(const {}: const fn()) where (): const ConstTrait { + CONST_ITEM; + CONST_PARAM; + const { + const || {} + } + id!( + CONST_ITEM; + CONST_PARAM; + const { + const || {} + }; + &raw const (); + const + ); +} +trait ConstTrait { + const ASSOC_CONST: () = (); + const fn assoc_const_fn() {} +} +impl const ConstTrait for () { + const ASSOC_CONST: () = (); + const fn assoc_const_fn() {} +} + +macro_rules! unsafe_deref { + () => { + *(&() as *const ()) + }; +} +"#, + expect_file!["./test_data/highlight_const.html"], + false, + ); +} + #[test] fn test_highlight_doc_comment() { check_highlighting( @@ -1173,8 +1222,27 @@ fn benchmark_syntax_highlighting_parser() { .highlight(HL_CONFIG, file_id) .unwrap() .iter() - .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function)) + .filter(|it| { + matches!(it.highlight.tag, HlTag::Symbol(SymbolKind::Function | SymbolKind::Method)) + }) .count() }; assert_eq!(hash, 1169); } + +#[test] +fn highlight_trait_with_lifetimes_regression_16958() { + let (analysis, file_id) = fixture::file( + r#" +pub trait Deserialize<'de> { + fn deserialize(); +} + +fn f<'de, T: Deserialize<'de>>() { + T::deserialize(); +} +"# + .trim(), + ); + let _ = analysis.highlight(HL_CONFIG, file_id).unwrap(); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs b/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs index ca4713997030..99e243086079 100644 --- a/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs +++ b/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs @@ -7,7 +7,7 @@ use ide_db::{ }; use syntax::TextRange; -use crate::{navigation_target::ToNav, runnables::runnable_fn, Runnable, TryToNav}; +use crate::{runnables::runnable_fn, NavigationTarget, Runnable, TryToNav}; #[derive(Debug)] pub enum TestItemKind { @@ -56,7 +56,12 @@ fn find_crate_by_id(crate_graph: &CrateGraph, crate_id: &str) -> Option }) } -fn discover_tests_in_module(db: &RootDatabase, module: Module, prefix_id: String) -> Vec { +fn discover_tests_in_module( + db: &RootDatabase, + module: Module, + prefix_id: String, + only_in_this_file: bool, +) -> Vec { let sema = Semantics::new(db); let mut r = vec![]; @@ -64,9 +69,9 @@ fn discover_tests_in_module(db: &RootDatabase, module: Module, prefix_id: String let module_name = c.name(db).as_ref().and_then(|n| n.as_str()).unwrap_or("[mod without name]").to_owned(); let module_id = format!("{prefix_id}::{module_name}"); - let module_children = discover_tests_in_module(db, c, module_id.clone()); + let module_children = discover_tests_in_module(db, c, module_id.clone(), only_in_this_file); if !module_children.is_empty() { - let nav = c.to_nav(db).call_site; + let nav = NavigationTarget::from_module_to_decl(sema.db, c).call_site; r.push(TestItem { id: module_id, kind: TestItemKind::Module, @@ -76,7 +81,9 @@ fn discover_tests_in_module(db: &RootDatabase, module: Module, prefix_id: String text_range: Some(nav.focus_or_full_range()), runnable: None, }); - r.extend(module_children); + if !only_in_this_file || c.is_inline(db) { + r.extend(module_children); + } } } for def in module.declarations(db) { @@ -112,6 +119,55 @@ pub(crate) fn discover_tests_in_crate_by_test_id( discover_tests_in_crate(db, crate_id) } +pub(crate) fn discover_tests_in_file(db: &RootDatabase, file_id: FileId) -> Vec { + let sema = Semantics::new(db); + + let Some(module) = sema.file_to_module_def(file_id) else { return vec![] }; + let Some((mut tests, id)) = find_module_id_and_test_parents(&sema, module) else { + return vec![]; + }; + tests.extend(discover_tests_in_module(db, module, id, true)); + tests +} + +fn find_module_id_and_test_parents( + sema: &Semantics<'_, RootDatabase>, + module: Module, +) -> Option<(Vec, String)> { + let Some(parent) = module.parent(sema.db) else { + let name = module.krate().display_name(sema.db)?.to_string(); + return Some(( + vec![TestItem { + id: name.clone(), + kind: TestItemKind::Crate(module.krate().into()), + label: name.clone(), + parent: None, + file: None, + text_range: None, + runnable: None, + }], + name, + )); + }; + let (mut r, mut id) = find_module_id_and_test_parents(sema, parent)?; + let parent = Some(id.clone()); + id += "::"; + let module_name = &module.name(sema.db); + let module_name = module_name.as_ref().and_then(|n| n.as_str()).unwrap_or("[mod without name]"); + id += module_name; + let nav = NavigationTarget::from_module_to_decl(sema.db, module).call_site; + r.push(TestItem { + id: id.clone(), + kind: TestItemKind::Module, + label: module_name.to_owned(), + parent, + file: Some(nav.file_id), + text_range: Some(nav.focus_or_full_range()), + runnable: None, + }); + Some((r, id)) +} + pub(crate) fn discover_tests_in_crate(db: &RootDatabase, crate_id: CrateId) -> Vec { let crate_graph = db.crate_graph(); if !crate_graph[crate_id].origin.is_local() { @@ -133,6 +189,6 @@ pub(crate) fn discover_tests_in_crate(db: &RootDatabase, crate_id: CrateId) -> V text_range: None, runnable: None, }]; - r.extend(discover_tests_in_module(db, module, crate_test_id)); + r.extend(discover_tests_in_module(db, module, crate_test_id, false)); r } diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs index e87fc89fea2a..d3eee0e02e48 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs @@ -175,9 +175,21 @@ fn on_opening_bracket_typed( } } - // If it's a statement in a block, we don't know how many statements should be included - if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) { - return None; + if let Some(parent) = expr.syntax().parent().and_then(ast::Expr::cast) { + let mut node = expr.syntax().clone(); + let all_prev_sib_attr = loop { + match node.prev_sibling() { + Some(sib) if sib.kind().is_trivia() || sib.kind() == SyntaxKind::ATTR => { + node = sib + } + Some(_) => break false, + None => break true, + }; + }; + + if all_prev_sib_attr { + expr = parent; + } } // Insert the closing bracket right after the expression. @@ -824,6 +836,21 @@ fn f() { 0 => {()}, 1 => (), } +} + "#, + ); + type_char( + '{', + r#" +fn main() { + #[allow(unreachable_code)] + $0g(); +} + "#, + r#" +fn main() { + #[allow(unreachable_code)] + {g()}; } "#, ); diff --git a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml index 05412e176b65..48e84a7b25eb 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml +++ b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml @@ -20,6 +20,7 @@ tracing.workspace = true hir-expand.workspace = true ide-db.workspace = true +paths.workspace = true proc-macro-api.workspace = true project-model.workspace = true span.workspace = true diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index fe680e47fef8..79d6fe36b561 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -38,7 +38,7 @@ pub fn load_workspace_at( load_config: &LoadCargoConfig, progress: &dyn Fn(String), ) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option)> { - let root = AbsPathBuf::assert(std::env::current_dir()?.join(root)); + let root = AbsPathBuf::assert_utf8(std::env::current_dir()?.join(root)); let root = ProjectManifest::discover_single(&root)?; let mut workspace = ProjectWorkspace::load(root, cargo_config, progress)?; @@ -387,7 +387,7 @@ fn expander_to_proc_macro( let name = From::from(expander.name()); let kind = match expander.kind() { proc_macro_api::ProcMacroKind::CustomDerive => ProcMacroKind::CustomDerive, - proc_macro_api::ProcMacroKind::FuncLike => ProcMacroKind::FuncLike, + proc_macro_api::ProcMacroKind::Bang => ProcMacroKind::Bang, proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr, }; let disabled = ignored_macros.iter().any(|replace| **replace == name); diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs index ac7f07117845..4d5531ae307f 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs @@ -23,7 +23,11 @@ fn benchmark_parse_macro_rules() { let _pt = bench("mbe parse macro rules"); rules .values() - .map(|it| DeclarativeMacro::parse_macro_rules(it, true, true).rules.len()) + .map(|it| { + DeclarativeMacro::parse_macro_rules(it, |_| span::Edition::CURRENT, true) + .rules + .len() + }) .sum() }; assert_eq!(hash, 1144); @@ -51,10 +55,12 @@ fn benchmark_expand_macro_rules() { assert_eq!(hash, 69413); } -fn macro_rules_fixtures() -> FxHashMap> { +fn macro_rules_fixtures() -> FxHashMap { macro_rules_fixtures_tt() .into_iter() - .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true, true))) + .map(|(id, tt)| { + (id, DeclarativeMacro::parse_macro_rules(&tt, |_| span::Edition::CURRENT, true)) + }) .collect() } @@ -80,7 +86,7 @@ fn macro_rules_fixtures_tt() -> FxHashMap> { /// Generate random invocation fixtures from rules fn invocation_fixtures( - rules: &FxHashMap>, + rules: &FxHashMap, ) -> Vec<(String, tt::Subtree)> { let mut seed = 123456789; let mut res = Vec::new(); @@ -128,11 +134,7 @@ fn invocation_fixtures( } return res; - fn collect_from_op( - op: &Op, - token_trees: &mut Vec>, - seed: &mut usize, - ) { + fn collect_from_op(op: &Op, token_trees: &mut Vec>, seed: &mut usize) { return match op { Op::Var { kind, .. } => match kind.as_ref() { Some(MetaVarKind::Ident) => token_trees.push(make_ident("foo")), diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander.rs b/src/tools/rust-analyzer/crates/mbe/src/expander.rs index 9366048fd955..2f2c0aa6ff59 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander.rs @@ -6,22 +6,21 @@ mod matcher; mod transcriber; use rustc_hash::FxHashMap; +use span::Span; use syntax::SmolStr; -use tt::Span; use crate::{parser::MetaVarKind, ExpandError, ExpandResult}; -pub(crate) fn expand_rules( - rules: &[crate::Rule], - input: &tt::Subtree, - marker: impl Fn(&mut S) + Copy, - is_2021: bool, +pub(crate) fn expand_rules( + rules: &[crate::Rule], + input: &tt::Subtree, + marker: impl Fn(&mut Span) + Copy, new_meta_vars: bool, - call_site: S, -) -> ExpandResult> { - let mut match_: Option<(matcher::Match, &crate::Rule)> = None; + call_site: Span, +) -> ExpandResult> { + let mut match_: Option<(matcher::Match, &crate::Rule)> = None; for rule in rules { - let new_match = matcher::match_(&rule.lhs, input, is_2021); + let new_match = matcher::match_(&rule.lhs, input); if new_match.err.is_none() { // If we find a rule that applies without errors, we're done. @@ -110,30 +109,24 @@ pub(crate) fn expand_rules( /// In other words, `Bindings` is a *multi* mapping from `SmolStr` to /// `tt::TokenTree`, where the index to select a particular `TokenTree` among /// many is not a plain `usize`, but a `&[usize]`. -#[derive(Debug, Clone, PartialEq, Eq)] -struct Bindings { - inner: FxHashMap>, -} - -impl Default for Bindings { - fn default() -> Self { - Self { inner: Default::default() } - } +#[derive(Debug, Default, Clone, PartialEq, Eq)] +struct Bindings { + inner: FxHashMap, } #[derive(Debug, Clone, PartialEq, Eq)] -enum Binding { - Fragment(Fragment), - Nested(Vec>), +enum Binding { + Fragment(Fragment), + Nested(Vec), Empty, Missing(MetaVarKind), } #[derive(Debug, Clone, PartialEq, Eq)] -enum Fragment { +enum Fragment { Empty, /// token fragments are just copy-pasted into the output - Tokens(tt::TokenTree), + Tokens(tt::TokenTree), /// Expr ast fragments are surrounded with `()` on insertion to preserve /// precedence. Note that this impl is different from the one currently in /// `rustc` -- `rustc` doesn't translate fragments into token trees at all. @@ -141,7 +134,7 @@ enum Fragment { /// At one point in time, we tried to use "fake" delimiters here à la /// proc-macro delimiter=none. As we later discovered, "none" delimiters are /// tricky to handle in the parser, and rustc doesn't handle those either. - Expr(tt::Subtree), + Expr(tt::Subtree), /// There are roughly two types of paths: paths in expression context, where a /// separator `::` between an identifier and its following generic argument list /// is mandatory, and paths in type context, where `::` can be omitted. @@ -151,5 +144,5 @@ enum Fragment { /// and is trasncribed as an expression-context path, verbatim transcription /// would cause a syntax error. We need to fix it up just before transcribing; /// see `transcriber::fix_up_and_push_path_tt()`. - Path(tt::Subtree), + Path(tt::Subtree), } diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs index eea92cfba4c2..3170834d54ff 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs @@ -62,8 +62,9 @@ use std::rc::Rc; use smallvec::{smallvec, SmallVec}; +use span::Span; use syntax::SmolStr; -use tt::{DelimSpan, Span}; +use tt::DelimSpan; use crate::{ expander::{Binding, Bindings, ExpandResult, Fragment}, @@ -72,7 +73,7 @@ use crate::{ ExpandError, MetaTemplate, ValueResult, }; -impl Bindings { +impl Bindings { fn push_optional(&mut self, name: &SmolStr) { self.inner.insert(name.clone(), Binding::Fragment(Fragment::Empty)); } @@ -81,14 +82,14 @@ impl Bindings { self.inner.insert(name.clone(), Binding::Empty); } - fn bindings(&self) -> impl Iterator> { + fn bindings(&self) -> impl Iterator { self.inner.values() } } -#[derive(Clone, Debug, PartialEq, Eq)] -pub(super) struct Match { - pub(super) bindings: Bindings, +#[derive(Clone, Default, Debug, PartialEq, Eq)] +pub(super) struct Match { + pub(super) bindings: Bindings, /// We currently just keep the first error and count the rest to compare matches. pub(super) err: Option, pub(super) err_count: usize, @@ -98,19 +99,7 @@ pub(super) struct Match { pub(super) bound_count: usize, } -impl Default for Match { - fn default() -> Self { - Self { - bindings: Default::default(), - err: Default::default(), - err_count: Default::default(), - unmatched_tts: Default::default(), - bound_count: Default::default(), - } - } -} - -impl Match { +impl Match { fn add_err(&mut self, err: ExpandError) { let prev_err = self.err.take(); self.err = prev_err.or(Some(err)); @@ -119,16 +108,12 @@ impl Match { } /// Matching errors are added to the `Match`. -pub(super) fn match_( - pattern: &MetaTemplate, - input: &tt::Subtree, - is_2021: bool, -) -> Match { - let mut res = match_loop(pattern, input, is_2021); +pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match { + let mut res = match_loop(pattern, input); res.bound_count = count(res.bindings.bindings()); return res; - fn count<'a, S: 'a>(bindings: impl Iterator>) -> usize { + fn count<'a>(bindings: impl Iterator) -> usize { bindings .map(|it| match it { Binding::Fragment(_) => 1, @@ -141,10 +126,10 @@ pub(super) fn match_( } #[derive(Debug, Clone)] -enum BindingKind { +enum BindingKind { Empty(SmolStr), Optional(SmolStr), - Fragment(SmolStr, Fragment), + Fragment(SmolStr, Fragment), Missing(SmolStr, MetaVarKind), Nested(usize, usize), } @@ -158,18 +143,13 @@ enum LinkNode { Parent { idx: usize, len: usize }, } -struct BindingsBuilder { - nodes: Vec>>>>, +#[derive(Default)] +struct BindingsBuilder { + nodes: Vec>>>, nested: Vec>>, } -impl Default for BindingsBuilder { - fn default() -> Self { - Self { nodes: Default::default(), nested: Default::default() } - } -} - -impl BindingsBuilder { +impl BindingsBuilder { fn alloc(&mut self) -> BindingsIdx { let idx = self.nodes.len(); self.nodes.push(Vec::new()); @@ -206,7 +186,7 @@ impl BindingsBuilder { self.nodes[idx.0].push(LinkNode::Node(Rc::new(BindingKind::Optional(var.clone())))); } - fn push_fragment(&mut self, idx: &mut BindingsIdx, var: &SmolStr, fragment: Fragment) { + fn push_fragment(&mut self, idx: &mut BindingsIdx, var: &SmolStr, fragment: Fragment) { self.nodes[idx.0] .push(LinkNode::Node(Rc::new(BindingKind::Fragment(var.clone(), fragment)))); } @@ -227,11 +207,11 @@ impl BindingsBuilder { idx.0 = new_idx; } - fn build(self, idx: &BindingsIdx) -> Bindings { + fn build(self, idx: &BindingsIdx) -> Bindings { self.build_inner(&self.nodes[idx.0]) } - fn build_inner(&self, link_nodes: &[LinkNode>>]) -> Bindings { + fn build_inner(&self, link_nodes: &[LinkNode>]) -> Bindings { let mut bindings = Bindings::default(); let mut nodes = Vec::new(); self.collect_nodes(link_nodes, &mut nodes); @@ -281,7 +261,7 @@ impl BindingsBuilder { &'a self, id: usize, len: usize, - nested_refs: &mut Vec<&'a [LinkNode>>]>, + nested_refs: &mut Vec<&'a [LinkNode>]>, ) { self.nested[id].iter().take(len).for_each(|it| match it { LinkNode::Node(id) => nested_refs.push(&self.nodes[*id]), @@ -289,7 +269,7 @@ impl BindingsBuilder { }); } - fn collect_nested(&self, idx: usize, nested_idx: usize, nested: &mut Vec>) { + fn collect_nested(&self, idx: usize, nested_idx: usize, nested: &mut Vec) { let last = &self.nodes[idx]; let mut nested_refs: Vec<&[_]> = Vec::new(); self.nested[nested_idx].iter().for_each(|it| match *it { @@ -300,7 +280,7 @@ impl BindingsBuilder { nested.extend(nested_refs.into_iter().map(|iter| self.build_inner(iter))); } - fn collect_nodes_ref<'a>(&'a self, id: usize, len: usize, nodes: &mut Vec<&'a BindingKind>) { + fn collect_nodes_ref<'a>(&'a self, id: usize, len: usize, nodes: &mut Vec<&'a BindingKind>) { self.nodes[id].iter().take(len).for_each(|it| match it { LinkNode::Node(it) => nodes.push(it), LinkNode::Parent { idx, len } => self.collect_nodes_ref(*idx, *len, nodes), @@ -309,8 +289,8 @@ impl BindingsBuilder { fn collect_nodes<'a>( &'a self, - link_nodes: &'a [LinkNode>>], - nodes: &mut Vec<&'a BindingKind>, + link_nodes: &'a [LinkNode>], + nodes: &mut Vec<&'a BindingKind>, ) { link_nodes.iter().for_each(|it| match it { LinkNode::Node(it) => nodes.push(it), @@ -320,22 +300,22 @@ impl BindingsBuilder { } #[derive(Debug, Clone)] -struct MatchState<'t, S> { +struct MatchState<'t> { /// The position of the "dot" in this matcher - dot: OpDelimitedIter<'t, S>, + dot: OpDelimitedIter<'t>, /// Token subtree stack /// When matching against matchers with nested delimited submatchers (e.g., `pat ( pat ( .. ) /// pat ) pat`), we need to keep track of the matchers we are descending into. This stack does /// that where the bottom of the stack is the outermost matcher. - stack: SmallVec<[OpDelimitedIter<'t, S>; 4]>, + stack: SmallVec<[OpDelimitedIter<'t>; 4]>, /// The "parent" matcher position if we are in a repetition. That is, the matcher position just /// before we enter the repetition. - up: Option>>, + up: Option>>, /// The separator if we are in a repetition. - sep: Option>, + sep: Option, /// The KleeneOp of this sequence if we are in a repetition. sep_kind: Option, @@ -347,7 +327,7 @@ struct MatchState<'t, S> { bindings: BindingsIdx, /// Cached result of meta variable parsing - meta_result: Option<(TtIter<'t, S>, ExpandResult>>)>, + meta_result: Option<(TtIter<'t, Span>, ExpandResult>)>, /// Is error occurred in this state, will `poised` to "parent" is_error: bool, @@ -372,18 +352,17 @@ struct MatchState<'t, S> { /// - `bb_items`: the set of items that are waiting for the black-box parser. /// - `error_items`: the set of items in errors, used for error-resilient parsing #[inline] -fn match_loop_inner<'t, S: Span>( - src: TtIter<'t, S>, - stack: &[TtIter<'t, S>], - res: &mut Match, - bindings_builder: &mut BindingsBuilder, - cur_items: &mut SmallVec<[MatchState<'t, S>; 1]>, - bb_items: &mut SmallVec<[MatchState<'t, S>; 1]>, - next_items: &mut Vec>, - eof_items: &mut SmallVec<[MatchState<'t, S>; 1]>, - error_items: &mut SmallVec<[MatchState<'t, S>; 1]>, - is_2021: bool, - delim_span: tt::DelimSpan, +fn match_loop_inner<'t>( + src: TtIter<'t, Span>, + stack: &[TtIter<'t, Span>], + res: &mut Match, + bindings_builder: &mut BindingsBuilder, + cur_items: &mut SmallVec<[MatchState<'t>; 1]>, + bb_items: &mut SmallVec<[MatchState<'t>; 1]>, + next_items: &mut Vec>, + eof_items: &mut SmallVec<[MatchState<'t>; 1]>, + error_items: &mut SmallVec<[MatchState<'t>; 1]>, + delim_span: tt::DelimSpan, ) { macro_rules! try_push { ($items: expr, $it:expr) => { @@ -494,7 +473,7 @@ fn match_loop_inner<'t, S: Span>( OpDelimited::Op(Op::Var { kind, name, .. }) => { if let &Some(kind) = kind { let mut fork = src.clone(); - let match_res = match_meta_var(kind, &mut fork, is_2021, delim_span); + let match_res = match_meta_var(kind, &mut fork, delim_span); match match_res.err { None => { // Some meta variables are optional (e.g. vis) @@ -607,10 +586,10 @@ fn match_loop_inner<'t, S: Span>( } } -fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: bool) -> Match { +fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match { let span = src.delimiter.delim_span(); let mut src = TtIter::new(src); - let mut stack: SmallVec<[TtIter<'_, S>; 1]> = SmallVec::new(); + let mut stack: SmallVec<[TtIter<'_, Span>; 1]> = SmallVec::new(); let mut res = Match::default(); let mut error_recover_item = None; @@ -647,7 +626,6 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: &mut next_items, &mut eof_items, &mut error_items, - is_2021, span, ); stdx::always!(cur_items.is_empty()); @@ -758,12 +736,11 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: } } -fn match_meta_var( +fn match_meta_var( kind: MetaVarKind, - input: &mut TtIter<'_, S>, - is_2021: bool, - delim_span: DelimSpan, -) -> ExpandResult>> { + input: &mut TtIter<'_, Span>, + delim_span: DelimSpan, +) -> ExpandResult> { let fragment = match kind { MetaVarKind::Path => { return input.expect_fragment(parser::PrefixEntryPoint::Path).map(|it| { @@ -771,8 +748,7 @@ fn match_meta_var( }); } MetaVarKind::Ty => parser::PrefixEntryPoint::Ty, - MetaVarKind::Pat if is_2021 => parser::PrefixEntryPoint::PatTop, - MetaVarKind::Pat => parser::PrefixEntryPoint::Pat, + MetaVarKind::Pat => parser::PrefixEntryPoint::PatTop, MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat, MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt, MetaVarKind::Block => parser::PrefixEntryPoint::Block, @@ -846,7 +822,7 @@ fn match_meta_var( input.expect_fragment(fragment).map(|it| it.map(Fragment::Tokens)) } -fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate) { +fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate) { for op in pattern.iter() { match op { Op::Var { name, .. } => collector_fun(name.clone()), @@ -859,11 +835,11 @@ fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &Meta } } } -impl MetaTemplate { - fn iter_delimited_with(&self, delimiter: tt::Delimiter) -> OpDelimitedIter<'_, S> { +impl MetaTemplate { + fn iter_delimited_with(&self, delimiter: tt::Delimiter) -> OpDelimitedIter<'_> { OpDelimitedIter { inner: &self.0, idx: 0, delimited: delimiter } } - fn iter_delimited(&self, span: tt::DelimSpan) -> OpDelimitedIter<'_, S> { + fn iter_delimited(&self, span: tt::DelimSpan) -> OpDelimitedIter<'_> { OpDelimitedIter { inner: &self.0, idx: 0, @@ -873,27 +849,27 @@ impl MetaTemplate { } #[derive(Debug, Clone, Copy)] -enum OpDelimited<'a, S> { - Op(&'a Op), +enum OpDelimited<'a> { + Op(&'a Op), Open, Close, } #[derive(Debug, Clone, Copy)] -struct OpDelimitedIter<'a, S> { - inner: &'a [Op], - delimited: tt::Delimiter, +struct OpDelimitedIter<'a> { + inner: &'a [Op], + delimited: tt::Delimiter, idx: usize, } -impl<'a, S: Span> OpDelimitedIter<'a, S> { +impl<'a> OpDelimitedIter<'a> { fn is_eof(&self) -> bool { let len = self.inner.len() + if self.delimited.kind != tt::DelimiterKind::Invisible { 2 } else { 0 }; self.idx >= len } - fn peek(&self) -> Option> { + fn peek(&self) -> Option> { match self.delimited.kind { tt::DelimiterKind::Invisible => self.inner.get(self.idx).map(OpDelimited::Op), _ => match self.idx { @@ -909,8 +885,8 @@ impl<'a, S: Span> OpDelimitedIter<'a, S> { } } -impl<'a, S: Span> Iterator for OpDelimitedIter<'a, S> { - type Item = OpDelimited<'a, S>; +impl<'a> Iterator for OpDelimitedIter<'a> { + type Item = OpDelimited<'a>; fn next(&mut self) -> Option { let res = self.peek(); @@ -926,8 +902,8 @@ impl<'a, S: Span> Iterator for OpDelimitedIter<'a, S> { } } -impl TtIter<'_, S> { - fn expect_separator(&mut self, separator: &Separator) -> bool { +impl TtIter<'_, Span> { + fn expect_separator(&mut self, separator: &Separator) -> bool { let mut fork = self.clone(); let ok = match separator { Separator::Ident(lhs) => match fork.expect_ident_or_underscore() { @@ -957,7 +933,7 @@ impl TtIter<'_, S> { ok } - fn expect_tt(&mut self) -> Result, ()> { + fn expect_tt(&mut self) -> Result, ()> { if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = self.peek_n(0) { if punct.char == '\'' { self.expect_lifetime() @@ -976,7 +952,7 @@ impl TtIter<'_, S> { } } - fn expect_lifetime(&mut self) -> Result, ()> { + fn expect_lifetime(&mut self) -> Result, ()> { let punct = self.expect_single_punct()?; if punct.char != '\'' { return Err(()); @@ -997,7 +973,7 @@ impl TtIter<'_, S> { .into()) } - fn eat_char(&mut self, c: char) -> Option> { + fn eat_char(&mut self, c: char) -> Option> { let mut fork = self.clone(); match fork.expect_char(c) { Ok(_) => { diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs index 6d3055da2860..5e6e45f1521b 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs @@ -1,8 +1,9 @@ //! Transcriber takes a template, like `fn $ident() {}`, a set of bindings like //! `$ident => foo`, interpolates variables in the template, to get `fn foo() {}` +use span::Span; use syntax::SmolStr; -use tt::{Delimiter, Span}; +use tt::Delimiter; use crate::{ expander::{Binding, Bindings, Fragment}, @@ -10,8 +11,8 @@ use crate::{ CountError, ExpandError, ExpandResult, MetaTemplate, }; -impl Bindings { - fn get(&self, name: &str) -> Result<&Binding, ExpandError> { +impl Bindings { + fn get(&self, name: &str) -> Result<&Binding, ExpandError> { match self.inner.get(name) { Some(binding) => Ok(binding), None => Err(ExpandError::UnresolvedBinding(Box::new(Box::from(name)))), @@ -21,10 +22,10 @@ impl Bindings { fn get_fragment( &self, name: &str, - mut span: S, + mut span: Span, nesting: &mut [NestingState], - marker: impl Fn(&mut S), - ) -> Result, ExpandError> { + marker: impl Fn(&mut Span), + ) -> Result { macro_rules! binding_err { ($($arg:tt)*) => { ExpandError::binding_error(format!($($arg)*)) }; } @@ -134,15 +135,15 @@ impl Bindings { } } -pub(super) fn transcribe( - template: &MetaTemplate, - bindings: &Bindings, - marker: impl Fn(&mut S) + Copy, +pub(super) fn transcribe( + template: &MetaTemplate, + bindings: &Bindings, + marker: impl Fn(&mut Span) + Copy, new_meta_vars: bool, - call_site: S, -) -> ExpandResult> { + call_site: Span, +) -> ExpandResult> { let mut ctx = ExpandCtx { bindings, nesting: Vec::new(), new_meta_vars, call_site }; - let mut arena: Vec> = Vec::new(); + let mut arena: Vec> = Vec::new(); expand_subtree(&mut ctx, template, None, &mut arena, marker) } @@ -158,20 +159,20 @@ struct NestingState { } #[derive(Debug)] -struct ExpandCtx<'a, S> { - bindings: &'a Bindings, +struct ExpandCtx<'a> { + bindings: &'a Bindings, nesting: Vec, new_meta_vars: bool, - call_site: S, + call_site: Span, } -fn expand_subtree( - ctx: &mut ExpandCtx<'_, S>, - template: &MetaTemplate, - delimiter: Option>, - arena: &mut Vec>, - marker: impl Fn(&mut S) + Copy, -) -> ExpandResult> { +fn expand_subtree( + ctx: &mut ExpandCtx<'_>, + template: &MetaTemplate, + delimiter: Option>, + arena: &mut Vec>, + marker: impl Fn(&mut Span) + Copy, +) -> ExpandResult> { // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation let start_elements = arena.len(); let mut err = None; @@ -332,12 +333,12 @@ fn expand_subtree( } } -fn expand_var( - ctx: &mut ExpandCtx<'_, S>, +fn expand_var( + ctx: &mut ExpandCtx<'_>, v: &SmolStr, - id: S, - marker: impl Fn(&mut S), -) -> ExpandResult> { + id: Span, + marker: impl Fn(&mut Span), +) -> ExpandResult { // We already handle $crate case in mbe parser debug_assert!(v != "crate"); @@ -378,15 +379,15 @@ fn expand_var( } } -fn expand_repeat( - ctx: &mut ExpandCtx<'_, S>, - template: &MetaTemplate, +fn expand_repeat( + ctx: &mut ExpandCtx<'_>, + template: &MetaTemplate, kind: RepeatKind, - separator: &Option>, - arena: &mut Vec>, - marker: impl Fn(&mut S) + Copy, -) -> ExpandResult> { - let mut buf: Vec> = Vec::new(); + separator: &Option, + arena: &mut Vec>, + marker: impl Fn(&mut Span) + Copy, +) -> ExpandResult { + let mut buf: Vec> = Vec::new(); ctx.nesting.push(NestingState { idx: 0, at_end: false, hit: false }); // Dirty hack to make macro-expansion terminate. // This should be replaced by a proper macro-by-example implementation @@ -478,11 +479,7 @@ fn expand_repeat( ExpandResult { value: Fragment::Tokens(tt), err } } -fn push_fragment( - ctx: &ExpandCtx<'_, S>, - buf: &mut Vec>, - fragment: Fragment, -) { +fn push_fragment(ctx: &ExpandCtx<'_>, buf: &mut Vec>, fragment: Fragment) { match fragment { Fragment::Tokens(tt::TokenTree::Subtree(tt)) => push_subtree(buf, tt), Fragment::Expr(sub) => { @@ -494,7 +491,7 @@ fn push_fragment( } } -fn push_subtree(buf: &mut Vec>, tt: tt::Subtree) { +fn push_subtree(buf: &mut Vec>, tt: tt::Subtree) { match tt.delimiter.kind { tt::DelimiterKind::Invisible => buf.extend(Vec::from(tt.token_trees)), _ => buf.push(tt.into()), @@ -504,10 +501,10 @@ fn push_subtree(buf: &mut Vec>, tt: tt::Subtree) { /// Inserts the path separator `::` between an identifier and its following generic /// argument list, and then pushes into the buffer. See [`Fragment::Path`] for why /// we need this fixup. -fn fix_up_and_push_path_tt( - ctx: &ExpandCtx<'_, S>, - buf: &mut Vec>, - subtree: tt::Subtree, +fn fix_up_and_push_path_tt( + ctx: &ExpandCtx<'_>, + buf: &mut Vec>, + subtree: tt::Subtree, ) { stdx::always!(matches!(subtree.delimiter.kind, tt::DelimiterKind::Invisible)); let mut prev_was_ident = false; @@ -546,11 +543,7 @@ fn fix_up_and_push_path_tt( /// Handles `${count(t, depth)}`. `our_depth` is the recursion depth and `count_depth` is the depth /// defined by the metavar expression. -fn count( - binding: &Binding, - depth_curr: usize, - depth_max: usize, -) -> Result { +fn count(binding: &Binding, depth_curr: usize, depth_max: usize) -> Result { match binding { Binding::Nested(bs) => { if depth_curr == depth_max { @@ -564,8 +557,8 @@ fn count( } } -fn count_old( - binding: &Binding, +fn count_old( + binding: &Binding, our_depth: usize, count_depth: Option, ) -> Result { diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index 62fdce36892f..3a853512660e 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -17,8 +17,8 @@ mod tt_iter; #[cfg(test)] mod benchmark; +use span::{Edition, Span, SyntaxContextId}; use stdx::impl_from; -use tt::Span; use std::fmt; @@ -127,32 +127,29 @@ impl fmt::Display for CountError { /// `tt::TokenTree`, but there's a crucial difference: in macro rules, `$ident` /// and `$()*` have special meaning (see `Var` and `Repeat` data structures) #[derive(Clone, Debug, PartialEq, Eq)] -pub struct DeclarativeMacro { - rules: Box<[Rule]>, - // This is used for correctly determining the behavior of the pat fragment - // FIXME: This should be tracked by hygiene of the fragment identifier! - is_2021: bool, +pub struct DeclarativeMacro { + rules: Box<[Rule]>, err: Option>, } #[derive(Clone, Debug, PartialEq, Eq)] -struct Rule { - lhs: MetaTemplate, - rhs: MetaTemplate, +struct Rule { + lhs: MetaTemplate, + rhs: MetaTemplate, } -impl DeclarativeMacro { - pub fn from_err(err: ParseError, is_2021: bool) -> DeclarativeMacro { - DeclarativeMacro { rules: Box::default(), is_2021, err: Some(Box::new(err)) } +impl DeclarativeMacro { + pub fn from_err(err: ParseError) -> DeclarativeMacro { + DeclarativeMacro { rules: Box::default(), err: Some(Box::new(err)) } } /// The old, `macro_rules! m {}` flavor. pub fn parse_macro_rules( - tt: &tt::Subtree, - is_2021: bool, + tt: &tt::Subtree, + edition: impl Copy + Fn(SyntaxContextId) -> Edition, // FIXME: Remove this once we drop support for rust 1.76 (defaults to true then) new_meta_vars: bool, - ) -> DeclarativeMacro { + ) -> DeclarativeMacro { // Note: this parsing can be implemented using mbe machinery itself, by // matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing // manually seems easier. @@ -161,7 +158,7 @@ impl DeclarativeMacro { let mut err = None; while src.len() > 0 { - let rule = match Rule::parse(&mut src, true, new_meta_vars) { + let rule = match Rule::parse(edition, &mut src, true, new_meta_vars) { Ok(it) => it, Err(e) => { err = Some(Box::new(e)); @@ -184,16 +181,16 @@ impl DeclarativeMacro { } } - DeclarativeMacro { rules: rules.into_boxed_slice(), is_2021, err } + DeclarativeMacro { rules: rules.into_boxed_slice(), err } } /// The new, unstable `macro m {}` flavor. pub fn parse_macro2( - tt: &tt::Subtree, - is_2021: bool, + tt: &tt::Subtree, + edition: impl Copy + Fn(SyntaxContextId) -> Edition, // FIXME: Remove this once we drop support for rust 1.76 (defaults to true then) new_meta_vars: bool, - ) -> DeclarativeMacro { + ) -> DeclarativeMacro { let mut src = TtIter::new(tt); let mut rules = Vec::new(); let mut err = None; @@ -201,7 +198,7 @@ impl DeclarativeMacro { if tt::DelimiterKind::Brace == tt.delimiter.kind { cov_mark::hit!(parse_macro_def_rules); while src.len() > 0 { - let rule = match Rule::parse(&mut src, true, new_meta_vars) { + let rule = match Rule::parse(edition, &mut src, true, new_meta_vars) { Ok(it) => it, Err(e) => { err = Some(Box::new(e)); @@ -220,7 +217,7 @@ impl DeclarativeMacro { } } else { cov_mark::hit!(parse_macro_def_simple); - match Rule::parse(&mut src, false, new_meta_vars) { + match Rule::parse(edition, &mut src, false, new_meta_vars) { Ok(rule) => { if src.len() != 0 { err = Some(Box::new(ParseError::expected("remaining tokens in macro def"))); @@ -240,7 +237,7 @@ impl DeclarativeMacro { } } - DeclarativeMacro { rules: rules.into_boxed_slice(), is_2021, err } + DeclarativeMacro { rules: rules.into_boxed_slice(), err } } pub fn err(&self) -> Option<&ParseError> { @@ -249,18 +246,19 @@ impl DeclarativeMacro { pub fn expand( &self, - tt: &tt::Subtree, - marker: impl Fn(&mut S) + Copy, + tt: &tt::Subtree, + marker: impl Fn(&mut Span) + Copy, new_meta_vars: bool, - call_site: S, - ) -> ExpandResult> { - expander::expand_rules(&self.rules, tt, marker, self.is_2021, new_meta_vars, call_site) + call_site: Span, + ) -> ExpandResult> { + expander::expand_rules(&self.rules, tt, marker, new_meta_vars, call_site) } } -impl Rule { +impl Rule { fn parse( - src: &mut TtIter<'_, S>, + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + src: &mut TtIter<'_, Span>, expect_arrow: bool, new_meta_vars: bool, ) -> Result { @@ -271,14 +269,14 @@ impl Rule { } let rhs = src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?; - let lhs = MetaTemplate::parse_pattern(lhs)?; - let rhs = MetaTemplate::parse_template(rhs, new_meta_vars)?; + let lhs = MetaTemplate::parse_pattern(edition, lhs)?; + let rhs = MetaTemplate::parse_template(edition, rhs, new_meta_vars)?; Ok(crate::Rule { lhs, rhs }) } } -fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { +fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { for op in pattern.iter() { match op { Op::Subtree { tokens, .. } => validate(tokens)?, diff --git a/src/tools/rust-analyzer/crates/mbe/src/parser.rs b/src/tools/rust-analyzer/crates/mbe/src/parser.rs index afdbbef23147..eaf2fd8c273f 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/parser.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/parser.rs @@ -2,8 +2,8 @@ //! trees. use smallvec::{smallvec, SmallVec}; +use span::{Edition, Span, SyntaxContextId}; use syntax::SmolStr; -use tt::Span; use crate::{tt_iter::TtIter, ParseError}; @@ -21,30 +21,39 @@ use crate::{tt_iter::TtIter, ParseError}; /// Stuff to the right is a [`MetaTemplate`] template which is used to produce /// output. #[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) struct MetaTemplate(pub(crate) Box<[Op]>); +pub(crate) struct MetaTemplate(pub(crate) Box<[Op]>); -impl MetaTemplate { - pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result { - MetaTemplate::parse(pattern, Mode::Pattern, false) +impl MetaTemplate { + pub(crate) fn parse_pattern( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + pattern: &tt::Subtree, + ) -> Result { + MetaTemplate::parse(edition, pattern, Mode::Pattern, false) } pub(crate) fn parse_template( - template: &tt::Subtree, + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + template: &tt::Subtree, new_meta_vars: bool, ) -> Result { - MetaTemplate::parse(template, Mode::Template, new_meta_vars) + MetaTemplate::parse(edition, template, Mode::Template, new_meta_vars) } - pub(crate) fn iter(&self) -> impl Iterator> { + pub(crate) fn iter(&self) -> impl Iterator { self.0.iter() } - fn parse(tt: &tt::Subtree, mode: Mode, new_meta_vars: bool) -> Result { + fn parse( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + tt: &tt::Subtree, + mode: Mode, + new_meta_vars: bool, + ) -> Result { let mut src = TtIter::new(tt); let mut res = Vec::new(); while let Some(first) = src.peek_n(0) { - let op = next_op(first, &mut src, mode, new_meta_vars)?; + let op = next_op(edition, first, &mut src, mode, new_meta_vars)?; res.push(op); } @@ -53,15 +62,15 @@ impl MetaTemplate { } #[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) enum Op { +pub(crate) enum Op { Var { name: SmolStr, kind: Option, - id: S, + id: Span, }, Ignore { name: SmolStr, - id: S, + id: Span, }, Index { depth: usize, @@ -75,17 +84,17 @@ pub(crate) enum Op { depth: Option, }, Repeat { - tokens: MetaTemplate, + tokens: MetaTemplate, kind: RepeatKind, - separator: Option>, + separator: Option, }, Subtree { - tokens: MetaTemplate, - delimiter: tt::Delimiter, + tokens: MetaTemplate, + delimiter: tt::Delimiter, }, - Literal(tt::Literal), - Punct(SmallVec<[tt::Punct; 3]>), - Ident(tt::Ident), + Literal(tt::Literal), + Punct(SmallVec<[tt::Punct; 3]>), + Ident(tt::Ident), } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -114,15 +123,15 @@ pub(crate) enum MetaVarKind { } #[derive(Clone, Debug, Eq)] -pub(crate) enum Separator { - Literal(tt::Literal), - Ident(tt::Ident), - Puncts(SmallVec<[tt::Punct; 3]>), +pub(crate) enum Separator { + Literal(tt::Literal), + Ident(tt::Ident), + Puncts(SmallVec<[tt::Punct; 3]>), } // Note that when we compare a Separator, we just care about its textual value. -impl PartialEq for Separator { - fn eq(&self, other: &Separator) -> bool { +impl PartialEq for Separator { + fn eq(&self, other: &Separator) -> bool { use Separator::*; match (self, other) { @@ -144,12 +153,13 @@ enum Mode { Template, } -fn next_op( - first_peeked: &tt::TokenTree, - src: &mut TtIter<'_, S>, +fn next_op( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + first_peeked: &tt::TokenTree, + src: &mut TtIter<'_, Span>, mode: Mode, new_meta_vars: bool, -) -> Result, ParseError> { +) -> Result { let res = match first_peeked { tt::TokenTree::Leaf(tt::Leaf::Punct(p @ tt::Punct { char: '$', .. })) => { src.next().expect("first token already peeked"); @@ -162,7 +172,7 @@ fn next_op( tt::TokenTree::Subtree(subtree) => match subtree.delimiter.kind { tt::DelimiterKind::Parenthesis => { let (separator, kind) = parse_repeat(src)?; - let tokens = MetaTemplate::parse(subtree, mode, new_meta_vars)?; + let tokens = MetaTemplate::parse(edition, subtree, mode, new_meta_vars)?; Op::Repeat { tokens, separator, kind } } tt::DelimiterKind::Brace => match mode { @@ -189,13 +199,13 @@ fn next_op( Op::Ident(tt::Ident { text: "$crate".into(), span: ident.span }) } tt::Leaf::Ident(ident) => { - let kind = eat_fragment_kind(src, mode)?; + let kind = eat_fragment_kind(edition, src, mode)?; let name = ident.text.clone(); let id = ident.span; Op::Var { name, kind, id } } tt::Leaf::Literal(lit) if is_boolean_literal(lit) => { - let kind = eat_fragment_kind(src, mode)?; + let kind = eat_fragment_kind(edition, src, mode)?; let name = lit.text.clone(); let id = lit.span; Op::Var { name, kind, id } @@ -233,15 +243,16 @@ fn next_op( tt::TokenTree::Subtree(subtree) => { src.next().expect("first token already peeked"); - let tokens = MetaTemplate::parse(subtree, mode, new_meta_vars)?; + let tokens = MetaTemplate::parse(edition, subtree, mode, new_meta_vars)?; Op::Subtree { tokens, delimiter: subtree.delimiter } } }; Ok(res) } -fn eat_fragment_kind( - src: &mut TtIter<'_, S>, +fn eat_fragment_kind( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + src: &mut TtIter<'_, Span>, mode: Mode, ) -> Result, ParseError> { if let Mode::Pattern = mode { @@ -252,7 +263,10 @@ fn eat_fragment_kind( let kind = match ident.text.as_str() { "path" => MetaVarKind::Path, "ty" => MetaVarKind::Ty, - "pat" => MetaVarKind::Pat, + "pat" => match edition(ident.span.ctx) { + Edition::Edition2015 | Edition::Edition2018 => MetaVarKind::PatParam, + Edition::Edition2021 | Edition::Edition2024 => MetaVarKind::Pat, + }, "pat_param" => MetaVarKind::PatParam, "stmt" => MetaVarKind::Stmt, "block" => MetaVarKind::Block, @@ -271,13 +285,11 @@ fn eat_fragment_kind( Ok(None) } -fn is_boolean_literal(lit: &tt::Literal) -> bool { +fn is_boolean_literal(lit: &tt::Literal) -> bool { matches!(lit.text.as_str(), "true" | "false") } -fn parse_repeat( - src: &mut TtIter<'_, S>, -) -> Result<(Option>, RepeatKind), ParseError> { +fn parse_repeat(src: &mut TtIter<'_, Span>) -> Result<(Option, RepeatKind), ParseError> { let mut separator = Separator::Puncts(SmallVec::new()); for tt in src { let tt = match tt { @@ -314,7 +326,7 @@ fn parse_repeat( Err(ParseError::InvalidRepeat) } -fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, S>) -> Result, ()> { +fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, Span>) -> Result { let func = src.expect_ident()?; let args = src.expect_subtree()?; @@ -352,7 +364,7 @@ fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, S>) -> Ok(op) } -fn parse_depth(src: &mut TtIter<'_, S>) -> Result { +fn parse_depth(src: &mut TtIter<'_, Span>) -> Result { if src.len() == 0 { Ok(0) } else if let tt::Leaf::Literal(lit) = src.expect_literal()? { @@ -363,7 +375,7 @@ fn parse_depth(src: &mut TtIter<'_, S>) -> Result { } } -fn try_eat_comma(src: &mut TtIter<'_, S>) -> bool { +fn try_eat_comma(src: &mut TtIter<'_, Span>) -> bool { if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))) = src.peek_n(0) { let _ = src.next(); return true; diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs index 57d6082dd705..c934db6b715a 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs @@ -1,5 +1,7 @@ //! Conversions between [`SyntaxNode`] and [`tt::TokenTree`]. +use std::fmt; + use rustc_hash::{FxHashMap, FxHashSet}; use span::{SpanAnchor, SpanData, SpanMap}; use stdx::{never, non_empty_vec::NonEmptyVec}; @@ -9,30 +11,27 @@ use syntax::{ SyntaxKind::*, SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextSize, WalkEvent, T, }; -use tt::{ - buffer::{Cursor, TokenBuffer}, - Span, -}; +use tt::buffer::{Cursor, TokenBuffer}; use crate::{to_parser_input::to_parser_input, tt_iter::TtIter}; #[cfg(test)] mod tests; -pub trait SpanMapper { +pub trait SpanMapper { fn span_for(&self, range: TextRange) -> S; } impl SpanMapper> for SpanMap where - SpanData: Span, + SpanData: Copy, { fn span_for(&self, range: TextRange) -> SpanData { self.span_at(range.start()) } } -impl> SpanMapper for &SM { +impl> SpanMapper for &SM { fn span_for(&self, range: TextRange) -> S { SM::span_for(self, range) } @@ -78,8 +77,7 @@ pub fn syntax_node_to_token_tree( span: SpanData, ) -> tt::Subtree> where - SpanData: Span, - Ctx: Copy, + SpanData: Copy + fmt::Debug, SpanMap: SpanMapper>, { let mut c = Converter::new(node, map, Default::default(), Default::default(), span); @@ -98,8 +96,7 @@ pub fn syntax_node_to_token_tree_modified( ) -> tt::Subtree> where SpanMap: SpanMapper>, - SpanData: Span, - Ctx: Copy, + SpanData: Copy + fmt::Debug, { let mut c = Converter::new(node, map, append, remove, call_site); convert_tokens(&mut c) @@ -124,8 +121,7 @@ pub fn token_tree_to_syntax_node( entry_point: parser::TopEntryPoint, ) -> (Parse, SpanMap) where - SpanData: Span, - Ctx: Copy, + SpanData: Copy + fmt::Debug, { let buffer = match tt { tt::Subtree { @@ -161,7 +157,7 @@ pub fn parse_to_token_tree( text: &str, ) -> Option>> where - SpanData: Span, + SpanData: Copy + fmt::Debug, Ctx: Copy, { let lexed = parser::LexedStr::new(text); @@ -175,7 +171,7 @@ where /// Convert a string to a `TokenTree`. The passed span will be used for all spans of the produced subtree. pub fn parse_to_token_tree_static_span(span: S, text: &str) -> Option> where - S: Span, + S: Copy + fmt::Debug, { let lexed = parser::LexedStr::new(text); if lexed.errors().next().is_some() { @@ -186,11 +182,10 @@ where } /// Split token tree with separate expr: $($e:expr)SEP* -pub fn parse_exprs_with_sep( - tt: &tt::Subtree, - sep: char, - span: S, -) -> Vec> { +pub fn parse_exprs_with_sep(tt: &tt::Subtree, sep: char, span: S) -> Vec> +where + S: Copy + fmt::Debug, +{ if tt.token_trees.is_empty() { return Vec::new(); } @@ -226,7 +221,8 @@ pub fn parse_exprs_with_sep( fn convert_tokens(conv: &mut C) -> tt::Subtree where C: TokenConverter, - S: Span, + S: Copy + fmt::Debug, + C::Token: fmt::Debug, { let entry = tt::SubtreeBuilder { delimiter: tt::Delimiter::invisible_spanned(conv.call_site()), @@ -485,7 +481,7 @@ struct StaticRawConverter<'a, S> { span: S, } -trait SrcToken: std::fmt::Debug { +trait SrcToken { fn kind(&self, ctx: &Ctx) -> SyntaxKind; fn to_char(&self, ctx: &Ctx) -> Option; @@ -525,7 +521,7 @@ impl SrcToken, S> for usize { } } -impl SrcToken, S> for usize { +impl SrcToken, S> for usize { fn kind(&self, ctx: &StaticRawConverter<'_, S>) -> SyntaxKind { ctx.lexed.kind(*self) } @@ -541,7 +537,7 @@ impl SrcToken, S> for usize { impl TokenConverter> for RawConverter<'_, Ctx> where - SpanData: Span, + SpanData: Copy, { type Token = usize; @@ -584,7 +580,7 @@ where impl TokenConverter for StaticRawConverter<'_, S> where - S: Span, + S: Copy, { type Token = usize; @@ -709,7 +705,7 @@ impl SynToken { } } -impl SrcToken, S> for SynToken { +impl SrcToken, S> for SynToken { fn kind(&self, _ctx: &Converter) -> SyntaxKind { match self { SynToken::Ordinary(token) => token.kind(), @@ -748,7 +744,7 @@ impl SrcToken, S> for SynToke impl TokenConverter for Converter where - S: Span, + S: Copy, SpanMap: SpanMapper, { type Token = SynToken; @@ -828,7 +824,7 @@ where struct TtTreeSink<'a, Ctx> where - SpanData: Span, + SpanData: Copy, { buf: String, cursor: Cursor<'a, SpanData>, @@ -839,7 +835,7 @@ where impl<'a, Ctx> TtTreeSink<'a, Ctx> where - SpanData: Span, + SpanData: Copy, { fn new(cursor: Cursor<'a, SpanData>) -> Self { TtTreeSink { @@ -871,7 +867,7 @@ fn delim_to_str(d: tt::DelimiterKind, closing: bool) -> Option<&'static str> { impl TtTreeSink<'_, Ctx> where - SpanData: Span, + SpanData: Copy, { /// Parses a float literal as if it was a one to two name ref nodes with a dot inbetween. /// This occurs when a float literal is used as a field access. diff --git a/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs b/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs index 00a14f04686e..3f70149aa5ea 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs @@ -1,11 +1,13 @@ //! Convert macro-by-example tokens which are specific to macro expansion into a //! format that works for our parser. +use std::fmt; + use syntax::{SyntaxKind, SyntaxKind::*, T}; -use tt::{buffer::TokenBuffer, Span}; +use tt::buffer::TokenBuffer; -pub(crate) fn to_parser_input(buffer: &TokenBuffer<'_, S>) -> parser::Input { +pub(crate) fn to_parser_input(buffer: &TokenBuffer<'_, S>) -> parser::Input { let mut res = parser::Input::default(); let mut current = buffer.begin(); diff --git a/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs b/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs index f9913cb6f9b8..e3d12d87078b 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs @@ -1,9 +1,10 @@ //! A "Parser" structure for token trees. We use this when parsing a declarative //! macro definition into a list of patterns and templates. +use core::fmt; + use smallvec::{smallvec, SmallVec}; use syntax::SyntaxKind; -use tt::Span; use crate::{to_parser_input::to_parser_input, ExpandError, ExpandResult}; @@ -12,7 +13,7 @@ pub(crate) struct TtIter<'a, S> { pub(crate) inner: std::slice::Iter<'a, tt::TokenTree>, } -impl<'a, S: Span> TtIter<'a, S> { +impl<'a, S: Copy> TtIter<'a, S> { pub(crate) fn new(subtree: &'a tt::Subtree) -> TtIter<'a, S> { TtIter { inner: subtree.token_trees.iter() } } @@ -130,7 +131,12 @@ impl<'a, S: Span> TtIter<'a, S> { _ => Ok(smallvec![first]), } } + pub(crate) fn peek_n(&self, n: usize) -> Option<&'a tt::TokenTree> { + self.inner.as_slice().get(n) + } +} +impl<'a, S: Copy + fmt::Debug> TtIter<'a, S> { pub(crate) fn expect_fragment( &mut self, entry_point: parser::PrefixEntryPoint, @@ -185,10 +191,6 @@ impl<'a, S: Span> TtIter<'a, S> { }; ExpandResult { value: res, err } } - - pub(crate) fn peek_n(&self, n: usize) -> Option<&'a tt::TokenTree> { - self.inner.as_slice().get(n) - } } impl<'a, S> Iterator for TtIter<'a, S> { diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index 72848a1f2b79..54ed5f0ba236 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -490,6 +490,18 @@ fn match_expr(p: &mut Parser<'_>) -> CompletedMarker { m.complete(p, MATCH_EXPR) } +// test_err match_arms_recovery +// fn foo() { +// match () { +// _ => (),, +// _ => , +// _ => (), +// => (), +// if true => (), +// _ => (), +// () if => (), +// } +// } pub(crate) fn match_arm_list(p: &mut Parser<'_>) { assert!(p.at(T!['{'])); let m = p.start(); @@ -511,6 +523,10 @@ pub(crate) fn match_arm_list(p: &mut Parser<'_>) { error_block(p, "expected match arm"); continue; } + if p.at(T![,]) { + p.err_and_bump("expected pattern"); + continue; + } match_arm(p); } p.expect(T!['}']); @@ -544,26 +560,30 @@ fn match_arm(p: &mut Parser<'_>) { // } attributes::outer_attrs(p); - patterns::pattern_top_r(p, TokenSet::EMPTY); + patterns::pattern_top_r(p, TokenSet::new(&[T![=], T![if]])); if p.at(T![if]) { match_guard(p); } p.expect(T![=>]); - let blocklike = match expr_stmt(p, None) { - Some((_, blocklike)) => blocklike, - None => BlockLike::NotBlock, - }; + if p.eat(T![,]) { + p.error("expected expression"); + } else { + let blocklike = match expr_stmt(p, None) { + Some((_, blocklike)) => blocklike, + None => BlockLike::NotBlock, + }; - // test match_arms_commas - // fn foo() { - // match () { - // _ => (), - // _ => {} - // _ => () - // } - // } - if !p.eat(T![,]) && !blocklike.is_block() && !p.at(T!['}']) { - p.error("expected `,`"); + // test match_arms_commas + // fn foo() { + // match () { + // _ => (), + // _ => {} + // _ => () + // } + // } + if !p.eat(T![,]) && !blocklike.is_block() && !p.at(T!['}']) { + p.error("expected `,`"); + } } m.complete(p, MATCH_ARM); } @@ -579,7 +599,11 @@ fn match_guard(p: &mut Parser<'_>) -> CompletedMarker { assert!(p.at(T![if])); let m = p.start(); p.bump(T![if]); - expr(p); + if p.at(T![=]) { + p.error("expected expression"); + } else { + expr(p); + } m.complete(p, MATCH_GUARD) } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rast new file mode 100644 index 000000000000..5b191945e457 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rast @@ -0,0 +1,113 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + MATCH_EXPR + MATCH_KW "match" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE "\n " + MATCH_ARM + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + ERROR + COMMA "," + WHITESPACE "\n " + MATCH_ARM + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + COMMA "," + WHITESPACE "\n " + MATCH_ARM + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + MATCH_GUARD + IF_KW "if" + WHITESPACE " " + LITERAL + TRUE_KW "true" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + TUPLE_PAT + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + MATCH_GUARD + IF_KW "if" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 42: expected pattern +error 58: expected expression +error 85: expected pattern +error 100: expected pattern +error 145: expected expression diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rs new file mode 100644 index 000000000000..173103b2e379 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rs @@ -0,0 +1,11 @@ +fn foo() { + match () { + _ => (),, + _ => , + _ => (), + => (), + if true => (), + _ => (), + () if => (), + } +} diff --git a/src/tools/rust-analyzer/crates/paths/Cargo.toml b/src/tools/rust-analyzer/crates/paths/Cargo.toml index 3d8752b5a829..59a4ad9a2558 100644 --- a/src/tools/rust-analyzer/crates/paths/Cargo.toml +++ b/src/tools/rust-analyzer/crates/paths/Cargo.toml @@ -12,10 +12,14 @@ rust-version.workspace = true doctest = false [dependencies] +camino.workspace = true # Adding this dep sadly puts a lot of rust-analyzer crates after the # serde-derive crate. Even though we don't activate the derive feature here, # someone else in the crate graph certainly does! # serde.workspace = true +[features] +serde1 = ["camino/serde1"] + [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/src/tools/rust-analyzer/crates/paths/src/lib.rs b/src/tools/rust-analyzer/crates/paths/src/lib.rs index a63d251c20d4..2d3653401d2f 100644 --- a/src/tools/rust-analyzer/crates/paths/src/lib.rs +++ b/src/tools/rust-analyzer/crates/paths/src/lib.rs @@ -1,4 +1,4 @@ -//! Thin wrappers around `std::path`, distinguishing between absolute and +//! Thin wrappers around `std::path`/`camino::path`, distinguishing between absolute and //! relative paths. #![warn(rust_2018_idioms, unused_lifetimes)] @@ -7,16 +7,24 @@ use std::{ borrow::Borrow, ffi::OsStr, fmt, ops, - path::{Component, Path, PathBuf}, + path::{Path, PathBuf}, }; -/// Wrapper around an absolute [`PathBuf`]. -#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] -pub struct AbsPathBuf(PathBuf); +pub use camino::*; + +/// Wrapper around an absolute [`Utf8PathBuf`]. +#[derive(Debug, Clone, Ord, PartialOrd, Eq, Hash)] +pub struct AbsPathBuf(Utf8PathBuf); + +impl From for Utf8PathBuf { + fn from(AbsPathBuf(path_buf): AbsPathBuf) -> Utf8PathBuf { + path_buf + } +} impl From for PathBuf { fn from(AbsPathBuf(path_buf): AbsPathBuf) -> PathBuf { - path_buf + path_buf.into() } } @@ -27,9 +35,21 @@ impl ops::Deref for AbsPathBuf { } } +impl AsRef for AbsPathBuf { + fn as_ref(&self) -> &Utf8Path { + self.0.as_path() + } +} + +impl AsRef for AbsPathBuf { + fn as_ref(&self) -> &OsStr { + self.0.as_ref() + } +} + impl AsRef for AbsPathBuf { fn as_ref(&self) -> &Path { - self.0.as_path() + self.0.as_ref() } } @@ -45,9 +65,9 @@ impl Borrow for AbsPathBuf { } } -impl TryFrom for AbsPathBuf { - type Error = PathBuf; - fn try_from(path_buf: PathBuf) -> Result { +impl TryFrom for AbsPathBuf { + type Error = Utf8PathBuf; + fn try_from(path_buf: Utf8PathBuf) -> Result { if !path_buf.is_absolute() { return Err(path_buf); } @@ -55,16 +75,26 @@ impl TryFrom for AbsPathBuf { } } -impl TryFrom<&str> for AbsPathBuf { +impl TryFrom for AbsPathBuf { type Error = PathBuf; - fn try_from(path: &str) -> Result { - AbsPathBuf::try_from(PathBuf::from(path)) + fn try_from(path_buf: PathBuf) -> Result { + if !path_buf.is_absolute() { + return Err(path_buf); + } + Ok(AbsPathBuf(Utf8PathBuf::from_path_buf(path_buf)?)) } } -impl PartialEq for AbsPathBuf { - fn eq(&self, other: &AbsPath) -> bool { - self.as_path() == other +impl TryFrom<&str> for AbsPathBuf { + type Error = Utf8PathBuf; + fn try_from(path: &str) -> Result { + AbsPathBuf::try_from(Utf8PathBuf::from(path)) + } +} + +impl + ?Sized> PartialEq

for AbsPathBuf { + fn eq(&self, other: &P) -> bool { + self.0.as_std_path() == other.as_ref() } } @@ -74,19 +104,31 @@ impl AbsPathBuf { /// # Panics /// /// Panics if `path` is not absolute. - pub fn assert(path: PathBuf) -> AbsPathBuf { + pub fn assert(path: Utf8PathBuf) -> AbsPathBuf { AbsPathBuf::try_from(path) - .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display())) + .unwrap_or_else(|path| panic!("expected absolute path, got {}", path)) + } + + /// Wrap the given absolute path in `AbsPathBuf` + /// + /// # Panics + /// + /// Panics if `path` is not absolute. + pub fn assert_utf8(path: PathBuf) -> AbsPathBuf { + AbsPathBuf::assert( + Utf8PathBuf::from_path_buf(path) + .unwrap_or_else(|path| panic!("expected utf8 path, got {}", path.display())), + ) } /// Coerces to an `AbsPath` slice. /// - /// Equivalent of [`PathBuf::as_path`] for `AbsPathBuf`. + /// Equivalent of [`Utf8PathBuf::as_path`] for `AbsPathBuf`. pub fn as_path(&self) -> &AbsPath { AbsPath::assert(self.0.as_path()) } - /// Equivalent of [`PathBuf::pop`] for `AbsPathBuf`. + /// Equivalent of [`Utf8PathBuf::pop`] for `AbsPathBuf`. /// /// Note that this won't remove the root component, so `self` will still be /// absolute. @@ -97,18 +139,36 @@ impl AbsPathBuf { impl fmt::Display for AbsPathBuf { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0.display(), f) + fmt::Display::fmt(&self.0, f) } } -/// Wrapper around an absolute [`Path`]. -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] +/// Wrapper around an absolute [`Utf8Path`]. +#[derive(Debug, Ord, PartialOrd, Eq, Hash)] #[repr(transparent)] -pub struct AbsPath(Path); +pub struct AbsPath(Utf8Path); + +impl + ?Sized> PartialEq

for AbsPath { + fn eq(&self, other: &P) -> bool { + self.0.as_std_path() == other.as_ref() + } +} + +impl AsRef for AbsPath { + fn as_ref(&self) -> &Utf8Path { + &self.0 + } +} impl AsRef for AbsPath { fn as_ref(&self) -> &Path { - &self.0 + self.0.as_ref() + } +} + +impl AsRef for AbsPath { + fn as_ref(&self) -> &OsStr { + self.0.as_ref() } } @@ -120,9 +180,9 @@ impl ToOwned for AbsPath { } } -impl<'a> TryFrom<&'a Path> for &'a AbsPath { - type Error = &'a Path; - fn try_from(path: &'a Path) -> Result<&'a AbsPath, &'a Path> { +impl<'a> TryFrom<&'a Utf8Path> for &'a AbsPath { + type Error = &'a Utf8Path; + fn try_from(path: &'a Utf8Path) -> Result<&'a AbsPath, &'a Utf8Path> { if !path.is_absolute() { return Err(path); } @@ -136,24 +196,24 @@ impl AbsPath { /// # Panics /// /// Panics if `path` is not absolute. - pub fn assert(path: &Path) -> &AbsPath { + pub fn assert(path: &Utf8Path) -> &AbsPath { assert!(path.is_absolute()); - unsafe { &*(path as *const Path as *const AbsPath) } + unsafe { &*(path as *const Utf8Path as *const AbsPath) } } - /// Equivalent of [`Path::parent`] for `AbsPath`. + /// Equivalent of [`Utf8Path::parent`] for `AbsPath`. pub fn parent(&self) -> Option<&AbsPath> { self.0.parent().map(AbsPath::assert) } - /// Equivalent of [`Path::join`] for `AbsPath` with an additional normalize step afterwards. - pub fn absolutize(&self, path: impl AsRef) -> AbsPathBuf { + /// Equivalent of [`Utf8Path::join`] for `AbsPath` with an additional normalize step afterwards. + pub fn absolutize(&self, path: impl AsRef) -> AbsPathBuf { self.join(path).normalize() } - /// Equivalent of [`Path::join`] for `AbsPath`. - pub fn join(&self, path: impl AsRef) -> AbsPathBuf { - self.as_ref().join(path).try_into().unwrap() + /// Equivalent of [`Utf8Path::join`] for `AbsPath`. + pub fn join(&self, path: impl AsRef) -> AbsPathBuf { + Utf8Path::join(self.as_ref(), path).try_into().unwrap() } /// Normalize the given path: @@ -172,7 +232,7 @@ impl AbsPath { AbsPathBuf(normalize_path(&self.0)) } - /// Equivalent of [`Path::to_path_buf`] for `AbsPath`. + /// Equivalent of [`Utf8Path::to_path_buf`] for `AbsPath`. pub fn to_path_buf(&self) -> AbsPathBuf { AbsPathBuf::try_from(self.0.to_path_buf()).unwrap() } @@ -181,7 +241,7 @@ impl AbsPath { panic!("We explicitly do not provide canonicalization API, as that is almost always a wrong solution, see #14430") } - /// Equivalent of [`Path::strip_prefix`] for `AbsPath`. + /// Equivalent of [`Utf8Path::strip_prefix`] for `AbsPath`. /// /// Returns a relative path. pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> { @@ -195,57 +255,61 @@ impl AbsPath { } pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { - Some(( - self.file_stem()?.to_str()?, - self.extension().and_then(|extension| extension.to_str()), - )) + Some((self.file_stem()?, self.extension())) } // region:delegate-methods - // Note that we deliberately don't implement `Deref` here. + // Note that we deliberately don't implement `Deref` here. // - // The problem with `Path` is that it directly exposes convenience IO-ing - // methods. For example, `Path::exists` delegates to `fs::metadata`. + // The problem with `Utf8Path` is that it directly exposes convenience IO-ing + // methods. For example, `Utf8Path::exists` delegates to `fs::metadata`. // // For `AbsPath`, we want to make sure that this is a POD type, and that all // IO goes via `fs`. That way, it becomes easier to mock IO when we need it. - pub fn file_name(&self) -> Option<&OsStr> { + pub fn file_name(&self) -> Option<&str> { self.0.file_name() } - pub fn extension(&self) -> Option<&OsStr> { + pub fn extension(&self) -> Option<&str> { self.0.extension() } - pub fn file_stem(&self) -> Option<&OsStr> { + pub fn file_stem(&self) -> Option<&str> { self.0.file_stem() } pub fn as_os_str(&self) -> &OsStr { self.0.as_os_str() } + pub fn as_str(&self) -> &str { + self.0.as_str() + } #[deprecated(note = "use Display instead")] - pub fn display(&self) -> std::path::Display<'_> { - self.0.display() + pub fn display(&self) -> ! { + unimplemented!() } #[deprecated(note = "use std::fs::metadata().is_ok() instead")] - pub fn exists(&self) -> bool { - self.0.exists() + pub fn exists(&self) -> ! { + unimplemented!() + } + + pub fn components(&self) -> Utf8Components<'_> { + self.0.components() } // endregion:delegate-methods } impl fmt::Display for AbsPath { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0.display(), f) + fmt::Display::fmt(&self.0, f) } } -/// Wrapper around a relative [`PathBuf`]. +/// Wrapper around a relative [`Utf8PathBuf`]. #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] -pub struct RelPathBuf(PathBuf); +pub struct RelPathBuf(Utf8PathBuf); -impl From for PathBuf { - fn from(RelPathBuf(path_buf): RelPathBuf) -> PathBuf { +impl From for Utf8PathBuf { + fn from(RelPathBuf(path_buf): RelPathBuf) -> Utf8PathBuf { path_buf } } @@ -257,15 +321,21 @@ impl ops::Deref for RelPathBuf { } } -impl AsRef for RelPathBuf { - fn as_ref(&self) -> &Path { +impl AsRef for RelPathBuf { + fn as_ref(&self) -> &Utf8Path { self.0.as_path() } } -impl TryFrom for RelPathBuf { - type Error = PathBuf; - fn try_from(path_buf: PathBuf) -> Result { +impl AsRef for RelPathBuf { + fn as_ref(&self) -> &Path { + self.0.as_ref() + } +} + +impl TryFrom for RelPathBuf { + type Error = Utf8PathBuf; + fn try_from(path_buf: Utf8PathBuf) -> Result { if !path_buf.is_relative() { return Err(path_buf); } @@ -274,65 +344,79 @@ impl TryFrom for RelPathBuf { } impl TryFrom<&str> for RelPathBuf { - type Error = PathBuf; - fn try_from(path: &str) -> Result { - RelPathBuf::try_from(PathBuf::from(path)) + type Error = Utf8PathBuf; + fn try_from(path: &str) -> Result { + RelPathBuf::try_from(Utf8PathBuf::from(path)) } } impl RelPathBuf { /// Coerces to a `RelPath` slice. /// - /// Equivalent of [`PathBuf::as_path`] for `RelPathBuf`. + /// Equivalent of [`Utf8PathBuf::as_path`] for `RelPathBuf`. pub fn as_path(&self) -> &RelPath { RelPath::new_unchecked(self.0.as_path()) } } -/// Wrapper around a relative [`Path`]. +/// Wrapper around a relative [`Utf8Path`]. #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] #[repr(transparent)] -pub struct RelPath(Path); +pub struct RelPath(Utf8Path); + +impl AsRef for RelPath { + fn as_ref(&self) -> &Utf8Path { + &self.0 + } +} impl AsRef for RelPath { fn as_ref(&self) -> &Path { - &self.0 + self.0.as_ref() } } impl RelPath { /// Creates a new `RelPath` from `path`, without checking if it is relative. - pub fn new_unchecked(path: &Path) -> &RelPath { - unsafe { &*(path as *const Path as *const RelPath) } + pub fn new_unchecked(path: &Utf8Path) -> &RelPath { + unsafe { &*(path as *const Utf8Path as *const RelPath) } } - /// Equivalent of [`Path::to_path_buf`] for `RelPath`. + /// Equivalent of [`Utf8Path::to_path_buf`] for `RelPath`. pub fn to_path_buf(&self) -> RelPathBuf { RelPathBuf::try_from(self.0.to_path_buf()).unwrap() } + + pub fn as_utf8_path(&self) -> &Utf8Path { + self.as_ref() + } + + pub fn as_str(&self) -> &str { + self.0.as_str() + } } /// Taken from -fn normalize_path(path: &Path) -> PathBuf { +fn normalize_path(path: &Utf8Path) -> Utf8PathBuf { let mut components = path.components().peekable(); - let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().copied() { + let mut ret = if let Some(c @ Utf8Component::Prefix(..)) = components.peek().copied() { components.next(); - PathBuf::from(c.as_os_str()) + Utf8PathBuf::from(c.as_str()) } else { - PathBuf::new() + Utf8PathBuf::new() }; for component in components { match component { - Component::Prefix(..) => unreachable!(), - Component::RootDir => { - ret.push(component.as_os_str()); + Utf8Component::Prefix(..) => unreachable!(), + Utf8Component::RootDir => { + ret.push(component.as_str()); } - Component::CurDir => {} - Component::ParentDir => { + Utf8Component::CurDir => {} + Utf8Component::ParentDir => { ret.pop(); } - Component::Normal(c) => { + Utf8Component::Normal(c) => { ret.push(c); } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 978ad155609d..f30f6a0f23b9 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -23,7 +23,7 @@ snap = "1.1.0" indexmap = "2.1.0" # local deps -paths.workspace = true +paths = { workspace = true, features = ["serde1"] } tt.workspace = true stdx.workspace = true text-size.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index 6b16711a8d87..fd491644648a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -35,8 +35,11 @@ pub use version::{read_dylib_info, read_version, RustCInfo}; #[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] pub enum ProcMacroKind { CustomDerive, - FuncLike, Attr, + // This used to be called FuncLike, so that's what the server expects currently. + #[serde(alias = "bang")] + #[serde(rename(serialize = "FuncLike", deserialize = "FuncLike"))] + Bang, } /// A handle to an external process which load dylibs with macros (.so or .dll) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs index aa5aff455fd8..ad0e1f187b62 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs @@ -1,11 +1,9 @@ //! Defines messages for cross-process message passing based on `ndjson` wire protocol pub(crate) mod flat; -use std::{ - io::{self, BufRead, Write}, - path::PathBuf, -}; +use std::io::{self, BufRead, Write}; +use paths::Utf8PathBuf; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::ProcMacroKind; @@ -27,7 +25,7 @@ pub const CURRENT_API_VERSION: u32 = RUST_ANALYZER_SPAN_SUPPORT; #[derive(Debug, Serialize, Deserialize)] pub enum Request { /// Since [`NO_VERSION_CHECK_VERSION`] - ListMacros { dylib_path: PathBuf }, + ListMacros { dylib_path: Utf8PathBuf }, /// Since [`NO_VERSION_CHECK_VERSION`] ExpandMacro(Box), /// Since [`VERSION_CHECK_VERSION`] @@ -89,7 +87,7 @@ pub struct ExpandMacro { /// Possible attributes for the attribute-like macros. pub attributes: Option, - pub lib: PathBuf, + pub lib: Utf8PathBuf, /// Environment variables to set during macro expansion. pub env: Vec<(String, String)>, @@ -273,7 +271,7 @@ mod tests { macro_body: FlatTree::new(&tt, CURRENT_API_VERSION, &mut span_data_table), macro_name: Default::default(), attributes: None, - lib: std::env::current_dir().unwrap(), + lib: Utf8PathBuf::from_path_buf(std::env::current_dir().unwrap()).unwrap(), env: Default::default(), current_dir: Default::default(), has_global_spans: ExpnGlobals { diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs index caf9e237fdd7..99cdbd930e1c 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs @@ -88,8 +88,6 @@ impl std::fmt::Debug for TokenId { } } -impl tt::Span for TokenId {} - #[derive(Serialize, Deserialize, Debug)] pub struct FlatTree { subtree: Vec, diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs index 72f95643c8b5..35d48a155433 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs @@ -175,7 +175,7 @@ fn mk_child( env: &FxHashMap, null_stderr: bool, ) -> io::Result { - let mut cmd = Command::new(path.as_os_str()); + let mut cmd = Command::new(path); cmd.envs(env) .env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable") .stdin(Stdio::piped()) @@ -183,7 +183,7 @@ fn mk_child( .stderr(if null_stderr { Stdio::null() } else { Stdio::inherit() }); if cfg!(windows) { let mut path_var = std::ffi::OsString::new(); - path_var.push(path.parent().unwrap().parent().unwrap().as_os_str()); + path_var.push(path.parent().unwrap().parent().unwrap()); path_var.push("\\bin;"); path_var.push(std::env::var_os("PATH").unwrap_or_default()); cmd.env("PATH", path_var); diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs index 52b4cced5f58..22c34ff1678d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs @@ -1,16 +1,11 @@ //! Handles dynamic library loading for proc macro -use std::{ - fmt, - fs::File, - io, - path::{Path, PathBuf}, -}; +use std::{fmt, fs::File, io}; use libloading::Library; use memmap2::Mmap; use object::Object; -use paths::AbsPath; +use paths::{AbsPath, Utf8Path, Utf8PathBuf}; use proc_macro::bridge; use proc_macro_api::{read_dylib_info, ProcMacroKind}; @@ -26,7 +21,7 @@ fn is_derive_registrar_symbol(symbol: &str) -> bool { symbol.contains(NEW_REGISTRAR_SYMBOL) } -fn find_registrar_symbol(file: &Path) -> io::Result> { +fn find_registrar_symbol(file: &Utf8Path) -> io::Result> { let file = File::open(file)?; let buffer = unsafe { Mmap::map(&file)? }; @@ -62,12 +57,12 @@ fn find_registrar_symbol(file: &Path) -> io::Result> { /// /// It seems that on Windows that behaviour is default, so we do nothing in that case. #[cfg(windows)] -fn load_library(file: &Path) -> Result { +fn load_library(file: &Utf8Path) -> Result { unsafe { Library::new(file) } } #[cfg(unix)] -fn load_library(file: &Path) -> Result { +fn load_library(file: &Utf8Path) -> Result { use libloading::os::unix::Library as UnixLibrary; use std::os::raw::c_int; @@ -116,14 +111,14 @@ struct ProcMacroLibraryLibloading { } impl ProcMacroLibraryLibloading { - fn open(file: &Path) -> Result { + fn open(file: &Utf8Path) -> Result { let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| { - invalid_data_err(format!("Cannot find registrar symbol in file {}", file.display())) + invalid_data_err(format!("Cannot find registrar symbol in file {file}")) })?; - let abs_file: &AbsPath = file.try_into().map_err(|_| { - invalid_data_err(format!("expected an absolute path, got {}", file.display())) - })?; + let abs_file: &AbsPath = file + .try_into() + .map_err(|_| invalid_data_err(format!("expected an absolute path, got {file}")))?; let version_info = read_dylib_info(abs_file)?; let lib = load_library(file).map_err(invalid_data_err)?; @@ -138,10 +133,10 @@ pub struct Expander { } impl Expander { - pub fn new(lib: &Path) -> Result { + pub fn new(lib: &Utf8Path) -> Result { // Some libraries for dynamic loading require canonicalized path even when it is // already absolute - let lib = lib.canonicalize()?; + let lib = lib.canonicalize_utf8()?; let lib = ensure_file_with_lock_free_access(&lib)?; @@ -176,30 +171,26 @@ impl Expander { /// Copy the dylib to temp directory to prevent locking in Windows #[cfg(windows)] -fn ensure_file_with_lock_free_access(path: &Path) -> io::Result { +fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result { use std::collections::hash_map::RandomState; - use std::ffi::OsString; use std::hash::{BuildHasher, Hasher}; if std::env::var("RA_DONT_COPY_PROC_MACRO_DLL").is_ok() { return Ok(path.to_path_buf()); } - let mut to = std::env::temp_dir(); + let mut to = Utf8PathBuf::from_path_buf(std::env::temp_dir()).unwrap(); let file_name = path.file_name().ok_or_else(|| { - io::Error::new( - io::ErrorKind::InvalidInput, - format!("File path is invalid: {}", path.display()), - ) + io::Error::new(io::ErrorKind::InvalidInput, format!("File path is invalid: {path}")) })?; // Generate a unique number by abusing `HashMap`'s hasher. // Maybe this will also "inspire" a libs team member to finally put `rand` in libstd. let t = RandomState::new().build_hasher().finish(); - let mut unique_name = OsString::from(t.to_string()); - unique_name.push(file_name); + let mut unique_name = t.to_string(); + unique_name.push_str(file_name); to.push(unique_name); std::fs::copy(path, &to).unwrap(); @@ -207,6 +198,6 @@ fn ensure_file_with_lock_free_access(path: &Path) -> io::Result { } #[cfg(unix)] -fn ensure_file_with_lock_free_access(path: &Path) -> io::Result { - Ok(path.to_path_buf()) +fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result { + Ok(path.to_owned()) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 831632c64c0a..2472c1e3119c 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -33,12 +33,11 @@ use std::{ collections::{hash_map::Entry, HashMap}, env, ffi::OsString, - fs, - path::{Path, PathBuf}, - thread, + fs, thread, time::SystemTime, }; +use paths::{Utf8Path, Utf8PathBuf}; use proc_macro_api::{ msg::{ self, deserialize_span_data_index_map, serialize_span_data_index_map, ExpnGlobals, @@ -53,7 +52,7 @@ use crate::server::TokenStream; // see `build.rs` include!(concat!(env!("OUT_DIR"), "/rustc_version.rs")); -trait ProcMacroSrvSpan: tt::Span { +trait ProcMacroSrvSpan: Copy { type Server: proc_macro::bridge::server::Server>; fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server; } @@ -81,7 +80,7 @@ impl ProcMacroSrvSpan for Span { #[derive(Default)] pub struct ProcMacroSrv { - expanders: HashMap<(PathBuf, SystemTime), dylib::Expander>, + expanders: HashMap<(Utf8PathBuf, SystemTime), dylib::Expander>, span_mode: SpanMode, } @@ -149,23 +148,22 @@ impl ProcMacroSrv { pub fn list_macros( &mut self, - dylib_path: &Path, + dylib_path: &Utf8Path, ) -> Result, String> { let expander = self.expander(dylib_path)?; Ok(expander.list_macros()) } - fn expander(&mut self, path: &Path) -> Result<&dylib::Expander, String> { + fn expander(&mut self, path: &Utf8Path) -> Result<&dylib::Expander, String> { let time = fs::metadata(path) .and_then(|it| it.modified()) - .map_err(|err| format!("Failed to get file metadata for {}: {err}", path.display()))?; + .map_err(|err| format!("Failed to get file metadata for {path}: {err}",))?; Ok(match self.expanders.entry((path.to_path_buf(), time)) { - Entry::Vacant(v) => { - v.insert(dylib::Expander::new(path).map_err(|err| { - format!("Cannot create expander for {}: {err}", path.display()) - })?) - } + Entry::Vacant(v) => v.insert( + dylib::Expander::new(path) + .map_err(|err| format!("Cannot create expander for {path}: {err}",))?, + ), Entry::Occupied(e) => e.into_mut(), }) } @@ -306,6 +304,6 @@ impl Drop for EnvSnapshot { mod tests; #[cfg(test)] -pub fn proc_macro_test_dylib_path() -> std::path::PathBuf { +pub fn proc_macro_test_dylib_path() -> paths::Utf8PathBuf { proc_macro_test::PROC_MACRO_TEST_LOCATION.into() } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs index 686d5b0438aa..631fd84aa248 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs @@ -108,7 +108,7 @@ impl ProcMacros { (trait_name.to_string(), ProcMacroKind::CustomDerive) } bridge::client::ProcMacro::Bang { name, .. } => { - (name.to_string(), ProcMacroKind::FuncLike) + (name.to_string(), ProcMacroKind::Bang) } bridge::client::ProcMacro::Attr { name, .. } => { (name.to_string(), ProcMacroKind::Attr) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs index 408db60e872b..b1a448427c60 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs @@ -101,6 +101,8 @@ pub(super) struct TokenStreamBuilder { /// pub(super)lic implementation details for the `TokenStream` type, such as iterators. pub(super) mod token_stream { + use core::fmt; + use super::{TokenStream, TokenTree}; /// An iterator over `TokenStream`'s `TokenTree`s. @@ -122,7 +124,7 @@ pub(super) mod token_stream { /// /// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to /// change these errors into `LexError`s later. - impl TokenStream { + impl TokenStream { pub(crate) fn from_str(src: &str, call_site: S) -> Result, String> { let subtree = mbe::parse_to_token_tree_static_span(call_site, src).ok_or("lexing error")?; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs index 11b008fc0b4b..633428253808 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs @@ -254,14 +254,14 @@ fn list_test_macros() { let res = list().join("\n"); expect![[r#" - fn_like_noop [FuncLike] - fn_like_panic [FuncLike] - fn_like_error [FuncLike] - fn_like_clone_tokens [FuncLike] - fn_like_mk_literals [FuncLike] - fn_like_mk_idents [FuncLike] - fn_like_span_join [FuncLike] - fn_like_span_ops [FuncLike] + fn_like_noop [Bang] + fn_like_panic [Bang] + fn_like_error [Bang] + fn_like_clone_tokens [Bang] + fn_like_mk_literals [Bang] + fn_like_mk_idents [Bang] + fn_like_span_join [Bang] + fn_like_span_ops [Bang] attr_noop [Attr] attr_panic [Attr] attr_error [Attr] diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml index 924a4a89e216..097ee1f75cd7 100644 --- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml +++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml @@ -25,8 +25,9 @@ itertools.workspace = true # local deps base-db.workspace = true +span.workspace = true cfg.workspace = true -paths.workspace = true +paths = { workspace = true, features = ["serde1"] } stdx.workspace = true toolchain.workspace = true diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs index 709fc0371747..d40eb26063de 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs @@ -77,7 +77,7 @@ impl WorkspaceBuildScripts { cmd.args(&config.extra_args); cmd.arg("--manifest-path"); - cmd.arg(workspace_root.join("Cargo.toml").as_os_str()); + cmd.arg(workspace_root.join("Cargo.toml")); if let Some(target_dir) = &config.target_dir { cmd.arg("--target-dir").arg(target_dir); @@ -354,16 +354,11 @@ impl WorkspaceBuildScripts { } // cargo_metadata crate returns default (empty) path for // older cargos, which is not absolute, so work around that. - let out_dir = mem::take(&mut message.out_dir).into_os_string(); - if !out_dir.is_empty() { - let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir)); + let out_dir = mem::take(&mut message.out_dir); + if !out_dir.as_str().is_empty() { + let out_dir = AbsPathBuf::assert(out_dir); // inject_cargo_env(package, package_build_data); - // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() - if let Some(out_dir) = - out_dir.as_os_str().to_str().map(|s| s.to_owned()) - { - data.envs.push(("OUT_DIR".to_owned(), out_dir)); - } + data.envs.push(("OUT_DIR".to_owned(), out_dir.as_str().to_owned())); data.out_dir = Some(out_dir); data.cfgs = cfgs; } @@ -377,8 +372,8 @@ impl WorkspaceBuildScripts { if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) { - let filename = AbsPathBuf::assert(PathBuf::from(&filename)); - data.proc_macro_dylib_path = Some(filename); + let filename = AbsPath::assert(filename); + data.proc_macro_dylib_path = Some(filename.to_owned()); } } }); diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index 53b41ea1e87b..51c1b094f7b3 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -1,17 +1,16 @@ //! See [`CargoWorkspace`]. use std::ops; -use std::path::PathBuf; use std::str::from_utf8; use anyhow::Context; -use base_db::Edition; use cargo_metadata::{CargoOpt, MetadataCommand}; use la_arena::{Arena, Idx}; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; use serde::Deserialize; use serde_json::from_value; +use span::Edition; use toolchain::Tool; use crate::{utf8_stdout, InvocationLocation, ManifestPath, Sysroot}; @@ -100,7 +99,7 @@ pub struct CargoConfig { pub invocation_strategy: InvocationStrategy, pub invocation_location: InvocationLocation, /// Optional path to use instead of `target` when building - pub target_dir: Option, + pub target_dir: Option, } pub type Package = Idx; @@ -262,7 +261,7 @@ impl CargoWorkspace { } } } - meta.current_dir(current_dir.as_os_str()); + meta.current_dir(current_dir); let mut other_options = vec![]; // cargo metadata only supports a subset of flags of what cargo usually accepts, and usually @@ -351,7 +350,7 @@ impl CargoWorkspace { id: id.repr.clone(), name, version, - manifest: AbsPathBuf::assert(manifest_path.into()).try_into().unwrap(), + manifest: AbsPathBuf::assert(manifest_path).try_into().unwrap(), targets: Vec::new(), is_local, is_member, @@ -370,7 +369,7 @@ impl CargoWorkspace { let tgt = targets.alloc(TargetData { package: pkg, name, - root: AbsPathBuf::assert(src_path.into()), + root: AbsPathBuf::assert(src_path), kind: TargetKind::new(&kind), required_features, }); @@ -393,11 +392,9 @@ impl CargoWorkspace { packages[source].active_features.extend(node.features); } - let workspace_root = - AbsPathBuf::assert(PathBuf::from(meta.workspace_root.into_os_string())); + let workspace_root = AbsPathBuf::assert(meta.workspace_root); - let target_directory = - AbsPathBuf::assert(PathBuf::from(meta.target_directory.into_os_string())); + let target_directory = AbsPathBuf::assert(meta.target_directory); CargoWorkspace { packages, targets, workspace_root, target_directory } } @@ -409,7 +406,7 @@ impl CargoWorkspace { pub fn target_by_root(&self, root: &AbsPath) -> Option { self.packages() .filter(|&pkg| self[pkg].is_member) - .find_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root)) + .find_map(|pkg| self[pkg].targets.iter().find(|&&it| self[it].root == root)) .copied() } diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs index 5b91f5d80589..28696aa32775 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -127,8 +127,8 @@ impl ProjectManifest { .filter_map(Result::ok) .map(|it| it.path().join("Cargo.toml")) .filter(|it| it.exists()) - .map(AbsPathBuf::assert) - .filter_map(|it| it.try_into().ok()) + .map(AbsPathBuf::try_from) + .filter_map(|it| it.ok()?.try_into().ok()) .collect() } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index fba0aaa8ce9f..512588cc8f8f 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -49,12 +49,12 @@ //! user explores them belongs to that extension (it's totally valid to change //! rust-project.json over time via configuration request!) -use base_db::{CrateDisplayName, CrateId, CrateName, Dependency, Edition}; +use base_db::{CrateDisplayName, CrateId, CrateName, Dependency}; use la_arena::RawIdx; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::{de, Deserialize}; -use std::path::PathBuf; +use span::Edition; use crate::cfg_flag::CfgFlag; @@ -113,7 +113,7 @@ impl ProjectJson { .unwrap_or_else(|| root_module.starts_with(base)); let (include, exclude) = match crate_data.source { Some(src) => { - let absolutize = |dirs: Vec| { + let absolutize = |dirs: Vec| { dirs.into_iter().map(absolutize_on_base).collect::>() }; (absolutize(src.include_dirs), absolutize(src.exclude_dirs)) @@ -176,15 +176,15 @@ impl ProjectJson { #[derive(Deserialize, Debug, Clone)] pub struct ProjectJsonData { - sysroot: Option, - sysroot_src: Option, + sysroot: Option, + sysroot_src: Option, crates: Vec, } #[derive(Deserialize, Debug, Clone)] struct CrateData { display_name: Option, - root_module: PathBuf, + root_module: Utf8PathBuf, edition: EditionData, #[serde(default)] version: Option, @@ -194,7 +194,7 @@ struct CrateData { target: Option, #[serde(default)] env: FxHashMap, - proc_macro_dylib_path: Option, + proc_macro_dylib_path: Option, is_workspace_member: Option, source: Option, #[serde(default)] @@ -238,8 +238,8 @@ struct DepData { #[derive(Deserialize, Debug, Clone)] struct CrateSource { - include_dirs: Vec, - exclude_dirs: Vec, + include_dirs: Vec, + exclude_dirs: Vec, } fn deserialize_crate_name<'de, D>(de: D) -> std::result::Result diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs index 3127bae8b0c9..1142d6243d29 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -4,13 +4,13 @@ //! but we can't process `.rlib` and need source code instead. The source code //! is typically installed with `rustup component add rust-src` command. -use std::{env, fs, iter, ops, path::PathBuf, process::Command, sync::Arc}; +use std::{env, fs, iter, ops, process::Command, sync::Arc}; use anyhow::{format_err, Result}; use base_db::CrateName; use itertools::Itertools; use la_arena::{Arena, Idx}; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; use toolchain::{probe_for_binary, Tool}; @@ -419,7 +419,7 @@ fn discover_sysroot_dir( rustc.current_dir(current_dir).args(["--print", "sysroot"]); tracing::debug!("Discovering sysroot by {:?}", rustc); let stdout = utf8_stdout(rustc)?; - Ok(AbsPathBuf::assert(PathBuf::from(stdout))) + Ok(AbsPathBuf::assert(Utf8PathBuf::from(stdout))) } fn discover_sysroot_src_dir(sysroot_path: &AbsPathBuf) -> Option { diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs index b9b1b701f6d4..fc0b507b3328 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -1,12 +1,9 @@ -use std::{ - ops::Deref, - path::{Path, PathBuf}, -}; +use std::ops::Deref; use base_db::{CrateGraph, FileId, ProcMacroPaths}; use cfg::{CfgAtom, CfgDiff}; use expect_test::{expect_file, ExpectFile}; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::de::DeserializeOwned; use triomphe::Arc; @@ -113,17 +110,16 @@ fn replace_root(s: &mut String, direction: bool) { fn replace_fake_sys_root(s: &mut String) { let fake_sysroot_path = get_test_path("fake-sysroot"); let fake_sysroot_path = if cfg!(windows) { - let normalized_path = - fake_sysroot_path.to_str().expect("expected str").replace('\\', r#"\\"#); + let normalized_path = fake_sysroot_path.as_str().replace('\\', r#"\\"#); format!(r#"{}\\"#, normalized_path) } else { - format!("{}/", fake_sysroot_path.to_str().expect("expected str")) + format!("{}/", fake_sysroot_path.as_str()) }; *s = s.replace(&fake_sysroot_path, "$FAKESYSROOT$") } -fn get_test_path(file: &str) -> PathBuf { - let base = PathBuf::from(env!("CARGO_MANIFEST_DIR")); +fn get_test_path(file: &str) -> Utf8PathBuf { + let base = Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR")); base.join("test_data").join(file) } @@ -139,7 +135,7 @@ fn get_fake_sysroot() -> Sysroot { fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { let mut root = "$ROOT$".to_owned(); replace_root(&mut root, true); - let path = Path::new(&root); + let path = Utf8Path::new(&root); let base = AbsPath::assert(path); ProjectJson::new(base, data) } @@ -268,7 +264,7 @@ fn smoke_test_real_sysroot_cargo() { let cargo_workspace = CargoWorkspace::new(meta); let sysroot = Ok(Sysroot::discover( - AbsPath::assert(Path::new(env!("CARGO_MANIFEST_DIR"))), + AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))), &Default::default(), true, ) diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 1a138b17bad4..b8c5885108d3 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -6,13 +6,14 @@ use std::{collections::VecDeque, fmt, fs, iter, str::FromStr, sync}; use anyhow::{format_err, Context}; use base_db::{ - CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env, - FileId, LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult, + CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env, FileId, + LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult, }; use cfg::{CfgAtom, CfgDiff, CfgOptions}; use paths::{AbsPath, AbsPathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; use semver::Version; +use span::Edition; use stdx::always; use toolchain::Tool; use triomphe::Arc; @@ -718,19 +719,22 @@ impl ProjectWorkspace { ) -> (CrateGraph, ProcMacroPaths) { let _p = tracing::span!(tracing::Level::INFO, "ProjectWorkspace::to_crate_graph").entered(); - let (mut crate_graph, proc_macros) = match self { + let ((mut crate_graph, proc_macros), sysroot) = match self { ProjectWorkspace::Json { project, sysroot, rustc_cfg, toolchain: _, target_layout: _, - } => project_json_to_crate_graph( - rustc_cfg.clone(), - load, - project, - sysroot.as_ref().ok(), - extra_env, + } => ( + project_json_to_crate_graph( + rustc_cfg.clone(), + load, + project, + sysroot.as_ref().ok(), + extra_env, + ), + sysroot, ), ProjectWorkspace::Cargo { cargo, @@ -742,14 +746,17 @@ impl ProjectWorkspace { toolchain: _, target_layout: _, cargo_config_extra_env: _, - } => cargo_to_crate_graph( - load, - rustc.as_ref().map(|a| a.as_ref()).ok(), - cargo, - sysroot.as_ref().ok(), - rustc_cfg.clone(), - cfg_overrides, - build_scripts, + } => ( + cargo_to_crate_graph( + load, + rustc.as_ref().map(|a| a.as_ref()).ok(), + cargo, + sysroot.as_ref().ok(), + rustc_cfg.clone(), + cfg_overrides, + build_scripts, + ), + sysroot, ), ProjectWorkspace::DetachedFiles { files, @@ -757,11 +764,20 @@ impl ProjectWorkspace { rustc_cfg, toolchain: _, target_layout: _, - } => { - detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot.as_ref().ok()) - } + } => ( + detached_files_to_crate_graph( + rustc_cfg.clone(), + load, + files, + sysroot.as_ref().ok(), + ), + sysroot, + ), }; - if crate_graph.patch_cfg_if() { + + if matches!(sysroot.as_ref().map(|it| it.mode()), Ok(SysrootMode::Workspace(_))) + && crate_graph.patch_cfg_if() + { tracing::debug!("Patched std to depend on cfg-if") } else { tracing::debug!("Did not patch std to depend on cfg-if") @@ -1077,6 +1093,8 @@ fn cargo_to_crate_graph( } } + let mut delayed_dev_deps = vec![]; + // Now add a dep edge from all targets of upstream to the lib // target of downstream. for pkg in cargo.packages() { @@ -1091,11 +1109,31 @@ fn cargo_to_crate_graph( continue; } + // If the dependency is a dev-dependency with both crates being member libraries of + // the workspace we delay adding the edge. The reason can be read up on in + // https://github.com/rust-lang/rust-analyzer/issues/14167 + // but in short, such an edge is able to cause some form of cycle in the crate graph + // for normal dependencies. If we do run into a cycle like this, we want to prefer + // the non dev-dependency edge, and so the easiest way to do that is by adding the + // dev-dependency edges last. + if dep.kind == DepKind::Dev + && matches!(kind, TargetKind::Lib { .. }) + && cargo[dep.pkg].is_member + && cargo[pkg].is_member + { + delayed_dev_deps.push((from, name.clone(), to)); + continue; + } + add_dep(crate_graph, from, name.clone(), to) } } } + for (from, name, to) in delayed_dev_deps { + add_dep(crate_graph, from, name, to); + } + if has_private { // If the user provided a path to rustc sources, we add all the rustc_private crates // and create dependencies on them for the crates which opt-in to that @@ -1151,7 +1189,6 @@ fn detached_files_to_crate_graph( }; let display_name = detached_file .file_stem() - .and_then(|os_str| os_str.to_str()) .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_owned())); let detached_file_crate = crate_graph.add_crate_root( file_id, @@ -1228,6 +1265,7 @@ fn handle_rustc_crates( let kind @ TargetKind::Lib { is_proc_macro } = rustc_workspace[tgt].kind else { continue; }; + let pkg_crates = &mut rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new); if let Some(file_id) = load(&rustc_workspace[tgt].root) { let crate_id = add_target_crate_root( crate_graph, @@ -1246,7 +1284,7 @@ fn handle_rustc_crates( if let Some(proc_macro) = libproc_macro { add_proc_macro_dep(crate_graph, crate_id, proc_macro, is_proc_macro); } - rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); + pkg_crates.push(crate_id); } } } @@ -1533,7 +1571,7 @@ fn inject_cargo_env(package: &PackageData, env: &mut Env) { // CARGO_BIN_NAME, CARGO_BIN_EXE_ let manifest_dir = package.manifest.parent(); - env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned()); + env.set("CARGO_MANIFEST_DIR", manifest_dir.as_str().to_owned()); // Not always right, but works for common cases. env.set("CARGO", "cargo".into()); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index e6984d6f41b7..6d70124188d9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -43,6 +43,7 @@ nohash-hasher.workspace = true always-assert = "0.2.0" walkdir = "2.3.2" semver.workspace = true +memchr = "2.7.1" cfg.workspace = true flycheck.workspace = true @@ -63,7 +64,7 @@ parser.workspace = true toolchain.workspace = true vfs-notify.workspace = true vfs.workspace = true -memchr = "2.7.1" +paths.workspace = true [target.'cfg(windows)'.dependencies] winapi = "0.3.9" diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index e747ec87b1c1..78920f3abace 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -190,7 +190,7 @@ fn run_server() -> anyhow::Result<()> { Some(it) => it, None => { let cwd = env::current_dir()?; - AbsPathBuf::assert(cwd) + AbsPathBuf::assert_utf8(cwd) } }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 5c474908e7ae..fdd77199aa00 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -70,7 +70,7 @@ impl flags::AnalysisStats { let mut db_load_sw = self.stop_watch(); - let path = AbsPathBuf::assert(env::current_dir()?.join(&self.path)); + let path = AbsPathBuf::assert_utf8(env::current_dir()?.join(&self.path)); let manifest = ProjectManifest::discover_single(&path)?; let mut workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?; @@ -279,7 +279,8 @@ impl flags::AnalysisStats { let mut all = 0; let mut fail = 0; for &a in adts { - if db.generic_params(a.into()).iter().next().is_some() { + let generic_params = db.generic_params(a.into()); + if generic_params.iter().next().is_some() || generic_params.iter_lt().next().is_some() { // Data types with generics don't have layout. continue; } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs index bd2646126dcb..79d6226debf9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs @@ -16,7 +16,7 @@ impl flags::Diagnostics { let cargo_config = CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; let with_proc_macro_server = if let Some(p) = &self.proc_macro_srv { - let path = vfs::AbsPathBuf::assert(std::env::current_dir()?.join(p)); + let path = vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(p)); ProcMacroServerChoice::Explicit(path) } else { ProcMacroServerChoice::Sysroot diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs index 3f68c5d053b7..b3b8ab9a4049 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs @@ -235,6 +235,7 @@ pub struct RunTests { #[derive(Debug)] pub struct RustcTests { pub rustc_repo: PathBuf, + pub filter: Option, } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs index f3f5ec1ebde2..3ff9be7102fb 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs @@ -283,7 +283,7 @@ impl flags::Lsif { with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: false, }; - let path = AbsPathBuf::assert(env::current_dir()?.join(self.path)); + let path = AbsPathBuf::assert_utf8(env::current_dir()?.join(self.path)); let manifest = ProjectManifest::discover_single(&path)?; let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index 84f2e6008746..eeec13a14bee 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -76,7 +76,7 @@ impl Tester { ); let workspace = ProjectWorkspace::DetachedFiles { - files: vec![tmp_file.clone()], + files: vec![tmp_file], sysroot, rustc_cfg: vec![], toolchain: None, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index 1061a433a58f..aef2c1be2249 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -27,7 +27,8 @@ impl flags::Scip { with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: true, }; - let root = vfs::AbsPathBuf::assert(std::env::current_dir()?.join(&self.path)).normalize(); + let root = + vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(&self.path)).normalize(); let mut config = crate::config::Config::new( root.clone(), @@ -63,12 +64,7 @@ impl flags::Scip { special_fields: Default::default(), }) .into(), - project_root: format!( - "file://{}", - root.as_os_str() - .to_str() - .ok_or(anyhow::format_err!("Unable to normalize project_root path"))? - ), + project_root: format!("file://{root}"), text_document_encoding: scip_types::TextEncoding::UTF8.into(), special_fields: Default::default(), }; @@ -216,7 +212,7 @@ fn get_relative_filepath( rootpath: &vfs::AbsPathBuf, file_id: ide::FileId, ) -> Option { - Some(vfs.file_path(file_id).as_path()?.strip_prefix(rootpath)?.as_ref().to_str()?.to_owned()) + Some(vfs.file_path(file_id).as_path()?.strip_prefix(rootpath)?.as_str().to_owned()) } // SCIP Ranges have a (very large) optimization that ranges if they are on the same line diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index cbf152465909..7475a8e6e6d9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -7,11 +7,7 @@ //! configure the server itself, feature flags are passed into analysis, and //! tweak things like automatic insertion of `()` in completions. -use std::{ - fmt, iter, - ops::Not, - path::{Path, PathBuf}, -}; +use std::{fmt, iter, ops::Not}; use cfg::{CfgAtom, CfgDiff}; use flycheck::FlycheckConfig; @@ -27,6 +23,7 @@ use ide_db::{ }; use itertools::Itertools; use lsp_types::{ClientCapabilities, MarkupKind}; +use paths::{Utf8Path, Utf8PathBuf}; use project_model::{ CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource, }; @@ -327,7 +324,7 @@ config_data! { /// These directories will be ignored by rust-analyzer. They are /// relative to the workspace root, and globs are not supported. You may /// also need to add the folders to Code's `files.watcherExclude`. - files_excludeDirs: Vec = "[]", + files_excludeDirs: Vec = "[]", /// Controls file watching implementation. files_watcher: FilesWatcherDef = "\"client\"", @@ -378,6 +375,8 @@ config_data! { /// How to render the size information in a memory layout hover. hover_memoryLayout_size: Option = "\"both\"", + /// How many fields of a struct to display when hovering a struct. + hover_show_structFields: Option = "null", /// How many associated items of a trait to display when hovering a trait. hover_show_traitAssocItems: Option = "null", @@ -516,7 +515,7 @@ config_data! { /// This config takes a map of crate names with the exported proc-macro names to ignore as values. procMacro_ignored: FxHashMap, Box<[Box]>> = "{}", /// Internal config, path to proc-macro server executable. - procMacro_server: Option = "null", + procMacro_server: Option = "null", /// Exclude imports from find-all-references. references_excludeImports: bool = "false", @@ -864,7 +863,7 @@ impl Config { } let mut errors = Vec::new(); self.detached_files = - get_field::>(&mut json, &mut errors, "detachedFiles", None, "[]") + get_field::>(&mut json, &mut errors, "detachedFiles", None, "[]") .into_iter() .map(AbsPathBuf::assert) .collect(); @@ -956,7 +955,7 @@ impl Config { pub fn has_linked_projects(&self) -> bool { !self.data.linkedProjects.is_empty() } - pub fn linked_manifests(&self) -> impl Iterator + '_ { + pub fn linked_manifests(&self) -> impl Iterator + '_ { self.data.linkedProjects.iter().filter_map(|it| match it { ManifestOrProjectJson::Manifest(p) => Some(&**p), ManifestOrProjectJson::ProjectJson(_) => None, @@ -1014,6 +1013,17 @@ impl Config { ) } + pub fn did_change_watched_files_relative_pattern_support(&self) -> bool { + try_or_def!( + self.caps + .workspace + .as_ref()? + .did_change_watched_files + .as_ref()? + .relative_pattern_support? + ) + } + pub fn prefill_caches(&self) -> bool { self.data.cachePriming_enable } @@ -1410,9 +1420,11 @@ impl Config { } } - fn target_dir_from_config(&self) -> Option { + fn target_dir_from_config(&self) -> Option { self.data.cargo_targetDir.as_ref().and_then(|target_dir| match target_dir { - TargetDirectory::UseSubdirectory(true) => Some(PathBuf::from("target/rust-analyzer")), + TargetDirectory::UseSubdirectory(true) => { + Some(Utf8PathBuf::from("target/rust-analyzer")) + } TargetDirectory::UseSubdirectory(false) => None, TargetDirectory::Directory(dir) if dir.is_relative() => Some(dir.clone()), TargetDirectory::Directory(_) => None, @@ -1691,6 +1703,7 @@ impl Config { }, keywords: self.data.hover_documentation_keywords_enable, max_trait_assoc_items_count: self.data.hover_show_traitAssocItems, + max_struct_field_count: self.data.hover_show_structFields, } } @@ -1951,7 +1964,7 @@ where #[derive(Deserialize, Debug, Clone)] #[serde(untagged)] enum ManifestOrProjectJson { - Manifest(PathBuf), + Manifest(Utf8PathBuf), ProjectJson(ProjectJsonData), } @@ -2134,7 +2147,7 @@ pub enum MemoryLayoutHoverRenderKindDef { #[serde(untagged)] pub enum TargetDirectory { UseSubdirectory(bool), - Directory(PathBuf), + Directory(Utf8PathBuf), } macro_rules! _config_data { @@ -2263,7 +2276,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "type": "array", "items": { "type": "string" }, }, - "Vec" => set! { + "Vec" => set! { "type": "array", "items": { "type": "string" }, }, @@ -2291,7 +2304,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "Option" => set! { "type": ["null", "string"], }, - "Option" => set! { + "Option" => set! { "type": ["null", "string"], }, "Option" => set! { @@ -2774,7 +2787,7 @@ mod tests { .unwrap(); assert_eq!(config.data.cargo_targetDir, Some(TargetDirectory::UseSubdirectory(true))); assert!( - matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(PathBuf::from("target/rust-analyzer"))) + matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(Utf8PathBuf::from("target/rust-analyzer"))) ); } @@ -2793,10 +2806,10 @@ mod tests { .unwrap(); assert_eq!( config.data.cargo_targetDir, - Some(TargetDirectory::Directory(PathBuf::from("other_folder"))) + Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder"))) ); assert!( - matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(PathBuf::from("other_folder"))) + matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(Utf8PathBuf::from("other_folder"))) ); } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs index 6e6cc53c251c..7c4deac93f21 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -519,14 +519,13 @@ fn clippy_code_description(code: Option<&str>) -> Option anyhow::Result<()> { - for change in params.changes { + for change in params.changes.iter().unique_by(|&it| &it.uri) { if let Ok(path) = from_proto::abs_path(&change.uri) { state.loader.handle.invalidate(path); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 1d98457add33..77692ed3ae76 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -4,7 +4,6 @@ use std::{ fs, io::Write as _, - path::PathBuf, process::{self, Stdio}, }; @@ -12,8 +11,8 @@ use anyhow::Context; use ide::{ AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange, - HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, RangeLimit, - ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit, + HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory, + Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit, }; use ide_db::SymbolKind; use itertools::Itertools; @@ -27,6 +26,7 @@ use lsp_types::{ SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, }; +use paths::Utf8PathBuf; use project_model::{ManifestPath, ProjectWorkspace, TargetKind}; use serde_json::json; use stdx::{format_to, never}; @@ -238,9 +238,12 @@ pub(crate) fn handle_discover_test( let (tests, scope) = match params.test_id { Some(id) => { let crate_id = id.split_once("::").map(|it| it.0).unwrap_or(&id); - (snap.analysis.discover_tests_in_crate_by_test_id(crate_id)?, vec![crate_id.to_owned()]) + ( + snap.analysis.discover_tests_in_crate_by_test_id(crate_id)?, + Some(vec![crate_id.to_owned()]), + ) } - None => (snap.analysis.discover_test_roots()?, vec![]), + None => (snap.analysis.discover_test_roots()?, None), }; for t in &tests { hack_recover_crate_name::insert_name(t.id.clone()); @@ -248,12 +251,13 @@ pub(crate) fn handle_discover_test( Ok(lsp_ext::DiscoverTestResults { tests: tests .into_iter() - .map(|t| { + .filter_map(|t| { let line_index = t.file.and_then(|f| snap.file_line_index(f).ok()); to_proto::test_item(&snap, t, line_index.as_ref()) }) .collect(), scope, + scope_file: None, }) } @@ -1465,7 +1469,7 @@ pub(crate) fn handle_inlay_hints( let inlay_hints_config = snap.config.inlay_hints(); Ok(Some( snap.analysis - .inlay_hints(&inlay_hints_config, file_id, Some(RangeLimit::Fixed(range)))? + .inlay_hints(&inlay_hints_config, file_id, Some(range))? .into_iter() .map(|it| { to_proto::inlay_hint( @@ -1499,10 +1503,11 @@ pub(crate) fn handle_inlay_hints_resolve( let hint_position = from_proto::offset(&line_index, original_hint.position)?; let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(); forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty(); - let resolve_hints = snap.analysis.inlay_hints( + let resolve_hints = snap.analysis.inlay_hints_resolve( &forced_resolve_inlay_hints_config, file_id, - Some(RangeLimit::NearestParent(hint_position)), + hint_position, + resolve_data.hash, )?; let mut resolved_hints = resolve_hints @@ -1542,7 +1547,7 @@ pub(crate) fn handle_call_hierarchy_prepare( let RangeInfo { range: _, info: navs } = nav_info; let res = navs .into_iter() - .filter(|it| it.kind == Some(SymbolKind::Function)) + .filter(|it| matches!(it.kind, Some(SymbolKind::Function | SymbolKind::Method))) .map(|it| to_proto::call_hierarchy_item(&snap, it)) .collect::>>()?; @@ -1736,8 +1741,8 @@ pub(crate) fn handle_open_docs( _ => (None, None), }; - let sysroot = sysroot.map(|p| p.root().as_os_str()); - let target_dir = cargo.map(|cargo| cargo.target_directory()).map(|p| p.as_os_str()); + let sysroot = sysroot.map(|p| p.root().as_str()); + let target_dir = cargo.map(|cargo| cargo.target_directory()).map(|p| p.as_str()); let Ok(remote_urls) = snap.analysis.external_docs(position, target_dir, sysroot) else { return if snap.config.local_docs() { @@ -2042,7 +2047,7 @@ fn run_rustfmt( cmd } RustfmtConfig::CustomCommand { command, args } => { - let cmd = PathBuf::from(&command); + let cmd = Utf8PathBuf::from(&command); let workspace = CargoTargetSpec::for_file(snap, file_id)?; let mut cmd = match workspace { Some(spec) => { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index 1c5a862c7035..2731e845f351 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -52,7 +52,7 @@ fn integrated_highlighting_benchmark() { let file_id = { let file = workspace_to_load.join(file); - let path = VfsPath::from(AbsPathBuf::assert(file)); + let path = VfsPath::from(AbsPathBuf::assert_utf8(file)); vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}")) }; @@ -112,7 +112,7 @@ fn integrated_completion_benchmark() { let file_id = { let file = workspace_to_load.join(file); - let path = VfsPath::from(AbsPathBuf::assert(file)); + let path = VfsPath::from(AbsPathBuf::assert_utf8(file)); vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}")) }; @@ -274,7 +274,7 @@ fn integrated_diagnostics_benchmark() { let file_id = { let file = workspace_to_load.join(file); - let path = VfsPath::from(AbsPathBuf::assert(file)); + let path = VfsPath::from(AbsPathBuf::assert_utf8(file)); vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}")) }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index 710ce7f8acbb..eac982f1b27b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs @@ -194,7 +194,8 @@ pub struct TestItem { #[serde(rename_all = "camelCase")] pub struct DiscoverTestResults { pub tests: Vec, - pub scope: Vec, + pub scope: Option>, + pub scope_file: Option>, } pub enum DiscoverTest {} @@ -800,6 +801,7 @@ pub struct CompletionResolveData { #[derive(Debug, Serialize, Deserialize)] pub struct InlayHintResolveData { pub file_id: u32, + pub hash: u64, } #[derive(Debug, Serialize, Deserialize)] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs index c5081c4bea02..3e00222b752d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs @@ -127,13 +127,14 @@ macro_rules! define_semantic_token_modifiers { define_semantic_token_modifiers![ standard { + ASYNC, DOCUMENTATION, DECLARATION, STATIC, DEFAULT_LIBRARY, } custom { - (ASYNC, "async"), + (ASSOCIATED, "associated"), (ATTRIBUTE_MODIFIER, "attribute"), (CALLABLE, "callable"), (CONSTANT, "constant"), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index e77d0c13bf2d..d8bb12528b98 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -1,7 +1,7 @@ //! Conversion of rust-analyzer specific types to lsp_types equivalents. use std::{ iter::once, - mem, path, + mem, sync::atomic::{AtomicU32, Ordering}, }; @@ -13,8 +13,9 @@ use ide::{ NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, }; -use ide_db::rust_doc::format_docs; +use ide_db::{rust_doc::format_docs, FxHasher}; use itertools::Itertools; +use paths::{Utf8Component, Utf8Prefix}; use semver::VersionReq; use serde_json::to_value; use vfs::AbsPath; @@ -52,6 +53,7 @@ pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Rang pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind { match symbol_kind { SymbolKind::Function => lsp_types::SymbolKind::FUNCTION, + SymbolKind::Method => lsp_types::SymbolKind::METHOD, SymbolKind::Struct => lsp_types::SymbolKind::STRUCT, SymbolKind::Enum => lsp_types::SymbolKind::ENUM, SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER, @@ -122,12 +124,12 @@ pub(crate) fn completion_item_kind( CompletionItemKind::BuiltinType => lsp_types::CompletionItemKind::STRUCT, CompletionItemKind::InferredType => lsp_types::CompletionItemKind::SNIPPET, CompletionItemKind::Keyword => lsp_types::CompletionItemKind::KEYWORD, - CompletionItemKind::Method => lsp_types::CompletionItemKind::METHOD, CompletionItemKind::Snippet => lsp_types::CompletionItemKind::SNIPPET, CompletionItemKind::UnresolvedReference => lsp_types::CompletionItemKind::REFERENCE, CompletionItemKind::Expression => lsp_types::CompletionItemKind::SNIPPET, CompletionItemKind::SymbolKind(symbol) => match symbol { SymbolKind::Attribute => lsp_types::CompletionItemKind::FUNCTION, + SymbolKind::Method => lsp_types::CompletionItemKind::METHOD, SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT, SymbolKind::ConstParam => lsp_types::CompletionItemKind::TYPE_PARAMETER, SymbolKind::Derive => lsp_types::CompletionItemKind::FUNCTION, @@ -444,30 +446,42 @@ pub(crate) fn inlay_hint( fields_to_resolve: &InlayFieldsToResolve, line_index: &LineIndex, file_id: FileId, - inlay_hint: InlayHint, + mut inlay_hint: InlayHint, ) -> Cancellable { - let needs_resolve = inlay_hint.needs_resolve; - let (label, tooltip, mut something_to_resolve) = - inlay_hint_label(snap, fields_to_resolve, needs_resolve, inlay_hint.label)?; + let resolve_hash = inlay_hint.needs_resolve().then(|| { + std::hash::BuildHasher::hash_one( + &std::hash::BuildHasherDefault::::default(), + &inlay_hint, + ) + }); + let mut something_to_resolve = false; let text_edits = if snap .config .visual_studio_code_version() // https://github.com/microsoft/vscode/issues/193124 .map_or(true, |version| VersionReq::parse(">=1.86.0").unwrap().matches(version)) - && needs_resolve + && resolve_hash.is_some() && fields_to_resolve.resolve_text_edits { something_to_resolve |= inlay_hint.text_edit.is_some(); None } else { - inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)) + inlay_hint.text_edit.take().map(|it| text_edit_vec(line_index, it)) }; + let (label, tooltip) = inlay_hint_label( + snap, + fields_to_resolve, + &mut something_to_resolve, + resolve_hash.is_some(), + inlay_hint.label, + )?; - let data = if needs_resolve && something_to_resolve { - Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index() }).unwrap()) - } else { - None + let data = match resolve_hash { + Some(hash) if something_to_resolve => Some( + to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index(), hash }).unwrap(), + ), + _ => None, }; Ok(lsp_types::InlayHint { @@ -492,15 +506,15 @@ pub(crate) fn inlay_hint( fn inlay_hint_label( snap: &GlobalStateSnapshot, fields_to_resolve: &InlayFieldsToResolve, + something_to_resolve: &mut bool, needs_resolve: bool, mut label: InlayHintLabel, -) -> Cancellable<(lsp_types::InlayHintLabel, Option, bool)> { - let mut something_to_resolve = false; +) -> Cancellable<(lsp_types::InlayHintLabel, Option)> { let (label, tooltip) = match &*label.parts { [InlayHintLabelPart { linked_location: None, .. }] => { let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap(); let hint_tooltip = if needs_resolve && fields_to_resolve.resolve_hint_tooltip { - something_to_resolve |= tooltip.is_some(); + *something_to_resolve |= tooltip.is_some(); None } else { match tooltip { @@ -524,7 +538,7 @@ fn inlay_hint_label( .into_iter() .map(|part| { let tooltip = if needs_resolve && fields_to_resolve.resolve_label_tooltip { - something_to_resolve |= part.tooltip.is_some(); + *something_to_resolve |= part.tooltip.is_some(); None } else { match part.tooltip { @@ -543,7 +557,7 @@ fn inlay_hint_label( } }; let location = if needs_resolve && fields_to_resolve.resolve_label_location { - something_to_resolve |= part.linked_location.is_some(); + *something_to_resolve |= part.linked_location.is_some(); None } else { part.linked_location.map(|range| location(snap, range)).transpose()? @@ -559,7 +573,7 @@ fn inlay_hint_label( (lsp_types::InlayHintLabel::LabelParts(parts), None) } }; - Ok((label, tooltip, something_to_resolve)) + Ok((label, tooltip)) } static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1); @@ -636,8 +650,7 @@ pub(crate) fn semantic_token_delta( fn semantic_token_type_and_modifiers( highlight: Highlight, ) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) { - let mut mods = semantic_tokens::ModifierSet::default(); - let type_ = match highlight.tag { + let ty = match highlight.tag { HlTag::Symbol(symbol) => match symbol { SymbolKind::Attribute => semantic_tokens::DECORATOR, SymbolKind::Derive => semantic_tokens::DERIVE, @@ -653,22 +666,10 @@ fn semantic_token_type_and_modifiers( SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD, SymbolKind::SelfType => semantic_tokens::SELF_TYPE_KEYWORD, SymbolKind::Local => semantic_tokens::VARIABLE, - SymbolKind::Function => { - if highlight.mods.contains(HlMod::Associated) { - semantic_tokens::METHOD - } else { - semantic_tokens::FUNCTION - } - } - SymbolKind::Const => { - mods |= semantic_tokens::CONSTANT; - mods |= semantic_tokens::STATIC; - semantic_tokens::VARIABLE - } - SymbolKind::Static => { - mods |= semantic_tokens::STATIC; - semantic_tokens::VARIABLE - } + SymbolKind::Method => semantic_tokens::METHOD, + SymbolKind::Function => semantic_tokens::FUNCTION, + SymbolKind::Const => semantic_tokens::VARIABLE, + SymbolKind::Static => semantic_tokens::VARIABLE, SymbolKind::Struct => semantic_tokens::STRUCT, SymbolKind::Enum => semantic_tokens::ENUM, SymbolKind::Variant => semantic_tokens::ENUM_MEMBER, @@ -715,12 +716,14 @@ fn semantic_token_type_and_modifiers( }, }; + let mut mods = semantic_tokens::ModifierSet::default(); for modifier in highlight.mods.iter() { let modifier = match modifier { - HlMod::Associated => continue, + HlMod::Associated => semantic_tokens::ASSOCIATED, HlMod::Async => semantic_tokens::ASYNC, HlMod::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER, HlMod::Callable => semantic_tokens::CALLABLE, + HlMod::Const => semantic_tokens::CONSTANT, HlMod::Consuming => semantic_tokens::CONSUMING, HlMod::ControlFlow => semantic_tokens::CONTROL_FLOW, HlMod::CrateRoot => semantic_tokens::CRATE_ROOT, @@ -742,7 +745,7 @@ fn semantic_token_type_and_modifiers( mods |= modifier; } - (type_, mods) + (ty, mods) } pub(crate) fn folding_range( @@ -814,9 +817,9 @@ pub(crate) fn url(snap: &GlobalStateSnapshot, file_id: FileId) -> lsp_types::Url /// When processing non-windows path, this is essentially the same as `Url::from_file_path`. pub(crate) fn url_from_abs_path(path: &AbsPath) -> lsp_types::Url { let url = lsp_types::Url::from_file_path(path).unwrap(); - match path.as_ref().components().next() { - Some(path::Component::Prefix(prefix)) - if matches!(prefix.kind(), path::Prefix::Disk(_) | path::Prefix::VerbatimDisk(_)) => + match path.components().next() { + Some(Utf8Component::Prefix(prefix)) + if matches!(prefix.kind(), Utf8Prefix::Disk(_) | Utf8Prefix::VerbatimDisk(_)) => { // Need to lowercase driver letter } @@ -1514,8 +1517,8 @@ pub(crate) fn test_item( snap: &GlobalStateSnapshot, test_item: ide::TestItem, line_index: Option<&LineIndex>, -) -> lsp_ext::TestItem { - lsp_ext::TestItem { +) -> Option { + Some(lsp_ext::TestItem { id: test_item.id, label: test_item.label, kind: match test_item.kind { @@ -1530,9 +1533,9 @@ pub(crate) fn test_item( | project_model::TargetKind::Example | project_model::TargetKind::BuildScript | project_model::TargetKind::Other => lsp_ext::TestItemKind::Package, - project_model::TargetKind::Test | project_model::TargetKind::Bench => { - lsp_ext::TestItemKind::Test - } + project_model::TargetKind::Test => lsp_ext::TestItemKind::Test, + // benches are not tests needed to be shown in the test explorer + project_model::TargetKind::Bench => return None, } } ide::TestItemKind::Module => lsp_ext::TestItemKind::Module, @@ -1548,7 +1551,7 @@ pub(crate) fn test_item( .map(|f| lsp_types::TextDocumentIdentifier { uri: url(snap, f) }), range: line_index.and_then(|l| Some(range(l, test_item.text_range?))), runnable: test_item.runnable.and_then(|r| runnable(snap, r).ok()), - } + }) } pub(crate) mod command { @@ -2728,12 +2731,12 @@ struct ProcMacro { #[test] #[cfg(target_os = "windows")] fn test_lowercase_drive_letter() { - use std::path::Path; + use paths::Utf8Path; - let url = url_from_abs_path(Path::new("C:\\Test").try_into().unwrap()); + let url = url_from_abs_path(Utf8Path::new("C:\\Test").try_into().unwrap()); assert_eq!(url.to_string(), "file:///c:/Test"); - let url = url_from_abs_path(Path::new(r#"\\localhost\C$\my_dir"#).try_into().unwrap()); + let url = url_from_abs_path(Utf8Path::new(r#"\\localhost\C$\my_dir"#).try_into().unwrap()); assert_eq!(url.to_string(), "file://localhost/C$/my_dir"); } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index ffe56e41435c..38df32351256 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -9,9 +9,8 @@ use std::{ use always_assert::always; use crossbeam_channel::{never, select, Receiver}; use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath}; -use itertools::Itertools; use lsp_server::{Connection, Notification, Request}; -use lsp_types::notification::Notification as _; +use lsp_types::{notification::Notification as _, TextDocumentIdentifier}; use stdx::thread::ThreadIntent; use vfs::FileId; @@ -533,31 +532,29 @@ impl GlobalState { let snapshot = self.snapshot(); move || { let tests = subscriptions - .into_iter() - .filter_map(|f| snapshot.analysis.crates_for(f).ok()) - .flatten() - .unique() - .filter_map(|c| snapshot.analysis.discover_tests_in_crate(c).ok()) + .iter() + .copied() + .filter_map(|f| snapshot.analysis.discover_tests_in_file(f).ok()) .flatten() .collect::>(); for t in &tests { hack_recover_crate_name::insert_name(t.id.clone()); } - let scope = tests - .iter() - .filter_map(|t| Some(t.id.split_once("::")?.0)) - .unique() - .map(|it| it.to_owned()) - .collect(); Task::DiscoverTest(lsp_ext::DiscoverTestResults { tests: tests .into_iter() - .map(|t| { + .filter_map(|t| { let line_index = t.file.and_then(|f| snapshot.file_line_index(f).ok()); to_proto::test_item(&snapshot, t, line_index.as_ref()) }) .collect(), - scope, + scope: None, + scope_file: Some( + subscriptions + .into_iter() + .map(|f| TextDocumentIdentifier { uri: to_proto::url(&snapshot, f) }) + .collect(), + ), }) } }); @@ -653,7 +650,7 @@ impl GlobalState { }; if let Some(state) = state { - self.report_progress("Building", state, msg, None, None); + self.report_progress("Building build-artifacts", state, msg, None, None); } } Task::LoadProcMacros(progress) => { @@ -669,7 +666,7 @@ impl GlobalState { }; if let Some(state) = state { - self.report_progress("Loading", state, msg, None, None); + self.report_progress("Loading proc-macros", state, msg, None, None); } } Task::BuildDepsHaveChanged => self.build_deps_changed = true, @@ -714,10 +711,9 @@ impl GlobalState { message += &format!( ": {}", match dir.strip_prefix(self.config.root_path()) { - Some(relative_path) => relative_path.as_ref(), + Some(relative_path) => relative_path.as_utf8_path(), None => dir.as_ref(), } - .display() ); } @@ -861,7 +857,7 @@ impl GlobalState { let title = if self.flycheck.len() == 1 { format!("{}", self.config.flycheck()) } else { - format!("cargo check (#{})", id + 1) + format!("{} (#{})", self.config.flycheck(), id + 1) }; self.report_progress( &title, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index c2725e1fad91..771a5599f6f5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -26,7 +26,6 @@ use itertools::Itertools; use load_cargo::{load_proc_macro, ProjectFolders}; use proc_macro_api::ProcMacroServer; use project_model::{ProjectWorkspace, WorkspaceBuildScripts}; -use rustc_hash::FxHashSet; use stdx::{format_to, thread::ThreadIntent}; use triomphe::Arc; use vfs::{AbsPath, AbsPathBuf, ChangeKind}; @@ -185,10 +184,8 @@ impl GlobalState { message.push_str( "`rust-analyzer.linkedProjects` have been specified, which may be incorrect. Specified project paths:\n", ); - message.push_str(&format!( - " {}", - self.config.linked_manifests().map(|it| it.display()).format("\n ") - )); + message + .push_str(&format!(" {}", self.config.linked_manifests().format("\n "))); if self.config.has_linked_project_jsons() { message.push_str("\nAdditionally, one or more project jsons are specified") } @@ -431,27 +428,46 @@ impl GlobalState { } if let FilesWatcher::Client = self.config.files().watcher { - let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions { - watchers: self - .workspaces - .iter() - .flat_map(|ws| ws.to_roots()) - .filter(|it| it.is_local) + let filter = + self.workspaces.iter().flat_map(|ws| ws.to_roots()).filter(|it| it.is_local); + + let watchers = if self.config.did_change_watched_files_relative_pattern_support() { + // When relative patterns are supported by the client, prefer using them + filter .flat_map(|root| { - root.include.into_iter().flat_map(|it| { - [ - format!("{it}/**/*.rs"), - format!("{it}/**/Cargo.toml"), - format!("{it}/**/Cargo.lock"), - ] + root.include.into_iter().flat_map(|base| { + [(base.clone(), "**/*.rs"), (base, "**/Cargo.{lock,toml}")] + }) + }) + .map(|(base, pat)| lsp_types::FileSystemWatcher { + glob_pattern: lsp_types::GlobPattern::Relative( + lsp_types::RelativePattern { + base_uri: lsp_types::OneOf::Right( + lsp_types::Url::from_file_path(base).unwrap(), + ), + pattern: pat.to_owned(), + }, + ), + kind: None, + }) + .collect() + } else { + // When they're not, integrate the base to make them into absolute patterns + filter + .flat_map(|root| { + root.include.into_iter().flat_map(|base| { + [format!("{base}/**/*.rs"), format!("{base}/**/Cargo.{{lock,toml}}")] }) }) .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern: lsp_types::GlobPattern::String(glob_pattern), kind: None, }) - .collect(), + .collect() }; + + let registration_options = + lsp_types::DidChangeWatchedFilesRegistrationOptions { watchers }; let registration = lsp_types::Registration { id: "workspace/didChangeWatchedFiles".to_owned(), method: "workspace/didChangeWatchedFiles".to_owned(), @@ -525,26 +541,23 @@ impl GlobalState { fn recreate_crate_graph(&mut self, cause: String) { // crate graph construction relies on these paths, record them so when one of them gets // deleted or created we trigger a reconstruction of the crate graph - let mut crate_graph_file_dependencies = FxHashSet::default(); + let mut crate_graph_file_dependencies = mem::take(&mut self.crate_graph_file_dependencies); + self.report_progress( + "Building CrateGraph", + crate::lsp::utils::Progress::Begin, + None, + None, + None, + ); let (crate_graph, proc_macro_paths, layouts, toolchains) = { // Create crate graph from all the workspaces let vfs = &mut self.vfs.write().0; - let loader = &mut self.loader; let load = |path: &AbsPath| { - let _p = tracing::span!(tracing::Level::DEBUG, "switch_workspaces::load").entered(); let vfs_path = vfs::VfsPath::from(path.to_path_buf()); crate_graph_file_dependencies.insert(vfs_path.clone()); - match vfs.file_id(&vfs_path) { - Some(file_id) => Some(file_id), - None => { - // FIXME: Consider not loading this here? - let contents = loader.handle.load_sync(path); - vfs.set_file_contents(vfs_path.clone(), contents); - vfs.file_id(&vfs_path) - } - } + vfs.file_id(&vfs_path) }; ws_to_crate_graph(&self.workspaces, self.config.extra_env(), load) @@ -564,6 +577,13 @@ impl GlobalState { change.set_toolchains(toolchains); self.analysis_host.apply_change(change); self.crate_graph_file_dependencies = crate_graph_file_dependencies; + self.report_progress( + "Building CrateGraph", + crate::lsp::utils::Progress::End, + None, + None, + None, + ); self.process_changes(); self.reload_flycheck(); @@ -732,6 +752,8 @@ pub fn ws_to_crate_graph( }); proc_macro_paths.push(crate_proc_macros); } + crate_graph.shrink_to_fit(); + proc_macro_paths.shrink_to_fit(); (crate_graph, proc_macro_paths, layouts, toolchains) } @@ -739,7 +761,7 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"]; const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"]; - let file_name = match path.file_name().unwrap_or_default().to_str() { + let file_name = match path.file_name() { Some(it) => it, None => return false, }; @@ -754,18 +776,18 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) // .cargo/config{.toml} if path.extension().unwrap_or_default() != "rs" { let is_cargo_config = matches!(file_name, "config.toml" | "config") - && path.parent().map(|parent| parent.as_ref().ends_with(".cargo")).unwrap_or(false); + && path.parent().map(|parent| parent.as_str().ends_with(".cargo")).unwrap_or(false); return is_cargo_config; } - if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_ref().ends_with(it)) { + if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_str().ends_with(it)) { return true; } let parent = match path.parent() { Some(it) => it, None => return false, }; - if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.as_ref().ends_with(it)) { + if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.as_str().ends_with(it)) { return true; } if file_name == "main.rs" { @@ -773,7 +795,7 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) Some(it) => it, None => return false, }; - if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.as_ref().ends_with(it)) { + if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.as_str().ends_with(it)) { return true; } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs index efd42fadf7e9..cf38032b9418 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs @@ -60,7 +60,7 @@ fn get_fake_sysroot() -> Sysroot { let sysroot_path = get_fake_sysroot_path(); // there's no `libexec/` directory with a `proc-macro-srv` binary in that // fake sysroot, so we give them both the same path: - let sysroot_dir = AbsPathBuf::assert(sysroot_path); + let sysroot_dir = AbsPathBuf::assert_utf8(sysroot_path); let sysroot_src_dir = sysroot_dir.clone(); Sysroot::load(sysroot_dir, Some(Ok(sysroot_src_dir)), false) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index 960f5b531d44..439b006977d3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -903,6 +903,7 @@ fn out_dirs_check() { } #[test] +#[cfg(not(windows))] // windows requires elevated permissions to create symlinks fn root_contains_symlink_out_dirs_check() { out_dirs_check_impl(true); } @@ -917,7 +918,7 @@ fn resolve_proc_macro() { } let sysroot = project_model::Sysroot::discover_no_source( - &AbsPathBuf::assert(std::env::current_dir().unwrap()), + &AbsPathBuf::assert_utf8(std::env::current_dir().unwrap()), &Default::default(), ) .unwrap(); @@ -1002,7 +1003,7 @@ pub fn foo(_input: TokenStream) -> TokenStream { }, "procMacro": { "enable": true, - "server": proc_macro_server_path.as_path().as_ref(), + "server": proc_macro_server_path.as_path().as_str(), } })) .root("foo") @@ -1039,7 +1040,7 @@ fn test_will_rename_files_same_level() { let tmp_dir = TestDir::new(); let tmp_dir_path = tmp_dir.path().to_owned(); - let tmp_dir_str = tmp_dir_path.to_str().unwrap(); + let tmp_dir_str = tmp_dir_path.as_str(); let base_path = PathBuf::from(format!("file://{tmp_dir_str}")); let code = r#" @@ -1084,7 +1085,7 @@ use crate::old_folder::nested::foo as bar; "documentChanges": [ { "textDocument": { - "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), + "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").as_str().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), "version": null }, "edits": [ @@ -1141,7 +1142,7 @@ use crate::old_folder::nested::foo as bar; "documentChanges": [ { "textDocument": { - "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), + "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").as_str().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), "version": null }, "edits": [ @@ -1162,7 +1163,7 @@ use crate::old_folder::nested::foo as bar; }, { "textDocument": { - "uri": format!("file://{}", tmp_dir_path.join("src").join("old_folder").join("nested.rs").to_str().unwrap().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), + "uri": format!("file://{}", tmp_dir_path.join("src").join("old_folder").join("nested.rs").as_str().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), "version": null }, "edits": [ diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs index 1d831b8b1053..8bbe6ff37247 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs @@ -1,7 +1,6 @@ use std::{ cell::{Cell, RefCell}, fs, - path::{Path, PathBuf}, sync::Once, time::Duration, }; @@ -9,6 +8,7 @@ use std::{ use crossbeam_channel::{after, select, Receiver}; use lsp_server::{Connection, Message, Notification, Request}; use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url}; +use paths::{Utf8Path, Utf8PathBuf}; use rust_analyzer::{config::Config, lsp, main_loop}; use serde::Serialize; use serde_json::{json, to_string_pretty, Value}; @@ -21,7 +21,7 @@ use crate::testdir::TestDir; pub(crate) struct Project<'a> { fixture: &'a str, tmp_dir: Option, - roots: Vec, + roots: Vec, config: serde_json::Value, root_dir_contains_symlink: bool, } @@ -359,7 +359,7 @@ impl Server { self.client.sender.send(Message::Notification(not)).unwrap(); } - pub(crate) fn path(&self) -> &Path { + pub(crate) fn path(&self) -> &Utf8Path { self.dir.path() } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs index b3ee7fa3d03f..d113bd512789 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs @@ -1,11 +1,12 @@ use std::{ fs, io, - path::{Path, PathBuf}, sync::atomic::{AtomicUsize, Ordering}, }; +use paths::{Utf8Path, Utf8PathBuf}; + pub(crate) struct TestDir { - path: PathBuf, + path: Utf8PathBuf, keep: bool, } @@ -51,10 +52,13 @@ impl TestDir { #[cfg(target_os = "windows")] std::os::windows::fs::symlink_dir(path, &symlink_path).unwrap(); - return TestDir { path: symlink_path, keep: false }; + return TestDir { + path: Utf8PathBuf::from_path_buf(symlink_path).unwrap(), + keep: false, + }; } - return TestDir { path, keep: false }; + return TestDir { path: Utf8PathBuf::from_path_buf(path).unwrap(), keep: false }; } panic!("Failed to create a temporary directory") } @@ -64,7 +68,7 @@ impl TestDir { self.keep = true; self } - pub(crate) fn path(&self) -> &Path { + pub(crate) fn path(&self) -> &Utf8Path { &self.path } } @@ -79,7 +83,7 @@ impl Drop for TestDir { let actual_path = filetype.is_symlink().then(|| fs::read_link(&self.path).unwrap()); if let Some(actual_path) = actual_path { - remove_dir_all(&actual_path).unwrap_or_else(|err| { + remove_dir_all(Utf8Path::from_path(&actual_path).unwrap()).unwrap_or_else(|err| { panic!( "failed to remove temporary link to directory {}: {err}", actual_path.display() @@ -88,18 +92,18 @@ impl Drop for TestDir { } remove_dir_all(&self.path).unwrap_or_else(|err| { - panic!("failed to remove temporary directory {}: {err}", self.path.display()) + panic!("failed to remove temporary directory {}: {err}", self.path) }); } } #[cfg(not(windows))] -fn remove_dir_all(path: &Path) -> io::Result<()> { +fn remove_dir_all(path: &Utf8Path) -> io::Result<()> { fs::remove_dir_all(path) } #[cfg(windows)] -fn remove_dir_all(path: &Path) -> io::Result<()> { +fn remove_dir_all(path: &Utf8Path) -> io::Result<()> { for _ in 0..99 { if fs::remove_dir_all(path).is_ok() { return Ok(()); diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs index 6b849ce37381..c9109c72d0d2 100644 --- a/src/tools/rust-analyzer/crates/span/src/lib.rs +++ b/src/tools/rust-analyzer/crates/span/src/lib.rs @@ -16,6 +16,56 @@ pub use self::{ pub use syntax::{TextRange, TextSize}; pub use vfs::FileId; +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Edition { + Edition2015, + Edition2018, + Edition2021, + Edition2024, +} + +impl Edition { + pub const CURRENT: Edition = Edition::Edition2021; + pub const DEFAULT: Edition = Edition::Edition2015; +} + +#[derive(Debug)] +pub struct ParseEditionError { + invalid_input: String, +} + +impl std::error::Error for ParseEditionError {} +impl fmt::Display for ParseEditionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "invalid edition: {:?}", self.invalid_input) + } +} + +impl std::str::FromStr for Edition { + type Err = ParseEditionError; + + fn from_str(s: &str) -> Result { + let res = match s { + "2015" => Edition::Edition2015, + "2018" => Edition::Edition2018, + "2021" => Edition::Edition2021, + "2024" => Edition::Edition2024, + _ => return Err(ParseEditionError { invalid_input: s.to_owned() }), + }; + Ok(res) + } +} + +impl fmt::Display for Edition { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Edition::Edition2015 => "2015", + Edition::Edition2018 => "2018", + Edition::Edition2021 => "2021", + Edition::Edition2024 => "2024", + }) + } +} #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct FilePosition { pub file_id: FileId, diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml index 9a8d73cf7ff4..1809ca7dea5d 100644 --- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml @@ -33,12 +33,8 @@ text-edit.workspace = true [dev-dependencies] rayon.workspace = true expect-test = "1.4.0" -proc-macro2 = "1.0.47" -quote = "1.0.20" -ungrammar = "1.16.1" test-utils.workspace = true -sourcegen.workspace = true [features] in-rust-tree = [] diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index c3d8e97c436c..e1765b25fd82 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -414,7 +414,7 @@ StmtList = '}' RefExpr = - Attr* '&' ('raw' | 'mut' | 'const') Expr + Attr* '&' (('raw' 'const'?)| ('raw'? 'mut') ) Expr TryExpr = Attr* Expr '?' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 75971861aa80..c82bc4151ac0 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -7,6 +7,807 @@ use crate::{ SyntaxNode, SyntaxToken, T, }; +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Abi { + pub(crate) syntax: SyntaxNode, +} +impl Abi { + pub fn extern_token(&self) -> Option { support::token(&self.syntax, T![extern]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ArgList { + pub(crate) syntax: SyntaxNode, +} +impl ArgList { + pub fn args(&self) -> AstChildren { support::children(&self.syntax) } + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ArrayExpr { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for ArrayExpr {} +impl ArrayExpr { + pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn exprs(&self) -> AstChildren { support::children(&self.syntax) } + pub fn l_brack_token(&self) -> Option { support::token(&self.syntax, T!['[']) } + pub fn r_brack_token(&self) -> Option { support::token(&self.syntax, T![']']) } + pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ArrayType { + pub(crate) syntax: SyntaxNode, +} +impl ArrayType { + pub fn const_arg(&self) -> Option { support::child(&self.syntax) } + pub fn ty(&self) -> Option { support::child(&self.syntax) } + pub fn l_brack_token(&self) -> Option { support::token(&self.syntax, T!['[']) } + pub fn r_brack_token(&self) -> Option { support::token(&self.syntax, T![']']) } + pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmExpr { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for AsmExpr {} +impl AsmExpr { + pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } + pub fn asm_token(&self) -> Option { support::token(&self.syntax, T![asm]) } + pub fn builtin_token(&self) -> Option { support::token(&self.syntax, T![builtin]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AssocItemList { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for AssocItemList {} +impl AssocItemList { + pub fn assoc_items(&self) -> AstChildren { support::children(&self.syntax) } + pub fn l_curly_token(&self) -> Option { support::token(&self.syntax, T!['{']) } + pub fn r_curly_token(&self) -> Option { support::token(&self.syntax, T!['}']) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AssocTypeArg { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasTypeBounds for AssocTypeArg {} +impl AssocTypeArg { + pub fn const_arg(&self) -> Option { support::child(&self.syntax) } + pub fn generic_arg_list(&self) -> Option { support::child(&self.syntax) } + pub fn name_ref(&self) -> Option { support::child(&self.syntax) } + pub fn param_list(&self) -> Option { support::child(&self.syntax) } + pub fn ret_type(&self) -> Option { support::child(&self.syntax) } + pub fn ty(&self) -> Option { support::child(&self.syntax) } + pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Attr { + pub(crate) syntax: SyntaxNode, +} +impl Attr { + pub fn meta(&self) -> Option { support::child(&self.syntax) } + pub fn excl_token(&self) -> Option { support::token(&self.syntax, T![!]) } + pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } + pub fn l_brack_token(&self) -> Option { support::token(&self.syntax, T!['[']) } + pub fn r_brack_token(&self) -> Option { support::token(&self.syntax, T![']']) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AwaitExpr { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for AwaitExpr {} +impl AwaitExpr { + pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn dot_token(&self) -> Option { support::token(&self.syntax, T![.]) } + pub fn await_token(&self) -> Option { support::token(&self.syntax, T![await]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct BecomeExpr { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for BecomeExpr {} +impl BecomeExpr { + pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn become_token(&self) -> Option { support::token(&self.syntax, T![become]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct BinExpr { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for BinExpr {} +impl BinExpr {} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct BlockExpr { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for BlockExpr {} +impl BlockExpr { + pub fn label(&self) -> Option` container. @@ -190,60 +190,60 @@ store-value: (hover_background_color, "#616161") // hover background color store-value: (grey, "#ccc") call-function: ( - "check-result-color", ( - "keyword", // item kind - "#d2991d", // color of item kind - "#d2991d", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "keyword", + "color": "#d2991d", + "hover_color": "#d2991d", + }, ) call-function: ( - "check-result-color", ( - "struct", // item kind - "#2dbfb8", // color of item kind - "#2dbfb8", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "struct", + "color": "#2dbfb8", + "hover_color": "#2dbfb8", + }, ) call-function: ( - "check-result-color", ( - "associatedtype", // item kind - "#d2991d", // color of item kind - "#d2991d", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "associatedtype", + "color": "#d2991d", + "hover_color": "#d2991d", + }, ) call-function: ( - "check-result-color", ( - "tymethod", // item kind - "#2bab63", // color of item kind - "#2bab63", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "tymethod", + "color": "#2bab63", + "hover_color": "#2bab63", + }, ) call-function: ( - "check-result-color", ( - "method", // item kind - "#2bab63", // color of item kind - "#2bab63", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "method", + "color": "#2bab63", + "hover_color": "#2bab63", + }, ) call-function: ( - "check-result-color", ( - "structfield", // item kind - "#ddd", // color of item kind - "#ddd", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "structfield", + "color": "#ddd", + "hover_color": "#ddd", + }, ) call-function: ( - "check-result-color", ( - "macro", // item kind - "#09bd00", // color of item kind - "#09bd00", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "macro", + "color": "#09bd00", + "hover_color": "#09bd00", + }, ) call-function: ( - "check-result-color", ( - "fn", // item kind - "#2bab63", // color of item kind - "#2bab63", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "fn", + "color": "#2bab63", + "hover_color": "#2bab63", + }, ) // Checking the `` container. @@ -287,60 +287,60 @@ store-value: (hover_background_color, "#ccc") // hover background color store-value: (grey, "#999") call-function: ( - "check-result-color", ( - "keyword", // item kind - "#3873ad", // color of item kind - "#3873ad", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "keyword", + "color": "#3873ad", + "hover_color": "#3873ad", + }, ) call-function: ( - "check-result-color", ( - "struct", // item kind - "#ad378a", // color of item kind - "#ad378a", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "struct", + "color": "#ad378a", + "hover_color": "#ad378a", + }, ) call-function: ( - "check-result-color", ( - "associatedtype", // item kind - "#3873ad", // color of item kind - "#3873ad", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "associatedtype", + "color": "#3873ad", + "hover_color": "#3873ad", + }, ) call-function: ( - "check-result-color", ( - "tymethod", // item kind - "#ad7c37", // color of item kind - "#ad7c37", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "tymethod", + "color": "#ad7c37", + "hover_color": "#ad7c37", + }, ) call-function: ( - "check-result-color", ( - "method", // item kind - "#ad7c37", // color of item kind - "#ad7c37", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "method", + "color": "#ad7c37", + "hover_color": "#ad7c37", + }, ) call-function: ( - "check-result-color", ( - "structfield", // item kind - "#000", // color of item kind - "#000", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "structfield", + "color": "#000", + "hover_color": "#000", + }, ) call-function: ( - "check-result-color", ( - "macro", // item kind - "#068000", // color of item kind - "#068000", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "macro", + "color": "#068000", + "hover_color": "#068000", + }, ) call-function: ( - "check-result-color", ( - "fn", // item kind - "#ad7c37", // color of item kind - "#ad7c37", // color of hovered/focused item kind - ), + "check-result-color", { + "result_kind": "fn", + "color": "#ad7c37", + "hover_color": "#ad7c37", + }, ) // Checking the `` container. @@ -358,11 +358,11 @@ show-text: true define-function: ( "check-alias", - (theme, alias, grey), + [theme, alias, grey], block { set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} reload: - write: (".search-input", "thisisanalias") + write-into: (".search-input", "thisisanalias") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... diff --git a/tests/rustdoc-gui/search-result-display.goml b/tests/rustdoc-gui/search-result-display.goml index 6ce13b8c3d30..b1a5548808e0 100644 --- a/tests/rustdoc-gui/search-result-display.goml +++ b/tests/rustdoc-gui/search-result-display.goml @@ -2,7 +2,7 @@ // Checks that the search results have the expected width. go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" set-window-size: (900, 1000) -write: (".search-input", "test") +write-into: (".search-input", "test") // To be SURE that the search will be run. press-key: 'Enter' wait-for: "#crate-search" @@ -69,7 +69,7 @@ assert-css: ("#search", {"width": "640px"}) show-text: true define-function: ( "check-filter", - (theme, border, filter, hover_border, hover_filter), + [theme, border, filter, hover_border, hover_filter], block { set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} reload: diff --git a/tests/rustdoc-gui/search-result-impl-disambiguation.goml b/tests/rustdoc-gui/search-result-impl-disambiguation.goml index 6d12032e891b..3e49ac33025e 100644 --- a/tests/rustdoc-gui/search-result-impl-disambiguation.goml +++ b/tests/rustdoc-gui/search-result-impl-disambiguation.goml @@ -5,7 +5,7 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" // This should link to the inherent impl -write: (".search-input", "ZyxwvutMethodDisambiguation -> bool") +write-into: (".search-input", "ZyxwvutMethodDisambiguation -> bool") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... @@ -25,7 +25,7 @@ assert: "section:target" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" // This should link to the trait impl -write: (".search-input", "ZyxwvutMethodDisambiguation, usize -> usize") +write-into: (".search-input", "ZyxwvutMethodDisambiguation, usize -> usize") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... diff --git a/tests/rustdoc-gui/search-result-keyword.goml b/tests/rustdoc-gui/search-result-keyword.goml index 1b2be6d4e3e2..370edce2ddd0 100644 --- a/tests/rustdoc-gui/search-result-keyword.goml +++ b/tests/rustdoc-gui/search-result-keyword.goml @@ -1,6 +1,6 @@ // Checks that the "keyword" results have the expected text alongside them. go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -write: (".search-input", "CookieMonster") +write-into: (".search-input", "CookieMonster") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... diff --git a/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml b/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml index 156d8d03ca2d..7e26229ec6e5 100644 --- a/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml +++ b/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml @@ -1,7 +1,7 @@ // Checks that the search tab results work correctly with function signature syntax // First, try a search-by-name go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -write: (".search-input", "Foo") +write-into: (".search-input", "Foo") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... @@ -23,7 +23,7 @@ wait-for-attribute: ("#search-tabs > button:nth-of-type(3)", {"class": "selected // Now try search-by-return go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -write: (".search-input", "-> String") +write-into: (".search-input", "-> String") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... @@ -45,7 +45,7 @@ wait-for-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected // Try with a search-by-return with no results go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -write: (".search-input", "-> Something") +write-into: (".search-input", "-> Something") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... @@ -55,7 +55,7 @@ assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Return Types" // Try with a search-by-parameter go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -write: (".search-input", "usize,pattern") +write-into: (".search-input", "usize,pattern") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... @@ -65,7 +65,7 @@ assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Parameters", // Try with a search-by-parameter-and-return go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -write: (".search-input", "pattern -> str") +write-into: (".search-input", "pattern -> str") // To be SURE that the search will be run. press-key: 'Enter' // Waiting for the search results to appear... diff --git a/tests/rustdoc-gui/search-tab.goml b/tests/rustdoc-gui/search-tab.goml index b52bb0688c1b..c33866593c35 100644 --- a/tests/rustdoc-gui/search-tab.goml +++ b/tests/rustdoc-gui/search-tab.goml @@ -4,9 +4,9 @@ show-text: true define-function: ( "check-colors", - (theme, background, background_selected, background_hover, border_bottom, + [theme, background, background_selected, background_hover, border_bottom, border_bottom_selected, border_bottom_hover, border_top, border_top_selected, - border_top_hover), + border_top_hover], block { // Setting the theme. set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} @@ -93,12 +93,12 @@ assert-property: ("#search-tabs > button:nth-child(3) > .count", {"offsetWidth": compare-elements-position: ( "#search-tabs > button:nth-child(1) > .count", "#search-tabs > button:nth-child(2) > .count", - ("y") + ["y"] ) compare-elements-position: ( "#search-tabs > button:nth-child(2) > .count", "#search-tabs > button:nth-child(3) > .count", - ("y") + ["y"] ) // Check that counts are beside the titles and haven't wrapped compare-elements-position-near: ( @@ -135,12 +135,12 @@ assert-property: ("#search-tabs > button:nth-child(3) > .count", {"offsetWidth": compare-elements-position: ( "#search-tabs > button:nth-child(1) > .count", "#search-tabs > button:nth-child(2) > .count", - ("y") + ["y"] ) compare-elements-position: ( "#search-tabs > button:nth-child(2) > .count", "#search-tabs > button:nth-child(3) > .count", - ("y") + ["y"] ) // Check that counts are NOT beside the titles; now they have wrapped compare-elements-position-near-false: ( diff --git a/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml b/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml index b55a1cfd92bc..9afde7c61da6 100644 --- a/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml +++ b/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml @@ -6,7 +6,7 @@ fail-on-request-error: false define-function: ( "check-setting", - (storage_value, setting_attribute_value, toggle_attribute_value), + [storage_value, setting_attribute_value, toggle_attribute_value], block { assert-local-storage: {"rustdoc-auto-hide-large-items": |storage_value|} click: "#settings-menu" diff --git a/tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml b/tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml index 5210ad8f793b..644396ed5782 100644 --- a/tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml +++ b/tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml @@ -3,7 +3,7 @@ define-function: ( "check-setting", - (storage_value, setting_attribute_value, toggle_attribute_value), + [storage_value, setting_attribute_value, toggle_attribute_value], block { assert-local-storage: {"rustdoc-auto-hide-method-docs": |storage_value|} click: "#settings-menu" diff --git a/tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml b/tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml index ecadd8fa80ed..3c09198dae50 100644 --- a/tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml +++ b/tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml @@ -2,7 +2,7 @@ define-function: ( "check-setting", - (storage_value, setting_attribute_value, toggle_attribute_value), + [storage_value, setting_attribute_value, toggle_attribute_value], block { assert-local-storage: {"rustdoc-auto-hide-trait-implementations": |storage_value|} click: "#settings-menu" diff --git a/tests/rustdoc-gui/setting-go-to-only-result.goml b/tests/rustdoc-gui/setting-go-to-only-result.goml index 45e0b349051e..f8535477c22d 100644 --- a/tests/rustdoc-gui/setting-go-to-only-result.goml +++ b/tests/rustdoc-gui/setting-go-to-only-result.goml @@ -2,7 +2,7 @@ define-function: ( "check-setting", - (storage_value, setting_attribute_value), + [storage_value, setting_attribute_value], block { assert-local-storage: {"rustdoc-go-to-only-result": |storage_value|} click: "#settings-menu" @@ -32,7 +32,7 @@ assert-local-storage: {"rustdoc-go-to-only-result": "true"} go-to: "file://" + |DOC_PATH| + "/lib2/index.html" // We enter it into the search. -write: (".search-input", "HasALongTraitWithParams") +write-into: (".search-input", "HasALongTraitWithParams") wait-for-document-property: {"title": "HasALongTraitWithParams in lib2 - Rust"} assert-window-property: ({"location": "/lib2/struct.HasALongTraitWithParams.html"}, ENDS_WITH) diff --git a/tests/rustdoc-gui/settings.goml b/tests/rustdoc-gui/settings.goml index e40c637dcf7d..0bb21c28cb52 100644 --- a/tests/rustdoc-gui/settings.goml +++ b/tests/rustdoc-gui/settings.goml @@ -304,7 +304,7 @@ wait-for: "#settings" assert-css: (".setting-radio", {"cursor": "pointer"}) assert-attribute-false: ("#settings", {"class": "popover"}, CONTAINS) -compare-elements-position: (".sub form", "#settings", ("x")) +compare-elements-position: (".sub form", "#settings", ["x"]) // Check that setting-line has the same margin in this mode as in the popover. assert-css: (".setting-line", {"margin": |setting_line_margin|}) diff --git a/tests/rustdoc-gui/sidebar-links-color.goml b/tests/rustdoc-gui/sidebar-links-color.goml index 774fbcac1e2c..0edffc51a816 100644 --- a/tests/rustdoc-gui/sidebar-links-color.goml +++ b/tests/rustdoc-gui/sidebar-links-color.goml @@ -6,12 +6,12 @@ show-text: true define-function: ( "check-colors", - ( + [ theme, struct, struct_hover, struct_hover_background, enum, enum_hover, enum_hover_background, union, union_hover, union_hover_background, trait, trait_hover, trait_hover_background, fn, fn_hover, fn_hover_background, type, type_hover, type_hover_background, keyword, keyword_hover, keyword_hover_background, - ), + ], block { set-local-storage: { "rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false" } reload: diff --git a/tests/rustdoc-gui/sidebar-mobile.goml b/tests/rustdoc-gui/sidebar-mobile.goml index d3a82d9ebe6f..8843de8d7e94 100644 --- a/tests/rustdoc-gui/sidebar-mobile.goml +++ b/tests/rustdoc-gui/sidebar-mobile.goml @@ -57,7 +57,7 @@ show-text: true define-function: ( "check-colors", - (theme, color, background), + [theme, color, background], block { set-local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": |theme|} reload: diff --git a/tests/rustdoc-gui/sidebar-source-code-display.goml b/tests/rustdoc-gui/sidebar-source-code-display.goml index 5149d4991f73..41c8e45f4a6d 100644 --- a/tests/rustdoc-gui/sidebar-source-code-display.goml +++ b/tests/rustdoc-gui/sidebar-source-code-display.goml @@ -30,9 +30,9 @@ show-text: true define-function: ( "check-colors", - ( + [ theme, color, color_hover, background, background_hover, background_toggle, - ), + ], block { set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} reload: diff --git a/tests/rustdoc-gui/sidebar-source-code.goml b/tests/rustdoc-gui/sidebar-source-code.goml index d7de43a2243a..3f7ef643d18e 100644 --- a/tests/rustdoc-gui/sidebar-source-code.goml +++ b/tests/rustdoc-gui/sidebar-source-code.goml @@ -6,7 +6,7 @@ show-text: true // First, check the sidebar colors. define-function: ( "check-colors", - (theme, color, background_color), + [theme, color, background_color], block { set-local-storage: { "rustdoc-theme": |theme|, diff --git a/tests/rustdoc-gui/sidebar.goml b/tests/rustdoc-gui/sidebar.goml index 82b4f2e9429c..115b1eb323c4 100644 --- a/tests/rustdoc-gui/sidebar.goml +++ b/tests/rustdoc-gui/sidebar.goml @@ -6,7 +6,7 @@ show-text: true // First, check the sidebar colors. define-function: ( "check-colors", - (theme, color, background_color), + [theme, color, background_color], block { set-local-storage: { "rustdoc-theme": |theme|, diff --git a/tests/rustdoc-gui/source-code-page.goml b/tests/rustdoc-gui/source-code-page.goml index 8b4d7617e0cd..e29d123d2270 100644 --- a/tests/rustdoc-gui/source-code-page.goml +++ b/tests/rustdoc-gui/source-code-page.goml @@ -21,7 +21,7 @@ assert-attribute-false: (".src-line-numbers > a:nth-child(7)", {"class": "line-h define-function: ( "check-colors", - (theme, color, background_color, highlight_color, highlight_background_color), + [theme, color, background_color, highlight_color, highlight_background_color], block { set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} reload: @@ -61,7 +61,7 @@ call-function: ("check-colors", { }) // This is to ensure that the content is correctly align with the line numbers. -compare-elements-position: ("//*[@id='1']", ".rust > code > span", ("y")) +compare-elements-position: ("//*[@id='1']", ".rust > code > span", ["y"]) // Check the `href` property so that users can treat anchors as links. assert-property: (".src-line-numbers > a:nth-child(1)", { "href": |DOC_PATH| + "/src/test_docs/lib.rs.html#1" @@ -122,7 +122,7 @@ store-property: ( ) define-function: ( "check-sidebar-dir-entry", - (x, y), + [x, y], block { assert: "details:first-of-type.dir-entry[open] > summary::marker" assert-css: ("#src-sidebar > details:first-of-type.dir-entry", {"padding-left": "4px"}) diff --git a/tests/rustdoc-gui/stab-badge.goml b/tests/rustdoc-gui/stab-badge.goml index bb3d2aaa3dca..46df0946c459 100644 --- a/tests/rustdoc-gui/stab-badge.goml +++ b/tests/rustdoc-gui/stab-badge.goml @@ -3,7 +3,7 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" show-text: true define-function: ( "check-badge", - (theme, background, color), + [theme, background, color], block { set-local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": |theme|} go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" diff --git a/tests/rustdoc-gui/target.goml b/tests/rustdoc-gui/target.goml index 26071df8d044..0f8f77093635 100644 --- a/tests/rustdoc-gui/target.goml +++ b/tests/rustdoc-gui/target.goml @@ -7,7 +7,7 @@ assert: "#method\.a_method:target" define-function: ( "check-style", - (theme, background, border), + [theme, background, border], block { set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} reload: diff --git a/tests/rustdoc-gui/toggle-docs.goml b/tests/rustdoc-gui/toggle-docs.goml index 9ea6d9b18f4c..cfd18bd2e14f 100644 --- a/tests/rustdoc-gui/toggle-docs.goml +++ b/tests/rustdoc-gui/toggle-docs.goml @@ -49,7 +49,7 @@ assert-attribute: ("details.toggle", {"open": ""}, ALL) show-text: true define-function: ( "check-color", - (theme, filter), + [theme, filter], block { // Setting the theme. set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} diff --git a/tests/rustdoc-gui/type-declation-overflow.goml b/tests/rustdoc-gui/type-declation-overflow.goml index a97cc98897ae..3709aa102661 100644 --- a/tests/rustdoc-gui/type-declation-overflow.goml +++ b/tests/rustdoc-gui/type-declation-overflow.goml @@ -47,18 +47,18 @@ assert-css: (".mobile-topbar h2", {"overflow-x": "hidden"}) // On desktop, they wrap when too big. set-window-size: (1100, 800) go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html" -compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ("y")) +compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ["y"]) go-to: "file://" + |DOC_PATH| + "/lib2/index.html" -compare-elements-position: (".main-heading h1", ".main-heading .out-of-band", ("y")) +compare-elements-position: (".main-heading h1", ".main-heading .out-of-band", ["y"]) // make sure there is a gap between them compare-elements-position-near-false: (".main-heading h1", ".main-heading .out-of-band", {"x": 550}) // On mobile, they always wrap. set-window-size: (600, 600) go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html" -compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ("y")) +compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ["y"]) go-to: "file://" + |DOC_PATH| + "/lib2/index.html" -compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ("y")) +compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ["y"]) // Now we will check that the scrolling is working. // First on an item with "hidden methods". diff --git a/tests/rustdoc-gui/unsafe-fn.goml b/tests/rustdoc-gui/unsafe-fn.goml index 8d26f15f37f1..83503121a046 100644 --- a/tests/rustdoc-gui/unsafe-fn.goml +++ b/tests/rustdoc-gui/unsafe-fn.goml @@ -13,7 +13,7 @@ define-function: ( "sup-check", // `theme` is the theme being tested. // `color` is the expected color of the `` element. - (theme, color), + [theme, color], block { // Set the theme. set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} @@ -23,6 +23,15 @@ define-function: ( }, ) -call-function: ("sup-check", ("ayu", "#c5c5c5")) -call-function: ("sup-check", ("dark", "#ddd")) -call-function: ("sup-check", ("light", "black")) +call-function: ("sup-check", { + "theme": "ayu", + "color": "#c5c5c5", +}) +call-function: ("sup-check", { + "theme": "dark", + "color": "#ddd", +}) +call-function: ("sup-check", { + "theme": "light", + "color": "black", +}) diff --git a/tests/rustdoc-gui/warning-block.goml b/tests/rustdoc-gui/warning-block.goml index 10e206049f53..a5a47f868db2 100644 --- a/tests/rustdoc-gui/warning-block.goml +++ b/tests/rustdoc-gui/warning-block.goml @@ -5,7 +5,7 @@ show-text: true store-value: (default_y_pos, 5) define-function: ( "check-warning", - (theme, color, border_color), + [theme, color, border_color], block { set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} reload: diff --git a/tests/rustdoc-gui/where-whitespace.goml b/tests/rustdoc-gui/where-whitespace.goml index da104fa40116..823ce9704076 100644 --- a/tests/rustdoc-gui/where-whitespace.goml +++ b/tests/rustdoc-gui/where-whitespace.goml @@ -3,25 +3,25 @@ go-to: "file://" + |DOC_PATH| + "/lib2/trait.Whitespace.html" show-text: true // First, we check in the trait definition if the where clause is "on its own" (not on the same // line than "pub trait Whitespace"). -compare-elements-position-false: (".item-decl code", "div.where", ("y")) +compare-elements-position-false: (".item-decl code", "div.where", ["y"]) // And that the code following it isn't on the same line either. -compare-elements-position-false: (".item-decl .fn", "div.where", ("y")) +compare-elements-position-false: (".item-decl .fn", "div.where", ["y"]) go-to: "file://" + |DOC_PATH| + "/lib2/struct.WhereWhitespace.html" // We make the screen a bit wider to ensure that the trait impl is on one line. set-window-size: (915, 915) -compare-elements-position-false: ("#method\.new .fn", "#method\.new div.where", ("y")) +compare-elements-position-false: ("#method\.new .fn", "#method\.new div.where", ["y"]) // We ensure that both the trait name and the struct name are on the same line in // "impl Whitespace<&K> for WhereWhitespace". compare-elements-position: ( "#trait-implementations-list .impl h3 .trait", "#trait-implementations-list .impl h3 .struct", - ("y"), + ["y"], ) // And we now check that the where condition isn't on the same line. compare-elements-position-false: ( "#trait-implementations-list .impl h3 .trait", "#trait-implementations-list .impl h3 div.where", - ("y"), + ["y"], ) diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/const-in-super-trait-and-item-bound.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/const-in-super-trait-and-item-bound.rs new file mode 100644 index 000000000000..df6de6769d57 --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/const-in-super-trait-and-item-bound.rs @@ -0,0 +1,23 @@ +// We used to ICE here while trying to synthesize auto trait impls. +// issue: 107715 +//@ check-pass + +pub const N: usize = 1; + +pub struct MapType, V> { + _array: K::Array, +} + +pub trait Subtrait: Supertrait<[u8; N]> {} + +pub trait Supertrait { + type Array: AnotherTrait; +} + +pub trait AnotherTrait { + const LENGTH: usize; +} + +pub struct Container { + _x: MapType, +} diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl-normalize.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl-normalize.rs new file mode 100644 index 000000000000..1b67c2bc8756 --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl-normalize.rs @@ -0,0 +1,17 @@ +// We used to ICE here while trying to synthesize auto trait impls. +// issue: 112242 +//@ check-pass +//@ compile-flags: -Znormalize-docs + +pub trait MyTrait<'a> { + type MyItem; +} +pub struct Inner(Q); +pub struct Outer(Inner); + +impl<'a, Q> std::marker::Unpin for Inner +where + Q: MyTrait<'a>, + >::MyItem: Copy, +{ +} diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl.rs new file mode 100644 index 000000000000..31d1b11ff310 --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/lifetime-generic-user-impl.rs @@ -0,0 +1,11 @@ +// We used to ICE here while trying to synthesize auto trait impls. +// issue: 123370 +//@ check-pass + +pub struct Inner<'a, Q>(&'a (), Q); + +pub struct Outer<'a, Q>(Inner<'a, Q>); + +impl<'a, Q: Trait<'a>> std::marker::Unpin for Inner<'static, Q> {} + +pub trait Trait<'a> {} diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.rs new file mode 100644 index 000000000000..f62f8396e991 --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.rs @@ -0,0 +1,18 @@ +// We used to ICE here while trying to synthesize auto trait impls. +// issue: 114657 + +pub trait Foo { + type FooType; +} + +pub trait Bar: Foo>::BarType> { + type BarType; +} + +pub(crate) const B: usize = 5; + +pub trait Tec: Bar {} + +pub struct Structure { //~ ERROR the trait bound `C: Bar<5>` is not satisfied + _field: C::BarType, //~ ERROR the trait bound `C: Bar<5>` is not satisfied +} diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.stderr b/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.stderr new file mode 100644 index 000000000000..d87e769b5053 --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `C: Bar<5>` is not satisfied + --> $DIR/projections-in-super-trait-bound-unsatisfied.rs:16:1 + | +LL | pub struct Structure { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Bar<5>` is not implemented for `C` + | +help: consider further restricting this bound + | +LL | pub struct Structure> { + | ++++++++ + +error[E0277]: the trait bound `C: Bar<5>` is not satisfied + --> $DIR/projections-in-super-trait-bound-unsatisfied.rs:17:13 + | +LL | _field: C::BarType, + | ^^^^^^^^^^ the trait `Bar<5>` is not implemented for `C` + | +help: consider further restricting this bound + | +LL | pub struct Structure> { + | ++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.rs new file mode 100644 index 000000000000..6c62415e06d5 --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.rs @@ -0,0 +1,10 @@ +// We used to ICE here while trying to synthesize auto trait impls. +// issue: 112828 + +struct Outer(Inner); +struct Inner; + +unsafe impl Send for Inner {} +//~^ ERROR the type parameter `Q` is not constrained by the impl trait, self type, or predicates + +trait Trait {} diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.stderr b/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.stderr new file mode 100644 index 000000000000..38d1a537fe45 --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/unconstrained-param-in-impl-ambiguity.stderr @@ -0,0 +1,9 @@ +error[E0207]: the type parameter `Q` is not constrained by the impl trait, self type, or predicates + --> $DIR/unconstrained-param-in-impl-ambiguity.rs:7:13 + | +LL | unsafe impl Send for Inner {} + | ^ unconstrained type parameter + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/rustdoc/search-index-summaries.rs b/tests/rustdoc/search-index-summaries.rs index efd366405bfe..529b42d0ca90 100644 --- a/tests/rustdoc/search-index-summaries.rs +++ b/tests/rustdoc/search-index-summaries.rs @@ -1,6 +1,6 @@ #![crate_name = "foo"] -// @hasraw 'search-index.js' 'Foo short link.' +// @hasraw 'search.desc/foo/foo-desc-0-.js' 'Foo short link.' // @!hasraw - 'www.example.com' // @!hasraw - 'More Foo.' diff --git a/tests/rustdoc/synthetic_auto/bounds.rs b/tests/rustdoc/synthetic_auto/bounds.rs new file mode 100644 index 000000000000..17528d01c8d2 --- /dev/null +++ b/tests/rustdoc/synthetic_auto/bounds.rs @@ -0,0 +1,21 @@ +pub struct Outer(Inner); +pub struct Inner(T); + +// @has bounds/struct.Outer.html +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \ +// "impl Unpin for Outerwhere \ +// T: for<'any> Trait = (), X = ()>," + +impl std::marker::Unpin for Inner +where + T: for<'any> Trait = (), X = ()>, +{} + +pub trait Trait: SuperTrait { + type A; + type B<'a>; +} + +pub trait SuperTrait { + type X; +} diff --git a/tests/rustdoc/synthetic_auto/complex.rs b/tests/rustdoc/synthetic_auto/complex.rs index 4c39f0bf1e07..2722f6d338fe 100644 --- a/tests/rustdoc/synthetic_auto/complex.rs +++ b/tests/rustdoc/synthetic_auto/complex.rs @@ -21,8 +21,8 @@ mod foo { // @has complex/struct.NotOuter.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \ -// "impl<'a, T, K: ?Sized> Send for Outer<'a, T, K>where K: for<'b> Fn((&'b bool, &'a u8)) \ -// -> &'b i8, T: MyTrait<'a>, >::MyItem: Copy, 'a: 'static" +// "impl<'a, T, K> Send for Outer<'a, T, K>where 'a: 'static, T: MyTrait<'a>, \ +// K: for<'b> Fn((&'b bool, &'a u8)) -> &'b i8 + ?Sized, >::MyItem: Copy," pub use foo::{Foo, Inner as NotInner, MyTrait as NotMyTrait, Outer as NotOuter}; diff --git a/tests/rustdoc/synthetic_auto/lifetimes.rs b/tests/rustdoc/synthetic_auto/lifetimes.rs index 71265b3078a0..23e1efdaeef1 100644 --- a/tests/rustdoc/synthetic_auto/lifetimes.rs +++ b/tests/rustdoc/synthetic_auto/lifetimes.rs @@ -10,7 +10,7 @@ where // @has lifetimes/struct.Foo.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \ -// "impl<'c, K> Send for Foo<'c, K>where K: for<'b> Fn(&'b bool) -> &'c u8, 'c: 'static" +// "impl<'c, K> Send for Foo<'c, K>where 'c: 'static, K: for<'b> Fn(&'b bool) -> &'c u8," // // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \ // "impl<'c, K> Sync for Foo<'c, K>where K: Sync" diff --git a/tests/rustdoc/synthetic_auto/no-redundancy.rs b/tests/rustdoc/synthetic_auto/no-redundancy.rs index d30b38dd4dc1..64dab429647a 100644 --- a/tests/rustdoc/synthetic_auto/no-redundancy.rs +++ b/tests/rustdoc/synthetic_auto/no-redundancy.rs @@ -1,6 +1,3 @@ -// FIXME(fmease, #119216): Reenable this test! -//@ ignore-test - pub struct Inner { field: T, } @@ -13,7 +10,7 @@ where // @has no_redundancy/struct.Outer.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \ -// "impl Send for Outerwhere T: Send + Copy" +// "impl Send for Outerwhere T: Copy + Send" pub struct Outer { inner_field: Inner, } diff --git a/tests/rustdoc/synthetic_auto/project.rs b/tests/rustdoc/synthetic_auto/project.rs index 7c9412ae9624..f4ede76e6deb 100644 --- a/tests/rustdoc/synthetic_auto/project.rs +++ b/tests/rustdoc/synthetic_auto/project.rs @@ -24,11 +24,11 @@ where // @has project/struct.Foo.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \ -// "impl<'c, K> Send for Foo<'c, K>where K: MyTrait, 'c: 'static" +// "impl<'c, K> Send for Foo<'c, K>where 'c: 'static, K: MyTrait," // // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \ -// "impl<'c, K> Sync for Foo<'c, K>where K: MyTrait, ::MyItem: OtherTrait, \ -// 'c: 'static," +// "impl<'c, K> Sync for Foo<'c, K>where 'c: 'static, K: MyTrait, \ +// ::MyItem: OtherTrait," pub struct Foo<'c, K: 'c> { inner_field: Inner<'c, K>, } diff --git a/tests/ui/async-await/async-is-unwindsafe.rs b/tests/ui/async-await/async-is-unwindsafe.rs index 53009b6e7410..d0202f72f008 100644 --- a/tests/ui/async-await/async-is-unwindsafe.rs +++ b/tests/ui/async-await/async-is-unwindsafe.rs @@ -11,6 +11,7 @@ fn main() { is_unwindsafe(async { //~^ ERROR the type `&mut Context<'_>` may not be safely transferred across an unwind boundary + //~| ERROR the type `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary use std::ptr::null; use std::task::{Context, RawWaker, RawWakerVTable, Waker}; let waker = unsafe { diff --git a/tests/ui/async-await/async-is-unwindsafe.stderr b/tests/ui/async-await/async-is-unwindsafe.stderr index 5d87fc747682..6bb06df9f39e 100644 --- a/tests/ui/async-await/async-is-unwindsafe.stderr +++ b/tests/ui/async-await/async-is-unwindsafe.stderr @@ -6,19 +6,18 @@ LL | is_unwindsafe(async { | |_____| | || LL | || +LL | || LL | || use std::ptr::null; -LL | || use std::task::{Context, RawWaker, RawWakerVTable, Waker}; ... || LL | || drop(cx_ref); LL | || }); | ||_____-^ `&mut Context<'_>` may not be safely transferred across an unwind boundary | |_____| - | within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}` + | within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}` | - = help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}`, the trait `UnwindSafe` is not implemented for `&mut Context<'_>`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}: UnwindSafe` - = note: `UnwindSafe` is implemented for `&Context<'_>`, but not for `&mut Context<'_>` + = help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`, the trait `UnwindSafe` is not implemented for `&mut Context<'_>`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}: UnwindSafe` note: future does not implement `UnwindSafe` as this value is used across an await - --> $DIR/async-is-unwindsafe.rs:25:18 + --> $DIR/async-is-unwindsafe.rs:26:18 | LL | let cx_ref = &mut cx; | ------ has type `&mut Context<'_>` which does not implement `UnwindSafe` @@ -31,6 +30,38 @@ note: required by a bound in `is_unwindsafe` LL | fn is_unwindsafe(_: impl std::panic::UnwindSafe) {} | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_unwindsafe` -error: aborting due to 1 previous error +error[E0277]: the type `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary + --> $DIR/async-is-unwindsafe.rs:12:5 + | +LL | is_unwindsafe(async { + | _____^_____________- + | |_____| + | || +LL | || +LL | || +LL | || use std::ptr::null; +... || +LL | || drop(cx_ref); +LL | || }); + | ||_____-^ `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary + | |_____| + | within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}` + | + = help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`, the trait `UnwindSafe` is not implemented for `&mut (dyn Any + 'static)`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}: UnwindSafe` +note: future does not implement `UnwindSafe` as this value is used across an await + --> $DIR/async-is-unwindsafe.rs:26:18 + | +LL | let mut cx = Context::from_waker(&waker); + | ------ has type `Context<'_>` which does not implement `UnwindSafe` +... +LL | async {}.await; // this needs an inner await point + | ^^^^^ await occurs here, with `mut cx` maybe used later +note: required by a bound in `is_unwindsafe` + --> $DIR/async-is-unwindsafe.rs:3:26 + | +LL | fn is_unwindsafe(_: impl std::panic::UnwindSafe) {} + | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_unwindsafe` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr b/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr deleted file mode 100644 index 34ec8aadbcf3..000000000000 --- a/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.mir.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0133]: call to unsafe function is unsafe and requires unsafe function or block - --> $DIR/const-extern-fn-requires-unsafe.rs:12:5 - | -LL | foo(); - | ^^^^^ call to unsafe function - | - = note: consult the function's documentation for information on how to avoid undefined behavior - -error[E0133]: call to unsafe function is unsafe and requires unsafe function or block - --> $DIR/const-extern-fn-requires-unsafe.rs:9:17 - | -LL | let a: [u8; foo()]; - | ^^^^^ call to unsafe function - | - = note: consult the function's documentation for information on how to avoid undefined behavior - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr b/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr deleted file mode 100644 index e6b8173eb051..000000000000 --- a/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.thir.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0133]: call to unsafe function `foo` is unsafe and requires unsafe function or block - --> $DIR/const-extern-fn-requires-unsafe.rs:12:5 - | -LL | foo(); - | ^^^^^ call to unsafe function - | - = note: consult the function's documentation for information on how to avoid undefined behavior - -error[E0133]: call to unsafe function `foo` is unsafe and requires unsafe function or block - --> $DIR/const-extern-fn-requires-unsafe.rs:9:17 - | -LL | let a: [u8; foo()]; - | ^^^^^ call to unsafe function - | - = note: consult the function's documentation for information on how to avoid undefined behavior - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/consts/const-int-unchecked.rs b/tests/ui/consts/const-int-unchecked.rs index 3fe96c2de23e..8de28aa2bb17 100644 --- a/tests/ui/consts/const-int-unchecked.rs +++ b/tests/ui/consts/const-int-unchecked.rs @@ -27,7 +27,7 @@ const SHL_U128: u128 = unsafe { intrinsics::unchecked_shl(5_u128, 128) }; const SHL_I8: i8 = unsafe { intrinsics::unchecked_shl(5_i8, 8) }; //~^ ERROR evaluation of constant value failed -const SHL_I16: i16 = unsafe { intrinsics::unchecked_shl(5_16, 16) }; +const SHL_I16: i16 = unsafe { intrinsics::unchecked_shl(5_i16, 16) }; //~^ ERROR evaluation of constant value failed const SHL_I32: i32 = unsafe { intrinsics::unchecked_shl(5_i32, 32) }; //~^ ERROR evaluation of constant value failed @@ -40,7 +40,7 @@ const SHL_I128: i128 = unsafe { intrinsics::unchecked_shl(5_i128, 128) }; const SHL_I8_NEG: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -1) }; //~^ ERROR evaluation of constant value failed -const SHL_I16_NEG: i16 = unsafe { intrinsics::unchecked_shl(5_16, -1) }; +const SHL_I16_NEG: i16 = unsafe { intrinsics::unchecked_shl(5_i16, -1) }; //~^ ERROR evaluation of constant value failed const SHL_I32_NEG: i32 = unsafe { intrinsics::unchecked_shl(5_i32, -1) }; //~^ ERROR evaluation of constant value failed @@ -54,7 +54,7 @@ const SHL_I128_NEG: i128 = unsafe { intrinsics::unchecked_shl(5_i128, -1) }; const SHL_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -6) }; //~^ ERROR evaluation of constant value failed -const SHL_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shl(5_16, -13) }; +const SHL_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shl(5_i16, -13) }; //~^ ERROR evaluation of constant value failed const SHL_I32_NEG_RANDOM: i32 = unsafe { intrinsics::unchecked_shl(5_i32, -25) }; //~^ ERROR evaluation of constant value failed @@ -82,7 +82,7 @@ const SHR_U128: u128 = unsafe { intrinsics::unchecked_shr(5_u128, 128) }; const SHR_I8: i8 = unsafe { intrinsics::unchecked_shr(5_i8, 8) }; //~^ ERROR evaluation of constant value failed -const SHR_I16: i16 = unsafe { intrinsics::unchecked_shr(5_16, 16) }; +const SHR_I16: i16 = unsafe { intrinsics::unchecked_shr(5_i16, 16) }; //~^ ERROR evaluation of constant value failed const SHR_I32: i32 = unsafe { intrinsics::unchecked_shr(5_i32, 32) }; //~^ ERROR evaluation of constant value failed @@ -95,7 +95,7 @@ const SHR_I128: i128 = unsafe { intrinsics::unchecked_shr(5_i128, 128) }; const SHR_I8_NEG: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -1) }; //~^ ERROR evaluation of constant value failed -const SHR_I16_NEG: i16 = unsafe { intrinsics::unchecked_shr(5_16, -1) }; +const SHR_I16_NEG: i16 = unsafe { intrinsics::unchecked_shr(5_i16, -1) }; //~^ ERROR evaluation of constant value failed const SHR_I32_NEG: i32 = unsafe { intrinsics::unchecked_shr(5_i32, -1) }; //~^ ERROR evaluation of constant value failed @@ -109,7 +109,7 @@ const SHR_I128_NEG: i128 = unsafe { intrinsics::unchecked_shr(5_i128, -1) }; const SHR_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -6) }; //~^ ERROR evaluation of constant value failed -const SHR_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shr(5_16, -13) }; +const SHR_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shr(5_i16, -13) }; //~^ ERROR evaluation of constant value failed const SHR_I32_NEG_RANDOM: i32 = unsafe { intrinsics::unchecked_shr(5_i32, -25) }; //~^ ERROR evaluation of constant value failed diff --git a/tests/ui/consts/const-int-unchecked.stderr b/tests/ui/consts/const-int-unchecked.stderr index ad14c8f68f8c..84b222972a13 100644 --- a/tests/ui/consts/const-int-unchecked.stderr +++ b/tests/ui/consts/const-int-unchecked.stderr @@ -37,8 +37,8 @@ LL | const SHL_I8: i8 = unsafe { intrinsics::unchecked_shl(5_i8, 8) }; error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:30:31 | -LL | const SHL_I16: i16 = unsafe { intrinsics::unchecked_shl(5_16, 16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 16 in `unchecked_shl` +LL | const SHL_I16: i16 = unsafe { intrinsics::unchecked_shl(5_i16, 16) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 16 in `unchecked_shl` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:32:31 @@ -67,8 +67,8 @@ LL | const SHL_I8_NEG: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -1) }; error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:43:35 | -LL | const SHL_I16_NEG: i16 = unsafe { intrinsics::unchecked_shl(5_16, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shl` +LL | const SHL_I16_NEG: i16 = unsafe { intrinsics::unchecked_shl(5_i16, -1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shl` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:45:35 @@ -97,8 +97,8 @@ LL | const SHL_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -6) error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:57:42 | -LL | const SHL_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shl(5_16, -13) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -13 in `unchecked_shl` +LL | const SHL_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shl(5_i16, -13) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -13 in `unchecked_shl` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:59:42 @@ -157,8 +157,8 @@ LL | const SHR_I8: i8 = unsafe { intrinsics::unchecked_shr(5_i8, 8) }; error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:85:31 | -LL | const SHR_I16: i16 = unsafe { intrinsics::unchecked_shr(5_16, 16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 16 in `unchecked_shr` +LL | const SHR_I16: i16 = unsafe { intrinsics::unchecked_shr(5_i16, 16) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by 16 in `unchecked_shr` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:87:31 @@ -187,8 +187,8 @@ LL | const SHR_I8_NEG: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -1) }; error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:98:35 | -LL | const SHR_I16_NEG: i16 = unsafe { intrinsics::unchecked_shr(5_16, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shr` +LL | const SHR_I16_NEG: i16 = unsafe { intrinsics::unchecked_shr(5_i16, -1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -1 in `unchecked_shr` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:100:35 @@ -217,8 +217,8 @@ LL | const SHR_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -6) error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:112:42 | -LL | const SHR_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shr(5_16, -13) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -13 in `unchecked_shr` +LL | const SHR_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shr(5_i16, -13) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing shift by -13 in `unchecked_shr` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:114:42 diff --git a/tests/ui/derives/auxiliary/rustc-serialize.rs b/tests/ui/derives/auxiliary/rustc-serialize.rs new file mode 100644 index 000000000000..24177af931c4 --- /dev/null +++ b/tests/ui/derives/auxiliary/rustc-serialize.rs @@ -0,0 +1,16 @@ +#![crate_type = "lib"] + +pub trait Decoder { + type Error; + + fn read_enum(&mut self, name: &str, f: F) -> Result + where F: FnOnce(&mut Self) -> Result; + fn read_enum_variant(&mut self, names: &[&str], f: F) + -> Result + where F: FnMut(&mut Self, usize) -> Result; + +} + +pub trait Decodable: Sized { + fn decode(d: &mut D) -> Result; +} diff --git a/tests/ui/derives/rustc-decodable-issue-123156.rs b/tests/ui/derives/rustc-decodable-issue-123156.rs new file mode 100644 index 000000000000..1983837ed8d4 --- /dev/null +++ b/tests/ui/derives/rustc-decodable-issue-123156.rs @@ -0,0 +1,11 @@ +//@ check-pass +//@ edition:2021 +//@ aux-build:rustc-serialize.rs + +#![crate_type = "lib"] +#![allow(deprecated, soft_unstable)] + +extern crate rustc_serialize; + +#[derive(RustcDecodable)] +pub enum Foo {} diff --git a/tests/ui/derives/rustc-decodable-issue-123156.stderr b/tests/ui/derives/rustc-decodable-issue-123156.stderr new file mode 100644 index 000000000000..ee7b33d59bb9 --- /dev/null +++ b/tests/ui/derives/rustc-decodable-issue-123156.stderr @@ -0,0 +1,10 @@ +Future incompatibility report: Future breakage diagnostic: +warning: use of unstable library feature 'rustc_encodable_decodable': derive macro for `rustc-serialize`; should not be used in new code + --> $DIR/rustc-decodable-issue-123156.rs:10:10 + | +LL | #[derive(RustcDecodable)] + | ^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #64266 + diff --git a/tests/ui/higher-ranked/builtin-closure-like-bounds.rs b/tests/ui/higher-ranked/builtin-closure-like-bounds.rs new file mode 100644 index 000000000000..dee290cc4396 --- /dev/null +++ b/tests/ui/higher-ranked/builtin-closure-like-bounds.rs @@ -0,0 +1,58 @@ +//@ edition:2024 +//@ compile-flags: -Zunstable-options +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Makes sure that we support closure/coroutine goals where the signature of +// the item references higher-ranked lifetimes from the *predicate* binder, +// not its own internal signature binder. +// +// This was fixed in . + +#![feature(unboxed_closures, gen_blocks)] + +trait Dispatch { + fn dispatch(self); +} + +struct Fut(T); +impl Fn<(&'a (),)>> Dispatch for Fut +where + for<'a> >::Output: Future, +{ + fn dispatch(self) { + (self.0)(&()); + } +} + +struct Gen(T); +impl Fn<(&'a (),)>> Dispatch for Gen +where + for<'a> >::Output: Iterator, +{ + fn dispatch(self) { + (self.0)(&()); + } +} + +struct Closure(T); +impl Fn<(&'a (),)>> Dispatch for Closure +where + for<'a> >::Output: Fn<(&'a (),)>, +{ + fn dispatch(self) { + (self.0)(&())(&()); + } +} + +fn main() { + async fn foo(_: &()) {} + Fut(foo).dispatch(); + + gen fn bar(_: &()) {} + Gen(bar).dispatch(); + + fn uwu<'a>(x: &'a ()) -> impl Fn(&'a ()) { |_| {} } + Closure(uwu).dispatch(); +} diff --git a/tests/ui/higher-ranked/closure-bound-codegen-ice.rs b/tests/ui/higher-ranked/closure-bound-codegen-ice.rs new file mode 100644 index 000000000000..4d7ae12d7a74 --- /dev/null +++ b/tests/ui/higher-ranked/closure-bound-codegen-ice.rs @@ -0,0 +1,33 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ build-pass + +// Regression test for incomplete handling of Fn-trait goals, +// fixed in #122267. + +trait Trait { + type Assoc<'a>: FnOnce(&'a ()); +} + +impl Trait for () { + type Assoc<'a> = fn(&'a ()); +} + +trait Indir { + fn break_me() {} +} + +impl Indir for F +where + for<'a> F::Assoc<'a>: FnOnce(&'a ()), +{ + fn break_me() {} +} + +fn foo() { + F::break_me() +} + +fn main() { + foo::<()>(); +} diff --git a/tests/ui/higher-ranked/trait-bounds/fn-ptr.classic.stderr b/tests/ui/higher-ranked/trait-bounds/fn-ptr.classic.stderr deleted file mode 100644 index b322ea41c436..000000000000 --- a/tests/ui/higher-ranked/trait-bounds/fn-ptr.classic.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0277]: expected a `Fn(&'w ())` closure, found `fn(&'w ())` - --> $DIR/fn-ptr.rs:12:5 - | -LL | ice(); - | ^^^^^ expected an `Fn(&'w ())` closure, found `fn(&'w ())` - | - = help: the trait `for<'w> Fn<(&'w (),)>` is not implemented for `fn(&'w ())` -note: required by a bound in `ice` - --> $DIR/fn-ptr.rs:7:25 - | -LL | fn ice() - | --- required by a bound in this function -LL | where -LL | for<'w> fn(&'w ()): Fn(&'w ()), - | ^^^^^^^^^^ required by this bound in `ice` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/higher-ranked/trait-bounds/fn-ptr.current.stderr b/tests/ui/higher-ranked/trait-bounds/fn-ptr.current.stderr deleted file mode 100644 index f3583cd218b8..000000000000 --- a/tests/ui/higher-ranked/trait-bounds/fn-ptr.current.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0277]: expected a `Fn(&'w ())` closure, found `fn(&'w ())` - --> $DIR/fn-ptr.rs:13:5 - | -LL | ice(); - | ^^^^^ expected an `Fn(&'w ())` closure, found `fn(&'w ())` - | - = help: the trait `for<'w> Fn<(&'w (),)>` is not implemented for `fn(&'w ())` -note: required by a bound in `ice` - --> $DIR/fn-ptr.rs:8:25 - | -LL | fn ice() - | --- required by a bound in this function -LL | where -LL | for<'w> fn(&'w ()): Fn(&'w ()), - | ^^^^^^^^^^ required by this bound in `ice` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/higher-ranked/trait-bounds/fn-ptr.rs b/tests/ui/higher-ranked/trait-bounds/fn-ptr.rs index 9298c10c3417..7a4c15f4d4b1 100644 --- a/tests/ui/higher-ranked/trait-bounds/fn-ptr.rs +++ b/tests/ui/higher-ranked/trait-bounds/fn-ptr.rs @@ -1,7 +1,7 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] check-pass +//@ check-pass fn ice() where @@ -11,5 +11,4 @@ where fn main() { ice(); - //[current]~^ ERROR expected a `Fn(&'w ())` closure, found `fn(&'w ())` } diff --git a/tests/ui/higher-ranked/trait-bounds/future.classic.stderr b/tests/ui/higher-ranked/trait-bounds/future.classic.stderr deleted file mode 100644 index ef31b7266c7e..000000000000 --- a/tests/ui/higher-ranked/trait-bounds/future.classic.stderr +++ /dev/null @@ -1,6 +0,0 @@ -error: the compiler unexpectedly panicked. this is a bug. - -query stack during panic: -#0 [evaluate_obligation] evaluating trait selection obligation `for<'a> {async fn body@$DIR/future.rs:32:35: 34:2}: core::future::future::Future` -#1 [codegen_select_candidate] computing candidate for `` -end of query stack diff --git a/tests/ui/higher-ranked/trait-bounds/future.current.stderr b/tests/ui/higher-ranked/trait-bounds/future.current.stderr deleted file mode 100644 index 673bc48a424e..000000000000 --- a/tests/ui/higher-ranked/trait-bounds/future.current.stderr +++ /dev/null @@ -1,6 +0,0 @@ -error: the compiler unexpectedly panicked. this is a bug. - -query stack during panic: -#0 [evaluate_obligation] evaluating trait selection obligation `for<'a> {async fn body of strlen()}: core::future::future::Future` -#1 [codegen_select_candidate] computing candidate for `` -end of query stack diff --git a/tests/ui/higher-ranked/trait-bounds/future.rs b/tests/ui/higher-ranked/trait-bounds/future.rs index 4b52f04dbe05..7105015b6901 100644 --- a/tests/ui/higher-ranked/trait-bounds/future.rs +++ b/tests/ui/higher-ranked/trait-bounds/future.rs @@ -3,14 +3,7 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] check-pass -//@[current] known-bug: #112347 -//@[current] build-fail -//@[current] failure-status: 101 -//@[current] normalize-stderr-test "note: .*\n\n" -> "" -//@[current] normalize-stderr-test "thread 'rustc' panicked.*\n.*\n" -> "" -//@[current] normalize-stderr-test "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " -//@[current] rustc-env:RUST_BACKTRACE=0 +//@ check-pass #![feature(unboxed_closures)] diff --git a/tests/ui/lifetimes/issue-105675.rs b/tests/ui/lifetimes/issue-105675.rs index 2e2eaca0d335..0472537e7f34 100644 --- a/tests/ui/lifetimes/issue-105675.rs +++ b/tests/ui/lifetimes/issue-105675.rs @@ -4,7 +4,7 @@ fn main() { let f = | _ , y: &u32 , z | (); thing(f); //~^ ERROR implementation of `FnOnce` is not general enough - //~^^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough let f = | x, y: _ , z: u32 | (); thing(f); //~^ ERROR implementation of `FnOnce` is not general enough diff --git a/tests/ui/lifetimes/lifetime-errors/issue_74400.rs b/tests/ui/lifetimes/lifetime-errors/issue_74400.rs index b02e38bec3b3..72345fa294a4 100644 --- a/tests/ui/lifetimes/lifetime-errors/issue_74400.rs +++ b/tests/ui/lifetimes/lifetime-errors/issue_74400.rs @@ -1,5 +1,5 @@ //! Regression test for #74400: Type mismatch in function arguments E0631, E0271 are falsely -//! recognized as E0308 mismatched types. +//! recognized as "implementation of `FnOnce` is not general enough". use std::convert::identity; @@ -13,6 +13,6 @@ fn g(data: &[T]) { //~^ ERROR the parameter type //~| ERROR the parameter type //~| ERROR the parameter type - //~| ERROR implementation of `FnOnce` is not general + //~| ERROR implementation of `FnOnce` is not general enough //~| ERROR implementation of `Fn` is not general enough } diff --git a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr index 0b1289742758..24f2500abf82 100644 --- a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr +++ b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr @@ -4,7 +4,7 @@ error: strict provenance disallows casting integer `usize` to pointer `*const u8 LL | let dangling = 16_usize as *const u8; | ^^^^^^^^^^^^^^^^^^^^^ | - = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::from_exposed_addr()` instead + = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead note: the lint level is defined here --> $DIR/lint-strict-provenance-fuzzy-casts.rs:2:9 | diff --git a/tests/ui/marker_trait_attr/unsound-overlap.rs b/tests/ui/marker_trait_attr/unsound-overlap.rs index 2e5101b822c0..2ce26b610f65 100644 --- a/tests/ui/marker_trait_attr/unsound-overlap.rs +++ b/tests/ui/marker_trait_attr/unsound-overlap.rs @@ -8,6 +8,7 @@ trait B {} impl B for T {} impl A for T {} impl A for &str {} +//~^ ERROR type annotations needed: cannot satisfy `&str: A` impl A for (T,) {} trait TraitWithAssoc { type Assoc; diff --git a/tests/ui/marker_trait_attr/unsound-overlap.stderr b/tests/ui/marker_trait_attr/unsound-overlap.stderr index 5e58f5227ed6..13498fa4b220 100644 --- a/tests/ui/marker_trait_attr/unsound-overlap.stderr +++ b/tests/ui/marker_trait_attr/unsound-overlap.stderr @@ -1,5 +1,19 @@ +error[E0283]: type annotations needed: cannot satisfy `&str: A` + --> $DIR/unsound-overlap.rs:10:12 + | +LL | impl A for &str {} + | ^^^^ + | +note: multiple `impl`s satisfying `&str: A` found + --> $DIR/unsound-overlap.rs:9:1 + | +LL | impl A for T {} + | ^^^^^^^^^^^^^^^^^^ +LL | impl A for &str {} + | ^^^^^^^^^^^^^^^ + error[E0119]: conflicting implementations of trait `TraitWithAssoc` for type `((&str,),)` - --> $DIR/unsound-overlap.rs:20:1 + --> $DIR/unsound-overlap.rs:21:1 | LL | impl TraitWithAssoc for T { | ------------------------------- first implementation here @@ -7,6 +21,7 @@ LL | impl TraitWithAssoc for T { LL | impl TraitWithAssoc for ((&str,),) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `((&str,),)` -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0119`. +Some errors have detailed explanations: E0119, E0283. +For more information about an error, try `rustc --explain E0119`. diff --git a/tests/ui/methods/probe-overflow-due-to-sized-predicate-ordering.rs b/tests/ui/methods/probe-overflow-due-to-sized-predicate-ordering.rs new file mode 100644 index 000000000000..08e15117c4b2 --- /dev/null +++ b/tests/ui/methods/probe-overflow-due-to-sized-predicate-ordering.rs @@ -0,0 +1,30 @@ +//@ check-pass +// Regression test due to #123279 + +pub trait Job: AsJob { + fn run_once(&self); +} + +impl Job for F { + fn run_once(&self) { + todo!() + } +} + +pub trait AsJob {} + +// Ensure that `T: Sized + Job` by reordering the explicit `Sized` to where +// the implicit sized pred would go. +impl AsJob for T {} + +pub struct LoopingJobService { + job: Box, +} + +impl Job for LoopingJobService { + fn run_once(&self) { + self.job.run_once() + } +} + +fn main() {} diff --git a/tests/ui/repr/repr-align.rs b/tests/ui/repr/repr-align.rs index 58ecf9a51832..33aa727d4bd0 100644 --- a/tests/ui/repr/repr-align.rs +++ b/tests/ui/repr/repr-align.rs @@ -15,6 +15,10 @@ struct S2(i32); #[repr(align(536870912))] // ok: this is the largest accepted alignment struct S3(i32); +#[repr(align(0))] //~ ERROR: invalid `repr(align)` attribute: not a power of two + //~| ERROR: invalid `repr(align)` attribute: not a power of two +struct S4(i32); + #[repr(align(16.0))] //~ ERROR: invalid `repr(align)` attribute: not an unsuffixed integer //~| ERROR: invalid `repr(align)` attribute: not an unsuffixed integer enum E0 { A, B } @@ -30,4 +34,8 @@ enum E2 { A, B } #[repr(align(536870912))] // ok: this is the largest accepted alignment enum E3 { A, B } +#[repr(align(0))] //~ ERROR: invalid `repr(align)` attribute: not a power of two + //~| ERROR: invalid `repr(align)` attribute: not a power of two +enum E4 { A, B } + fn main() {} diff --git a/tests/ui/repr/repr-align.stderr b/tests/ui/repr/repr-align.stderr index bb0e17ba3955..660247840c48 100644 --- a/tests/ui/repr/repr-align.stderr +++ b/tests/ui/repr/repr-align.stderr @@ -16,24 +16,36 @@ error[E0589]: invalid `repr(align)` attribute: larger than 2^29 LL | #[repr(align(4294967296))] | ^^^^^^^^^^ -error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer +error[E0589]: invalid `repr(align)` attribute: not a power of two --> $DIR/repr-align.rs:18:14 | +LL | #[repr(align(0))] + | ^ + +error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer + --> $DIR/repr-align.rs:22:14 + | LL | #[repr(align(16.0))] | ^^^^ error[E0589]: invalid `repr(align)` attribute: not a power of two - --> $DIR/repr-align.rs:22:14 + --> $DIR/repr-align.rs:26:14 | LL | #[repr(align(15))] | ^^ error[E0589]: invalid `repr(align)` attribute: larger than 2^29 - --> $DIR/repr-align.rs:26:14 + --> $DIR/repr-align.rs:30:14 | LL | #[repr(align(4294967296))] | ^^^^^^^^^^ +error[E0589]: invalid `repr(align)` attribute: not a power of two + --> $DIR/repr-align.rs:37:14 + | +LL | #[repr(align(0))] + | ^ + error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer --> $DIR/repr-align.rs:3:14 | @@ -58,16 +70,24 @@ LL | #[repr(align(4294967296))] | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer +error[E0589]: invalid `repr(align)` attribute: not a power of two --> $DIR/repr-align.rs:18:14 | +LL | #[repr(align(0))] + | ^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer + --> $DIR/repr-align.rs:22:14 + | LL | #[repr(align(16.0))] | ^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0589]: invalid `repr(align)` attribute: not a power of two - --> $DIR/repr-align.rs:22:14 + --> $DIR/repr-align.rs:26:14 | LL | #[repr(align(15))] | ^^ @@ -75,13 +95,21 @@ LL | #[repr(align(15))] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0589]: invalid `repr(align)` attribute: larger than 2^29 - --> $DIR/repr-align.rs:26:14 + --> $DIR/repr-align.rs:30:14 | LL | #[repr(align(4294967296))] | ^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 12 previous errors +error[E0589]: invalid `repr(align)` attribute: not a power of two + --> $DIR/repr-align.rs:37:14 + | +LL | #[repr(align(0))] + | ^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 16 previous errors For more information about this error, try `rustc --explain E0589`. diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr deleted file mode 100644 index 7bbd4e158982..000000000000 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/fn-ptr.rs:11:21 - | -LL | #[target_feature(enable = "sse2")] - | ---------------------------------- `#[target_feature]` added here -... -LL | let foo: fn() = foo; - | ---- ^^^ cannot coerce functions with `#[target_feature]` to safe function pointers - | | - | expected due to this - | - = note: expected fn pointer `fn()` - found fn item `fn() {foo}` - = note: fn items are distinct from fn pointers - = note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers -help: consider casting to a fn pointer - | -LL | let foo: fn() = foo as fn(); - | ~~~~~~~~~~~ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr deleted file mode 100644 index 7bbd4e158982..000000000000 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/fn-ptr.rs:11:21 - | -LL | #[target_feature(enable = "sse2")] - | ---------------------------------- `#[target_feature]` added here -... -LL | let foo: fn() = foo; - | ---- ^^^ cannot coerce functions with `#[target_feature]` to safe function pointers - | | - | expected due to this - | - = note: expected fn pointer `fn()` - found fn item `fn() {foo}` - = note: fn items are distinct from fn pointers - = note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers -help: consider casting to a fn pointer - | -LL | let foo: fn() = foo as fn(); - | ~~~~~~~~~~~ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr deleted file mode 100644 index cabc475fa61c..000000000000 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr +++ /dev/null @@ -1,115 +0,0 @@ -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:28:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:31:5 - | -LL | avx_bmi2(); - | ^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:34:5 - | -LL | Quux.avx_bmi2(); - | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:41:5 - | -LL | avx_bmi2(); - | ^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:44:5 - | -LL | Quux.avx_bmi2(); - | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:51:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:54:5 - | -LL | avx_bmi2(); - | ^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: bmi2 - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:57:5 - | -LL | Quux.avx_bmi2(); - | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: bmi2 - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:65:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:70:15 - | -LL | const _: () = sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:74:15 - | -LL | const _: () = sse2_and_fxsr(); - | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: sse2 and fxsr - = note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` - -error: call to function with `#[target_feature]` is unsafe and requires unsafe block (error E0133) - --> $DIR/safe-calls.rs:82:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` -note: an unsafe function restricts its caller, but its body is safe by default - --> $DIR/safe-calls.rs:81:1 - | -LL | unsafe fn needs_unsafe_block() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: the lint level is defined here - --> $DIR/safe-calls.rs:78:8 - | -LL | #[deny(unsafe_op_in_unsafe_fn)] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 12 previous errors - -For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr deleted file mode 100644 index 13b58fde862d..000000000000 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.thir.stderr +++ /dev/null @@ -1,115 +0,0 @@ -error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:28:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:31:5 - | -LL | avx_bmi2(); - | ^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:34:5 - | -LL | Quux.avx_bmi2(); - | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:41:5 - | -LL | avx_bmi2(); - | ^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:44:5 - | -LL | Quux.avx_bmi2(); - | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:51:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:54:5 - | -LL | avx_bmi2(); - | ^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: bmi2 - -error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:57:5 - | -LL | Quux.avx_bmi2(); - | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: bmi2 - -error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:65:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:70:15 - | -LL | const _: () = sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:74:15 - | -LL | const _: () = sse2_and_fxsr(); - | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target features: sse2 and fxsr - = note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` - -error: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe block (error E0133) - --> $DIR/safe-calls.rs:82:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` -note: an unsafe function restricts its caller, but its body is safe by default - --> $DIR/safe-calls.rs:81:1 - | -LL | unsafe fn needs_unsafe_block() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: the lint level is defined here - --> $DIR/safe-calls.rs:78:8 - | -LL | #[deny(unsafe_op_in_unsafe_fn)] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 12 previous errors - -For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/sanitizer/cfi-coroutine.rs b/tests/ui/sanitizer/cfi-coroutine.rs index 24e59cf5b4d0..5c6a489a7e89 100644 --- a/tests/ui/sanitizer/cfi-coroutine.rs +++ b/tests/ui/sanitizer/cfi-coroutine.rs @@ -3,6 +3,7 @@ //@ revisions: cfi kcfi // FIXME(#122848) Remove only-linux once OSX CFI binaries work //@ only-linux +//@ edition: 2024 //@ [cfi] needs-sanitizer-cfi //@ [kcfi] needs-sanitizer-kcfi //@ compile-flags: -C target-feature=-crt-static @@ -10,16 +11,22 @@ //@ [cfi] compile-flags: -Z sanitizer=cfi //@ [kcfi] compile-flags: -Z sanitizer=kcfi //@ [kcfi] compile-flags: -C panic=abort -Z panic-abort-tests -C prefer-dynamic=off -//@ compile-flags: --test +//@ compile-flags: --test -Z unstable-options //@ run-pass #![feature(coroutines)] #![feature(coroutine_trait)] +#![feature(noop_waker)] +#![feature(gen_blocks)] +#![feature(async_iterator)] use std::ops::{Coroutine, CoroutineState}; use std::pin::{pin, Pin}; +use std::task::{Context, Poll, Waker}; +use std::async_iter::AsyncIterator; -fn main() { +#[test] +fn general_coroutine() { let mut coro = |x: i32| { yield x; "done" @@ -28,3 +35,33 @@ fn main() { assert_eq!(abstract_coro.as_mut().resume(2), CoroutineState::Yielded(2)); assert_eq!(abstract_coro.as_mut().resume(0), CoroutineState::Complete("done")); } + +async fn async_fn() {} + +#[test] +fn async_coroutine() { + let f: fn() -> Pin>> = || Box::pin(async_fn()); + let _ = async { f().await; }; + assert_eq!(f().as_mut().poll(&mut Context::from_waker(Waker::noop())), Poll::Ready(())); +} + +async gen fn async_gen_fn() -> u8 { + yield 5; +} + +#[test] +fn async_gen_coroutine() { + let f: fn() -> Pin>> = || Box::pin(async_gen_fn()); + assert_eq!(f().as_mut().poll_next(&mut Context::from_waker(Waker::noop())), + Poll::Ready(Some(5))); +} + +gen fn gen_fn() -> u8 { + yield 6; +} + +#[test] +fn gen_coroutine() { + let f: fn() -> Box> = || Box::new(gen_fn()); + assert_eq!(f().next(), Some(6)); +} diff --git a/tests/ui/simd/intrinsic/ptr-cast.rs b/tests/ui/simd/intrinsic/ptr-cast.rs index 83d86baf334a..628203462414 100644 --- a/tests/ui/simd/intrinsic/ptr-cast.rs +++ b/tests/ui/simd/intrinsic/ptr-cast.rs @@ -5,7 +5,7 @@ extern "rust-intrinsic" { fn simd_cast_ptr(x: T) -> U; fn simd_expose_addr(x: T) -> U; - fn simd_from_exposed_addr(x: T) -> U; + fn simd_with_exposed_provenance(x: T) -> U; } #[derive(Copy, Clone)] @@ -24,10 +24,10 @@ fn main() { let exposed_addr: V = simd_expose_addr(const_ptrs); - let from_exposed_addr: V<*mut i8> = simd_from_exposed_addr(exposed_addr); + let with_exposed_provenance: V<*mut i8> = simd_with_exposed_provenance(exposed_addr); assert!(const_ptrs.0 == [ptr as *const u8, core::ptr::null()]); assert!(exposed_addr.0 == [ptr as usize, 0]); - assert!(from_exposed_addr.0 == ptrs.0); + assert!(with_exposed_provenance.0 == ptrs.0); } } diff --git a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs index d08ca644a1c8..5c96c653df52 100644 --- a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs +++ b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs @@ -2,6 +2,6 @@ fn main() { let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes; //~^ ERROR expected a pattern, found an expression //~| ERROR cannot find type `T` in this scope -//~| ERROR type and const arguments are not allowed on builtin type `str` +//~| ERROR const and type arguments are not allowed on builtin type `str` //~| ERROR expected unit struct, unit variant or constant, found associated function `str<, T>::as_bytes` } diff --git a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr index 19d4ac713ce3..d62c019a1e19 100644 --- a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr +++ b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr @@ -10,11 +10,11 @@ error[E0412]: cannot find type `T` in this scope LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes; | ^ not found in this scope -error[E0109]: type and const arguments are not allowed on builtin type `str` +error[E0109]: const and type arguments are not allowed on builtin type `str` --> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:15 | LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes; - | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ type and const arguments not allowed + | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ const and type arguments not allowed | | | not allowed on builtin type `str` | diff --git a/tests/ui/specialization/issue-39448.rs b/tests/ui/specialization/issue-39448.rs index a15c4bd6b7ff..1c8843d983a4 100644 --- a/tests/ui/specialization/issue-39448.rs +++ b/tests/ui/specialization/issue-39448.rs @@ -22,6 +22,7 @@ trait FromA { } impl> FromA for U { + //~^ ERROR cycle detected when computing whether impls specialize one another default fn from(x: T) -> Self { ToA::to(x) } @@ -42,7 +43,7 @@ where #[allow(dead_code)] fn foo(x: T, y: U) -> U { - x.foo(y.to()).to() //~ ERROR overflow evaluating the requirement + x.foo(y.to()).to() } fn main() { diff --git a/tests/ui/specialization/issue-39448.stderr b/tests/ui/specialization/issue-39448.stderr index dc5db4f4285c..e2c5f8c48468 100644 --- a/tests/ui/specialization/issue-39448.stderr +++ b/tests/ui/specialization/issue-39448.stderr @@ -8,28 +8,21 @@ LL | #![feature(specialization)] = help: consider using `min_specialization` instead, which is more stable and complete = note: `#[warn(incomplete_features)]` on by default -error[E0275]: overflow evaluating the requirement `T: FromA` - --> $DIR/issue-39448.rs:45:13 - | -LL | x.foo(y.to()).to() - | ^^ - | -note: required for `T` to implement `FromA` - --> $DIR/issue-39448.rs:24:29 +error[E0391]: cycle detected when computing whether impls specialize one another + --> $DIR/issue-39448.rs:24:1 | LL | impl> FromA for U { - | -------- ^^^^^^^^ ^ - | | - | unsatisfied trait bound introduced here -note: required for `U` to implement `ToA` - --> $DIR/issue-39448.rs:34:12 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | impl ToA for T - | ^^^^^^ ^ -LL | where -LL | U: FromA, - | -------- unsatisfied trait bound introduced here + = note: ...which requires evaluating trait selection obligation `u16: FromA`... + = note: ...which again requires computing whether impls specialize one another, completing the cycle +note: cycle used when building specialization graph of trait `FromA` + --> $DIR/issue-39448.rs:20:1 + | +LL | trait FromA { + | ^^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error; 1 warning emitted -For more information about this error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/specialization/issue-39618.rs b/tests/ui/specialization/issue-39618.rs index 5b9b012e6653..14a6fcf572ea 100644 --- a/tests/ui/specialization/issue-39618.rs +++ b/tests/ui/specialization/issue-39618.rs @@ -2,8 +2,6 @@ // FIXME(JohnTitor): Centril pointed out this looks suspicions, we should revisit here. // More context: https://github.com/rust-lang/rust/pull/69192#discussion_r379846796 -//@ check-pass - #![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Foo { @@ -19,6 +17,7 @@ impl Bar for T where T: Foo { } impl Foo for T where T: Bar { + //~^ ERROR cycle detected when computing whether impls specialize one another fn foo(&self) {} } diff --git a/tests/ui/specialization/issue-39618.stderr b/tests/ui/specialization/issue-39618.stderr index 19de60c7c178..756162ce92c3 100644 --- a/tests/ui/specialization/issue-39618.stderr +++ b/tests/ui/specialization/issue-39618.stderr @@ -1,5 +1,5 @@ warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-39618.rs:7:12 + --> $DIR/issue-39618.rs:5:12 | LL | #![feature(specialization)] | ^^^^^^^^^^^^^^ @@ -8,5 +8,21 @@ LL | #![feature(specialization)] = help: consider using `min_specialization` instead, which is more stable and complete = note: `#[warn(incomplete_features)]` on by default -warning: 1 warning emitted +error[E0391]: cycle detected when computing whether impls specialize one another + --> $DIR/issue-39618.rs:19:1 + | +LL | impl Foo for T where T: Bar { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ...which requires evaluating trait selection obligation `u64: Bar`... + = note: ...which again requires computing whether impls specialize one another, completing the cycle +note: cycle used when building specialization graph of trait `Foo` + --> $DIR/issue-39618.rs:7:1 + | +LL | trait Foo { + | ^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/stability-attribute/stability-in-private-module.rs b/tests/ui/stability-attribute/stability-in-private-module.rs index f12e9198b0d7..df94931690b7 100644 --- a/tests/ui/stability-attribute/stability-in-private-module.rs +++ b/tests/ui/stability-attribute/stability-in-private-module.rs @@ -1,4 +1,4 @@ fn main() { - let _ = std::thread::thread_info::current_thread(); - //~^ERROR module `thread_info` is private + let _ = std::sys::os::errno(); + //~^ERROR module `sys` is private } diff --git a/tests/ui/stability-attribute/stability-in-private-module.stderr b/tests/ui/stability-attribute/stability-in-private-module.stderr index 9eb4d3efc8bd..e65f8aa9b1fc 100644 --- a/tests/ui/stability-attribute/stability-in-private-module.stderr +++ b/tests/ui/stability-attribute/stability-in-private-module.stderr @@ -1,13 +1,13 @@ -error[E0603]: module `thread_info` is private - --> $DIR/stability-in-private-module.rs:2:26 +error[E0603]: module `sys` is private + --> $DIR/stability-in-private-module.rs:2:18 | -LL | let _ = std::thread::thread_info::current_thread(); - | ^^^^^^^^^^^ -------------- function `current_thread` is not publicly re-exported - | | - | private module +LL | let _ = std::sys::os::errno(); + | ^^^ ----- function `errno` is not publicly re-exported + | | + | private module | -note: the module `thread_info` is defined here - --> $SRC_DIR/std/src/thread/mod.rs:LL:COL +note: the module `sys` is defined here + --> $SRC_DIR/std/src/lib.rs:LL:COL error: aborting due to 1 previous error diff --git a/tests/ui/statics/nested-allocations-dont-inherit-codegen-attrs.rs b/tests/ui/statics/nested-allocations-dont-inherit-codegen-attrs.rs new file mode 100644 index 000000000000..0b7e659c7b75 --- /dev/null +++ b/tests/ui/statics/nested-allocations-dont-inherit-codegen-attrs.rs @@ -0,0 +1,11 @@ +//@ build-pass + +// Make sure that the nested static allocation for `FOO` doesn't inherit `no_mangle`. +#[no_mangle] +pub static mut FOO: &mut [i32] = &mut [42]; + +// Make sure that the nested static allocation for `BAR` doesn't inherit `export_name`. +#[export_name = "BAR_"] +pub static mut BAR: &mut [i32] = &mut [42]; + +fn main() {} diff --git a/tests/ui/statics/nested_thread_local.rs b/tests/ui/statics/nested_thread_local.rs new file mode 100644 index 000000000000..a512016335a0 --- /dev/null +++ b/tests/ui/statics/nested_thread_local.rs @@ -0,0 +1,14 @@ +// Check that we forbid nested statics in `thread_local` statics. + +#![feature(const_refs_to_cell)] +#![feature(thread_local)] + +#[thread_local] +static mut FOO: &u32 = { + //~^ ERROR: does not support implicit nested statics + // Prevent promotion (that would trigger on `&42` as an expression) + let x = 42; + &{ x } +}; + +fn main() {} diff --git a/tests/ui/statics/nested_thread_local.stderr b/tests/ui/statics/nested_thread_local.stderr new file mode 100644 index 000000000000..30c742626fa9 --- /dev/null +++ b/tests/ui/statics/nested_thread_local.stderr @@ -0,0 +1,8 @@ +error: #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead + --> $DIR/nested_thread_local.rs:7:1 + | +LL | static mut FOO: &u32 = { + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/traits/stack-error-order-dependence-2.rs b/tests/ui/traits/stack-error-order-dependence-2.rs new file mode 100644 index 000000000000..323685aa15bb --- /dev/null +++ b/tests/ui/traits/stack-error-order-dependence-2.rs @@ -0,0 +1,24 @@ +//@ check-pass +// Regression test for . +// This time EXCEPT without `dyn` builtin bounds :^) + +pub trait Trait: Supertrait {} + +trait Impossible {} +impl Trait for F {} + +pub trait Supertrait {} + +impl Supertrait for T {} + +fn needs_supertrait() {} +fn needs_trait() {} + +struct A; +impl Trait for A where A: Supertrait {} +impl Supertrait for A {} + +fn main() { + needs_supertrait::(); + needs_trait::(); +} diff --git a/tests/ui/traits/stack-error-order-dependence.rs b/tests/ui/traits/stack-error-order-dependence.rs new file mode 100644 index 000000000000..037c292a542d --- /dev/null +++ b/tests/ui/traits/stack-error-order-dependence.rs @@ -0,0 +1,19 @@ +//@ check-pass +// Regression test for . + +pub trait Trait: Supertrait {} + +trait Impossible {} +impl Trait for F {} + +pub trait Supertrait {} + +impl Supertrait for T {} + +fn needs_supertrait() {} +fn needs_trait() {} + +fn main() { + needs_supertrait::(); + needs_trait::(); +} diff --git a/triagebot.toml b/triagebot.toml index 0df71c60d545..55f0d32398ff 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -788,6 +788,7 @@ compiler-team-contributors = [ "@Nadrieril", "@fmease", "@fee1-dead", + "@BoxyUwU", ] compiler = [ "compiler-team",