mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
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:
commit
e5e2b0be26
@ -3433,7 +3433,6 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"rustc_ast",
|
||||
"rustc_span",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -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),
|
||||
|
@ -7,6 +7,5 @@ edition = "2021"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
tracing = "0.1"
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
|
@ -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(),
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) => {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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")));
|
||||
|
@ -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') {
|
||||
|
@ -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 },
|
||||
|
@ -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), .. })) => {
|
||||
|
@ -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();
|
||||
|
@ -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)"),
|
||||
|
@ -1223,6 +1223,7 @@ symbols! {
|
||||
simd,
|
||||
simd_add,
|
||||
simd_and,
|
||||
simd_as,
|
||||
simd_bitmask,
|
||||
simd_cast,
|
||||
simd_ceil,
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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))
|
||||
|
@ -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 libm’s fmin.
|
||||
/// This matches the behavior of libm’s fmax.
|
||||
///
|
||||
/// ```
|
||||
/// let x = 1.0f32;
|
||||
|
@ -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 libm’s fmin.
|
||||
/// This matches the behavior of libm’s fmax.
|
||||
///
|
||||
/// ```
|
||||
/// let x = 1.0_f64;
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 1d5d0e8b0e3134dc781adb98057e38ffdf200df2
|
||||
Subproject commit 11c98f6eb9c4ba48b2362ad4960343b312d056b8
|
@ -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()
|
||||
};
|
||||
|
@ -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>");
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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") {
|
||||
|
@ -72,8 +72,20 @@
|
||||
</div> {#- -#}
|
||||
<![endif]--> {#- -#}
|
||||
{{- layout.external_html.before_content|safe -}}
|
||||
<nav class="mobile-topbar"> {#- -#}
|
||||
<button class="sidebar-menu-toggle">☰</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">☰</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() %}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
23
src/test/run-make/raw-dylib-stdcall-ordinal/Makefile
Normal file
23
src/test/run-make/raw-dylib-stdcall-ordinal/Makefile
Normal 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
|
5
src/test/run-make/raw-dylib-stdcall-ordinal/driver.rs
Normal file
5
src/test/run-make/raw-dylib-stdcall-ordinal/driver.rs
Normal file
@ -0,0 +1,5 @@
|
||||
extern crate raw_dylib_test;
|
||||
|
||||
fn main() {
|
||||
raw_dylib_test::library_function();
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
exported_function_stdcall(6)
|
||||
exported_function_fastcall(125)
|
@ -0,0 +1,4 @@
|
||||
LIBRARY exporter
|
||||
EXPORTS
|
||||
exported_function_stdcall@4 @15 NONAME
|
||||
@exported_function_fastcall@4 @18 NONAME
|
@ -0,0 +1,4 @@
|
||||
LIBRARY exporter
|
||||
EXPORTS
|
||||
_exported_function_stdcall@4 @15 NONAME
|
||||
@exported_function_fastcall@4 @18 NONAME
|
11
src/test/run-make/raw-dylib-stdcall-ordinal/exporter.c
Normal file
11
src/test/run-make/raw-dylib-stdcall-ordinal/exporter.c
Normal 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);
|
||||
}
|
20
src/test/run-make/raw-dylib-stdcall-ordinal/lib.rs
Normal file
20
src/test/run-make/raw-dylib-stdcall-ordinal/lib.rs
Normal 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);
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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"})
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"})
|
||||
|
@ -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.
|
||||
|
@ -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"})
|
||||
|
@ -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"})
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"})
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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>'
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
12
src/test/ui/const-generics/issues/issue-92186.rs
Normal file
12
src/test/ui/const-generics/issues/issue-92186.rs
Normal 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() {}
|
@ -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`.
|
||||
|
@ -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`.
|
||||
|
@ -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() {}
|
||||
|
@ -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() {}
|
@ -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`.
|
@ -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" {}
|
@ -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")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
@ -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)
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// only-windows-msvc
|
||||
// only-windows
|
||||
#![feature(raw_dylib)]
|
||||
//~^ WARN the feature `raw_dylib` is incomplete
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
@ -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" {}
|
@ -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
|
||||
|
@ -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)]
|
||||
|
@ -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);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
48
src/test/ui/simd/intrinsic/generic-as.rs
Normal file
48
src/test/ui/simd/intrinsic/generic-as.rs
Normal 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);
|
||||
}
|
||||
}
|
21
src/test/ui/simd/intrinsic/generic-cast-pointer-width.rs
Normal file
21
src/test/ui/simd/intrinsic/generic-cast-pointer-width.rs
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user