From 01800caddf74375e17bbdedb9606139704fc3aed Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 22 Nov 2021 19:57:08 -0800 Subject: Add test of qpath interpolations --- src/test/ui/macros/macro-interpolation.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/test/ui/macros/macro-interpolation.rs b/src/test/ui/macros/macro-interpolation.rs index abe1f2aaf15..2dee804fb31 100644 --- a/src/test/ui/macros/macro-interpolation.rs +++ b/src/test/ui/macros/macro-interpolation.rs @@ -14,8 +14,15 @@ macro_rules! overly_complicated { } +macro_rules! qpath { + (<$type:ty as $trait:path>::$name:ident) => { + <$type as $trait>::$name + }; +} + pub fn main() { + let _: qpath!(::Owned); + assert!(overly_complicated!(f, x, Option, { return Some(x); }, Some(8), Some(y), y) == 8) - } -- cgit 1.4.1-3-g733a5 From 558ddee2cea3063a487393ea292126259702fe97 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 22 Nov 2021 19:58:18 -0800 Subject: Add test of NtTy in a qpath Currently fails: error: expected identifier, found `ToOwned` --> src/test/ui/macros/macro-interpolation.rs:23:19 | LL | <$type as $trait>::$name | ^^^^^^ expected identifier ... LL | let _: qpath!(ty, ::Owned); | ----------------------------------- | | | this macro call doesn't expand to a type | in this macro invocation --- src/test/ui/macros/macro-interpolation.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/test/ui/macros/macro-interpolation.rs b/src/test/ui/macros/macro-interpolation.rs index 2dee804fb31..35003a79ad7 100644 --- a/src/test/ui/macros/macro-interpolation.rs +++ b/src/test/ui/macros/macro-interpolation.rs @@ -15,13 +15,18 @@ macro_rules! overly_complicated { } macro_rules! qpath { - (<$type:ty as $trait:path>::$name:ident) => { + (path, <$type:ty as $trait:path>::$name:ident) => { + <$type as $trait>::$name + }; + + (ty, <$type:ty as $trait:ty>::$name:ident) => { <$type as $trait>::$name }; } pub fn main() { - let _: qpath!(::Owned); + let _: qpath!(path, ::Owned); + let _: qpath!(ty, ::Owned); assert!(overly_complicated!(f, x, Option, { return Some(x); }, Some(8), Some(y), y) == 8) -- cgit 1.4.1-3-g733a5 From d32ca64692d95c08d6563f363ea3074d1d30ce52 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Tue, 28 Dec 2021 04:06:01 +0000 Subject: Allow isize/usize in simd_cast --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 20 ++++++++++++++++---- .../ui/simd/intrinsic/generic-cast-pointer-width.rs | 21 +++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/simd/intrinsic/generic-cast-pointer-width.rs (limited to 'src') diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 07d49b6e729..f472178dbc6 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1715,14 +1715,26 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, let (in_style, in_width) = match in_elem.kind() { // vectors of pointer-sized integers should've been // disallowed before here, so this unwrap is safe. - ty::Int(i) => (Style::Int(true), i.bit_width().unwrap()), - ty::Uint(u) => (Style::Int(false), u.bit_width().unwrap()), + ty::Int(i) => ( + Style::Int(true), + i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), + ), + ty::Uint(u) => ( + Style::Int(false), + u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), + ), ty::Float(f) => (Style::Float, f.bit_width()), _ => (Style::Unsupported, 0), }; let (out_style, out_width) = match out_elem.kind() { - ty::Int(i) => (Style::Int(true), i.bit_width().unwrap()), - ty::Uint(u) => (Style::Int(false), u.bit_width().unwrap()), + ty::Int(i) => ( + Style::Int(true), + i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), + ), + ty::Uint(u) => ( + Style::Int(false), + u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), + ), ty::Float(f) => (Style::Float, f.bit_width()), _ => (Style::Unsupported, 0), }; diff --git a/src/test/ui/simd/intrinsic/generic-cast-pointer-width.rs b/src/test/ui/simd/intrinsic/generic-cast-pointer-width.rs new file mode 100644 index 00000000000..b9382310deb --- /dev/null +++ b/src/test/ui/simd/intrinsic/generic-cast-pointer-width.rs @@ -0,0 +1,21 @@ +// run-pass +#![feature(repr_simd, platform_intrinsics)] + +extern "platform-intrinsic" { + fn simd_cast(x: T) -> U; +} + +#[derive(Copy, Clone)] +#[repr(simd)] +struct V([T; 4]); + +fn main() { + let u = V::([0, 1, 2, 3]); + let uu32: V = unsafe { simd_cast(u) }; + let ui64: V = unsafe { simd_cast(u) }; + + for (u, (uu32, ui64)) in u.0.iter().zip(uu32.0.iter().zip(ui64.0.iter())) { + assert_eq!(*u as u32, *uu32); + assert_eq!(*u as i64, *ui64); + } +} -- cgit 1.4.1-3-g733a5 From 8fae33d9b21e1e29da65506a2d6ac5bbbb3c4a86 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Thu, 30 Dec 2021 01:18:44 +0000 Subject: Add simd_as intrinsic --- compiler/rustc_codegen_llvm/src/builder.rs | 59 +++++--- compiler/rustc_codegen_llvm/src/intrinsic.rs | 10 +- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 150 +------------------ compiler/rustc_codegen_ssa/src/traits/builder.rs | 178 ++++++++++++++++++++++- compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_typeck/src/check/intrinsic.rs | 2 +- src/test/ui/simd/intrinsic/generic-as.rs | 34 +++++ 7 files changed, 261 insertions(+), 173 deletions(-) create mode 100644 src/test/ui/simd/intrinsic/generic-as.rs (limited to 'src') diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 5217fa2758f..4d87c3f1620 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -731,27 +731,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { - if !self.fptoint_sat_broken_in_llvm() { - let src_ty = self.cx.val_ty(val); - let float_width = self.cx.float_width(src_ty); - let int_width = self.cx.int_width(dest_ty); - let name = format!("llvm.fptoui.sat.i{}.f{}", int_width, float_width); - return Some(self.call_intrinsic(&name, &[val])); - } - - None + self.fptoint_sat(false, val, dest_ty) } fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { - if !self.fptoint_sat_broken_in_llvm() { - let src_ty = self.cx.val_ty(val); - let float_width = self.cx.float_width(src_ty); - let int_width = self.cx.int_width(dest_ty); - let name = format!("llvm.fptosi.sat.i{}.f{}", int_width, float_width); - return Some(self.call_intrinsic(&name, &[val])); - } - - None + self.fptoint_sat(true, val, dest_ty) } fn fptoui(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { @@ -1455,4 +1439,43 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { _ => false, } } + + fn fptoint_sat( + &mut self, + signed: bool, + val: &'ll Value, + dest_ty: &'ll Type, + ) -> Option<&'ll Value> { + if !self.fptoint_sat_broken_in_llvm() { + let src_ty = self.cx.val_ty(val); + let (float_ty, int_ty, vector_length) = if self.cx.type_kind(src_ty) == TypeKind::Vector + { + assert_eq!(self.cx.vector_length(src_ty), self.cx.vector_length(dest_ty)); + ( + self.cx.element_type(src_ty), + self.cx.element_type(dest_ty), + Some(self.cx.vector_length(src_ty)), + ) + } else { + (src_ty, dest_ty, None) + }; + let float_width = self.cx.float_width(float_ty); + let int_width = self.cx.int_width(int_ty); + + let instr = if signed { "fptosi" } else { "fptoui" }; + let name = if let Some(vector_length) = vector_length { + format!( + "llvm.{}.sat.v{}i{}.v{}f{}", + instr, vector_length, int_width, vector_length, float_width + ) + } else { + format!("llvm.{}.sat.i{}.f{}", instr, int_width, float_width) + }; + let f = + self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty)); + return Some(self.call(self.type_func(&[src_ty], dest_ty), f, &[val], None)); + } + + None + } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index f472178dbc6..8cfa0a64828 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1689,7 +1689,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, bitwise_red!(simd_reduce_all: vector_reduce_and, true); bitwise_red!(simd_reduce_any: vector_reduce_or, true); - if name == sym::simd_cast { + if name == sym::simd_cast || name == sym::simd_as { require_simd!(ret_ty, "return"); let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx()); require!( @@ -1761,10 +1761,10 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, }); } (Style::Float, Style::Int(out_is_signed)) => { - return Ok(if out_is_signed { - bx.fptosi(args[0].immediate(), llret_ty) - } else { - bx.fptoui(args[0].immediate(), llret_ty) + return Ok(match (out_is_signed, name == sym::simd_as) { + (false, false) => bx.fptoui(args[0].immediate(), llret_ty), + (true, false) => bx.fptosi(args[0].immediate(), llret_ty), + (_, true) => bx.cast_float_to_int(out_is_signed, args[0].immediate(), llret_ty), }); } (Style::Float, Style::Float) => { diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 679c4576701..68decce82ab 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -3,11 +3,10 @@ use super::place::PlaceRef; use super::{FunctionCx, LocalRef}; use crate::base; -use crate::common::{self, IntPredicate, RealPredicate}; +use crate::common::{self, IntPredicate}; use crate::traits::*; use crate::MemFlags; -use rustc_apfloat::{ieee, Float, Round, Status}; use rustc_middle::mir; use rustc_middle::ty::cast::{CastTy, IntTy}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; @@ -368,10 +367,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.inttoptr(usize_llval, ll_t_out) } (CastTy::Float, CastTy::Int(IntTy::I)) => { - cast_float_to_int(&mut bx, true, llval, ll_t_in, ll_t_out) + bx.cast_float_to_int(true, llval, ll_t_out) } (CastTy::Float, CastTy::Int(_)) => { - cast_float_to_int(&mut bx, false, llval, ll_t_in, ll_t_out) + bx.cast_float_to_int(false, llval, ll_t_out) } _ => bug!("unsupported cast: {:?} to {:?}", operand.layout.ty, cast.ty), }; @@ -768,146 +767,3 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // (*) this is only true if the type is suitable } } - -fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - signed: bool, - x: Bx::Value, - float_ty: Bx::Type, - int_ty: Bx::Type, -) -> Bx::Value { - if let Some(false) = bx.cx().sess().opts.debugging_opts.saturating_float_casts { - return if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; - } - - let try_sat_result = if signed { bx.fptosi_sat(x, int_ty) } else { bx.fptoui_sat(x, int_ty) }; - if let Some(try_sat_result) = try_sat_result { - return try_sat_result; - } - - let int_width = bx.cx().int_width(int_ty); - let float_width = bx.cx().float_width(float_ty); - // LLVM's fpto[su]i returns undef when the input x is infinite, NaN, or does not fit into the - // destination integer type after rounding towards zero. This `undef` value can cause UB in - // safe code (see issue #10184), so we implement a saturating conversion on top of it: - // Semantically, the mathematical value of the input is rounded towards zero to the next - // mathematical integer, and then the result is clamped into the range of the destination - // integer type. Positive and negative infinity are mapped to the maximum and minimum value of - // the destination integer type. NaN is mapped to 0. - // - // Define f_min and f_max as the largest and smallest (finite) floats that are exactly equal to - // a value representable in int_ty. - // They are exactly equal to int_ty::{MIN,MAX} if float_ty has enough significand bits. - // Otherwise, int_ty::MAX must be rounded towards zero, as it is one less than a power of two. - // int_ty::MIN, however, is either zero or a negative power of two and is thus exactly - // representable. Note that this only works if float_ty's exponent range is sufficiently large. - // f16 or 256 bit integers would break this property. Right now the smallest float type is f32 - // with exponents ranging up to 127, which is barely enough for i128::MIN = -2^127. - // On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because - // we're rounding towards zero, we just get float_ty::MAX (which is always an integer). - // This already happens today with u128::MAX = 2^128 - 1 > f32::MAX. - let int_max = |signed: bool, int_width: u64| -> u128 { - let shift_amount = 128 - int_width; - if signed { i128::MAX as u128 >> shift_amount } else { u128::MAX >> shift_amount } - }; - let int_min = |signed: bool, int_width: u64| -> i128 { - if signed { i128::MIN >> (128 - int_width) } else { 0 } - }; - - let compute_clamp_bounds_single = |signed: bool, int_width: u64| -> (u128, u128) { - let rounded_min = ieee::Single::from_i128_r(int_min(signed, int_width), Round::TowardZero); - assert_eq!(rounded_min.status, Status::OK); - let rounded_max = ieee::Single::from_u128_r(int_max(signed, int_width), Round::TowardZero); - assert!(rounded_max.value.is_finite()); - (rounded_min.value.to_bits(), rounded_max.value.to_bits()) - }; - let compute_clamp_bounds_double = |signed: bool, int_width: u64| -> (u128, u128) { - let rounded_min = ieee::Double::from_i128_r(int_min(signed, int_width), Round::TowardZero); - assert_eq!(rounded_min.status, Status::OK); - let rounded_max = ieee::Double::from_u128_r(int_max(signed, int_width), Round::TowardZero); - assert!(rounded_max.value.is_finite()); - (rounded_min.value.to_bits(), rounded_max.value.to_bits()) - }; - - let mut float_bits_to_llval = |bits| { - let bits_llval = match float_width { - 32 => bx.cx().const_u32(bits as u32), - 64 => bx.cx().const_u64(bits as u64), - n => bug!("unsupported float width {}", n), - }; - bx.bitcast(bits_llval, float_ty) - }; - let (f_min, f_max) = match float_width { - 32 => compute_clamp_bounds_single(signed, int_width), - 64 => compute_clamp_bounds_double(signed, int_width), - n => bug!("unsupported float width {}", n), - }; - let f_min = float_bits_to_llval(f_min); - let f_max = float_bits_to_llval(f_max); - // To implement saturation, we perform the following steps: - // - // 1. Cast x to an integer with fpto[su]i. This may result in undef. - // 2. Compare x to f_min and f_max, and use the comparison results to select: - // a) int_ty::MIN if x < f_min or x is NaN - // b) int_ty::MAX if x > f_max - // c) the result of fpto[su]i otherwise - // 3. If x is NaN, return 0.0, otherwise return the result of step 2. - // - // This avoids resulting undef because values in range [f_min, f_max] by definition fit into the - // destination type. It creates an undef temporary, but *producing* undef is not UB. Our use of - // undef does not introduce any non-determinism either. - // More importantly, the above procedure correctly implements saturating conversion. - // Proof (sketch): - // If x is NaN, 0 is returned by definition. - // Otherwise, x is finite or infinite and thus can be compared with f_min and f_max. - // This yields three cases to consider: - // (1) if x in [f_min, f_max], the result of fpto[su]i is returned, which agrees with - // saturating conversion for inputs in that range. - // (2) if x > f_max, then x is larger than int_ty::MAX. This holds even if f_max is rounded - // (i.e., if f_max < int_ty::MAX) because in those cases, nextUp(f_max) is already larger - // than int_ty::MAX. Because x is larger than int_ty::MAX, the return value of int_ty::MAX - // is correct. - // (3) if x < f_min, then x is smaller than int_ty::MIN. As shown earlier, f_min exactly equals - // int_ty::MIN and therefore the return value of int_ty::MIN is correct. - // QED. - - let int_max = bx.cx().const_uint_big(int_ty, int_max(signed, int_width)); - let int_min = bx.cx().const_uint_big(int_ty, int_min(signed, int_width) as u128); - let zero = bx.cx().const_uint(int_ty, 0); - - // Step 1 ... - let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; - let less_or_nan = bx.fcmp(RealPredicate::RealULT, x, f_min); - let greater = bx.fcmp(RealPredicate::RealOGT, x, f_max); - - // Step 2: We use two comparisons and two selects, with %s1 being the - // result: - // %less_or_nan = fcmp ult %x, %f_min - // %greater = fcmp olt %x, %f_max - // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result - // %s1 = select %greater, int_ty::MAX, %s0 - // Note that %less_or_nan uses an *unordered* comparison. This - // comparison is true if the operands are not comparable (i.e., if x is - // NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if - // x is NaN. - // - // Performance note: Unordered comparison can be lowered to a "flipped" - // comparison and a negation, and the negation can be merged into the - // select. Therefore, it not necessarily any more expensive than an - // ordered ("normal") comparison. Whether these optimizations will be - // performed is ultimately up to the backend, but at least x86 does - // perform them. - let s0 = bx.select(less_or_nan, int_min, fptosui_result); - let s1 = bx.select(greater, int_max, s0); - - // Step 3: NaN replacement. - // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN. - // Therefore we only need to execute this step for signed integer types. - if signed { - // LLVM has no isNaN predicate, so we use (x == x) instead - let cmp = bx.fcmp(RealPredicate::RealOEQ, x, x); - bx.select(cmp, s1, zero) - } else { - s1 - } -} diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 48d88095855..501544693f1 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -1,18 +1,21 @@ use super::abi::AbiBuilderMethods; use super::asm::AsmBuilderMethods; +use super::consts::ConstMethods; use super::coverageinfo::CoverageInfoBuilderMethods; use super::debuginfo::DebugInfoBuilderMethods; use super::intrinsic::IntrinsicCallMethods; -use super::type_::ArgAbiMethods; +use super::misc::MiscMethods; +use super::type_::{ArgAbiMethods, BaseTypeMethods}; use super::{HasCodegen, StaticBuilderMethods}; use crate::common::{ - AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, + AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind, }; use crate::mir::operand::OperandRef; use crate::mir::place::PlaceRef; use crate::MemFlags; +use rustc_apfloat::{ieee, Float, Round, Status}; use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout}; use rustc_middle::ty::Ty; use rustc_span::Span; @@ -202,6 +205,177 @@ pub trait BuilderMethods<'a, 'tcx>: fn intcast(&mut self, val: Self::Value, dest_ty: Self::Type, is_signed: bool) -> Self::Value; fn pointercast(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + fn cast_float_to_int( + &mut self, + signed: bool, + x: Self::Value, + dest_ty: Self::Type, + ) -> Self::Value { + let in_ty = self.cx().val_ty(x); + let (float_ty, int_ty) = if self.cx().type_kind(dest_ty) == TypeKind::Vector + && self.cx().type_kind(in_ty) == TypeKind::Vector + { + (self.cx().element_type(in_ty), self.cx().element_type(dest_ty)) + } else { + (in_ty, dest_ty) + }; + assert!(matches!(self.cx().type_kind(float_ty), TypeKind::Float | TypeKind::Double)); + assert_eq!(self.cx().type_kind(int_ty), TypeKind::Integer); + + if let Some(false) = self.cx().sess().opts.debugging_opts.saturating_float_casts { + return if signed { self.fptosi(x, dest_ty) } else { self.fptoui(x, dest_ty) }; + } + + let try_sat_result = + if signed { self.fptosi_sat(x, dest_ty) } else { self.fptoui_sat(x, dest_ty) }; + if let Some(try_sat_result) = try_sat_result { + return try_sat_result; + } + + let int_width = self.cx().int_width(int_ty); + let float_width = self.cx().float_width(float_ty); + // LLVM's fpto[su]i returns undef when the input x is infinite, NaN, or does not fit into the + // destination integer type after rounding towards zero. This `undef` value can cause UB in + // safe code (see issue #10184), so we implement a saturating conversion on top of it: + // Semantically, the mathematical value of the input is rounded towards zero to the next + // mathematical integer, and then the result is clamped into the range of the destination + // integer type. Positive and negative infinity are mapped to the maximum and minimum value of + // the destination integer type. NaN is mapped to 0. + // + // Define f_min and f_max as the largest and smallest (finite) floats that are exactly equal to + // a value representable in int_ty. + // They are exactly equal to int_ty::{MIN,MAX} if float_ty has enough significand bits. + // Otherwise, int_ty::MAX must be rounded towards zero, as it is one less than a power of two. + // int_ty::MIN, however, is either zero or a negative power of two and is thus exactly + // representable. Note that this only works if float_ty's exponent range is sufficiently large. + // f16 or 256 bit integers would break this property. Right now the smallest float type is f32 + // with exponents ranging up to 127, which is barely enough for i128::MIN = -2^127. + // On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because + // we're rounding towards zero, we just get float_ty::MAX (which is always an integer). + // This already happens today with u128::MAX = 2^128 - 1 > f32::MAX. + let int_max = |signed: bool, int_width: u64| -> u128 { + let shift_amount = 128 - int_width; + if signed { i128::MAX as u128 >> shift_amount } else { u128::MAX >> shift_amount } + }; + let int_min = |signed: bool, int_width: u64| -> i128 { + if signed { i128::MIN >> (128 - int_width) } else { 0 } + }; + + let compute_clamp_bounds_single = |signed: bool, int_width: u64| -> (u128, u128) { + let rounded_min = + ieee::Single::from_i128_r(int_min(signed, int_width), Round::TowardZero); + assert_eq!(rounded_min.status, Status::OK); + let rounded_max = + ieee::Single::from_u128_r(int_max(signed, int_width), Round::TowardZero); + assert!(rounded_max.value.is_finite()); + (rounded_min.value.to_bits(), rounded_max.value.to_bits()) + }; + let compute_clamp_bounds_double = |signed: bool, int_width: u64| -> (u128, u128) { + let rounded_min = + ieee::Double::from_i128_r(int_min(signed, int_width), Round::TowardZero); + assert_eq!(rounded_min.status, Status::OK); + let rounded_max = + ieee::Double::from_u128_r(int_max(signed, int_width), Round::TowardZero); + assert!(rounded_max.value.is_finite()); + (rounded_min.value.to_bits(), rounded_max.value.to_bits()) + }; + + let maybe_splat = |bx: &mut Self, val| { + if bx.cx().type_kind(dest_ty) == TypeKind::Vector { + bx.vector_splat(bx.vector_length(dest_ty), val) + } else { + val + } + }; + + let float_bits_to_llval = |bx: &mut Self, bits| { + let bits_llval = match float_width { + 32 => bx.cx().const_u32(bits as u32), + 64 => bx.cx().const_u64(bits as u64), + n => bug!("unsupported float width {}", n), + }; + bx.bitcast(bits_llval, float_ty) + }; + let (f_min, f_max) = match float_width { + 32 => compute_clamp_bounds_single(signed, int_width), + 64 => compute_clamp_bounds_double(signed, int_width), + n => bug!("unsupported float width {}", n), + }; + let f_min = float_bits_to_llval(self, f_min); + let f_max = float_bits_to_llval(self, f_max); + let f_min = maybe_splat(self, f_min); + let f_max = maybe_splat(self, f_max); + // To implement saturation, we perform the following steps: + // + // 1. Cast x to an integer with fpto[su]i. This may result in undef. + // 2. Compare x to f_min and f_max, and use the comparison results to select: + // a) int_ty::MIN if x < f_min or x is NaN + // b) int_ty::MAX if x > f_max + // c) the result of fpto[su]i otherwise + // 3. If x is NaN, return 0.0, otherwise return the result of step 2. + // + // This avoids resulting undef because values in range [f_min, f_max] by definition fit into the + // destination type. It creates an undef temporary, but *producing* undef is not UB. Our use of + // undef does not introduce any non-determinism either. + // More importantly, the above procedure correctly implements saturating conversion. + // Proof (sketch): + // If x is NaN, 0 is returned by definition. + // Otherwise, x is finite or infinite and thus can be compared with f_min and f_max. + // This yields three cases to consider: + // (1) if x in [f_min, f_max], the result of fpto[su]i is returned, which agrees with + // saturating conversion for inputs in that range. + // (2) if x > f_max, then x is larger than int_ty::MAX. This holds even if f_max is rounded + // (i.e., if f_max < int_ty::MAX) because in those cases, nextUp(f_max) is already larger + // than int_ty::MAX. Because x is larger than int_ty::MAX, the return value of int_ty::MAX + // is correct. + // (3) if x < f_min, then x is smaller than int_ty::MIN. As shown earlier, f_min exactly equals + // int_ty::MIN and therefore the return value of int_ty::MIN is correct. + // QED. + + let int_max = self.cx().const_uint_big(int_ty, int_max(signed, int_width)); + let int_min = self.cx().const_uint_big(int_ty, int_min(signed, int_width) as u128); + let zero = self.cx().const_uint(int_ty, 0); + let int_max = maybe_splat(self, int_max); + let int_min = maybe_splat(self, int_min); + let zero = maybe_splat(self, zero); + + // Step 1 ... + let fptosui_result = if signed { self.fptosi(x, dest_ty) } else { self.fptoui(x, dest_ty) }; + let less_or_nan = self.fcmp(RealPredicate::RealULT, x, f_min); + let greater = self.fcmp(RealPredicate::RealOGT, x, f_max); + + // Step 2: We use two comparisons and two selects, with %s1 being the + // result: + // %less_or_nan = fcmp ult %x, %f_min + // %greater = fcmp olt %x, %f_max + // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result + // %s1 = select %greater, int_ty::MAX, %s0 + // Note that %less_or_nan uses an *unordered* comparison. This + // comparison is true if the operands are not comparable (i.e., if x is + // NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if + // x is NaN. + // + // Performance note: Unordered comparison can be lowered to a "flipped" + // comparison and a negation, and the negation can be merged into the + // select. Therefore, it not necessarily any more expensive than an + // ordered ("normal") comparison. Whether these optimizations will be + // performed is ultimately up to the backend, but at least x86 does + // perform them. + let s0 = self.select(less_or_nan, int_min, fptosui_result); + let s1 = self.select(greater, int_max, s0); + + // Step 3: NaN replacement. + // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN. + // Therefore we only need to execute this step for signed integer types. + if signed { + // LLVM has no isNaN predicate, so we use (x == x) instead + let cmp = self.fcmp(RealPredicate::RealOEQ, x, x); + self.select(cmp, s1, zero) + } else { + s1 + } + } + fn icmp(&mut self, op: IntPredicate, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn fcmp(&mut self, op: RealPredicate, lhs: Self::Value, rhs: Self::Value) -> Self::Value; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 84cf8878af8..73043857617 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1186,6 +1186,7 @@ symbols! { simd, simd_add, simd_and, + simd_as, simd_bitmask, simd_cast, simd_ceil, diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 6314f2aba4e..4c612ed5be5 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -453,7 +453,7 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) sym::simd_scatter => (3, vec![param(0), param(1), param(2)], tcx.mk_unit()), sym::simd_insert => (2, vec![param(0), tcx.types.u32, param(1)], param(0)), sym::simd_extract => (2, vec![param(0), tcx.types.u32], param(1)), - sym::simd_cast => (2, vec![param(0)], param(1)), + sym::simd_cast | sym::simd_as => (2, vec![param(0)], param(1)), sym::simd_bitmask => (2, vec![param(0)], param(1)), sym::simd_select | sym::simd_select_bitmask => { (2, vec![param(0), param(1), param(1)], param(1)) diff --git a/src/test/ui/simd/intrinsic/generic-as.rs b/src/test/ui/simd/intrinsic/generic-as.rs new file mode 100644 index 00000000000..64aca04d553 --- /dev/null +++ b/src/test/ui/simd/intrinsic/generic-as.rs @@ -0,0 +1,34 @@ +// run-pass + +#![feature(repr_simd, platform_intrinsics)] + +extern "platform-intrinsic" { + fn simd_as(x: T) -> U; +} + +#[derive(Copy, Clone)] +#[repr(simd)] +struct V([T; 2]); + +fn main() { + unsafe { + let u = V::([u32::MIN, u32::MAX]); + let i: V = simd_as(u); + assert_eq!(i.0[0], u.0[0] as i16); + assert_eq!(i.0[1], u.0[1] as i16); + } + + unsafe { + let f = V::([f32::MIN, f32::MAX]); + let i: V = simd_as(f); + assert_eq!(i.0[0], f.0[0] as i16); + assert_eq!(i.0[1], f.0[1] as i16); + } + + unsafe { + let f = V::([f32::MIN, f32::MAX]); + let u: V = simd_as(f); + assert_eq!(u.0[0], f.0[0] as u8); + assert_eq!(u.0[1], f.0[1] as u8); + } +} -- cgit 1.4.1-3-g733a5 From 3cd49767cfd765d377f2349fd7f450258d9b8be8 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Thu, 30 Dec 2021 01:44:47 +0000 Subject: Add pointer-sized integer tests --- src/test/ui/simd/intrinsic/generic-as.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src') diff --git a/src/test/ui/simd/intrinsic/generic-as.rs b/src/test/ui/simd/intrinsic/generic-as.rs index 64aca04d553..a975190a2fa 100644 --- a/src/test/ui/simd/intrinsic/generic-as.rs +++ b/src/test/ui/simd/intrinsic/generic-as.rs @@ -31,4 +31,18 @@ fn main() { assert_eq!(u.0[0], f.0[0] as u8); assert_eq!(u.0[1], f.0[1] as u8); } + + unsafe { + let f = V::([f64::MIN, f64::MAX]); + let i: V = simd_as(f); + assert_eq!(i.0[0], f.0[0] as isize); + assert_eq!(i.0[1], f.0[1] as isize); + } + + unsafe { + let f = V::([f64::MIN, f64::MAX]); + let u: V = simd_as(f); + assert_eq!(u.0[0], f.0[0] as usize); + assert_eq!(u.0[1], f.0[1] as usize); + } } -- cgit 1.4.1-3-g733a5 From 0cf7fd1208f79a0f1cbf6341871bf6eaa0319abb Mon Sep 17 00:00:00 2001 From: Richard Cobbe Date: Mon, 1 Nov 2021 15:49:58 -0700 Subject: Call out to binutils' dlltool for raw-dylib on windows-gnu platforms. --- compiler/rustc_codegen_llvm/src/back/archive.rs | 205 ++++++++++++++++----- compiler/rustc_interface/src/tests.rs | 1 + compiler/rustc_metadata/src/native_libs.rs | 5 - compiler/rustc_session/src/options.rs | 2 + src/test/run-make/raw-dylib-c/Makefile | 7 +- src/test/run-make/raw-dylib-link-ordinal/Makefile | 6 +- .../run-make/raw-dylib-stdcall-ordinal/Makefile | 23 +++ .../run-make/raw-dylib-stdcall-ordinal/driver.rs | 5 + .../raw-dylib-stdcall-ordinal/expected_output.txt | 2 + .../raw-dylib-stdcall-ordinal/exporter-gnu.def | 4 + .../raw-dylib-stdcall-ordinal/exporter-msvc.def | 4 + .../run-make/raw-dylib-stdcall-ordinal/exporter.c | 11 ++ src/test/run-make/raw-dylib-stdcall-ordinal/lib.rs | 20 ++ .../feature-gate-raw-dylib-windows-gnu.rs | 8 - .../feature-gate-raw-dylib-windows-gnu.stderr | 18 -- .../feature-gate-raw-dylib-windows-msvc.rs | 7 - .../feature-gate-raw-dylib-windows-msvc.stderr | 12 -- .../ui/feature-gates/feature-gate-raw-dylib.rs | 6 + .../ui/feature-gates/feature-gate-raw-dylib.stderr | 12 ++ .../ui/rfc-2627-raw-dylib/link-ordinal-multiple.rs | 2 +- .../ui/rfc-2627-raw-dylib/multiple-declarations.rs | 3 +- .../multiple-declarations.stderr | 4 +- .../ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.rs | 8 - .../rfc-2627-raw-dylib/raw-dylib-msvc-only.stderr | 17 -- src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs | 3 +- .../ui/rfc-2627-raw-dylib/unsupported-abi.stderr | 2 +- 26 files changed, 268 insertions(+), 129 deletions(-) create mode 100644 src/test/run-make/raw-dylib-stdcall-ordinal/Makefile create mode 100644 src/test/run-make/raw-dylib-stdcall-ordinal/driver.rs create mode 100644 src/test/run-make/raw-dylib-stdcall-ordinal/expected_output.txt create mode 100644 src/test/run-make/raw-dylib-stdcall-ordinal/exporter-gnu.def create mode 100644 src/test/run-make/raw-dylib-stdcall-ordinal/exporter-msvc.def create mode 100644 src/test/run-make/raw-dylib-stdcall-ordinal/exporter.c create mode 100644 src/test/run-make/raw-dylib-stdcall-ordinal/lib.rs delete mode 100644 src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.rs delete mode 100644 src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.stderr delete mode 100644 src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.rs delete mode 100644 src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.stderr create mode 100644 src/test/ui/feature-gates/feature-gate-raw-dylib.rs create mode 100644 src/test/ui/feature-gates/feature-gate-raw-dylib.stderr delete mode 100644 src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.rs delete mode 100644 src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.stderr (limited to 'src') diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 2fb5a0f9faf..5703a72c686 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -1,6 +1,7 @@ //! A helper class for dealing with static archives -use std::ffi::{CStr, CString}; +use std::env; +use std::ffi::{CStr, CString, OsString}; use std::io; use std::mem; use std::path::{Path, PathBuf}; @@ -158,54 +159,127 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { output_path.with_extension("lib") }; - // we've checked for \0 characters in the library name already - let dll_name_z = CString::new(lib_name).unwrap(); - // All import names are Rust identifiers and therefore cannot contain \0 characters. - // FIXME: when support for #[link_name] implemented, ensure that import.name values don't - // have any \0 characters - let import_name_and_ordinal_vector: Vec<(CString, Option)> = dll_imports + let mingw_gnu_toolchain = self.config.sess.target.llvm_target.ends_with("pc-windows-gnu"); + + let import_name_and_ordinal_vector: Vec<(String, Option)> = dll_imports .iter() .map(|import: &DllImport| { if self.config.sess.target.arch == "x86" { - (LlvmArchiveBuilder::i686_decorated_name(import), import.ordinal) + ( + LlvmArchiveBuilder::i686_decorated_name(import, mingw_gnu_toolchain), + import.ordinal, + ) } else { - (CString::new(import.name.to_string()).unwrap(), import.ordinal) + (import.name.to_string(), import.ordinal) } }) .collect(); - let output_path_z = rustc_fs_util::path_to_c_string(&output_path); + if mingw_gnu_toolchain { + // The binutils linker used on -windows-gnu targets cannot read the import + // libraries generated by LLVM: in our attempts, the linker produced an .EXE + // that loaded but crashed with an AV upon calling one of the imported + // functions. Therefore, use binutils to create the import library instead, + // by writing a .DEF file to the temp dir and calling binutils's dlltool. + let def_file_path = + tmpdir.as_ref().join(format!("{}_imports", lib_name)).with_extension("def"); + + let def_file_content = format!( + "EXPORTS\n{}", + import_name_and_ordinal_vector + .into_iter() + .map(|(name, ordinal)| { + match ordinal { + Some(n) => format!("{} @{} NONAME", name, n), + None => name, + } + }) + .collect::>() + .join("\n") + ); - tracing::trace!("invoking LLVMRustWriteImportLibrary"); - tracing::trace!(" dll_name {:#?}", dll_name_z); - tracing::trace!(" output_path {}", output_path.display()); - tracing::trace!( - " import names: {}", - dll_imports.iter().map(|import| import.name.to_string()).collect::>().join(", "), - ); + match std::fs::write(&def_file_path, def_file_content) { + Ok(_) => {} + Err(e) => { + self.config.sess.fatal(&format!("Error writing .DEF file: {}", e)); + } + }; - let ffi_exports: Vec = import_name_and_ordinal_vector - .iter() - .map(|(name_z, ordinal)| LLVMRustCOFFShortExport::new(name_z.as_ptr(), *ordinal)) - .collect(); - let result = unsafe { - crate::llvm::LLVMRustWriteImportLibrary( - dll_name_z.as_ptr(), - output_path_z.as_ptr(), - ffi_exports.as_ptr(), - ffi_exports.len(), - llvm_machine_type(&self.config.sess.target.arch) as u16, - !self.config.sess.target.is_like_msvc, - ) - }; + let dlltool = find_binutils_dlltool(self.config.sess); + let result = std::process::Command::new(dlltool) + .args([ + "-d", + def_file_path.to_str().unwrap(), + "-D", + lib_name, + "-l", + output_path.to_str().unwrap(), + ]) + .output(); + + match result { + Err(e) => { + self.config.sess.fatal(&format!("Error calling dlltool: {}", e.to_string())); + } + Ok(output) if !output.status.success() => self.config.sess.fatal(&format!( + "Dlltool could not create import library: {}\n{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + )), + _ => {} + } + } else { + // we've checked for \0 characters in the library name already + let dll_name_z = CString::new(lib_name).unwrap(); + + let output_path_z = rustc_fs_util::path_to_c_string(&output_path); + + tracing::trace!("invoking LLVMRustWriteImportLibrary"); + tracing::trace!(" dll_name {:#?}", dll_name_z); + tracing::trace!(" output_path {}", output_path.display()); + tracing::trace!( + " import names: {}", + dll_imports + .iter() + .map(|import| import.name.to_string()) + .collect::>() + .join(", "), + ); - if result == crate::llvm::LLVMRustResult::Failure { - self.config.sess.fatal(&format!( - "Error creating import library for {}: {}", - lib_name, - llvm::last_error().unwrap_or("unknown LLVM error".to_string()) - )); - } + // All import names are Rust identifiers and therefore cannot contain \0 characters. + // FIXME: when support for #[link_name] is implemented, ensure that the import names + // still don't contain any \0 characters. Also need to check that the names don't + // contain substrings like " @" or "NONAME" that are keywords or otherwise reserved + // in definition files. + let cstring_import_name_and_ordinal_vector: Vec<(CString, Option)> = + import_name_and_ordinal_vector + .into_iter() + .map(|(name, ordinal)| (CString::new(name).unwrap(), ordinal)) + .collect(); + + let ffi_exports: Vec = cstring_import_name_and_ordinal_vector + .iter() + .map(|(name_z, ordinal)| LLVMRustCOFFShortExport::new(name_z.as_ptr(), *ordinal)) + .collect(); + let result = unsafe { + crate::llvm::LLVMRustWriteImportLibrary( + dll_name_z.as_ptr(), + output_path_z.as_ptr(), + ffi_exports.as_ptr(), + ffi_exports.len(), + llvm_machine_type(&self.config.sess.target.arch) as u16, + !self.config.sess.target.is_like_msvc, + ) + }; + + if result == crate::llvm::LLVMRustResult::Failure { + self.config.sess.fatal(&format!( + "Error creating import library for {}: {}", + lib_name, + llvm::last_error().unwrap_or("unknown LLVM error".to_string()) + )); + } + }; self.add_archive(&output_path, |_| false).unwrap_or_else(|e| { self.config.sess.fatal(&format!( @@ -332,22 +406,61 @@ impl<'a> LlvmArchiveBuilder<'a> { } } - fn i686_decorated_name(import: &DllImport) -> CString { + fn i686_decorated_name(import: &DllImport, mingw: bool) -> String { let name = import.name; - // We verified during construction that `name` does not contain any NULL characters, so the - // conversion to CString is guaranteed to succeed. - CString::new(match import.calling_convention { - DllCallingConvention::C => format!("_{}", name), - DllCallingConvention::Stdcall(arg_list_size) => format!("_{}@{}", name, arg_list_size), + let prefix = if mingw { "" } else { "_" }; + + match import.calling_convention { + DllCallingConvention::C => format!("{}{}", prefix, name), + DllCallingConvention::Stdcall(arg_list_size) => { + format!("{}{}@{}", prefix, name, arg_list_size) + } DllCallingConvention::Fastcall(arg_list_size) => format!("@{}@{}", name, arg_list_size), DllCallingConvention::Vectorcall(arg_list_size) => { format!("{}@@{}", name, arg_list_size) } - }) - .unwrap() + } } } fn string_to_io_error(s: String) -> io::Error { io::Error::new(io::ErrorKind::Other, format!("bad archive: {}", s)) } + +fn find_binutils_dlltool(sess: &Session) -> OsString { + assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc); + if let Some(dlltool_path) = &sess.opts.debugging_opts.dlltool { + return dlltool_path.clone().into_os_string(); + } + + let mut tool_name: OsString = if sess.host.arch != sess.target.arch { + // We are cross-compiling, so we need the tool with the prefix matching our target + if sess.target.arch == "x86" { + "i686-w64-mingw32-dlltool" + } else { + "x86_64-w64-mingw32-dlltool" + } + } else { + // We are not cross-compiling, so we just want `dlltool` + "dlltool" + } + .into(); + + if sess.host.options.is_like_windows { + // If we're compiling on Windows, add the .exe suffix + tool_name.push(".exe"); + } + + // NOTE: it's not clear how useful it is to explicitly search PATH. + for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { + let full_path = dir.join(&tool_name); + if full_path.is_file() { + return full_path.into_os_string(); + } + } + + // The user didn't specify the location of the dlltool binary, and we weren't able + // to find the appropriate one on the PATH. Just return the name of the tool + // and let the invocation fail with a hopefully useful error message. + tool_name +} diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 2d3cb52f5fd..23946ce3c5c 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -640,6 +640,7 @@ fn test_debugging_options_tracking_hash() { untracked!(borrowck, String::from("other")); untracked!(deduplicate_diagnostics, false); untracked!(dep_tasks, true); + untracked!(dlltool, Some(PathBuf::from("custom_dlltool.exe"))); untracked!(dont_buffer_diagnostics, true); untracked!(dump_dep_graph, true); untracked!(dump_mir, Some(String::from("abc"))); diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 2431b819a3f..e75478375bb 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -274,11 +274,6 @@ impl Collector<'tcx> { span, "`#[link(...)]` with `kind = \"raw-dylib\"` only supported on Windows", ); - } else if !self.tcx.sess.target.options.is_like_msvc { - self.tcx.sess.span_warn( - span, - "`#[link(...)]` with `kind = \"raw-dylib\"` not supported on windows-gnu", - ); } if lib_name.as_str().contains('\0') { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index e894e46a301..e487598f057 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1073,6 +1073,8 @@ options! { dep_tasks: bool = (false, parse_bool, [UNTRACKED], "print tasks that execute and the color their dep node gets (requires debug build) \ (default: no)"), + dlltool: Option = (None, parse_opt_pathbuf, [UNTRACKED], + "import library generation tool (windows-gnu only)"), dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED], "emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) \ (default: no)"), diff --git a/src/test/run-make/raw-dylib-c/Makefile b/src/test/run-make/raw-dylib-c/Makefile index 26ab4d34764..166305672e6 100644 --- a/src/test/run-make/raw-dylib-c/Makefile +++ b/src/test/run-make/raw-dylib-c/Makefile @@ -1,14 +1,19 @@ # Test the behavior of #[link(.., kind = "raw-dylib")] on windows-msvc -# only-windows-msvc +# only-windows -include ../../run-make-fulldeps/tools.mk all: $(call COMPILE_OBJ,"$(TMPDIR)"/extern_1.obj,extern_1.c) $(call COMPILE_OBJ,"$(TMPDIR)"/extern_2.obj,extern_2.c) +ifdef IS_MSVC $(CC) "$(TMPDIR)"/extern_1.obj -link -dll -out:"$(TMPDIR)"/extern_1.dll $(CC) "$(TMPDIR)"/extern_2.obj -link -dll -out:"$(TMPDIR)"/extern_2.dll +else + $(CC) "$(TMPDIR)"/extern_1.obj -shared -o "$(TMPDIR)"/extern_1.dll + $(CC) "$(TMPDIR)"/extern_2.obj -shared -o "$(TMPDIR)"/extern_2.dll +endif $(RUSTC) --crate-type lib --crate-name raw_dylib_test lib.rs $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" "$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt diff --git a/src/test/run-make/raw-dylib-link-ordinal/Makefile b/src/test/run-make/raw-dylib-link-ordinal/Makefile index 04b257d0632..0e84a749b05 100644 --- a/src/test/run-make/raw-dylib-link-ordinal/Makefile +++ b/src/test/run-make/raw-dylib-link-ordinal/Makefile @@ -1,12 +1,16 @@ # Test the behavior of #[link(.., kind = "raw-dylib")] and #[link_ordinal] on windows-msvc -# only-windows-msvc +# only-windows -include ../../run-make-fulldeps/tools.mk all: $(call COMPILE_OBJ,"$(TMPDIR)"/exporter.obj,exporter.c) +ifdef IS_MSVC $(CC) "$(TMPDIR)"/exporter.obj exporter.def -link -dll -out:"$(TMPDIR)"/exporter.dll +else + $(CC) "$(TMPDIR)"/exporter.obj exporter.def -shared -o "$(TMPDIR)"/exporter.dll +endif $(RUSTC) --crate-type lib --crate-name raw_dylib_test lib.rs $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" "$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/Makefile b/src/test/run-make/raw-dylib-stdcall-ordinal/Makefile new file mode 100644 index 00000000000..69f62669d62 --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/Makefile @@ -0,0 +1,23 @@ +# Test the behavior of #[link(.., kind = "raw-dylib")], #[link_ordinal], and alternative calling conventions on i686 windows. + +# only-x86 +# only-windows + +-include ../../run-make-fulldeps/tools.mk + +all: + $(call COMPILE_OBJ,"$(TMPDIR)"/exporter.obj,exporter.c) +ifdef IS_MSVC + $(CC) "$(TMPDIR)"/exporter.obj exporter-msvc.def -link -dll -out:"$(TMPDIR)"/exporter.dll +else + $(CC) "$(TMPDIR)"/exporter.obj exporter-gnu.def -shared -o "$(TMPDIR)"/exporter.dll +endif + $(RUSTC) --crate-type lib --crate-name raw_dylib_test lib.rs + $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" + "$(TMPDIR)"/driver > "$(TMPDIR)"/actual_output.txt + +ifdef RUSTC_BLESS_TEST + cp "$(TMPDIR)"/actual_output.txt expected_output.txt +else + $(DIFF) expected_output.txt "$(TMPDIR)"/actual_output.txt +endif diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/driver.rs b/src/test/run-make/raw-dylib-stdcall-ordinal/driver.rs new file mode 100644 index 00000000000..4059ede11fc --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/driver.rs @@ -0,0 +1,5 @@ +extern crate raw_dylib_test; + +fn main() { + raw_dylib_test::library_function(); +} diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/expected_output.txt b/src/test/run-make/raw-dylib-stdcall-ordinal/expected_output.txt new file mode 100644 index 00000000000..20157763745 --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/expected_output.txt @@ -0,0 +1,2 @@ +exported_function_stdcall(6) +exported_function_fastcall(125) diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-gnu.def b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-gnu.def new file mode 100644 index 00000000000..8d28d714b7e --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-gnu.def @@ -0,0 +1,4 @@ +LIBRARY exporter +EXPORTS + exported_function_stdcall@4 @15 NONAME + @exported_function_fastcall@4 @18 NONAME \ No newline at end of file diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-msvc.def b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-msvc.def new file mode 100644 index 00000000000..5a4c79a58ed --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-msvc.def @@ -0,0 +1,4 @@ +LIBRARY exporter +EXPORTS + _exported_function_stdcall@4 @15 NONAME + @exported_function_fastcall@4 @18 NONAME \ No newline at end of file diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/exporter.c b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter.c new file mode 100644 index 00000000000..1fb45bf010f --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter.c @@ -0,0 +1,11 @@ +#include + +void __stdcall exported_function_stdcall(int i) { + printf("exported_function_stdcall(%d)\n", i); + fflush(stdout); +} + +void __fastcall exported_function_fastcall(int i) { + printf("exported_function_fastcall(%d)\n", i); + fflush(stdout); +} diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/lib.rs b/src/test/run-make/raw-dylib-stdcall-ordinal/lib.rs new file mode 100644 index 00000000000..07dd3d7be9b --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/lib.rs @@ -0,0 +1,20 @@ +#![feature(raw_dylib)] + +#[link(name = "exporter", kind = "raw-dylib")] +extern "stdcall" { + #[link_ordinal(15)] + fn imported_function_stdcall(i: i32); +} + +#[link(name = "exporter", kind = "raw-dylib")] +extern "fastcall" { + #[link_ordinal(18)] + fn imported_function_fastcall(i: i32); +} + +pub fn library_function() { + unsafe { + imported_function_stdcall(6); + imported_function_fastcall(125); + } +} diff --git a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.rs b/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.rs deleted file mode 100644 index 33f9c539313..00000000000 --- a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.rs +++ /dev/null @@ -1,8 +0,0 @@ -// gate-test-raw_dylib -// only-windows-gnu -#[link(name = "foo", kind = "raw-dylib")] -//~^ ERROR: kind="raw-dylib" is unstable -//~| WARNING: `#[link(...)]` with `kind = "raw-dylib"` not supported on windows-gnu -extern "C" {} - -fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.stderr b/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.stderr deleted file mode 100644 index 14dfadf4126..00000000000 --- a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.stderr +++ /dev/null @@ -1,18 +0,0 @@ -warning: `#[link(...)]` with `kind = "raw-dylib"` not supported on windows-gnu - --> $DIR/feature-gate-raw-dylib-windows-gnu.rs:3:1 - | -LL | #[link(name = "foo", kind = "raw-dylib")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0658]: kind="raw-dylib" is unstable - --> $DIR/feature-gate-raw-dylib-windows-gnu.rs:3:1 - | -LL | #[link(name = "foo", kind = "raw-dylib")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #58713 for more information - = help: add `#![feature(raw_dylib)]` to the crate attributes to enable - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.rs b/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.rs deleted file mode 100644 index 49de24ea9ab..00000000000 --- a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.rs +++ /dev/null @@ -1,7 +0,0 @@ -// gate-test-raw_dylib -// only-windows-msvc -#[link(name = "foo", kind = "raw-dylib")] -//~^ ERROR: kind="raw-dylib" is unstable -extern "C" {} - -fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.stderr b/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.stderr deleted file mode 100644 index 11988080812..00000000000 --- a/src/test/ui/feature-gates/feature-gate-raw-dylib-windows-msvc.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: kind="raw-dylib" is unstable - --> $DIR/feature-gate-raw-dylib-windows-msvc.rs:3:1 - | -LL | #[link(name = "foo", kind = "raw-dylib")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #58713 for more information - = help: add `#![feature(raw_dylib)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-raw-dylib.rs b/src/test/ui/feature-gates/feature-gate-raw-dylib.rs new file mode 100644 index 00000000000..995d9ced480 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-raw-dylib.rs @@ -0,0 +1,6 @@ +// only-windows +#[link(name = "foo", kind = "raw-dylib")] +//~^ ERROR: kind="raw-dylib" is unstable +extern "C" {} + +fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-raw-dylib.stderr b/src/test/ui/feature-gates/feature-gate-raw-dylib.stderr new file mode 100644 index 00000000000..bb64af38b2c --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-raw-dylib.stderr @@ -0,0 +1,12 @@ +error[E0658]: kind="raw-dylib" is unstable + --> $DIR/feature-gate-raw-dylib.rs:2:1 + | +LL | #[link(name = "foo", kind = "raw-dylib")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #58713 for more information + = help: add `#![feature(raw_dylib)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-multiple.rs b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-multiple.rs index 98741212677..45796f077c2 100644 --- a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-multiple.rs +++ b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-multiple.rs @@ -1,4 +1,4 @@ -// only-windows-msvc +// only-windows #![feature(raw_dylib)] //~^ WARN the feature `raw_dylib` is incomplete diff --git a/src/test/ui/rfc-2627-raw-dylib/multiple-declarations.rs b/src/test/ui/rfc-2627-raw-dylib/multiple-declarations.rs index d02bebc9d61..13c9aa01e34 100644 --- a/src/test/ui/rfc-2627-raw-dylib/multiple-declarations.rs +++ b/src/test/ui/rfc-2627-raw-dylib/multiple-declarations.rs @@ -1,4 +1,5 @@ -// only-i686-pc-windows-msvc +// only-x86 +// only-windows // compile-flags: --crate-type lib --emit link #![allow(clashing_extern_declarations)] #![feature(raw_dylib)] diff --git a/src/test/ui/rfc-2627-raw-dylib/multiple-declarations.stderr b/src/test/ui/rfc-2627-raw-dylib/multiple-declarations.stderr index a9cfd6b23f9..93ca8f4d8d4 100644 --- a/src/test/ui/rfc-2627-raw-dylib/multiple-declarations.stderr +++ b/src/test/ui/rfc-2627-raw-dylib/multiple-declarations.stderr @@ -1,5 +1,5 @@ warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/multiple-declarations.rs:4:12 + --> $DIR/multiple-declarations.rs:5:12 | LL | #![feature(raw_dylib)] | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | #![feature(raw_dylib)] = note: see issue #58713 for more information error: multiple declarations of external function `f` from library `foo.dll` have different calling conventions - --> $DIR/multiple-declarations.rs:14:9 + --> $DIR/multiple-declarations.rs:15:9 | LL | fn f(x: i32); | ^^^^^^^^^^^^^ diff --git a/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.rs b/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.rs deleted file mode 100644 index e9690f03f45..00000000000 --- a/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.rs +++ /dev/null @@ -1,8 +0,0 @@ -// only-windows-gnu -// check-pass -// compile-flags: --crate-type lib -#![feature(raw_dylib)] -//~^ WARNING: the feature `raw_dylib` is incomplete -#[link(name = "foo", kind = "raw-dylib")] -//~^ WARNING: `#[link(...)]` with `kind = "raw-dylib"` not supported on windows-gnu -extern "C" {} diff --git a/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.stderr b/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.stderr deleted file mode 100644 index 6e24112b3c3..00000000000 --- a/src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.stderr +++ /dev/null @@ -1,17 +0,0 @@ -warning: the feature `raw_dylib` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/raw-dylib-msvc-only.rs:4:12 - | -LL | #![feature(raw_dylib)] - | ^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #58713 for more information - -warning: `#[link(...)]` with `kind = "raw-dylib"` not supported on windows-gnu - --> $DIR/raw-dylib-msvc-only.rs:6:1 - | -LL | #[link(name = "foo", kind = "raw-dylib")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: 2 warnings emitted - diff --git a/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs b/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs index e5a5ac2eb2b..dc647fd63f5 100644 --- a/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs +++ b/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.rs @@ -1,4 +1,5 @@ -// only-x86_64-pc-windows-msvc +// only-x86_64 +// only-windows // compile-flags: --crate-type lib --emit link #![allow(incomplete_features)] #![feature(raw_dylib)] diff --git a/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr b/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr index fc9008128ae..d8a2a6af9c1 100644 --- a/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr +++ b/src/test/ui/rfc-2627-raw-dylib/unsupported-abi.stderr @@ -1,5 +1,5 @@ error: ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture - --> $DIR/unsupported-abi.rs:7:5 + --> $DIR/unsupported-abi.rs:8:5 | LL | fn f(x: i32); | ^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 7dac6260920bc4fbd6988609bd80af05aa60a400 Mon Sep 17 00:00:00 2001 From: b-naber Date: Tue, 11 Jan 2022 20:25:57 +0100 Subject: add and update tests --- src/test/ui/const-generics/issues/issue-92186.rs | 12 ++++++++++++ .../types-mismatch-const-args.full.stderr | 17 ++++++++++++++--- .../const-generics/types-mismatch-const-args.min.stderr | 13 ++++++++++++- src/test/ui/const-generics/types-mismatch-const-args.rs | 2 ++ 4 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/const-generics/issues/issue-92186.rs (limited to 'src') diff --git a/src/test/ui/const-generics/issues/issue-92186.rs b/src/test/ui/const-generics/issues/issue-92186.rs new file mode 100644 index 00000000000..9ced4667d24 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-92186.rs @@ -0,0 +1,12 @@ +// check-pass + +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +pub struct Foo; +pub trait Bar {} + +impl Bar for Foo<{ 1 }> {} +impl Bar for Foo<{ 2 }> {} + +fn main() {} diff --git a/src/test/ui/const-generics/types-mismatch-const-args.full.stderr b/src/test/ui/const-generics/types-mismatch-const-args.full.stderr index 565c9ba1ff1..4d6b752867f 100644 --- a/src/test/ui/const-generics/types-mismatch-const-args.full.stderr +++ b/src/test/ui/const-generics/types-mismatch-const-args.full.stderr @@ -15,9 +15,20 @@ LL | let _: A<'a, u16, {2u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data | | | expected due to this | - = note: expected struct `A<'a, u16, {2u32}, {3u32}>` - found struct `A<'b, u32, {2u32}, {3u32}>` + = note: expected struct `A<'a, u16, _, _>` + found struct `A<'b, u32, _, _>` -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/types-mismatch-const-args.rs:18:41 + | +LL | let _: A<'a, u16, {4u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data: PhantomData }; + | -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u16`, found `u32` + | | + | expected due to this + | + = note: expected struct `A<'a, u16, 4_u32, _>` + found struct `A<'b, u32, 2_u32, _>` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/types-mismatch-const-args.min.stderr b/src/test/ui/const-generics/types-mismatch-const-args.min.stderr index ec9221d2486..8b60238cb0c 100644 --- a/src/test/ui/const-generics/types-mismatch-const-args.min.stderr +++ b/src/test/ui/const-generics/types-mismatch-const-args.min.stderr @@ -20,6 +20,17 @@ LL | let _: A<'a, u16, {2u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data = note: expected struct `A<'a, u16, _, _>` found struct `A<'b, u32, _, _>` -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/types-mismatch-const-args.rs:18:41 + | +LL | let _: A<'a, u16, {4u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data: PhantomData }; + | -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u16`, found `u32` + | | + | expected due to this + | + = note: expected struct `A<'a, u16, 4_u32, _>` + found struct `A<'b, u32, 2_u32, _>` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/types-mismatch-const-args.rs b/src/test/ui/const-generics/types-mismatch-const-args.rs index c2092c4268e..43ef28b268f 100644 --- a/src/test/ui/const-generics/types-mismatch-const-args.rs +++ b/src/test/ui/const-generics/types-mismatch-const-args.rs @@ -15,6 +15,8 @@ fn a<'a, 'b>() { //~^ ERROR mismatched types let _: A<'a, u16, {2u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data: PhantomData }; //~^ ERROR mismatched types + let _: A<'a, u16, {4u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data: PhantomData }; + //~^ ERROR mismatched types } pub fn main() {} -- cgit 1.4.1-3-g733a5 From 6a5f8b1aef1417d7dc85b5d0a229d2db1930eb7c Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Thu, 6 Jan 2022 19:48:24 -0500 Subject: Simplify and unify rustdoc sidebar styles This switches to just use size, weight, and spacing to distinguish headings in the sidebar. We no longer use boxes, horizontal bars, or centering to distinguish headings. This makes it much easier to understand the hierarchy of headings, and reduces visual noise. I also refactored how the mobile topbar works. Previously, we tried to shift around elements from the sidebar to make the topbar. Now, the topbar gets its own elements, which can be styled on their own. This makes styling and reasoning about those elements simpler. Because the heading font sizes are bigger, increase the sidebar width slightly. As a very minor change, removed version from the "All types" page. It's now only on the crate page. --- src/librustdoc/html/render/context.rs | 12 +- src/librustdoc/html/render/mod.rs | 31 +-- src/librustdoc/html/static/css/rustdoc.css | 304 +++++++++------------- src/librustdoc/html/static/css/themes/ayu.css | 27 +- src/librustdoc/html/static/css/themes/dark.css | 27 +- src/librustdoc/html/static/css/themes/light.css | 27 +- src/librustdoc/html/static/js/main.js | 60 ++--- src/librustdoc/html/static/js/source-script.js | 2 +- src/librustdoc/templates/page.html | 14 +- src/test/rustdoc-gui/anchors.goml | 2 +- src/test/rustdoc-gui/code-blocks-overflow.goml | 2 +- src/test/rustdoc-gui/docblock-table-overflow.goml | 4 +- src/test/rustdoc-gui/headings.goml | 2 +- src/test/rustdoc-gui/item-info-width.goml | 2 +- src/test/rustdoc-gui/search-result-display.goml | 2 +- src/test/rustdoc-gui/sidebar-mobile.goml | 23 +- src/test/rustdoc-gui/sidebar-source-code.goml | 4 + src/test/rustdoc-gui/sidebar.goml | 16 +- src/test/rustdoc-gui/type-declation-overflow.goml | 11 +- src/test/rustdoc/cap-lints.rs | 2 +- src/test/rustdoc/crate-version-escape.rs | 3 +- src/test/rustdoc/crate-version.rs | 2 +- src/test/rustdoc/titles.rs | 7 +- src/test/rustdoc/typedef.rs | 2 +- 24 files changed, 228 insertions(+), 360 deletions(-) (limited to 'src') diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 865e14f694d..2455d56bd2b 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -554,16 +554,8 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { extra_scripts: &[], static_extra_scripts: &[], }; - let sidebar = if let Some(ref version) = self.shared.cache.crate_version { - format!( - "

