Auto merge of #93048 - matthiaskrgr:rollup-cz5ma34, r=matthiaskrgr

Rollup of 9 pull requests

Successful merges:

 - #90782 (Implement raw-dylib support for windows-gnu)
 - #91150 (Let qpath contain NtTy: `<$:ty as $:ty>::…`)
 - #92425 (Improve SIMD casts)
 - #92692 (Simplify and unify rustdoc sidebar styles)
 - #92780 (Directly use ConstValue for single literals in blocks)
 - #92924 (Delete pretty printer tracing)
 - #93018 (Remove some unused `Ord` derives based on `Span`)
 - #93026 (fix typo in `max` description for f32/f64)
 - #93035 (Fix stdarch submodule pointing to commit outside tree)

Failed merges:

 - #92861 (Rustdoc mobile: put out-of-band info on its own line)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2022-01-18 22:46:47 +00:00
commit e5e2b0be26
73 changed files with 905 additions and 745 deletions

View File

@ -3433,7 +3433,6 @@ version = "0.0.0"
dependencies = [
"rustc_ast",
"rustc_span",
"tracing",
]
[[package]]

View File

@ -2006,7 +2006,7 @@ bitflags::bitflags! {
}
}
#[derive(Clone, PartialEq, PartialOrd, Encodable, Decodable, Debug, Hash, HashStable_Generic)]
#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Hash, HashStable_Generic)]
pub enum InlineAsmTemplatePiece {
String(String),
Placeholder { operand_idx: usize, modifier: Option<char>, span: Span },
@ -2211,7 +2211,7 @@ pub enum IsAuto {
No,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable, Debug)]
#[derive(HashStable_Generic)]
pub enum Unsafe {
Yes(Span),

View File

@ -7,6 +7,5 @@ edition = "2021"
doctest = false
[dependencies]
tracing = "0.1"
rustc_span = { path = "../rustc_span" }
rustc_ast = { path = "../rustc_ast" }

View File

@ -138,7 +138,6 @@ use ring::RingBuffer;
use std::borrow::Cow;
use std::collections::VecDeque;
use std::fmt;
use tracing::debug;
/// How to break. Described in more detail in the module docs.
#[derive(Clone, Copy, PartialEq)]
@ -193,22 +192,6 @@ impl fmt::Display for Token {
}
}
fn buf_str(buf: &RingBuffer<BufEntry>, left: usize, right: usize, lim: usize) -> String {
let mut i = left;
let mut l = lim;
let mut s = String::from("[");
while i != right && l != 0 {
l -= 1;
if i != left {
s.push_str(", ");
}
s.push_str(&format!("{}={}", buf[i].size, &buf[i].token));
i += 1;
}
s.push(']');
s
}
#[derive(Copy, Clone)]
enum PrintStackBreak {
Fits,
@ -267,7 +250,6 @@ impl Default for BufEntry {
impl Printer {
pub fn new() -> Self {
let linewidth = 78;
debug!("Printer::new {}", linewidth);
let mut buf = RingBuffer::new();
buf.advance_right();
Printer {
@ -310,16 +292,13 @@ impl Printer {
} else {
self.advance_right();
}
debug!("pp Begin({})/buffer Vec<{},{}>", b.offset, self.left, self.right);
self.scan_push(BufEntry { token: Token::Begin(b), size: -self.right_total });
}
fn scan_end(&mut self) {
if self.scan_stack.is_empty() {
debug!("pp End/print Vec<{},{}>", self.left, self.right);
self.print_end();
} else {
debug!("pp End/buffer Vec<{},{}>", self.left, self.right);
self.advance_right();
self.scan_push(BufEntry { token: Token::End, size: -1 });
}
@ -334,7 +313,6 @@ impl Printer {
} else {
self.advance_right();
}
debug!("pp Break({})/buffer Vec<{},{}>", b.offset, self.left, self.right);
self.check_stack(0);
self.scan_push(BufEntry { token: Token::Break(b), size: -self.right_total });
self.right_total += b.blank_space;
@ -342,10 +320,8 @@ impl Printer {
fn scan_string(&mut self, s: Cow<'static, str>) {
if self.scan_stack.is_empty() {
debug!("pp String('{}')/print Vec<{},{}>", s, self.left, self.right);
self.print_string(s);
} else {
debug!("pp String('{}')/buffer Vec<{},{}>", s, self.left, self.right);
self.advance_right();
let len = s.len() as isize;
self.buf[self.right] = BufEntry { token: Token::String(s), size: len };
@ -355,18 +331,8 @@ impl Printer {
}
fn check_stream(&mut self) {
debug!(
"check_stream Vec<{}, {}> with left_total={}, right_total={}",
self.left, self.right, self.left_total, self.right_total
);
if self.right_total - self.left_total > self.space {
debug!(
"scan window is {}, longer than space on line ({})",
self.right_total - self.left_total,
self.space
);
if Some(&self.left) == self.scan_stack.back() {
debug!("setting {} to infinity and popping", self.left);
let scanned = self.scan_pop_bottom();
self.buf[scanned].size = SIZE_INFINITY;
}
@ -378,7 +344,6 @@ impl Printer {
}
fn scan_push(&mut self, entry: BufEntry) {
debug!("scan_push {}", self.right);
self.buf[self.right] = entry;
self.scan_stack.push_front(self.right);
}
@ -401,11 +366,6 @@ impl Printer {
}
fn advance_left(&mut self) {
debug!(
"advance_left Vec<{},{}>, sizeof({})={}",
self.left, self.right, self.left, self.buf[self.left].size
);
let mut left_size = self.buf[self.left].size;
while left_size >= 0 {
@ -465,14 +425,12 @@ impl Printer {
}
fn print_newline(&mut self, amount: isize) {
debug!("NEWLINE {}", amount);
self.out.push('\n');
self.pending_indentation = 0;
self.indent(amount);
}
fn indent(&mut self, amount: isize) {
debug!("INDENT {}", amount);
self.pending_indentation += amount;
}
@ -485,17 +443,14 @@ impl Printer {
fn print_begin(&mut self, b: BeginToken, l: isize) {
if l > self.space {
let col = self.margin - self.space + b.offset;
debug!("print Begin -> push broken block at col {}", col);
self.print_stack
.push(PrintStackElem { offset: col, pbreak: PrintStackBreak::Broken(b.breaks) });
} else {
debug!("print Begin -> push fitting block");
self.print_stack.push(PrintStackElem { offset: 0, pbreak: PrintStackBreak::Fits });
}
}
fn print_end(&mut self) {
debug!("print End -> pop End");
self.print_stack.pop().unwrap();
}
@ -503,22 +458,18 @@ impl Printer {
let top = self.get_top();
match top.pbreak {
PrintStackBreak::Fits => {
debug!("print Break({}) in fitting block", b.blank_space);
self.space -= b.blank_space;
self.indent(b.blank_space);
}
PrintStackBreak::Broken(Breaks::Consistent) => {
debug!("print Break({}+{}) in consistent block", top.offset, b.offset);
self.print_newline(top.offset + b.offset);
self.space = self.margin - (top.offset + b.offset);
}
PrintStackBreak::Broken(Breaks::Inconsistent) => {
if l > self.space {
debug!("print Break({}+{}) w/ newline in inconsistent", top.offset, b.offset);
self.print_newline(top.offset + b.offset);
self.space = self.margin - (top.offset + b.offset);
} else {
debug!("print Break({}) w/o newline in inconsistent", b.blank_space);
self.indent(b.blank_space);
self.space -= b.blank_space;
}
@ -528,7 +479,6 @@ impl Printer {
fn print_string(&mut self, s: Cow<'static, str>) {
let len = s.len() as isize;
debug!("print String({})", s);
// assert!(len <= space);
self.space -= len;
@ -545,8 +495,6 @@ impl Printer {
}
fn print(&mut self, token: Token, l: isize) {
debug!("print {} {} (remaining line space={})", token, l, self.space);
debug!("{}", buf_str(&self.buf, self.left, self.right, 6));
match token {
Token::Begin(b) => self.print_begin(b, l),
Token::End => self.print_end(),

View File

@ -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,55 +159,128 @@ 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<u16>)> = 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<u16>)> = 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");
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::<Vec<_>>().join(", "),
);
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::<Vec<String>>()
.join("\n")
);
let ffi_exports: Vec<LLVMRustCOFFShortExport> = 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,
)
match std::fs::write(&def_file_path, def_file_content) {
Ok(_) => {}
Err(e) => {
self.config.sess.fatal(&format!("Error writing .DEF file: {}", e));
}
};
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::<Vec<_>>()
.join(", "),
);
// 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<u16>)> =
import_name_and_ordinal_vector
.into_iter()
.map(|(name, ordinal)| (CString::new(name).unwrap(), ordinal))
.collect();
let ffi_exports: Vec<LLVMRustCOFFShortExport> = 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())
));
}
};
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!(
"failed to add native library {}: {}",
@ -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
}

View File

@ -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));
Some(self.call(self.type_func(&[src_ty], dest_ty), f, &[val], None))
} else {
None
}
}
}

View File

@ -1688,7 +1688,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!(
@ -1714,14 +1714,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),
};
@ -1748,10 +1760,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) => {

View File

@ -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
}
}

View File

@ -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,179 @@ 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())
};
// 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 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 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);
// If we're working with vectors, constants must be "splatted": the constant is duplicated
// into each lane of the vector. The algorithm stays the same, we are just using the
// same constant across all lanes.
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 f_min = maybe_splat(self, f_min);
let f_max = maybe_splat(self, f_max);
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;

