diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index f90190797aee9..6c0551848d688 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -46,6 +46,7 @@ impl<'a, T: Clone + IntoDiagArg> IntoDiagArg for &'a T { } } +#[macro_export] macro_rules! into_diag_arg_using_display { ($( $ty:ty ),+ $(,)?) => { $( diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index e66a834ab9e88..86b8b6d6b2b6b 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -355,6 +355,9 @@ hir_analysis_pattern_type_wild_pat = "wildcard patterns are not permitted for pa hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind} .label = not allowed in type signatures +hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}` + .note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}` + hir_analysis_requires_note = the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}` hir_analysis_return_type_notation_equality_bound = diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 4fd7c870fc730..c26f982fa473c 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -8,11 +8,13 @@ use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, ErrorGuaranteed}; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::ItemKind; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; +use rustc_macros::LintDiagnostic; use rustc_middle::query::Providers; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::trait_def::TraitSpecializationKind; @@ -136,6 +138,8 @@ where infcx.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, false); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); + lint_redundant_lifetimes(tcx, body_def_id, &outlives_env); + let errors = infcx.resolve_regions(&outlives_env); if errors.is_empty() { return Ok(()); @@ -2010,6 +2014,137 @@ fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) -> Result<(), Error res } +fn lint_redundant_lifetimes<'tcx>( + tcx: TyCtxt<'tcx>, + owner_id: LocalDefId, + outlives_env: &OutlivesEnvironment<'tcx>, +) { + let def_kind = tcx.def_kind(owner_id); + match def_kind { + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::TraitAlias + | DefKind::Fn + | DefKind::Const + | DefKind::Impl { of_trait: _ } => { + // Proceed + } + DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => { + let parent_def_id = tcx.local_parent(owner_id); + if matches!(tcx.def_kind(parent_def_id), DefKind::Impl { of_trait: true }) { + // Don't check for redundant lifetimes for associated items of trait + // implementations, since the signature is required to be compatible + // with the trait, even if the implementation implies some lifetimes + // are redundant. + return; + } + } + DefKind::Mod + | DefKind::Variant + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TyParam + | DefKind::ConstParam + | DefKind::Static { .. } + | DefKind::Ctor(_, _) + | DefKind::Macro(_) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::InlineConst + | DefKind::OpaqueTy + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::Closure => return, + } + + // The ordering of this lifetime map is a bit subtle. + // + // Specifically, we want to find a "candidate" lifetime that precedes a "victim" lifetime, + // where we can prove that `'candidate = 'victim`. + // + // `'static` must come first in this list because we can never replace `'static` with + // something else, but if we find some lifetime `'a` where `'a = 'static`, we want to + // suggest replacing `'a` with `'static`. + let mut lifetimes = vec![tcx.lifetimes.re_static]; + lifetimes.extend( + ty::GenericArgs::identity_for_item(tcx, owner_id).iter().filter_map(|arg| arg.as_region()), + ); + // If we are in a function, add its late-bound lifetimes too. + if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) { + for var in tcx.fn_sig(owner_id).instantiate_identity().bound_vars() { + let ty::BoundVariableKind::Region(kind) = var else { continue }; + lifetimes.push(ty::Region::new_late_param(tcx, owner_id.to_def_id(), kind)); + } + } + lifetimes.retain(|candidate| candidate.has_name()); + + // Keep track of lifetimes which have already been replaced with other lifetimes. + // This makes sure that if `'a = 'b = 'c`, we don't say `'c` should be replaced by + // both `'a` and `'b`. + let mut shadowed = FxHashSet::default(); + + for (idx, &candidate) in lifetimes.iter().enumerate() { + // Don't suggest removing a lifetime twice. We only need to check this + // here and not up in the `victim` loop because equality is transitive, + // so if A = C and B = C, then A must = B, so it'll be shadowed too in + // A's victim loop. + if shadowed.contains(&candidate) { + continue; + } + + for &victim in &lifetimes[(idx + 1)..] { + // We should only have late-bound lifetimes of the `BrNamed` variety, + // since we get these signatures straight from `hir_lowering`. And any + // other regions (ReError/ReStatic/etc.) shouldn't matter, since we + // can't really suggest to remove them. + let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) + | ty::ReLateParam(ty::LateParamRegion { + bound_region: ty::BoundRegionKind::BrNamed(def_id, _), + .. + })) = victim.kind() + else { + continue; + }; + + // Do not rename lifetimes not local to this item since they'll overlap + // with the lint running on the parent. We still want to consider parent + // lifetimes which make child lifetimes redundant, otherwise we would + // have truncated the `identity_for_item` args above. + if tcx.parent(def_id) != owner_id.to_def_id() { + continue; + } + + // If `candidate <: victim` and `victim <: candidate`, then they're equal. + if outlives_env.free_region_map().sub_free_regions(tcx, candidate, victim) + && outlives_env.free_region_map().sub_free_regions(tcx, victim, candidate) + { + shadowed.insert(victim); + tcx.emit_node_span_lint( + rustc_lint_defs::builtin::REDUNDANT_LIFETIMES, + tcx.local_def_id_to_hir_id(def_id.expect_local()), + tcx.def_span(def_id), + RedundantLifetimeArgsLint { candidate, victim }, + ); + } + } + } +} + +#[derive(LintDiagnostic)] +#[diag(hir_analysis_redundant_lifetime_args)] +#[note] +struct RedundantLifetimeArgsLint<'tcx> { + /// The lifetime we have found to be redundant. + victim: ty::Region<'tcx>, + // The lifetime we can replace the victim with. + candidate: ty::Region<'tcx>, +} + pub fn provide(providers: &mut Providers) { *providers = Providers { check_mod_type_wf, check_well_formed, ..*providers }; } 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 0b8ac9926e419..affd678fc6cc7 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -20,7 +20,6 @@ use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::*; use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeSuperVisitable, TypeVisitor}; -use rustc_session::lint; use rustc_span::def_id::DefId; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; @@ -867,31 +866,6 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }) => { self.visit_lifetime(lifetime); walk_list!(self, visit_param_bound, bounds); - - if lifetime.res != hir::LifetimeName::Static { - for bound in bounds { - let hir::GenericBound::Outlives(lt) = bound else { - continue; - }; - if lt.res != hir::LifetimeName::Static { - continue; - } - self.insert_lifetime(lt, ResolvedArg::StaticLifetime); - self.tcx.node_span_lint( - lint::builtin::UNUSED_LIFETIMES, - lifetime.hir_id, - lifetime.ident.span, - format!("unnecessary lifetime parameter `{}`", lifetime.ident), - |lint| { - let help = format!( - "you can use the `'static` lifetime directly, in place of `{}`", - lifetime.ident, - ); - lint.help(help); - }, - ); - } - } } &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => { self.visit_ty(lhs_ty); 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 59f0fac5aa76f..9fb8b4ac40e5a 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2223,6 +2223,24 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Err(LitToConstError::TypeError) => todo!(), } } + + hir::ExprKind::Path(hir::QPath::Resolved( + _, + &hir::Path { + res: Res::Def(DefKind::ConstParam, def_id), .. + }, + )) => { + let ty = tcx + .type_of(def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"); + let item_def_id = tcx.parent(def_id); + let generics = tcx.generics_of(item_def_id); + let index = generics.param_def_id_to_index[&def_id]; + let name = tcx.item_name(def_id); + ty::Const::new_param(tcx, ty::ParamConst::new(index, name), ty) + } + _ => { let err = tcx .dcx() diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 53b5273803c6f..2713690f8120a 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -79,6 +79,7 @@ declare_lint_pass! { PROC_MACRO_BACK_COMPAT, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, PUB_USE_OF_PRIVATE_EXTERN_CRATE, + REDUNDANT_LIFETIMES, REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE, RENAMED_AND_REMOVED_LINTS, @@ -1707,6 +1708,33 @@ declare_lint! { "detects lifetime parameters that are never used" } +declare_lint! { + /// The `redundant_lifetimes` lint detects lifetime parameters that are + /// redundant because they are equal to another named lifetime. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[deny(redundant_lifetimes)] + /// + /// // `'a = 'static`, so all usages of `'a` can be replaced with `'static` + /// pub fn bar<'a: 'static>() {} + /// + /// // `'a = 'b`, so all usages of `'b` can be replaced with `'a` + /// pub fn bar<'a: 'b, 'b: 'a>() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused lifetime parameters may signal a mistake or unfinished code. + /// Consider removing the parameter. + pub REDUNDANT_LIFETIMES, + Allow, + "detects lifetime parameters that are redundant because they are equal to some other named lifetime" +} + declare_lint! { /// The `tyvar_behind_raw_pointer` lint detects raw pointer to an /// inference variable. diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 0daf83162dbbf..6275c5d2a1194 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -757,7 +757,7 @@ pub struct GlobalCtxt<'tcx> { impl<'tcx> GlobalCtxt<'tcx> { /// Installs `self` in a `TyCtxt` and `ImplicitCtxt` for the duration of /// `f`. - pub fn enter<'a: 'tcx, F, R>(&'a self, f: F) -> R + pub fn enter(&'tcx self, f: F) -> R where F: FnOnce(TyCtxt<'tcx>) -> R, { diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index ee18647cdd8fd..cc1d6e50f6d9a 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -5,13 +5,13 @@ use std::fmt::Write; use std::ops::ControlFlow; use crate::ty::{ - AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque, PolyTraitPredicate, - Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitor, + self, AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque, + PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitor, }; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{Applicability, Diag, DiagArgValue, IntoDiagArg}; +use rustc_errors::{into_diag_arg_using_display, Applicability, Diag, DiagArgValue, IntoDiagArg}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -19,10 +19,9 @@ use rustc_hir::{PredicateOrigin, WherePredicate}; use rustc_span::{BytePos, Span}; use rustc_type_ir::TyKind::*; -impl<'tcx> IntoDiagArg for Ty<'tcx> { - fn into_diag_arg(self) -> DiagArgValue { - self.to_string().into_diag_arg() - } +into_diag_arg_using_display! { + Ty<'_>, + ty::Region<'_>, } impl<'tcx> Ty<'tcx> { 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 ccb229616e855..b26f968bf5e8a 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -154,6 +154,14 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { }) { let (child_field_idx, child_capture) = child_captures.next().unwrap(); + // This analysis only makes sense if the parent capture is a + // prefix of the child capture. + assert!( + child_capture.place.projections.len() >= parent_capture.place.projections.len(), + "parent capture ({parent_capture:#?}) expected to be prefix of \ + child capture ({child_capture:#?})" + ); + // Store this set of additional projections (fields and derefs). // We need to re-apply them later. let child_precise_captures = @@ -244,7 +252,6 @@ fn child_prefix_matches_parent_projections( bug!("expected capture to be an upvar"); }; - assert!(child_capture.place.projections.len() >= parent_capture.place.projections.len()); parent_base.var_path.hir_id == child_base.var_path.hir_id && std::iter::zip(&child_capture.place.projections, &parent_capture.place.projections) .all(|(child, parent)| child.kind == parent.kind) diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs index 0762016ef75b3..221224eed0170 100644 --- a/compiler/rustc_smir/src/rustc_smir/builder.rs +++ b/compiler/rustc_smir/src/rustc_smir/builder.rs @@ -4,9 +4,10 @@ //! monomorphic body using internal representation. //! After that, we convert the internal representation into a stable one. use crate::rustc_smir::{Stable, Tables}; +use rustc_hir::def::DefKind; use rustc_middle::mir; use rustc_middle::mir::visit::MutVisitor; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; /// Builds a monomorphic body for a given instance. pub struct BodyBuilder<'tcx> { @@ -16,46 +17,43 @@ pub struct BodyBuilder<'tcx> { impl<'tcx> BodyBuilder<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>) -> Self { + let instance = match instance.def { + // To get the fallback body of an intrinsic, we need to convert it to an item. + ty::InstanceDef::Intrinsic(def_id) => ty::Instance::new(def_id, instance.args), + _ => instance, + }; BodyBuilder { tcx, instance } } /// Build a stable monomorphic body for a given instance based on the MIR body. /// - /// Note that we skip instantiation for static and constants. Trying to do so can cause ICE. - /// - /// We do monomorphize non-generic functions to eval unevaluated constants. + /// All constants are also evaluated. pub fn build(mut self, tables: &mut Tables<'tcx>) -> stable_mir::mir::Body { - let mut body = self.tcx.instance_mir(self.instance.def).clone(); - if self.tcx.def_kind(self.instance.def_id()).is_fn_like() || !self.instance.args.is_empty() + let body = tables.tcx.instance_mir(self.instance.def).clone(); + let mono_body = if !self.instance.args.is_empty() + // Without the `generic_const_exprs` feature gate, anon consts in signatures do not + // get generic parameters. Which is wrong, but also not a problem without + // generic_const_exprs + || self.tcx.def_kind(self.instance.def_id()) != DefKind::AnonConst { - self.visit_body(&mut body); - } - body.stable(tables) - } - - fn monomorphize(&self, value: T) -> T - where - T: ty::TypeFoldable>, - { - self.instance.instantiate_mir_and_normalize_erasing_regions( - self.tcx, - ty::ParamEnv::reveal_all(), - ty::EarlyBinder::bind(value), - ) + let mut mono_body = self.instance.instantiate_mir_and_normalize_erasing_regions( + tables.tcx, + ty::ParamEnv::reveal_all(), + ty::EarlyBinder::bind(body), + ); + self.visit_body(&mut mono_body); + mono_body + } else { + // Already monomorphic. + body + }; + mono_body.stable(tables) } } impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> { - fn visit_ty_const(&mut self, ct: &mut ty::Const<'tcx>, _location: mir::Location) { - *ct = self.monomorphize(*ct); - } - - fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: mir::visit::TyContext) { - *ty = self.monomorphize(*ty); - } - fn visit_constant(&mut self, constant: &mut mir::ConstOperand<'tcx>, location: mir::Location) { - let const_ = self.monomorphize(constant.const_); + let const_ = constant.const_; let val = match const_.eval(self.tcx, ty::ParamEnv::reveal_all(), constant.span) { Ok(v) => v, Err(mir::interpret::ErrorHandled::Reported(..)) => return, @@ -68,10 +66,6 @@ impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> { self.super_constant(constant, location); } - fn visit_args(&mut self, args: &mut GenericArgsRef<'tcx>, _: mir::Location) { - *args = self.monomorphize(*args); - } - fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 77cd4662ae575..902b76e8c1e26 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -133,10 +133,14 @@ fn layout_of_uncached<'tcx>( ty::PatternKind::Range { start, end, include_end } => { if let Abi::Scalar(scalar) | Abi::ScalarPair(scalar, _) = &mut layout.abi { if let Some(start) = start { - scalar.valid_range_mut().start = start.eval_bits(tcx, param_env); + scalar.valid_range_mut().start = start + .try_eval_bits(tcx, param_env) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; } if let Some(end) = end { - let mut end = end.eval_bits(tcx, param_env); + let mut end = end + .try_eval_bits(tcx, param_env) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; if !include_end { end = end.wrapping_sub(1); } diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs index aafa89c03e000..a032a180fcfdd 100644 --- a/compiler/stable_mir/src/mir/mono.rs +++ b/compiler/stable_mir/src/mir/mono.rs @@ -41,13 +41,22 @@ impl Instance { with(|cx| cx.instance_args(self.def)) } - /// Get the body of an Instance. The body will be eagerly monomorphized. + /// Get the body of an Instance. + /// + /// The body will be eagerly monomorphized and all constants will already be evaluated. + /// + /// This method will return the intrinsic fallback body if one was defined. pub fn body(&self) -> Option { with(|context| context.instance_body(self.def)) } /// Check whether this instance has a body available. /// + /// For intrinsics with fallback body, this will return `true`. It is up to the user to decide + /// whether to specialize the intrinsic or to use its fallback body. + /// + /// For more information on fallback body, see . + /// /// This call is much cheaper than `instance.body().is_some()`, since it doesn't try to build /// the StableMIR body. pub fn has_body(&self) -> bool { diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs index 90e1bff52a365..555ad581b8568 100644 --- a/library/std/src/sys/pal/windows/api.rs +++ b/library/std/src/sys/pal/windows/api.rs @@ -34,6 +34,102 @@ use core::ptr::addr_of; use super::c; +/// Creates a null-terminated UTF-16 string from a str. +pub macro wide_str($str:literal) {{ + const _: () = { + if core::slice::memchr::memchr(0, $str.as_bytes()).is_some() { + panic!("null terminated strings cannot contain interior nulls"); + } + }; + crate::sys::pal::windows::api::utf16!(concat!($str, '\0')) +}} + +/// Creates a UTF-16 string from a str without null termination. +pub macro utf16($str:expr) {{ + const UTF8: &str = $str; + const UTF16_LEN: usize = crate::sys::pal::windows::api::utf16_len(UTF8); + const UTF16: [u16; UTF16_LEN] = crate::sys::pal::windows::api::to_utf16(UTF8); + &UTF16 +}} + +#[cfg(test)] +mod tests; + +/// Gets the UTF-16 length of a UTF-8 string, for use in the wide_str macro. +pub const fn utf16_len(s: &str) -> usize { + let s = s.as_bytes(); + let mut i = 0; + let mut len = 0; + while i < s.len() { + // the length of a UTF-8 encoded code-point is given by the number of + // leading ones, except in the case of ASCII. + let utf8_len = match s[i].leading_ones() { + 0 => 1, + n => n as usize, + }; + i += utf8_len; + // Note that UTF-16 surrogates (U+D800 to U+DFFF) are not encodable as UTF-8, + // so (unlike with WTF-8) we don't have to worry about how they'll get re-encoded. + len += if utf8_len < 4 { 1 } else { 2 }; + } + len +} + +/// Const convert UTF-8 to UTF-16, for use in the wide_str macro. +/// +/// Note that this is designed for use in const contexts so is not optimized. +pub const fn to_utf16(s: &str) -> [u16; UTF16_LEN] { + let mut output = [0_u16; UTF16_LEN]; + let mut pos = 0; + let s = s.as_bytes(); + let mut i = 0; + while i < s.len() { + match s[i].leading_ones() { + // Decode UTF-8 based on its length. + // See https://en.wikipedia.org/wiki/UTF-8 + 0 => { + // ASCII is the same in both encodings + output[pos] = s[i] as u16; + i += 1; + pos += 1; + } + 2 => { + // Bits: 110xxxxx 10xxxxxx + output[pos] = ((s[i] as u16 & 0b11111) << 6) | (s[i + 1] as u16 & 0b111111); + i += 2; + pos += 1; + } + 3 => { + // Bits: 1110xxxx 10xxxxxx 10xxxxxx + output[pos] = ((s[i] as u16 & 0b1111) << 12) + | ((s[i + 1] as u16 & 0b111111) << 6) + | (s[i + 2] as u16 & 0b111111); + i += 3; + pos += 1; + } + 4 => { + // Bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + let mut c = ((s[i] as u32 & 0b111) << 18) + | ((s[i + 1] as u32 & 0b111111) << 12) + | ((s[i + 2] as u32 & 0b111111) << 6) + | (s[i + 3] as u32 & 0b111111); + // re-encode as UTF-16 (see https://en.wikipedia.org/wiki/UTF-16) + // - Subtract 0x10000 from the code point + // - For the high surrogate, shift right by 10 then add 0xD800 + // - For the low surrogate, take the low 10 bits then add 0xDC00 + c -= 0x10000; + output[pos] = ((c >> 10) + 0xD800) as u16; + output[pos + 1] = ((c & 0b1111111111) + 0xDC00) as u16; + i += 4; + pos += 2; + } + // valid UTF-8 cannot have any other values + _ => unreachable!(), + } + } + output +} + /// Helper method for getting the size of `T` as a u32. /// Errors at compile time if the size would overflow. /// diff --git a/library/std/src/sys/pal/windows/api/tests.rs b/library/std/src/sys/pal/windows/api/tests.rs new file mode 100644 index 0000000000000..fab022c7b93d8 --- /dev/null +++ b/library/std/src/sys/pal/windows/api/tests.rs @@ -0,0 +1,16 @@ +use crate::sys::pal::windows::api::{utf16, wide_str}; + +macro_rules! check_utf16 { + ($str:literal) => {{ + assert!(wide_str!($str).iter().copied().eq($str.encode_utf16().chain([0]))); + assert!(utf16!($str).iter().copied().eq($str.encode_utf16())); + }}; +} + +#[test] +fn test_utf16_macros() { + check_utf16!("hello world"); + check_utf16!("€4.50"); + check_utf16!("𨉟呐㗂越"); + check_utf16!("Pchnąć w tę łódź jeża lub ośm skrzyń fig"); +} diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 6a561518fad65..a734c2bd4c71e 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -5,6 +5,7 @@ use crate::io::ErrorKind; use crate::mem::MaybeUninit; use crate::os::windows::ffi::{OsStrExt, OsStringExt}; use crate::path::PathBuf; +use crate::sys::pal::windows::api::wide_str; use crate::time::Duration; pub use self::rand::hashmap_random_keys; @@ -12,6 +13,8 @@ pub use self::rand::hashmap_random_keys; #[macro_use] pub mod compat; +mod api; + pub mod alloc; pub mod args; pub mod c; @@ -41,8 +44,6 @@ cfg_if::cfg_if! { } } -mod api; - /// Map a Result to io::Result. trait IoResult { fn io_result(self) -> crate::io::Result; @@ -60,7 +61,7 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already // exists, we have to call it ourselves. - thread::Thread::set_name(&c"main"); + thread::Thread::set_name_wide(wide_str!("main")); } // SAFETY: must be called only once during runtime cleanup. diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index c0c63c3340f4f..9b1c5b34bbf27 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -59,13 +59,17 @@ impl Thread { pub fn set_name(name: &CStr) { if let Ok(utf8) = name.to_str() { if let Ok(utf16) = to_u16s(utf8) { - unsafe { - c::SetThreadDescription(c::GetCurrentThread(), utf16.as_ptr()); - }; + Self::set_name_wide(&utf16) }; }; } + pub fn set_name_wide(name: &[u16]) { + unsafe { + c::SetThreadDescription(c::GetCurrentThread(), name.as_ptr()); + }; + } + pub fn join(self) { let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; if rc == c::WAIT_FAILED { diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index fc6f51e8272ae..4cab2d6425736 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -231,7 +231,7 @@ impl<'tcx> Context<'tcx> { rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo), }; let mut page_buffer = Buffer::html(); - print_item(self, it, &mut page_buffer, &page); + print_item(self, it, &mut page_buffer); layout::render( &clone_shared.layout, &page, diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index fbb521a6188ae..168db5c0948ce 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -31,11 +31,10 @@ use crate::html::format::{ display_fn, join_with_double_colon, print_abi_with_space, print_constness_with_space, print_where_clause, visibility_print_with_space, Buffer, Ending, PrintWithSpace, }; -use crate::html::layout::Page; +use crate::html::highlight; use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine}; use crate::html::render::{document_full, document_item_info}; use crate::html::url_parts_builder::UrlPartsBuilder; -use crate::html::{highlight, static_files}; use askama::Template; use itertools::Itertools; @@ -157,8 +156,6 @@ struct PathComponent { #[derive(Template)] #[template(path = "print_item.html")] struct ItemVars<'a> { - static_root_path: &'a str, - clipboard_svg: &'static static_files::StaticFile, typ: &'a str, name: &'a str, item_type: &'a str, @@ -178,12 +175,7 @@ fn print_where_clause_and_check<'a, 'tcx: 'a>( len_before != buffer.len() } -pub(super) fn print_item( - cx: &mut Context<'_>, - item: &clean::Item, - buf: &mut Buffer, - page: &Page<'_>, -) { +pub(super) fn print_item(cx: &mut Context<'_>, item: &clean::Item, buf: &mut Buffer) { debug_assert!(!item.is_stripped()); let typ = match *item.kind { clean::ModuleItem(_) => { @@ -252,8 +244,6 @@ pub(super) fn print_item( }; let item_vars = ItemVars { - static_root_path: &page.get_static_root_path(), - clipboard_svg: &static_files::STATIC_FILES.clipboard_svg, typ, name: item.name.as_ref().unwrap().as_str(), item_type: &item.type_().to_string(), @@ -1237,22 +1227,18 @@ fn item_opaque_ty( } fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TypeAlias) { - fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) { - wrap_item(w, |w| { - write!( - w, - "{attrs}{vis}type {name}{generics}{where_clause} = {type_};", - attrs = render_attributes_in_pre(it, "", cx), - vis = visibility_print_with_space(it, cx), - name = it.name.unwrap(), - generics = t.generics.print(cx), - where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), - type_ = t.type_.print(cx), - ); - }); - } - - write_content(w, cx, it, t); + wrap_item(w, |w| { + write!( + w, + "{attrs}{vis}type {name}{generics}{where_clause} = {type_};", + attrs = render_attributes_in_pre(it, "", cx), + vis = visibility_print_with_space(it, cx), + name = it.name.unwrap(), + generics = t.generics.print(cx), + where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), + type_ = t.type_.print(cx), + ); + }); write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 0bb073b1ceac0..e9c687b42fadc 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1603,6 +1603,16 @@ a.tooltip:hover::after { border-color: var(--settings-button-border-focus); } +#settings-menu > a { + line-height: 0; + font-size: 0; +} +#settings-menu > a:before { + content: url('wheel-63255fc4502dca9a.svg'); + width: 22px; + height: 22px; +} + #sidebar-button > a:before { content: url('data:image/svg+xml,\ @@ -1622,11 +1632,17 @@ a.tooltip:hover::after { padding-left: 2px; border: 0; width: 33px; + line-height: 0; + font-size: 0; } -#copy-path > img { + +#copy-path:before { filter: var(--copy-path-img-filter); + content: url('clipboard-24048e6d87f63d07.svg'); + width: 19px; + height: 18px; } -#copy-path:hover > img { +#copy-path:hover:before { filter: var(--copy-path-img-hover-filter); } diff --git a/src/librustdoc/html/static/images/clipboard.svg b/src/librustdoc/html/static/images/clipboard.svg index 8adbd99630488..e437c83fb6b6d 100644 --- a/src/librustdoc/html/static/images/clipboard.svg +++ b/src/librustdoc/html/static/images/clipboard.svg @@ -1 +1 @@ - + diff --git a/src/librustdoc/html/static/images/favicon-16x16.png b/src/librustdoc/html/static/images/favicon-16x16.png deleted file mode 100644 index ea4b45cae1618..0000000000000 Binary files a/src/librustdoc/html/static/images/favicon-16x16.png and /dev/null differ diff --git a/src/librustdoc/html/static/images/wheel.svg b/src/librustdoc/html/static/images/wheel.svg index 83c07f63d10ab..ba30f13dd58e9 100644 --- a/src/librustdoc/html/static/images/wheel.svg +++ b/src/librustdoc/html/static/images/wheel.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index ca9a78f51b38e..d8874c2fda0a0 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -106,7 +106,6 @@ static_files! { license_mit => "static/LICENSE-MIT.txt", rust_logo_svg => "static/images/rust-logo.svg", rust_favicon_svg => "static/images/favicon.svg", - rust_favicon_png_16 => "static/images/favicon-16x16.png", rust_favicon_png_32 => "static/images/favicon-32x32.png", fira_sans_regular => "static/fonts/FiraSans-Regular.woff2", fira_sans_medium => "static/fonts/FiraSans-Medium.woff2", diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index 0f3debae66c77..0941f758de57c 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -6,13 +6,10 @@ {# #} {# #} {{page.title}} {# #} - {# #} + {# #} {# #} {# #} {% else %} - {# #} {# #} {# #}
{{+ display_krate_version_extra}}
{# #} {% endif %} {% else %} -
+
{# #}

Files

{# #}
{# #} {% endif %} {{ sidebar|safe }} {# #} - + {# #}
{# #} {% if page.css_class != "src" %}
{% endif %}
{# #} {# #} {# #} diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html index 1d215c26968d8..76e770453b6ea 100644 --- a/src/librustdoc/html/templates/print_item.html +++ b/src/librustdoc/html/templates/print_item.html @@ -7,9 +7,7 @@

{% endfor %} {{name}} {# #} {# #}

{# #} diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html index d982134181c52..3251b4c14c95a 100644 --- a/src/librustdoc/html/templates/sidebar.html +++ b/src/librustdoc/html/templates/sidebar.html @@ -5,7 +5,7 @@

{# #} {% endif %}