Crate {}

\ -
\ -

Version {}

\ -
\ -

Back to index

", - crate_name, - Escape(version), - ) + let sidebar = if self.shared.cache.crate_version.is_some() { + format!("

Crate {}

", crate_name) } else { String::new() }; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index a79b3e8ed08..29a793f3110 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1744,13 +1744,6 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { buffer, "

{}{}

", match *it.kind { - clean::StructItem(..) => "Struct ", - clean::TraitItem(..) => "Trait ", - clean::PrimitiveItem(..) => "Primitive Type ", - clean::UnionItem(..) => "Union ", - clean::EnumItem(..) => "Enum ", - clean::TypedefItem(..) => "Type Definition ", - clean::ForeignTypeItem => "Foreign Type ", clean::ModuleItem(..) => if it.is_crate() { "Crate " @@ -1763,26 +1756,14 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { ); } + buffer.write_str("
"); if it.is_crate() { + write!(buffer, "
    "); if let Some(ref version) = cx.cache().crate_version { - write!( - buffer, - "
    \ -
    \ -

    Version {}

    \ -
    ", - Escape(version), - ); + write!(buffer, "
  • Version {}
  • ", Escape(version)); } - } - - buffer.write_str("
    "); - if it.is_crate() { - write!( - buffer, - "

    See all {}'s items

    ", - it.name.as_ref().expect("crates always have a name"), - ); + write!(buffer, "
  • All Items
  • "); + buffer.write_str("
