diff --git a/Cargo.lock b/Cargo.lock index 529e17b158fc8..4c02b21dd71e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3433,7 +3433,6 @@ version = "0.0.0" dependencies = [ "rustc_ast", "rustc_span", - "tracing", ] [[package]] diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index d0732b35b6e9e..565488ab6a52d 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -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, 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), diff --git a/compiler/rustc_ast_pretty/Cargo.toml b/compiler/rustc_ast_pretty/Cargo.toml index 29f2be4cf4648..5ad8714e9fec9 100644 --- a/compiler/rustc_ast_pretty/Cargo.toml +++ b/compiler/rustc_ast_pretty/Cargo.toml @@ -7,6 +7,5 @@ edition = "2021" doctest = false [dependencies] -tracing = "0.1" rustc_span = { path = "../rustc_span" } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_ast_pretty/src/pp.rs b/compiler/rustc_ast_pretty/src/pp.rs index ad9d15f1ce345..25437f8b53a94 100644 --- a/compiler/rustc_ast_pretty/src/pp.rs +++ b/compiler/rustc_ast_pretty/src/pp.rs @@ -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, 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(), diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 2fb5a0f9faf82..5703a72c686e5 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -1,6 +1,7 @@ //! A helper class for dealing with static archives -use std::ffi::{CStr, CString}; +use std::env; +use std::ffi::{CStr, CString, OsString}; use std::io; use std::mem; use std::path::{Path, PathBuf}; @@ -158,54 +159,127 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { output_path.with_extension("lib") }; - // we've checked for \0 characters in the library name already - let dll_name_z = CString::new(lib_name).unwrap(); - // All import names are Rust identifiers and therefore cannot contain \0 characters. - // FIXME: when support for #[link_name] implemented, ensure that import.name values don't - // have any \0 characters - let import_name_and_ordinal_vector: Vec<(CString, Option)> = dll_imports + let mingw_gnu_toolchain = self.config.sess.target.llvm_target.ends_with("pc-windows-gnu"); + + let import_name_and_ordinal_vector: Vec<(String, Option)> = dll_imports .iter() .map(|import: &DllImport| { if self.config.sess.target.arch == "x86" { - (LlvmArchiveBuilder::i686_decorated_name(import), import.ordinal) + ( + LlvmArchiveBuilder::i686_decorated_name(import, mingw_gnu_toolchain), + import.ordinal, + ) } else { - (CString::new(import.name.to_string()).unwrap(), import.ordinal) + (import.name.to_string(), import.ordinal) } }) .collect(); - let output_path_z = rustc_fs_util::path_to_c_string(&output_path); + if mingw_gnu_toolchain { + // The binutils linker used on -windows-gnu targets cannot read the import + // libraries generated by LLVM: in our attempts, the linker produced an .EXE + // that loaded but crashed with an AV upon calling one of the imported + // functions. Therefore, use binutils to create the import library instead, + // by writing a .DEF file to the temp dir and calling binutils's dlltool. + let def_file_path = + tmpdir.as_ref().join(format!("{}_imports", lib_name)).with_extension("def"); + + let def_file_content = format!( + "EXPORTS\n{}", + import_name_and_ordinal_vector + .into_iter() + .map(|(name, ordinal)| { + match ordinal { + Some(n) => format!("{} @{} NONAME", name, n), + None => name, + } + }) + .collect::>() + .join("\n") + ); - tracing::trace!("invoking LLVMRustWriteImportLibrary"); - tracing::trace!(" dll_name {:#?}", dll_name_z); - tracing::trace!(" output_path {}", output_path.display()); - tracing::trace!( - " import names: {}", - dll_imports.iter().map(|import| import.name.to_string()).collect::>().join(", "), - ); + match std::fs::write(&def_file_path, def_file_content) { + Ok(_) => {} + Err(e) => { + self.config.sess.fatal(&format!("Error writing .DEF file: {}", e)); + } + }; - let ffi_exports: Vec = import_name_and_ordinal_vector - .iter() - .map(|(name_z, ordinal)| LLVMRustCOFFShortExport::new(name_z.as_ptr(), *ordinal)) - .collect(); - let result = unsafe { - crate::llvm::LLVMRustWriteImportLibrary( - dll_name_z.as_ptr(), - output_path_z.as_ptr(), - ffi_exports.as_ptr(), - ffi_exports.len(), - llvm_machine_type(&self.config.sess.target.arch) as u16, - !self.config.sess.target.is_like_msvc, - ) - }; + let dlltool = find_binutils_dlltool(self.config.sess); + let result = std::process::Command::new(dlltool) + .args([ + "-d", + def_file_path.to_str().unwrap(), + "-D", + lib_name, + "-l", + output_path.to_str().unwrap(), + ]) + .output(); + + match result { + Err(e) => { + self.config.sess.fatal(&format!("Error calling dlltool: {}", e.to_string())); + } + Ok(output) if !output.status.success() => self.config.sess.fatal(&format!( + "Dlltool could not create import library: {}\n{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + )), + _ => {} + } + } else { + // we've checked for \0 characters in the library name already + let dll_name_z = CString::new(lib_name).unwrap(); + + let output_path_z = rustc_fs_util::path_to_c_string(&output_path); + + tracing::trace!("invoking LLVMRustWriteImportLibrary"); + tracing::trace!(" dll_name {:#?}", dll_name_z); + tracing::trace!(" output_path {}", output_path.display()); + tracing::trace!( + " import names: {}", + dll_imports + .iter() + .map(|import| import.name.to_string()) + .collect::>() + .join(", "), + ); - if result == crate::llvm::LLVMRustResult::Failure { - self.config.sess.fatal(&format!( - "Error creating import library for {}: {}", - lib_name, - llvm::last_error().unwrap_or("unknown LLVM error".to_string()) - )); - } + // All import names are Rust identifiers and therefore cannot contain \0 characters. + // FIXME: when support for #[link_name] is implemented, ensure that the import names + // still don't contain any \0 characters. Also need to check that the names don't + // contain substrings like " @" or "NONAME" that are keywords or otherwise reserved + // in definition files. + let cstring_import_name_and_ordinal_vector: Vec<(CString, Option)> = + import_name_and_ordinal_vector + .into_iter() + .map(|(name, ordinal)| (CString::new(name).unwrap(), ordinal)) + .collect(); + + let ffi_exports: Vec = cstring_import_name_and_ordinal_vector + .iter() + .map(|(name_z, ordinal)| LLVMRustCOFFShortExport::new(name_z.as_ptr(), *ordinal)) + .collect(); + let result = unsafe { + crate::llvm::LLVMRustWriteImportLibrary( + dll_name_z.as_ptr(), + output_path_z.as_ptr(), + ffi_exports.as_ptr(), + ffi_exports.len(), + llvm_machine_type(&self.config.sess.target.arch) as u16, + !self.config.sess.target.is_like_msvc, + ) + }; + + if result == crate::llvm::LLVMRustResult::Failure { + self.config.sess.fatal(&format!( + "Error creating import library for {}: {}", + lib_name, + llvm::last_error().unwrap_or("unknown LLVM error".to_string()) + )); + } + }; self.add_archive(&output_path, |_| false).unwrap_or_else(|e| { self.config.sess.fatal(&format!( @@ -332,22 +406,61 @@ impl<'a> LlvmArchiveBuilder<'a> { } } - fn i686_decorated_name(import: &DllImport) -> CString { + fn i686_decorated_name(import: &DllImport, mingw: bool) -> String { let name = import.name; - // We verified during construction that `name` does not contain any NULL characters, so the - // conversion to CString is guaranteed to succeed. - CString::new(match import.calling_convention { - DllCallingConvention::C => format!("_{}", name), - DllCallingConvention::Stdcall(arg_list_size) => format!("_{}@{}", name, arg_list_size), + let prefix = if mingw { "" } else { "_" }; + + match import.calling_convention { + DllCallingConvention::C => format!("{}{}", prefix, name), + DllCallingConvention::Stdcall(arg_list_size) => { + format!("{}{}@{}", prefix, name, arg_list_size) + } DllCallingConvention::Fastcall(arg_list_size) => format!("@{}@{}", name, arg_list_size), DllCallingConvention::Vectorcall(arg_list_size) => { format!("{}@@{}", name, arg_list_size) } - }) - .unwrap() + } } } fn string_to_io_error(s: String) -> io::Error { io::Error::new(io::ErrorKind::Other, format!("bad archive: {}", s)) } + +fn find_binutils_dlltool(sess: &Session) -> OsString { + assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc); + if let Some(dlltool_path) = &sess.opts.debugging_opts.dlltool { + return dlltool_path.clone().into_os_string(); + } + + let mut tool_name: OsString = if sess.host.arch != sess.target.arch { + // We are cross-compiling, so we need the tool with the prefix matching our target + if sess.target.arch == "x86" { + "i686-w64-mingw32-dlltool" + } else { + "x86_64-w64-mingw32-dlltool" + } + } else { + // We are not cross-compiling, so we just want `dlltool` + "dlltool" + } + .into(); + + if sess.host.options.is_like_windows { + // If we're compiling on Windows, add the .exe suffix + tool_name.push(".exe"); + } + + // NOTE: it's not clear how useful it is to explicitly search PATH. + for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { + let full_path = dir.join(&tool_name); + if full_path.is_file() { + return full_path.into_os_string(); + } + } + + // The user didn't specify the location of the dlltool binary, and we weren't able + // to find the appropriate one on the PATH. Just return the name of the tool + // and let the invocation fail with a hopefully useful error message. + tool_name +} diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 5217fa2758f79..8a9450c20dda4 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -731,27 +731,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { - if !self.fptoint_sat_broken_in_llvm() { - let src_ty = self.cx.val_ty(val); - let float_width = self.cx.float_width(src_ty); - let int_width = self.cx.int_width(dest_ty); - let name = format!("llvm.fptoui.sat.i{}.f{}", int_width, float_width); - return Some(self.call_intrinsic(&name, &[val])); - } - - None + self.fptoint_sat(false, val, dest_ty) } fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { - if !self.fptoint_sat_broken_in_llvm() { - let src_ty = self.cx.val_ty(val); - let float_width = self.cx.float_width(src_ty); - let int_width = self.cx.int_width(dest_ty); - let name = format!("llvm.fptosi.sat.i{}.f{}", int_width, float_width); - return Some(self.call_intrinsic(&name, &[val])); - } - - None + self.fptoint_sat(true, val, dest_ty) } fn fptoui(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { @@ -1455,4 +1439,43 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { _ => false, } } + + fn fptoint_sat( + &mut self, + signed: bool, + val: &'ll Value, + dest_ty: &'ll Type, + ) -> Option<&'ll Value> { + if !self.fptoint_sat_broken_in_llvm() { + let src_ty = self.cx.val_ty(val); + let (float_ty, int_ty, vector_length) = if self.cx.type_kind(src_ty) == TypeKind::Vector + { + assert_eq!(self.cx.vector_length(src_ty), self.cx.vector_length(dest_ty)); + ( + self.cx.element_type(src_ty), + self.cx.element_type(dest_ty), + Some(self.cx.vector_length(src_ty)), + ) + } else { + (src_ty, dest_ty, None) + }; + let float_width = self.cx.float_width(float_ty); + let int_width = self.cx.int_width(int_ty); + + let instr = if signed { "fptosi" } else { "fptoui" }; + let name = if let Some(vector_length) = vector_length { + format!( + "llvm.{}.sat.v{}i{}.v{}f{}", + instr, vector_length, int_width, vector_length, float_width + ) + } else { + format!("llvm.{}.sat.i{}.f{}", instr, int_width, float_width) + }; + let f = + self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty)); + Some(self.call(self.type_func(&[src_ty], dest_ty), f, &[val], None)) + } else { + None + } + } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index cebb6d13c4e9f..5adfa18035a0e 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -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) => { diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 679c45767018d..68decce82ab52 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -3,11 +3,10 @@ use super::place::PlaceRef; use super::{FunctionCx, LocalRef}; use crate::base; -use crate::common::{self, IntPredicate, RealPredicate}; +use crate::common::{self, IntPredicate}; use crate::traits::*; use crate::MemFlags; -use rustc_apfloat::{ieee, Float, Round, Status}; use rustc_middle::mir; use rustc_middle::ty::cast::{CastTy, IntTy}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; @@ -368,10 +367,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.inttoptr(usize_llval, ll_t_out) } (CastTy::Float, CastTy::Int(IntTy::I)) => { - cast_float_to_int(&mut bx, true, llval, ll_t_in, ll_t_out) + bx.cast_float_to_int(true, llval, ll_t_out) } (CastTy::Float, CastTy::Int(_)) => { - cast_float_to_int(&mut bx, false, llval, ll_t_in, ll_t_out) + bx.cast_float_to_int(false, llval, ll_t_out) } _ => bug!("unsupported cast: {:?} to {:?}", operand.layout.ty, cast.ty), }; @@ -768,146 +767,3 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // (*) this is only true if the type is suitable } } - -fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - signed: bool, - x: Bx::Value, - float_ty: Bx::Type, - int_ty: Bx::Type, -) -> Bx::Value { - if let Some(false) = bx.cx().sess().opts.debugging_opts.saturating_float_casts { - return if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; - } - - let try_sat_result = if signed { bx.fptosi_sat(x, int_ty) } else { bx.fptoui_sat(x, int_ty) }; - if let Some(try_sat_result) = try_sat_result { - return try_sat_result; - } - - let int_width = bx.cx().int_width(int_ty); - let float_width = bx.cx().float_width(float_ty); - // LLVM's fpto[su]i returns undef when the input x is infinite, NaN, or does not fit into the - // destination integer type after rounding towards zero. This `undef` value can cause UB in - // safe code (see issue #10184), so we implement a saturating conversion on top of it: - // Semantically, the mathematical value of the input is rounded towards zero to the next - // mathematical integer, and then the result is clamped into the range of the destination - // integer type. Positive and negative infinity are mapped to the maximum and minimum value of - // the destination integer type. NaN is mapped to 0. - // - // Define f_min and f_max as the largest and smallest (finite) floats that are exactly equal to - // a value representable in int_ty. - // They are exactly equal to int_ty::{MIN,MAX} if float_ty has enough significand bits. - // Otherwise, int_ty::MAX must be rounded towards zero, as it is one less than a power of two. - // int_ty::MIN, however, is either zero or a negative power of two and is thus exactly - // representable. Note that this only works if float_ty's exponent range is sufficiently large. - // f16 or 256 bit integers would break this property. Right now the smallest float type is f32 - // with exponents ranging up to 127, which is barely enough for i128::MIN = -2^127. - // On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because - // we're rounding towards zero, we just get float_ty::MAX (which is always an integer). - // This already happens today with u128::MAX = 2^128 - 1 > f32::MAX. - let int_max = |signed: bool, int_width: u64| -> u128 { - let shift_amount = 128 - int_width; - if signed { i128::MAX as u128 >> shift_amount } else { u128::MAX >> shift_amount } - }; - let int_min = |signed: bool, int_width: u64| -> i128 { - if signed { i128::MIN >> (128 - int_width) } else { 0 } - }; - - let compute_clamp_bounds_single = |signed: bool, int_width: u64| -> (u128, u128) { - let rounded_min = ieee::Single::from_i128_r(int_min(signed, int_width), Round::TowardZero); - assert_eq!(rounded_min.status, Status::OK); - let rounded_max = ieee::Single::from_u128_r(int_max(signed, int_width), Round::TowardZero); - assert!(rounded_max.value.is_finite()); - (rounded_min.value.to_bits(), rounded_max.value.to_bits()) - }; - let compute_clamp_bounds_double = |signed: bool, int_width: u64| -> (u128, u128) { - let rounded_min = ieee::Double::from_i128_r(int_min(signed, int_width), Round::TowardZero); - assert_eq!(rounded_min.status, Status::OK); - let rounded_max = ieee::Double::from_u128_r(int_max(signed, int_width), Round::TowardZero); - assert!(rounded_max.value.is_finite()); - (rounded_min.value.to_bits(), rounded_max.value.to_bits()) - }; - - let mut float_bits_to_llval = |bits| { - let bits_llval = match float_width { - 32 => bx.cx().const_u32(bits as u32), - 64 => bx.cx().const_u64(bits as u64), - n => bug!("unsupported float width {}", n), - }; - bx.bitcast(bits_llval, float_ty) - }; - let (f_min, f_max) = match float_width { - 32 => compute_clamp_bounds_single(signed, int_width), - 64 => compute_clamp_bounds_double(signed, int_width), - n => bug!("unsupported float width {}", n), - }; - let f_min = float_bits_to_llval(f_min); - let f_max = float_bits_to_llval(f_max); - // To implement saturation, we perform the following steps: - // - // 1. Cast x to an integer with fpto[su]i. This may result in undef. - // 2. Compare x to f_min and f_max, and use the comparison results to select: - // a) int_ty::MIN if x < f_min or x is NaN - // b) int_ty::MAX if x > f_max - // c) the result of fpto[su]i otherwise - // 3. If x is NaN, return 0.0, otherwise return the result of step 2. - // - // This avoids resulting undef because values in range [f_min, f_max] by definition fit into the - // destination type. It creates an undef temporary, but *producing* undef is not UB. Our use of - // undef does not introduce any non-determinism either. - // More importantly, the above procedure correctly implements saturating conversion. - // Proof (sketch): - // If x is NaN, 0 is returned by definition. - // Otherwise, x is finite or infinite and thus can be compared with f_min and f_max. - // This yields three cases to consider: - // (1) if x in [f_min, f_max], the result of fpto[su]i is returned, which agrees with - // saturating conversion for inputs in that range. - // (2) if x > f_max, then x is larger than int_ty::MAX. This holds even if f_max is rounded - // (i.e., if f_max < int_ty::MAX) because in those cases, nextUp(f_max) is already larger - // than int_ty::MAX. Because x is larger than int_ty::MAX, the return value of int_ty::MAX - // is correct. - // (3) if x < f_min, then x is smaller than int_ty::MIN. As shown earlier, f_min exactly equals - // int_ty::MIN and therefore the return value of int_ty::MIN is correct. - // QED. - - let int_max = bx.cx().const_uint_big(int_ty, int_max(signed, int_width)); - let int_min = bx.cx().const_uint_big(int_ty, int_min(signed, int_width) as u128); - let zero = bx.cx().const_uint(int_ty, 0); - - // Step 1 ... - let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; - let less_or_nan = bx.fcmp(RealPredicate::RealULT, x, f_min); - let greater = bx.fcmp(RealPredicate::RealOGT, x, f_max); - - // Step 2: We use two comparisons and two selects, with %s1 being the - // result: - // %less_or_nan = fcmp ult %x, %f_min - // %greater = fcmp olt %x, %f_max - // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result - // %s1 = select %greater, int_ty::MAX, %s0 - // Note that %less_or_nan uses an *unordered* comparison. This - // comparison is true if the operands are not comparable (i.e., if x is - // NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if - // x is NaN. - // - // Performance note: Unordered comparison can be lowered to a "flipped" - // comparison and a negation, and the negation can be merged into the - // select. Therefore, it not necessarily any more expensive than an - // ordered ("normal") comparison. Whether these optimizations will be - // performed is ultimately up to the backend, but at least x86 does - // perform them. - let s0 = bx.select(less_or_nan, int_min, fptosui_result); - let s1 = bx.select(greater, int_max, s0); - - // Step 3: NaN replacement. - // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN. - // Therefore we only need to execute this step for signed integer types. - if signed { - // LLVM has no isNaN predicate, so we use (x == x) instead - let cmp = bx.fcmp(RealPredicate::RealOEQ, x, x); - bx.select(cmp, s1, zero) - } else { - s1 - } -} diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 48d88095855d1..5a06fb4610587 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -1,18 +1,21 @@ use super::abi::AbiBuilderMethods; use super::asm::AsmBuilderMethods; +use super::consts::ConstMethods; use super::coverageinfo::CoverageInfoBuilderMethods; use super::debuginfo::DebugInfoBuilderMethods; use super::intrinsic::IntrinsicCallMethods; -use super::type_::ArgAbiMethods; +use super::misc::MiscMethods; +use super::type_::{ArgAbiMethods, BaseTypeMethods}; use super::{HasCodegen, StaticBuilderMethods}; use crate::common::{ - AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, + AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind, }; use crate::mir::operand::OperandRef; use crate::mir::place::PlaceRef; use crate::MemFlags; +use rustc_apfloat::{ieee, Float, Round, Status}; use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout}; use rustc_middle::ty::Ty; use rustc_span::Span; @@ -202,6 +205,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; diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 816e770f01252..0e27a82b2b13b 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -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"))); diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 639d2e617c77a..13cd8e4a046b0 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -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') { diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 51e4afaf2204a..fafd847a1cbaa 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -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 }, diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index d6c35dfef8888..19a73732fcac3 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -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, @@ -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), .. })) => { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 4e60b7593c651..48502112e3a71 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -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); //~ 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::>(), - "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::>(), + "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(); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 0b9623d1c7d6f..c48d8d689c10e 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -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 = (None, parse_opt_pathbuf, [UNTRACKED], + "import library generation tool (windows-gnu only)"), dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED], "emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) \ (default: no)"), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index af87399ac95dc..702e359466079 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1223,6 +1223,7 @@ symbols! { simd, simd_add, simd_and, + simd_as, simd_bitmask, simd_cast, simd_ceil, diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index d174e00df77a9..af3540386f9fc 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -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( 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 diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index ab732f510ff92..cd2e0f18e0cc0 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -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(); diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 6314f2aba4efe..4c612ed5be51a 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -453,7 +453,7 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) sym::simd_scatter => (3, vec![param(0), param(1), param(2)], tcx.mk_unit()), sym::simd_insert => (2, vec![param(0), tcx.types.u32, param(1)], param(0)), sym::simd_extract => (2, vec![param(0), tcx.types.u32], param(1)), - sym::simd_cast => (2, vec![param(0)], param(1)), + sym::simd_cast | sym::simd_as => (2, vec![param(0)], param(1)), sym::simd_bitmask => (2, vec![param(0)], param(1)), sym::simd_select | sym::simd_select_bitmask => { (2, vec![param(0), param(1), param(1)], param(1)) diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 85ceede5b9e3a..0b8ed0cc174b4 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -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; diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 4049c95b130f2..5a3cd2a4b9260 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -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; diff --git a/library/stdarch b/library/stdarch index 1d5d0e8b0e313..11c98f6eb9c4b 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit 1d5d0e8b0e3134dc781adb98057e38ffdf200df2 +Subproject commit 11c98f6eb9c4ba48b2362ad4960343b312d056b8 diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 865e14f694dcc..2455d56bd2b3f 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -554,16 +554,8 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { extra_scripts: &[], static_extra_scripts: &[], }; - let sidebar = if let Some(ref version) = self.shared.cache.crate_version { - format!( - "

Crate {}

\ -
\ -

Version {}

\ -
\ -

Back to index

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

Crate {}

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

{}{}

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

    Version {}

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

    See all {}'s items

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

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

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

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