View File

@ -646,6 +646,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")));

View File

@ -274,11 +274,6 @@ impl Collector<'_> {
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') {

View File

@ -105,7 +105,7 @@ impl<'a> Iterator for SwitchTargetsIter<'a> {
impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {}
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, PartialOrd)]
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
pub enum TerminatorKind<'tcx> {
/// Block should have one successor in the graph; we jump there.
Goto { target: BasicBlock },

View File

@ -36,6 +36,7 @@ impl<'tcx> Const<'tcx> {
Self::from_opt_const_arg_anon_const(tcx, ty::WithOptConstParam::unknown(def_id))
}
#[instrument(skip(tcx), level = "debug")]
pub fn from_opt_const_arg_anon_const(
tcx: TyCtxt<'tcx>,
def: ty::WithOptConstParam<LocalDefId>,
@ -51,6 +52,7 @@ impl<'tcx> Const<'tcx> {
};
let expr = &tcx.hir().body(body_id).value;
debug!(?expr);
let ty = tcx.type_of(def.def_id_for_type_of());
@ -67,11 +69,21 @@ impl<'tcx> Const<'tcx> {
}
}
#[instrument(skip(tcx), level = "debug")]
fn try_eval_lit_or_param(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
) -> Option<&'tcx Self> {
// Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
// currently have to be wrapped in curly brackets, so it's necessary to special-case.
let expr = match &expr.kind {
hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => {
block.expr.as_ref().unwrap()
}
_ => expr,
};
let lit_input = match expr.kind {
hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }),
hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => match expr.kind {
@ -97,15 +109,6 @@ impl<'tcx> Const<'tcx> {
}
}
// Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
// currently have to be wrapped in curly brackets, so it's necessary to special-case.
let expr = match &expr.kind {
hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => {
block.expr.as_ref().unwrap()
}
_ => expr,
};
use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath};
match expr.kind {
ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => {

View File

@ -139,22 +139,46 @@ impl<'a> Parser<'a> {
style: PathStyle,
ty_generics: Option<&Generics>,
) -> PResult<'a, Path> {
maybe_whole!(self, NtPath, |path| {
let reject_generics_if_mod_style = |parser: &Parser<'_>, path: &Path| {
// Ensure generic arguments don't end up in attribute paths, such as:
//
// macro_rules! m {
// ($p:path) => { #[$p] struct S; }
// }
//
// m!(inline<u8>); //~ ERROR: unexpected generic arguments in path
//
if style == PathStyle::Mod && path.segments.iter().any(|segment| segment.args.is_some())
{
self.struct_span_err(
path.segments
.iter()
.filter_map(|segment| segment.args.as_ref())
.map(|arg| arg.span())
.collect::<Vec<_>>(),
"unexpected generic arguments in path",
)
.emit();
parser
.struct_span_err(
path.segments
.iter()
.filter_map(|segment| segment.args.as_ref())
.map(|arg| arg.span())
.collect::<Vec<_>>(),
"unexpected generic arguments in path",
)
.emit();
}
};
maybe_whole!(self, NtPath, |path| {
reject_generics_if_mod_style(self, &path);
path
});
if let token::Interpolated(nt) = &self.token.kind {
if let token::NtTy(ty) = &**nt {
if let ast::TyKind::Path(None, path) = &ty.kind {
let path = path.clone();
self.bump();
reject_generics_if_mod_style(self, &path);
return Ok(path);
}
}
}
let lo = self.token.span;
let mut segments = Vec::new();
let mod_sep_ctxt = self.token.span.ctxt();

View File

@ -1161,6 +1161,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<PathBuf> = (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)"),

View File

@ -1223,6 +1223,7 @@ symbols! {
simd,
simd_add,
simd_and,
simd_as,
simd_bitmask,
simd_cast,
simd_ceil,

View File

@ -53,6 +53,7 @@ pub fn add_placeholder_note(err: &mut rustc_errors::DiagnosticBuilder<'_>) {
/// If there are types that satisfy both impls, invokes `on_overlap`
/// with a suitably-freshened `ImplHeader` with those types
/// substituted. Otherwise, invokes `no_overlap`.
#[instrument(skip(tcx, skip_leak_check, on_overlap, no_overlap), level = "debug")]
pub fn overlapping_impls<F1, F2, R>(
tcx: TyCtxt<'_>,
impl1_def_id: DefId,
@ -65,12 +66,6 @@ where
F1: FnOnce(OverlapResult<'_>) -> R,
F2: FnOnce() -> R,
{
debug!(
"overlapping_impls(\
impl1_def_id={:?}, \
impl2_def_id={:?})",
impl1_def_id, impl2_def_id,
);
// Before doing expensive operations like entering an inference context, do
// a quick check via fast_reject to tell if the impl headers could possibly
// unify.
@ -85,6 +80,7 @@ where
.any(|(ty1, ty2)| {
let t1 = fast_reject::simplify_type(tcx, ty1, SimplifyParams::No, StripReferences::No);
let t2 = fast_reject::simplify_type(tcx, ty2, SimplifyParams::No, StripReferences::No);
if let (Some(t1), Some(t2)) = (t1, t2) {
// Simplified successfully
t1 != t2

View File

@ -117,9 +117,8 @@ pub fn translate_substs<'a, 'tcx>(
/// Specialization is determined by the sets of types to which the impls apply;
/// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies
/// to.
#[instrument(skip(tcx), level = "debug")]
pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool {
debug!("specializes({:?}, {:?})", impl1_def_id, impl2_def_id);
// The feature gate should prevent introducing new specializations, but not
// taking advantage of upstream ones.
let features = tcx.features();

View File

@ -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))

View File

@ -675,7 +675,7 @@ impl f32 {
/// Returns the maximum of the two numbers.
///
/// Follows the IEEE-754 2008 semantics for maxNum, except for handling of signaling NaNs.
/// This matches the behavior of libms fmin.
/// This matches the behavior of libms fmax.
///
/// ```
/// let x = 1.0f32;

View File

@ -691,7 +691,7 @@ impl f64 {
/// Returns the maximum of the two numbers.
///
/// Follows the IEEE-754 2008 semantics for maxNum, except for handling of signaling NaNs.
/// This matches the behavior of libms fmin.
/// This matches the behavior of libms fmax.
///
/// ```
/// let x = 1.0_f64;

@ -1 +1 @@
Subproject commit 1d5d0e8b0e3134dc781adb98057e38ffdf200df2
Subproject commit 11c98f6eb9c4ba48b2362ad4960343b312d056b8

View File

@ -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!(
"<h2 class=\"location\">Crate {}</h2>\
<div class=\"block version\">\
<p>Version {}</p>\
</div>\
<a id=\"all-types\" href=\"index.html\"><p>Back to index</p></a>",
crate_name,
Escape(version),
)
let sidebar = if self.shared.cache.crate_version.is_some() {
format!("<h2 class=\"location\">Crate {}</h2>", crate_name)
} else {
String::new()
};

View File

@ -1744,13 +1744,6 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
buffer,
"<h2 class=\"location\"><a href=\"#\">{}{}</a></h2>",
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) {
);
}
if it.is_crate() {
if let Some(ref version) = cx.cache().crate_version {
write!(
buffer,
"<div class=\"block version\">\
<div class=\"narrow-helper\"></div>\
<p>Version {}</p>\
</div>",
Escape(version),
);
}
}
buffer.write_str("<div class=\"sidebar-elems\">");
if it.is_crate() {
write!(
buffer,
"<a id=\"all-types\" href=\"all.html\"><p>See all {}'s items</p></a>",
it.name.as_ref().expect("crates always have a name"),
);
write!(buffer, "<div class=\"block\"><ul>");
if let Some(ref version) = cx.cache().crate_version {
write!(buffer, "<li class=\"version\">Version {}</li>", Escape(version));
}
write!(buffer, "<li><a id=\"all-types\" href=\"all.html\">All Items</a></li>");
buffer.write_str("</div></ul>");
}
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("<h2 class=\"location\">Other items in<br>");
buffer.write_str("<h2 class=\"location\">In ");
for (i, name) in cx.current.iter().take(parentlen).enumerate() {
if i > 0 {
buffer.write_str("::<wbr>");

View File

@ -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;
/* Hide the logo and item name from the sidebar. Those are displayed
in the mobile-topbar instead. */
.sidebar .sidebar-logo,
.sidebar .location {
display: none;
}
.sidebar-elems {
margin-top: 1em;
}
.sidebar {
position: fixed;
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 15px;
position: static;
padding: 0;
padding-left: 15px;
z-index: 11;
overflow-y: hidden;
}
/* 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;
}
.rustdoc.source > .sidebar {
position: fixed;
top: 0;
left: 0;
margin: 0;
z-index: 11;
width: 0;
}
.sidebar.mobile {
position: sticky !important;
top: 0;
left: 0;
width: 100%;
margin-left: 0;
background-color: rgba(0,0,0,0);
.mobile-topbar .location {
border: none;
margin: 0;
margin-left: auto;
padding: 0.3em;
padding-right: 0.6em;
text-overflow: ellipsis;
overflow-x: hidden;
}
.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;
.mobile-topbar .logo-container {
max-height: 45px;
}
.sidebar .location:empty {
padding: 0;
}
.rustdoc:not(.source) .sidebar .logo-container {
width: 35px;
height: 35px;
margin-top: 5px;
margin-bottom: 5px;
float: left;
margin-left: 50px;
}
.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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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() {

View File

@ -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") {

View File

@ -72,8 +72,20 @@
</div> {#- -#}
<![endif]--> {#- -#}
{{- layout.external_html.before_content|safe -}}
<nav class="mobile-topbar"> {#- -#}
<button class="sidebar-menu-toggle">&#9776;</button> {#- -#}
<a class="sidebar-logo" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {#- -#}
<div class="logo-container"> {#- -#}
{%- if !layout.logo.is_empty() -%}
<img src="{{layout.logo}}" alt="logo"> {#- -#}
{%- else -%}
<img class="rust-logo" src="{{static_root_path|safe}}rust-logo{{page.resource_suffix}}.svg" alt="logo"> {#- -#}
{%- endif -%}
</div>
</a> {#- -#}
<h2 class="location"></h2>
</nav>
<nav class="sidebar"> {#- -#}
<div class="sidebar-menu" role="button">&#9776;</div> {#- -#}
<a class="sidebar-logo" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {#- -#}
<div class="logo-container"> {#- -#}
{%- if !layout.logo.is_empty() %}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,5 @@
extern crate raw_dylib_test;
fn main() {
raw_dylib_test::library_function();
}

View File

@ -0,0 +1,2 @@
exported_function_stdcall(6)
exported_function_fastcall(125)

View File

@ -0,0 +1,4 @@
LIBRARY exporter
EXPORTS
exported_function_stdcall@4 @15 NONAME
@exported_function_fastcall@4 @18 NONAME

View File

@ -0,0 +1,4 @@
LIBRARY exporter
EXPORTS
_exported_function_stdcall@4 @15 NONAME
@exported_function_fastcall@4 @18 NONAME

View File

@ -0,0 +1,11 @@
#include <stdio.h>
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);
}

View File

@ -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);
}
}

View File

@ -20,7 +20,7 @@ assert-css: (".srclink", {"text-decoration": "underline solid rgb(56, 115, 173)"
assert-css: ("#top-doc-prose-title", {"color": "rgb(0, 0, 0)"})
assert-css: (".sidebar a", {"color": "rgb(0, 0, 0)"})
assert-css: (".sidebar a", {"color": "rgb(56, 115, 173)"})
assert-css: (".in-band a", {"color": "rgb(0, 0, 0)"})
// We move the cursor over the "Implementations" title so the anchor is displayed.

View File

@ -5,4 +5,4 @@ size: (1080, 600)
assert-count: (".docblock > .example-wrap", 2)
assert: ".docblock > .example-wrap > .language-txt"
assert: ".docblock > .example-wrap > .rust-example-rendered"
assert-css: (".docblock > .example-wrap > pre", {"width": "796px", "overflow-x": "auto"}, ALL)
assert-css: (".docblock > .example-wrap > pre", {"width": "785.25px", "overflow-x": "auto"}, ALL)

View File

@ -4,7 +4,7 @@ goto: file://|DOC_PATH|/lib2/long_table/struct.Foo.html
size: (1100, 800)
// Logically, the ".docblock" and the "<p>" should have the same scroll width.
compare-elements-property: (".top-doc .docblock", ".top-doc .docblock > p", ["scrollWidth"])
assert-property: (".top-doc .docblock", {"scrollWidth": "816"})
assert-property: (".top-doc .docblock", {"scrollWidth": "801"})
// However, since there is overflow in the <table>, its scroll width is bigger.
assert-property: (".top-doc .docblock table", {"scrollWidth": "1573"})
@ -16,6 +16,6 @@ compare-elements-property: (
"#implementations + details .docblock > p",
["scrollWidth"],
)
assert-property: ("#implementations + details .docblock", {"scrollWidth": "816"})
assert-property: ("#implementations + details .docblock", {"scrollWidth": "801"})
// However, since there is overflow in the <table>, its scroll width is bigger.
assert-property: ("#implementations + details .docblock table", {"scrollWidth": "1573"})

View File

@ -110,7 +110,7 @@ assert-css: ("h6#sub-sub-heading-for-enum-impl-item-doc", {"font-size": "15.2px"
assert-css: ("h6#sub-sub-heading-for-enum-impl-item-doc", {"border-bottom-width": "0px"})
assert-text: (".sidebar .others h3", "Modules")
assert-css: (".sidebar .others h3", {"border-bottom-width": "1px"}, ALL)
assert-css: (".sidebar .others h3", {"border-bottom-width": "0px"}, ALL)
goto: file://|DOC_PATH|/test_docs/union.HeavilyDocumentedUnion.html

View File

@ -3,5 +3,5 @@ goto: file://|DOC_PATH|/lib2/struct.Foo.html
// We set a fixed size so there is no chance of "random" resize.
size: (1100, 800)
// We check that ".item-info" is bigger than its content.
assert-css: (".item-info", {"width": "807px"})
assert-css: (".item-info", {"width": "757px"})
assert-css: (".item-info .stab", {"width": "341px"})

View File

@ -5,7 +5,7 @@ write: (".search-input", "test")
wait-for: "#titles"
// The width is returned by "getComputedStyle" which returns the exact number instead of the
// CSS rule which is "50%"...
assert-css: (".search-results div.desc", {"width": "320px"})
assert-css: (".search-results div.desc", {"width": "295px"})
size: (600, 100)
// As counter-intuitive as it may seem, in this width, the width is "100%", which is why
// when computed it's larger.

View File

@ -4,25 +4,28 @@
goto: file://|DOC_PATH|/test_docs/struct.Foo.html
// Switching to "mobile view" by reducing the width to 600px.
size: (600, 600)
assert-css: (".sidebar-elems", {"display": "block", "left": "-246px"})
assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
// Opening the sidebar menu.
click: ".sidebar-menu"
assert-css: (".sidebar-elems", {"display": "block", "left": "0px"})
click: ".sidebar-menu-toggle"
assert-css: (".sidebar", {"display": "block", "left": "0px"})
// Closing the sidebar menu.
click: ".sidebar-menu"
assert-css: (".sidebar-elems", {"display": "block", "left": "-246px"})
click: ".sidebar-menu-toggle"
assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
// Force the sidebar open by focusing a link inside it.
// This makes it easier for keyboard users to get to it.
focus: ".sidebar-title a"
assert-css: (".sidebar-elems", {"display": "block", "left": "0px"})
assert-css: (".sidebar", {"display": "block", "left": "0px"})
// When we tab out of the sidebar, close it.
focus: ".search-input"
assert-css: (".sidebar-elems", {"display": "block", "left": "-246px"})
assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
// Open the sidebar menu.
click: ".sidebar-menu"
assert-css: (".sidebar-elems", {"left": "0px"})
click: ".sidebar-menu-toggle"
assert-css: (".sidebar", {"left": "0px"})
// Click elsewhere.
click: "body"
assert-css: (".sidebar-elems", {"left": "-246px"})
assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
// Check that the topbar is visible
assert-property: (".mobile-topbar", {"clientHeight": "45"})

View File

@ -10,6 +10,7 @@ click: (10, 10)
// We wait for the sidebar to be expanded (there is a 0.5s animation).
wait-for: 600
assert-css: ("nav.sidebar.expanded", {"width": "300px"})
assert-css: ("nav.sidebar.expanded a", {"font-size": "14.4px"})
// We collapse the sidebar.
click: (10, 10)
// We wait for the sidebar to be collapsed (there is a 0.5s animation).
@ -30,3 +31,6 @@ click: (10, 10)
// We ensure that the class has been removed.
assert-false: "nav.sidebar.expanded"
assert: "nav.sidebar"
// Check that the topbar is not visible
assert-property: (".mobile-topbar", {"offsetParent": "null"})

View File

@ -1,8 +1,14 @@
goto: file://|DOC_PATH|/test_docs/index.html
show-text: true
local-storage: {"rustdoc-theme": "light"}
// We reload the page so the local storage settings are being used.
reload:
assert-text: (".sidebar > .location", "Crate test_docs")
// In modules, we only have one "location" element.
assert-count: (".sidebar .location", 1)
assert-text: (".sidebar-elems > #all-types", "See all test_docs's items")
assert-text: ("#all-types", "All Items")
assert-css: ("#all-types", {"color": "rgb(56, 115, 173)"})
// We check that we have the crates list and that the "current" on is "test_docs".
assert-text: (".sidebar-elems .crate > ul > li > a.current", "test_docs")
// And we're also supposed to have the list of items in the current module.
@ -24,13 +30,14 @@ assert-count: (".sidebar .location", 2)
assert-false: ".sidebar-elems > .crate"
click: ".sidebar-links a"
assert-property: ("html", {"scrollTop": "389"})
assert-property-false: ("html", {"scrollTop": "0"})
click: ".sidebar h2.location"
click: ".sidebar h2.location a"
assert-property: ("html", {"scrollTop": "0"})
// We now go back to the crate page to click on the "lib2" crate link.
goto: file://|DOC_PATH|/test_docs/index.html
assert-css: (".sidebar-elems .crate > ul > li:first-child > a", {"color": "rgb(56, 115, 173)"})
click: ".sidebar-elems .crate > ul > li:first-child > a"
// PAGE: lib2/index.html
@ -51,8 +58,7 @@ click: "#functions + .item-table .item-left > a"
// In items containing no items (like functions or constants) and in modules, we have one
// "location" elements.
assert-count: (".sidebar .location", 1)
// There is a "<br>" tag between "in" and "lib2", but it doesn't count as a space.
assert-text: (".sidebar .sidebar-elems .location", "Other items inlib2")
assert-text: (".sidebar .sidebar-elems .location", "In lib2")
// We check that we don't have the crate list.
assert-false: ".sidebar-elems > .crate"

View File

@ -11,7 +11,7 @@ assert-property: (".item-decl pre", {"scrollWidth": "1324"})
goto: file://|DOC_PATH|/lib2/too_long/type.ReallyLongTypeNameLongLongLong.html
assert-property: ("body", {"scrollWidth": "1100"})
// We now check that the section width hasn't grown because of it.
assert-property: ("#main-content", {"scrollWidth": "840"})
assert-property: ("#main-content", {"scrollWidth": "825"})
// And now checking that it has scrollable content.
assert-property: (".item-decl pre", {"scrollWidth": "1103"})
@ -20,6 +20,13 @@ assert-property: (".item-decl pre", {"scrollWidth": "1103"})
goto: file://|DOC_PATH|/lib2/too_long/constant.ReallyLongTypeNameLongLongLongConstBecauseWhyNotAConstRightGigaGigaSupraLong.html
assert-property: ("body", {"scrollWidth": "1100"})
// We now check that the section width hasn't grown because of it.
assert-property: ("#main-content", {"scrollWidth": "840"})
assert-property: ("#main-content", {"scrollWidth": "825"})
// And now checking that it has scrollable content.
assert-property: (".item-decl pre", {"scrollWidth": "950"})
// On mobile:
size: (600, 600)
goto: file://|DOC_PATH|/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html
assert-property: (".mobile-topbar .location", {"scrollWidth": "504"})
assert-property: (".mobile-topbar .location", {"clientWidth": "504"})
assert-css: (".mobile-topbar .location", {"overflow-x": "hidden"})

View File

@ -3,7 +3,7 @@
// therefore should not concern itself with the lints.
#[deny(warnings)]
// @has cap_lints/struct.Foo.html //* 'Struct Foo'
// @has cap_lints/struct.Foo.html //* 'Foo'
pub struct Foo {
field: i32,
}

View File

@ -2,5 +2,4 @@
#![crate_name = "foo"]
// @has 'foo/index.html' '//div[@class="block version"]/p' 'Version <script>alert("hi")</script>'
// @has 'foo/all.html' '//div[@class="block version"]/p' 'Version <script>alert("hi")</script>'
// @has 'foo/index.html' '//li[@class="version"]' 'Version <script>alert("hi")</script>'

View File

@ -1,3 +1,3 @@
// compile-flags: --crate-version=1.3.37
// @has 'crate_version/index.html' '//div[@class="block version"]/p' 'Version 1.3.37'
// @has 'crate_version/index.html' '//*[@class="version"]' 'Version 1.3.37'

View File

@ -1,10 +1,11 @@
#![crate_name = "foo"]
#![feature(rustdoc_internals)]
// @matches 'foo/index.html' '//h1' 'Crate foo'
// @matches 'foo/index.html' '//h2[@class="location"]' 'Crate foo'
// @matches 'foo/foo_mod/index.html' '//h1' 'Module foo::foo_mod'
// @matches 'foo/foo_mod/index.html' '//h2[@class="location"]' 'Module foo_mod'
pub mod foo_mod {
pub struct __Thing {}
}
@ -18,15 +19,19 @@ extern "C" {
pub fn foo_fn() {}
// @matches 'foo/trait.FooTrait.html' '//h1' 'Trait foo::FooTrait'
// @matches 'foo/trait.FooTrait.html' '//h2[@class="location"]' 'FooTrait'
pub trait FooTrait {}
// @matches 'foo/struct.FooStruct.html' '//h1' 'Struct foo::FooStruct'
// @matches 'foo/struct.FooStruct.html' '//h2[@class="location"]' 'FooStruct'
pub struct FooStruct;
// @matches 'foo/enum.FooEnum.html' '//h1' 'Enum foo::FooEnum'
// @matches 'foo/enum.FooEnum.html' '//h2[@class="location"]' 'FooEnum'
pub enum FooEnum {}
// @matches 'foo/type.FooType.html' '//h1' 'Type Definition foo::FooType'
// @matches 'foo/type.FooType.html' '//h2[@class="location"]' 'FooType'
pub type FooType = FooStruct;
// @matches 'foo/macro.foo_macro.html' '//h1' 'Macro foo::foo_macro'

View File

@ -12,7 +12,7 @@ impl MyStruct {
// @has - '//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' 'impl MyAlias'
// @has - '//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' 'impl MyTrait for MyAlias'
// @has - 'Alias docstring'
// @has - '//*[@class="sidebar"]//*[@class="location"]' 'Type Definition MyAlias'
// @has - '//*[@class="sidebar"]//*[@class="location"]' 'MyAlias'
// @has - '//*[@class="sidebar"]//a[@href="#implementations"]' 'Methods'
// @has - '//*[@class="sidebar"]//a[@href="#trait-implementations"]' 'Trait Implementations'
/// Alias docstring

View File

@ -0,0 +1,12 @@
// check-pass
#![feature(generic_const_exprs)]
#![allow(incomplete_features)]
pub struct Foo<const N: usize>;
pub trait Bar<T> {}
impl<T> Bar<T> for Foo<{ 1 }> {}
impl<T> Bar<T> for Foo<{ 2 }> {}
fn main() {}

View File

@ -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`.

View File

@ -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`.

View File

@ -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() {}

View File

@ -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() {}

View File

@ -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 <https://github.com/rust-lang/rust/issues/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`.

View File

@ -1,5 +1,4 @@
// gate-test-raw_dylib
// only-windows-msvc
// only-windows
#[link(name = "foo", kind = "raw-dylib")]
//~^ ERROR: kind="raw-dylib" is unstable
extern "C" {}

View File

@ -1,5 +1,5 @@
error[E0658]: kind="raw-dylib" is unstable
--> $DIR/feature-gate-raw-dylib-windows-msvc.rs:3:1
--> $DIR/feature-gate-raw-dylib.rs:2:1
|
LL | #[link(name = "foo", kind = "raw-dylib")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -14,8 +14,20 @@ macro_rules! overly_complicated {
}
macro_rules! qpath {
(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!(path, <str as ToOwned>::Owned);
let _: qpath!(ty, <str as ToOwned>::Owned);
assert!(overly_complicated!(f, x, Option<usize>, { return Some(x); },
Some(8), Some(y), y) == 8)
}

View File

@ -1,4 +1,4 @@
// only-windows-msvc
// only-windows
#![feature(raw_dylib)]
//~^ WARN the feature `raw_dylib` is incomplete

View File

@ -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)]

View File

@ -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 <https://github.com/rust-lang/rust/issues/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);
| ^^^^^^^^^^^^^

View File

@ -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" {}

View File

@ -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 <https://github.com/rust-lang/rust/issues/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

View File

@ -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)]

View File

@ -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);
| ^^^^^^^^^^^^^

View File

@ -0,0 +1,48 @@
// run-pass
#![feature(repr_simd, platform_intrinsics)]
extern "platform-intrinsic" {
fn simd_as<T, U>(x: T) -> U;
}
#[derive(Copy, Clone)]
#[repr(simd)]
struct V<T>([T; 2]);
fn main() {
unsafe {
let u = V::<u32>([u32::MIN, u32::MAX]);
let i: V<i16> = 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>([f32::MIN, f32::MAX]);
let i: V<i16> = 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>([f32::MIN, f32::MAX]);
let u: V<u8> = simd_as(f);
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>([f64::MIN, f64::MAX]);
let i: V<isize> = 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>([f64::MIN, f64::MAX]);
let u: V<usize> = simd_as(f);
assert_eq!(u.0[0], f.0[0] as usize);
assert_eq!(u.0[1], f.0[1] as usize);
}
}

View File

@ -0,0 +1,21 @@
// run-pass
#![feature(repr_simd, platform_intrinsics)]
extern "platform-intrinsic" {
fn simd_cast<T, U>(x: T) -> U;
}
#[derive(Copy, Clone)]
#[repr(simd)]
struct V<T>([T; 4]);
fn main() {
let u = V::<usize>([0, 1, 2, 3]);
let uu32: V<u32> = unsafe { simd_cast(u) };
let ui64: V<i64> = 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);
}
}