"); } match *it.kind { @@ -1806,7 +1787,7 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { // to navigate the documentation (though slightly inefficiently). if !it.is_mod() { - buffer.write_str("

Other items in
"); + buffer.write_str("

In "); for (i, name) in cx.current.iter().take(parentlen).enumerate() { if i > 0 { buffer.write_str("::"); diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index bfdce74de67..dbc068ce6b1 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -169,8 +169,7 @@ h1.fqn { section hierarchies. */ h2, .top-doc h3, -.top-doc h4, -.sidebar .others h3 { +.top-doc h4 { border-bottom: 1px solid; } h3.code-header { @@ -206,7 +205,11 @@ div.impl-items > div { } h1, h2, h3, h4, h5, h6, -.sidebar, a.source, .search-input, .search-results .result-name, +.sidebar, +.mobile-topbar, +a.source, +.search-input, +.search-results .result-name, .content table td:first-child > a, .item-left > a, .out-of-band, @@ -261,6 +264,11 @@ textarea { margin: 0; } +button { + /* Buttons on Safari have different default padding than other platforms. Make them the same. */ + padding: 1px 6px; +} + /* end tweaks for normalize.css 8 */ .rustdoc { @@ -359,15 +367,25 @@ nav.sub { } .sidebar { - width: 200px; + font-size: 0.9rem; + width: 250px; + min-width: 200px; overflow-y: scroll; position: sticky; - min-width: 200px; height: 100vh; top: 0; left: 0; } +.sidebar-elems, +.sidebar > .location { + padding-left: 24px; +} + +.sidebar .location { + overflow-wrap: anywhere; +} + .rustdoc.source .sidebar { width: 50px; min-width: 0px; @@ -396,6 +414,10 @@ nav.sub { visibility: visible; } +#all-types { + margin-top: 1em; +} + /* Improve the scrollbar display on firefox */ * { scrollbar-width: initial; @@ -415,48 +437,28 @@ nav.sub { -webkit-box-shadow: inset 0; } -.sidebar .block > ul > li { - margin-right: -10px; -} - /* Everything else */ .hidden { display: none !important; } -.logo-container { +.sidebar .logo-container { display: flex; margin-top: 10px; margin-bottom: 10px; justify-content: center; } +.version { + overflow-wrap: break-word; +} + .logo-container > img { height: 100px; width: 100px; } -.sidebar .location { - border: 1px solid; - font-size: 1.0625rem; - margin: 30px 10px 20px 10px; - text-align: center; - word-wrap: break-word; - font-weight: inherit; - padding: 0; -} - -.sidebar .version { - font-size: 0.9375rem; - text-align: center; - border-bottom: 1px solid; - overflow-wrap: break-word; - overflow-wrap: anywhere; - word-wrap: break-word; /* deprecated */ - word-break: break-word; /* Chrome, non-standard */ -} - .location:empty { border: none; } @@ -470,48 +472,45 @@ nav.sub { .block { padding: 0; - margin-bottom: 14px; -} -.block h2, .block h3 { - text-align: center; } .block ul, .block li { - margin: 0 10px; padding: 0; list-style: none; } .block a { display: block; + padding: 0.3em; + margin-left: -0.3em; + text-overflow: ellipsis; overflow: hidden; - line-height: 15px; - padding: 7px 5px; - font-size: 0.875rem; - font-weight: 300; - transition: border 500ms ease-out; } -.sidebar-title { - border-top: 1px solid; - border-bottom: 1px solid; - text-align: center; - font-size: 1.0625rem; - margin-bottom: 5px; - font-weight: inherit; +.sidebar h2 { + border-bottom: none; + font-weight: 500; padding: 0; + margin: 0; + margin-top: 1rem; + margin-bottom: 1rem; } -.sidebar-links { - margin-bottom: 15px; +.sidebar h3 { + font-size: 1.1rem; + font-weight: 500; + padding: 0; + margin: 0; + margin-top: 0.5rem; + margin-bottom: 0.25rem; } -.sidebar-links > a { - padding-left: 10px; - width: 100%; +.sidebar-links, +.block { + margin-bottom: 2em; } -.sidebar-menu { +.mobile-topbar { display: none; } @@ -784,13 +783,12 @@ nav.sub { margin-top: 0; } -nav:not(.sidebar) { +nav.sub { flex-grow: 1; - border-bottom: 1px solid; padding-bottom: 10px; margin-bottom: 25px; } -.source nav:not(.sidebar).sub { +.source nav.sub { margin-left: 32px; } nav.main { @@ -1395,18 +1393,6 @@ pre.rust { margin-left: 5px; } -#all-types { - text-align: center; - border: 1px solid; - margin: 0 10px; - margin-bottom: 10px; - display: block; - border-radius: 7px; -} -#all-types > p { - margin: 5px 0; -} - #sidebar-toggle { position: sticky; top: 0; @@ -1752,8 +1738,12 @@ details.rustdoc-toggle[open] > summary.hideme::after { } @media (max-width: 700px) { - body { + .rustdoc { padding-top: 0px; + /* Sidebar should overlay main content, rather than pushing main content to the right. + Turn off `display: flex` on the body element. */ + display: block; + scroll-margin-top: 45px; } main { @@ -1761,134 +1751,110 @@ details.rustdoc-toggle[open] > summary.hideme::after { padding-top: 0px; } - /* Space is at a premium on mobile, so remove the theme-picker icon. */ - #theme-picker { - display: none; - width: 0; - } - .rustdoc { flex-direction: column; } - .rustdoc:not(.source) > .sidebar { - width: 100%; - height: 45px; - min-height: 40px; - max-height: 45px; - margin: 0; - padding: 0 15px; - position: static; - z-index: 11; - overflow-y: hidden; + /* Hide the logo and item name from the sidebar. Those are displayed + in the mobile-topbar instead. */ + .sidebar .sidebar-logo, + .sidebar .location { + display: none; } - .rustdoc.source > .sidebar { + .sidebar-elems { + margin-top: 1em; + } + + .sidebar { position: fixed; - top: 0; - left: 0; + top: 45px; + /* Hide the sidebar offscreen while not in use. Doing this instead of display: none means + the sidebar stays visible for screen readers, which is useful for navigation. */ + left: -1000px; + margin-left: 0; + background-color: rgba(0,0,0,0); margin: 0; + padding: 0; + padding-left: 15px; z-index: 11; - width: 0; } - .sidebar.mobile { - position: sticky !important; + /* The source view uses a different design for the sidebar toggle, and doesn't have a topbar, + so don't bump down the main content or the sidebar. */ + .source main, + .source .sidebar { top: 0; + padding: 0; + } + + .sidebar.shown, + .sidebar.expanded, + .sidebar:focus-within { left: 0; - width: 100%; - margin-left: 0; - background-color: rgba(0,0,0,0); } - .sidebar > .location { - float: right; - margin: 0px; - margin-top: 2px; - padding: 3px 10px 1px 10px; - min-height: 39px; - background: inherit; - text-align: left; - font-size: 1.5rem; + .rustdoc.source > .sidebar { + position: fixed; + margin: 0; + z-index: 11; + width: 0; } - .sidebar .location:empty { - padding: 0; + .mobile-topbar .location { + border: none; + margin: 0; + margin-left: auto; + padding: 0.3em; + padding-right: 0.6em; + text-overflow: ellipsis; + overflow-x: hidden; } - .rustdoc:not(.source) .sidebar .logo-container { - width: 35px; - height: 35px; - margin-top: 5px; - margin-bottom: 5px; - float: left; - margin-left: 50px; + .mobile-topbar .logo-container { + max-height: 45px; } - .sidebar .logo-container > img { + .mobile-topbar .logo-container > img { max-width: 35px; max-height: 35px; + margin-left: 20px; + margin-top: 5px; + margin-bottom: 5px; } - .sidebar-menu { - position: fixed; + .mobile-topbar { + display: flex; + flex-direction: row; + position: sticky; z-index: 10; font-size: 2rem; cursor: pointer; - width: 45px; + height: 45px; + width: 100%; left: 0; top: 0; - text-align: center; - display: block; - border-bottom: 1px solid; - border-right: 1px solid; - height: 45px; } - .rustdoc.source > .sidebar > .sidebar-menu { + .source .mobile-topbar { display: none; } - /* We do NOT hide this element so that alternative device readers still have this information - available. */ - .sidebar-elems { - position: fixed; - z-index: 1; - top: 45px; - bottom: 0; - width: 246px; - /* We move the sidebar to the left by its own width so it doesn't appear. */ - left: -246px; - overflow-y: auto; - border-right: 1px solid; - } - - .sidebar > .block.version { - overflow: hidden; - border-bottom: none; - margin-bottom: 0; - height: 100%; - padding-left: 12px; - } - .sidebar > .block.version > div.narrow-helper { - float: left; - width: 1px; - height: 100%; - } - .sidebar > .block.version > p { - /* hide Version text if too narrow */ - margin: 0; - min-width: 55px; - /* vertically center */ - display: flex; - align-items: center; - height: 100%; + .sidebar-menu-toggle { + width: 45px; + border: none; } .source nav:not(.sidebar).sub { margin-left: 32px; } + /* Space is at a premium on mobile, so remove the theme-picker icon. */ + #theme-picker { + display: none; + width: 0; + } + .content { margin-left: 0px; } @@ -1925,28 +1891,6 @@ details.rustdoc-toggle[open] > summary.hideme::after { height: 50px; } - .show-it, .sidebar-elems:focus-within { - z-index: 2; - left: 0; - } - - .show-it > .block.items { - margin: 8px 0; - } - - .show-it > .block.items > ul { - margin: 0; - } - - .show-it > .block.items > ul > li { - text-align: center; - margin: 2px 0; - } - - .show-it > .block.items > ul > li > a { - font-size: 1.3125rem; - } - /* Because of ios, we need to actually have a full height sidebar title so the * actual sidebar can show up. But then we need to make it transparent so we don't * hide content. The filler just allows to create the background for the sidebar @@ -1967,10 +1911,6 @@ details.rustdoc-toggle[open] > summary.hideme::after { left: -11px; } - #all-types { - margin: 10px; - } - .sidebar.expanded #sidebar-toggle { font-size: 1.5rem; } diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index 885fe7c09a9..82a2be67ceb 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -57,7 +57,7 @@ pre, .rustdoc.source .example-wrap { background-color: #191f26; } -.sidebar { +.sidebar, .mobile-topbar, .sidebar-menu-toggle { background-color: #14191f; } @@ -100,12 +100,6 @@ pre, .rustdoc.source .example-wrap { background-color: #14191f; } -.sidebar .location { - border-color: #000; - background-color: #0f1419; - color: #fff; -} - .sidebar-elems .location { color: #ff7733; } @@ -114,15 +108,6 @@ pre, .rustdoc.source .example-wrap { color: #fff; } -.sidebar .version { - border-bottom-color: #424c57; -} - -.sidebar-title { - border-top-color: #5c6773; - border-bottom-color: #5c6773; -} - .block a:hover { background: transparent; color: #ffb44c; @@ -228,7 +213,8 @@ a.anchor, .small-section-header a, #source-sidebar a, pre.rust a, -.sidebar a, +.sidebar h2 a, +.sidebar h3 a, .in-band a { color: #c5c5c5; } @@ -581,13 +567,6 @@ kbd { } } -#all-types { - background-color: #14191f; -} -#all-types:hover { - background-color: rgba(70, 70, 70, 0.33); -} - .search-results .result-name span.alias { color: #c5c5c5; } diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index 71af7797832..761bf50dd36 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -28,7 +28,7 @@ pre, .rustdoc.source .example-wrap { background-color: #2A2A2A; } -.sidebar { +.sidebar, .mobile-topbar, .sidebar-menu-toggle { background-color: #505050; } @@ -69,21 +69,6 @@ pre, .rustdoc.source .example-wrap { background-color: #565656; } -.sidebar .location { - border-color: #fff; - background: #575757; - color: #DDD; -} - -.sidebar .version { - border-bottom-color: #DDD; -} - -.sidebar-title { - border-top-color: #777; - border-bottom-color: #777; -} - .block a:hover { background: #444; } @@ -186,7 +171,8 @@ a.anchor, .small-section-header a, #source-sidebar a, pre.rust a, -.sidebar a, +.sidebar h2 a, +.sidebar h3 a, .in-band a { color: #ddd; } @@ -453,13 +439,6 @@ kbd { } } -#all-types { - background-color: #505050; -} -#all-types:hover { - background-color: #606060; -} - .search-results .result-name span.alias { color: #fff; } diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index e462fd16237..7cca7d4004b 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -30,7 +30,7 @@ pre, .rustdoc.source .example-wrap { background-color: #F5F5F5; } -.sidebar { +.sidebar, .mobile-topbar, .sidebar-menu-toggle { background-color: #F5F5F5; } @@ -69,21 +69,6 @@ pre, .rustdoc.source .example-wrap { background-color: #f1f1f1; } -.sidebar .location { - border-color: #000; - background-color: #fff; - color: #333; -} - -.sidebar .version { - border-bottom-color: #DDD; -} - -.sidebar-title { - border-top-color: #777; - border-bottom-color: #777; -} - .block a:hover { background: #F5F5F5; } @@ -183,7 +168,8 @@ a.anchor, .small-section-header a, #source-sidebar a, pre.rust a, -.sidebar a, +.sidebar h2 a, +.sidebar h3 a, .in-band a { color: #000; } @@ -440,13 +426,6 @@ kbd { } } -#all-types { - background-color: #fff; -} -#all-types:hover { - background-color: #f9f9f9; -} - .search-results .result-name span.alias { color: #000; } diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index e7d1eedd358..161b95d9993 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -67,6 +67,13 @@ function resourcePath(basename, extension) { ty: sidebarVars.attributes["data-ty"].value, relpath: sidebarVars.attributes["data-relpath"].value, }; + // FIXME: It would be nicer to generate this text content directly in HTML, + // but with the current code it's hard to get the right information in the right place. + var mobileLocationTitle = document.querySelector(".mobile-topbar h2.location"); + var locationTitle = document.querySelector(".sidebar h2.location"); + if (mobileLocationTitle && locationTitle) { + mobileLocationTitle.innerText = locationTitle.innerText; + } } }()); @@ -309,37 +316,6 @@ function hideThemeButtonState() { return null; } - function showSidebar() { - var elems = document.getElementsByClassName("sidebar-elems")[0]; - if (elems) { - addClass(elems, "show-it"); - } - var sidebar = document.getElementsByClassName("sidebar")[0]; - if (sidebar) { - addClass(sidebar, "mobile"); - var filler = document.getElementById("sidebar-filler"); - if (!filler) { - var div = document.createElement("div"); - div.id = "sidebar-filler"; - sidebar.appendChild(div); - } - } - } - - function hideSidebar() { - var elems = document.getElementsByClassName("sidebar-elems")[0]; - if (elems) { - removeClass(elems, "show-it"); - } - var sidebar = document.getElementsByClassName("sidebar")[0]; - removeClass(sidebar, "mobile"); - var filler = document.getElementById("sidebar-filler"); - if (filler) { - filler.remove(); - } - document.getElementsByTagName("body")[0].style.marginTop = ""; - } - var toggleAllDocsId = "toggle-all-docs"; var main = document.getElementById(MAIN_ID); var savedHash = ""; @@ -374,7 +350,8 @@ function hideThemeButtonState() { function onHashChange(ev) { // If we're in mobile mode, we should hide the sidebar in any case. - hideSidebar(); + var sidebar = document.getElementsByClassName("sidebar")[0]; + removeClass(sidebar, "shown"); handleHashes(ev); } @@ -866,6 +843,11 @@ function hideThemeButtonState() { }); }()); + function hideSidebar() { + var sidebar = document.getElementsByClassName("sidebar")[0]; + removeClass(sidebar, "shown"); + } + function handleClick(id, f) { var elem = document.getElementById(id); if (elem) { @@ -906,16 +888,16 @@ function hideThemeButtonState() { }; }); - var sidebar_menu = document.getElementsByClassName("sidebar-menu")[0]; - if (sidebar_menu) { - sidebar_menu.onclick = function() { + var sidebar_menu_toggle = document.getElementsByClassName("sidebar-menu-toggle")[0]; + if (sidebar_menu_toggle) { + sidebar_menu_toggle.addEventListener("click", function() { var sidebar = document.getElementsByClassName("sidebar")[0]; - if (hasClass(sidebar, "mobile")) { - hideSidebar(); + if (!hasClass(sidebar, "shown")) { + addClass(sidebar, "shown"); } else { - showSidebar(); + removeClass(sidebar, "shown"); } - }; + }); } var buildHelperPopup = function() { diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js index 81dc0b2fb1e..498f60e9f25 100644 --- a/src/librustdoc/html/static/js/source-script.js +++ b/src/librustdoc/html/static/js/source-script.js @@ -139,7 +139,7 @@ function createSourceSidebar() { currentFile, hasFoundFile); }); - container.insertBefore(sidebar, document.querySelector(".sidebar-logo").nextSibling); + container.appendChild(sidebar); // Focus on the current file in the source files sidebar. var selected_elem = sidebar.getElementsByClassName("selected")[0]; if (typeof selected_elem !== "undefined") { diff --git a/src/librustdoc/templates/page.html b/src/librustdoc/templates/page.html index 1ef001ec2b7..1322b854b7f 100644 --- a/src/librustdoc/templates/page.html +++ b/src/librustdoc/templates/page.html @@ -72,8 +72,20 @@

{#- -#} {#- -#} {{- layout.external_html.before_content|safe -}} +