Auto merge of #77381 - Dylan-DPC:rollup-0sr6p5p, r=Dylan-DPC
Rollup of 12 pull requests Successful merges: - #76909 (Add Iterator::advance_by and DoubleEndedIterator::advance_back_by) - #77153 (Fix recursive nonterminal expansion during pretty-print/reparse check) - #77202 (Defer Apple SDKROOT detection to link time.) - #77303 (const evaluatable: improve `TooGeneric` handling) - #77305 (move candidate_from_obligation_no_cache) - #77315 (Rename AllocErr to AllocError) - #77319 (Stable hashing: add comments and tests concerning platform-independence) - #77324 (Don't fire `const_item_mutation` lint on writes through a pointer) - #77343 (Validate `rustc_args_required_const`) - #77349 (Update cargo) - #77360 (References to ZSTs may be at arbitrary aligned addresses) - #77371 (Remove trailing space in error message) Failed merges: r? `@ghost`
This commit is contained in:
commit
b218b952f8
|
@ -1524,6 +1524,9 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
|
||||||
// NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
|
// NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
|
||||||
add_pre_link_args(cmd, sess, flavor);
|
add_pre_link_args(cmd, sess, flavor);
|
||||||
|
|
||||||
|
// NO-OPT-OUT, OBJECT-FILES-NO
|
||||||
|
add_apple_sdk(cmd, sess, flavor);
|
||||||
|
|
||||||
// NO-OPT-OUT
|
// NO-OPT-OUT
|
||||||
add_link_script(cmd, sess, tmpdir, crate_type);
|
add_link_script(cmd, sess, tmpdir, crate_type);
|
||||||
|
|
||||||
|
@ -2083,3 +2086,86 @@ fn are_upstream_rust_objects_already_included(sess: &Session) -> bool {
|
||||||
config::Lto::No | config::Lto::ThinLocal => false,
|
config::Lto::No | config::Lto::ThinLocal => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
|
||||||
|
let arch = &sess.target.target.arch;
|
||||||
|
let os = &sess.target.target.target_os;
|
||||||
|
let llvm_target = &sess.target.target.llvm_target;
|
||||||
|
if sess.target.target.target_vendor != "apple"
|
||||||
|
|| !matches!(os.as_str(), "ios" | "tvos")
|
||||||
|
|| flavor != LinkerFlavor::Gcc
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let sdk_name = match (arch.as_str(), os.as_str()) {
|
||||||
|
("aarch64", "tvos") => "appletvos",
|
||||||
|
("x86_64", "tvos") => "appletvsimulator",
|
||||||
|
("arm", "ios") => "iphoneos",
|
||||||
|
("aarch64", "ios") => "iphoneos",
|
||||||
|
("x86", "ios") => "iphonesimulator",
|
||||||
|
("x86_64", "ios") if llvm_target.contains("macabi") => "macosx10.15",
|
||||||
|
("x86_64", "ios") => "iphonesimulator",
|
||||||
|
_ => {
|
||||||
|
sess.err(&format!("unsupported arch `{}` for os `{}`", arch, os));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let sdk_root = match get_apple_sdk_root(sdk_name) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
sess.err(&e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let arch_name = llvm_target.split('-').next().expect("LLVM target must have a hyphen");
|
||||||
|
cmd.args(&["-arch", arch_name, "-isysroot", &sdk_root, "-Wl,-syslibroot", &sdk_root]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_apple_sdk_root(sdk_name: &str) -> Result<String, String> {
|
||||||
|
// Following what clang does
|
||||||
|
// (https://github.com/llvm/llvm-project/blob/
|
||||||
|
// 296a80102a9b72c3eda80558fb78a3ed8849b341/clang/lib/Driver/ToolChains/Darwin.cpp#L1661-L1678)
|
||||||
|
// to allow the SDK path to be set. (For clang, xcrun sets
|
||||||
|
// SDKROOT; for rustc, the user or build system can set it, or we
|
||||||
|
// can fall back to checking for xcrun on PATH.)
|
||||||
|
if let Ok(sdkroot) = env::var("SDKROOT") {
|
||||||
|
let p = Path::new(&sdkroot);
|
||||||
|
match sdk_name {
|
||||||
|
// Ignore `SDKROOT` if it's clearly set for the wrong platform.
|
||||||
|
"appletvos"
|
||||||
|
if sdkroot.contains("TVSimulator.platform")
|
||||||
|
|| sdkroot.contains("MacOSX.platform") => {}
|
||||||
|
"appletvsimulator"
|
||||||
|
if sdkroot.contains("TVOS.platform") || sdkroot.contains("MacOSX.platform") => {}
|
||||||
|
"iphoneos"
|
||||||
|
if sdkroot.contains("iPhoneSimulator.platform")
|
||||||
|
|| sdkroot.contains("MacOSX.platform") => {}
|
||||||
|
"iphonesimulator"
|
||||||
|
if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("MacOSX.platform") => {
|
||||||
|
}
|
||||||
|
"macosx10.15"
|
||||||
|
if sdkroot.contains("iPhoneOS.platform")
|
||||||
|
|| sdkroot.contains("iPhoneSimulator.platform") => {}
|
||||||
|
// Ignore `SDKROOT` if it's not a valid path.
|
||||||
|
_ if !p.is_absolute() || p == Path::new("/") || !p.exists() => {}
|
||||||
|
_ => return Ok(sdkroot),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let res =
|
||||||
|
Command::new("xcrun").arg("--show-sdk-path").arg("-sdk").arg(sdk_name).output().and_then(
|
||||||
|
|output| {
|
||||||
|
if output.status.success() {
|
||||||
|
Ok(String::from_utf8(output.stdout).unwrap())
|
||||||
|
} else {
|
||||||
|
let error = String::from_utf8(output.stderr);
|
||||||
|
let error = format!("process exit with error: {}", error.unwrap());
|
||||||
|
Err(io::Error::new(io::ErrorKind::Other, &error[..]))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(output) => Ok(output.trim().to_string()),
|
||||||
|
Err(e) => Err(format!("failed to get {} SDK path: {}", sdk_name, e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -125,15 +125,28 @@ impl SipHasher128 {
|
||||||
|
|
||||||
// A specialized write function for values with size <= 8.
|
// A specialized write function for values with size <= 8.
|
||||||
//
|
//
|
||||||
// The hashing of multi-byte integers depends on endianness. E.g.:
|
// The input must be zero-extended to 64-bits by the caller. This extension
|
||||||
|
// isn't hashed, but the implementation requires it for correctness.
|
||||||
|
//
|
||||||
|
// This function, given the same integer size and value, has the same effect
|
||||||
|
// on both little- and big-endian hardware. It operates on values without
|
||||||
|
// depending on their sequence in memory, so is independent of endianness.
|
||||||
|
//
|
||||||
|
// However, we want SipHasher128 to be platform-dependent, in order to be
|
||||||
|
// consistent with the platform-dependent SipHasher in libstd. In other
|
||||||
|
// words, we want:
|
||||||
|
//
|
||||||
// - little-endian: `write_u32(0xDDCCBBAA)` == `write([0xAA, 0xBB, 0xCC, 0xDD])`
|
// - little-endian: `write_u32(0xDDCCBBAA)` == `write([0xAA, 0xBB, 0xCC, 0xDD])`
|
||||||
// - big-endian: `write_u32(0xDDCCBBAA)` == `write([0xDD, 0xCC, 0xBB, 0xAA])`
|
// - big-endian: `write_u32(0xDDCCBBAA)` == `write([0xDD, 0xCC, 0xBB, 0xAA])`
|
||||||
//
|
//
|
||||||
// This function does the right thing for little-endian hardware. On
|
// Therefore, in order to produce endian-dependent results, SipHasher128's
|
||||||
// big-endian hardware `x` must be byte-swapped first to give the right
|
// `write_xxx` Hasher trait methods byte-swap `x` prior to zero-extending.
|
||||||
// behaviour. After any byte-swapping, the input must be zero-extended to
|
//
|
||||||
// 64-bits. The caller is responsible for the byte-swapping and
|
// If clients of SipHasher128 itself want platform-independent results, they
|
||||||
// zero-extension.
|
// *also* must byte-swap integer inputs before invoking the `write_xxx`
|
||||||
|
// methods on big-endian hardware (that is, two byte-swaps must occur--one
|
||||||
|
// in the client, and one in SipHasher128). Additionally, they must extend
|
||||||
|
// `usize` and `isize` types to 64 bits on 32-bit systems.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn short_write<T>(&mut self, _x: T, x: u64) {
|
fn short_write<T>(&mut self, _x: T, x: u64) {
|
||||||
let size = mem::size_of::<T>();
|
let size = mem::size_of::<T>();
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::{mem, slice};
|
|
||||||
|
|
||||||
// Hash just the bytes of the slice, without length prefix
|
// Hash just the bytes of the slice, without length prefix
|
||||||
struct Bytes<'a>(&'a [u8]);
|
struct Bytes<'a>(&'a [u8]);
|
||||||
|
@ -399,20 +398,55 @@ fn test_hash_no_concat_alias() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_write_short_works() {
|
fn test_short_write_works() {
|
||||||
let test_usize = 0xd0c0b0a0usize;
|
let test_u8 = 0xFF_u8;
|
||||||
|
let test_u16 = 0x1122_u16;
|
||||||
|
let test_u32 = 0x22334455_u32;
|
||||||
|
let test_u64 = 0x33445566_778899AA_u64;
|
||||||
|
let test_u128 = 0x11223344_55667788_99AABBCC_DDEEFF77_u128;
|
||||||
|
let test_usize = 0xD0C0B0A0_usize;
|
||||||
|
|
||||||
|
let test_i8 = -1_i8;
|
||||||
|
let test_i16 = -2_i16;
|
||||||
|
let test_i32 = -3_i32;
|
||||||
|
let test_i64 = -4_i64;
|
||||||
|
let test_i128 = -5_i128;
|
||||||
|
let test_isize = -6_isize;
|
||||||
|
|
||||||
let mut h1 = SipHasher128::new_with_keys(0, 0);
|
let mut h1 = SipHasher128::new_with_keys(0, 0);
|
||||||
h1.write_usize(test_usize);
|
|
||||||
h1.write(b"bytes");
|
h1.write(b"bytes");
|
||||||
h1.write(b"string");
|
h1.write(b"string");
|
||||||
h1.write_u8(0xFFu8);
|
h1.write_u8(test_u8);
|
||||||
h1.write_u8(0x01u8);
|
h1.write_u16(test_u16);
|
||||||
|
h1.write_u32(test_u32);
|
||||||
|
h1.write_u64(test_u64);
|
||||||
|
h1.write_u128(test_u128);
|
||||||
|
h1.write_usize(test_usize);
|
||||||
|
h1.write_i8(test_i8);
|
||||||
|
h1.write_i16(test_i16);
|
||||||
|
h1.write_i32(test_i32);
|
||||||
|
h1.write_i64(test_i64);
|
||||||
|
h1.write_i128(test_i128);
|
||||||
|
h1.write_isize(test_isize);
|
||||||
|
|
||||||
let mut h2 = SipHasher128::new_with_keys(0, 0);
|
let mut h2 = SipHasher128::new_with_keys(0, 0);
|
||||||
h2.write(unsafe {
|
|
||||||
slice::from_raw_parts(&test_usize as *const _ as *const u8, mem::size_of::<usize>())
|
|
||||||
});
|
|
||||||
h2.write(b"bytes");
|
h2.write(b"bytes");
|
||||||
h2.write(b"string");
|
h2.write(b"string");
|
||||||
h2.write(&[0xFFu8, 0x01u8]);
|
h2.write(&test_u8.to_ne_bytes());
|
||||||
assert_eq!(h1.finish128(), h2.finish128());
|
h2.write(&test_u16.to_ne_bytes());
|
||||||
|
h2.write(&test_u32.to_ne_bytes());
|
||||||
|
h2.write(&test_u64.to_ne_bytes());
|
||||||
|
h2.write(&test_u128.to_ne_bytes());
|
||||||
|
h2.write(&test_usize.to_ne_bytes());
|
||||||
|
h2.write(&test_i8.to_ne_bytes());
|
||||||
|
h2.write(&test_i16.to_ne_bytes());
|
||||||
|
h2.write(&test_i32.to_ne_bytes());
|
||||||
|
h2.write(&test_i64.to_ne_bytes());
|
||||||
|
h2.write(&test_i128.to_ne_bytes());
|
||||||
|
h2.write(&test_isize.to_ne_bytes());
|
||||||
|
|
||||||
|
let h1_hash = h1.finish128();
|
||||||
|
let h2_hash = h2.finish128();
|
||||||
|
|
||||||
|
assert_eq!(h1_hash, h2_hash);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,9 @@ use smallvec::SmallVec;
|
||||||
use std::hash::{BuildHasher, Hash, Hasher};
|
use std::hash::{BuildHasher, Hash, Hasher};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
/// When hashing something that ends up affecting properties like symbol names,
|
/// When hashing something that ends up affecting properties like symbol names,
|
||||||
/// we want these symbol names to be calculated independently of other factors
|
/// we want these symbol names to be calculated independently of other factors
|
||||||
/// like what architecture you're compiling *from*.
|
/// like what architecture you're compiling *from*.
|
||||||
|
@ -129,7 +132,8 @@ impl Hasher for StableHasher {
|
||||||
fn write_isize(&mut self, i: isize) {
|
fn write_isize(&mut self, i: isize) {
|
||||||
// Always treat isize as i64 so we get the same results on 32 and 64 bit
|
// Always treat isize as i64 so we get the same results on 32 and 64 bit
|
||||||
// platforms. This is important for symbol hashes when cross compiling,
|
// platforms. This is important for symbol hashes when cross compiling,
|
||||||
// for example.
|
// for example. Sign extending here is preferable as it means that the
|
||||||
|
// same negative number hashes the same on both 32 and 64 bit platforms.
|
||||||
self.state.write_i64((i as i64).to_le());
|
self.state.write_i64((i as i64).to_le());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// The tests below compare the computed hashes to particular expected values
|
||||||
|
// in order to test that we produce the same results on different platforms,
|
||||||
|
// regardless of endianness and `usize` and `isize` size differences (this
|
||||||
|
// of course assumes we run these tests on platforms that differ in those
|
||||||
|
// ways). The expected values depend on the hashing algorithm used, so they
|
||||||
|
// need to be updated whenever StableHasher changes its hashing algorithm.
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hash_integers() {
|
||||||
|
// Test that integers are handled consistently across platforms.
|
||||||
|
let test_u8 = 0xAB_u8;
|
||||||
|
let test_u16 = 0xFFEE_u16;
|
||||||
|
let test_u32 = 0x445577AA_u32;
|
||||||
|
let test_u64 = 0x01234567_13243546_u64;
|
||||||
|
let test_u128 = 0x22114433_66557788_99AACCBB_EEDDFF77_u128;
|
||||||
|
let test_usize = 0xD0C0B0A0_usize;
|
||||||
|
|
||||||
|
let test_i8 = -100_i8;
|
||||||
|
let test_i16 = -200_i16;
|
||||||
|
let test_i32 = -300_i32;
|
||||||
|
let test_i64 = -400_i64;
|
||||||
|
let test_i128 = -500_i128;
|
||||||
|
let test_isize = -600_isize;
|
||||||
|
|
||||||
|
let mut h = StableHasher::new();
|
||||||
|
test_u8.hash(&mut h);
|
||||||
|
test_u16.hash(&mut h);
|
||||||
|
test_u32.hash(&mut h);
|
||||||
|
test_u64.hash(&mut h);
|
||||||
|
test_u128.hash(&mut h);
|
||||||
|
test_usize.hash(&mut h);
|
||||||
|
test_i8.hash(&mut h);
|
||||||
|
test_i16.hash(&mut h);
|
||||||
|
test_i32.hash(&mut h);
|
||||||
|
test_i64.hash(&mut h);
|
||||||
|
test_i128.hash(&mut h);
|
||||||
|
test_isize.hash(&mut h);
|
||||||
|
|
||||||
|
// This depends on the hashing algorithm. See note at top of file.
|
||||||
|
let expected = (2736651863462566372, 8121090595289675650);
|
||||||
|
|
||||||
|
assert_eq!(h.finalize(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hash_usize() {
|
||||||
|
// Test that usize specifically is handled consistently across platforms.
|
||||||
|
let test_usize = 0xABCDEF01_usize;
|
||||||
|
|
||||||
|
let mut h = StableHasher::new();
|
||||||
|
test_usize.hash(&mut h);
|
||||||
|
|
||||||
|
// This depends on the hashing algorithm. See note at top of file.
|
||||||
|
let expected = (5798740672699530587, 11186240177685111648);
|
||||||
|
|
||||||
|
assert_eq!(h.finalize(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hash_isize() {
|
||||||
|
// Test that isize specifically is handled consistently across platforms.
|
||||||
|
let test_isize = -7_isize;
|
||||||
|
|
||||||
|
let mut h = StableHasher::new();
|
||||||
|
test_isize.hash(&mut h);
|
||||||
|
|
||||||
|
// This depends on the hashing algorithm. See note at top of file.
|
||||||
|
let expected = (14721296605626097289, 11385941877786388409);
|
||||||
|
|
||||||
|
assert_eq!(h.finalize(), expected);
|
||||||
|
}
|
|
@ -232,7 +232,7 @@ impl<'tcx> ty::TyS<'tcx> {
|
||||||
let n = tcx.lift(&n).unwrap();
|
let n = tcx.lift(&n).unwrap();
|
||||||
match n.try_eval_usize(tcx, ty::ParamEnv::empty()) {
|
match n.try_eval_usize(tcx, ty::ParamEnv::empty()) {
|
||||||
_ if t.is_simple_ty() => format!("array `{}`", self).into(),
|
_ if t.is_simple_ty() => format!("array `{}`", self).into(),
|
||||||
Some(n) => format!("array of {} element{} ", n, pluralize!(n)).into(),
|
Some(n) => format!("array of {} element{}", n, pluralize!(n)).into(),
|
||||||
None => "array".into(),
|
None => "array".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||||
use rustc_middle::ty::{self, subst::Subst, TyCtxt};
|
use rustc_middle::ty::{self, subst::Subst, TyCtxt};
|
||||||
use rustc_span::source_map::Span;
|
use rustc_span::source_map::Span;
|
||||||
use rustc_target::abi::{Abi, LayoutOf};
|
use rustc_target::abi::{Abi, LayoutOf};
|
||||||
use std::convert::TryInto;
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
|
||||||
pub fn note_on_undefined_behavior_error() -> &'static str {
|
pub fn note_on_undefined_behavior_error() -> &'static str {
|
||||||
"The rules on what exactly is undefined behavior aren't clear, \
|
"The rules on what exactly is undefined behavior aren't clear, \
|
||||||
|
@ -148,10 +148,10 @@ pub(super) fn op_to_const<'tcx>(
|
||||||
Scalar::Raw { data, .. } => {
|
Scalar::Raw { data, .. } => {
|
||||||
assert!(mplace.layout.is_zst());
|
assert!(mplace.layout.is_zst());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
data,
|
u64::try_from(data).unwrap() % mplace.layout.align.abi.bytes(),
|
||||||
mplace.layout.align.abi.bytes().into(),
|
0,
|
||||||
"this MPlaceTy must come from `try_as_mplace` being used on a zst, so we know what
|
"this MPlaceTy must come from a validated constant, thus we can assume the \
|
||||||
value this integer address must have",
|
alignment is correct",
|
||||||
);
|
);
|
||||||
ConstValue::Scalar(Scalar::zst())
|
ConstValue::Scalar(Scalar::zst())
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,11 +60,15 @@ impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> {
|
||||||
// so emitting a lint would be redundant.
|
// so emitting a lint would be redundant.
|
||||||
if !lhs.projection.is_empty() {
|
if !lhs.projection.is_empty() {
|
||||||
if let Some(def_id) = self.is_const_item(lhs.local) {
|
if let Some(def_id) = self.is_const_item(lhs.local) {
|
||||||
self.lint_const_item_usage(def_id, loc, |lint| {
|
// Don't lint on writes through a pointer
|
||||||
let mut lint = lint.build("attempting to modify a `const` item");
|
// (e.g. `unsafe { *FOO = 0; *BAR.field = 1; }`)
|
||||||
lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified");
|
if !matches!(lhs.projection.last(), Some(PlaceElem::Deref)) {
|
||||||
lint
|
self.lint_const_item_usage(def_id, loc, |lint| {
|
||||||
})
|
let mut lint = lint.build("attempting to modify a `const` item");
|
||||||
|
lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified");
|
||||||
|
lint
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We are looking for MIR of the form:
|
// We are looking for MIR of the form:
|
||||||
|
|
|
@ -137,7 +137,7 @@ fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Vec<usize>> {
|
||||||
LitKind::Int(a, _) => {
|
LitKind::Int(a, _) => {
|
||||||
ret.push(a as usize);
|
ret.push(a as usize);
|
||||||
}
|
}
|
||||||
_ => return None,
|
_ => bug!("invalid arg index"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(ret)
|
Some(ret)
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
use rustc_ast as ast;
|
use rustc_ast as ast;
|
||||||
use rustc_ast::token::{self, Nonterminal, Token, TokenKind};
|
use rustc_ast::token::{self, Nonterminal, Token, TokenKind};
|
||||||
use rustc_ast::tokenstream::{self, Spacing, TokenStream, TokenTree};
|
use rustc_ast::tokenstream::{self, TokenStream, TokenTree};
|
||||||
use rustc_ast_pretty::pprust;
|
use rustc_ast_pretty::pprust;
|
||||||
use rustc_data_structures::sync::Lrc;
|
use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_errors::{Diagnostic, FatalError, Level, PResult};
|
use rustc_errors::{Diagnostic, FatalError, Level, PResult};
|
||||||
|
@ -435,31 +435,42 @@ pub fn tokenstream_probably_equal_for_proc_macro(
|
||||||
token_trees.into_iter()
|
token_trees.into_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
let expand_nt = |tree: TokenTree| {
|
fn expand_token(tree: TokenTree, sess: &ParseSess) -> impl Iterator<Item = TokenTree> {
|
||||||
if let TokenTree::Token(Token { kind: TokenKind::Interpolated(nt), span }) = &tree {
|
// When checking tokenstreams for 'probable equality', we are comparing
|
||||||
// When checking tokenstreams for 'probable equality', we are comparing
|
// a captured (from parsing) `TokenStream` to a reparsed tokenstream.
|
||||||
// a captured (from parsing) `TokenStream` to a reparsed tokenstream.
|
// The reparsed Tokenstream will never have `None`-delimited groups,
|
||||||
// The reparsed Tokenstream will never have `None`-delimited groups,
|
// since they are only ever inserted as a result of macro expansion.
|
||||||
// since they are only ever inserted as a result of macro expansion.
|
// Therefore, inserting a `None`-delimtied group here (when we
|
||||||
// Therefore, inserting a `None`-delimtied group here (when we
|
// convert a nested `Nonterminal` to a tokenstream) would cause
|
||||||
// convert a nested `Nonterminal` to a tokenstream) would cause
|
// a mismatch with the reparsed tokenstream.
|
||||||
// a mismatch with the reparsed tokenstream.
|
//
|
||||||
//
|
// Note that we currently do not handle the case where the
|
||||||
// Note that we currently do not handle the case where the
|
// reparsed stream has a `Parenthesis`-delimited group
|
||||||
// reparsed stream has a `Parenthesis`-delimited group
|
// inserted. This will cause a spurious mismatch:
|
||||||
// inserted. This will cause a spurious mismatch:
|
// issue #75734 tracks resolving this.
|
||||||
// issue #75734 tracks resolving this.
|
|
||||||
nt_to_tokenstream(nt, sess, *span).into_trees()
|
let expanded: SmallVec<[_; 1]> =
|
||||||
} else {
|
if let TokenTree::Token(Token { kind: TokenKind::Interpolated(nt), span }) = &tree {
|
||||||
TokenStream::new(vec![(tree, Spacing::Alone)]).into_trees()
|
nt_to_tokenstream(nt, sess, *span)
|
||||||
}
|
.into_trees()
|
||||||
};
|
.flat_map(|t| expand_token(t, sess))
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
// Filter before and after breaking tokens,
|
||||||
|
// since we may want to ignore both glued and unglued tokens.
|
||||||
|
std::iter::once(tree)
|
||||||
|
.filter(semantic_tree)
|
||||||
|
.flat_map(break_tokens)
|
||||||
|
.filter(semantic_tree)
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
expanded.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
// Break tokens after we expand any nonterminals, so that we break tokens
|
// Break tokens after we expand any nonterminals, so that we break tokens
|
||||||
// that are produced as a result of nonterminal expansion.
|
// that are produced as a result of nonterminal expansion.
|
||||||
let tokens = tokens.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens);
|
let tokens = tokens.trees().flat_map(|t| expand_token(t, sess));
|
||||||
let reparsed_tokens =
|
let reparsed_tokens = reparsed_tokens.trees().flat_map(|t| expand_token(t, sess));
|
||||||
reparsed_tokens.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens);
|
|
||||||
|
|
||||||
tokens.eq_by(reparsed_tokens, |t, rt| tokentree_probably_equal_for_proc_macro(&t, &rt, sess))
|
tokens.eq_by(reparsed_tokens, |t, rt| tokentree_probably_equal_for_proc_macro(&t, &rt, sess))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,12 @@ use rustc_middle::hir::map::Map;
|
||||||
use rustc_middle::ty::query::Providers;
|
use rustc_middle::ty::query::Providers;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
|
|
||||||
use rustc_ast::{Attribute, NestedMetaItem};
|
use rustc_ast::{Attribute, LitKind, NestedMetaItem};
|
||||||
use rustc_errors::struct_span_err;
|
use rustc_errors::{pluralize, struct_span_err};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::LocalDefId;
|
use rustc_hir::def_id::LocalDefId;
|
||||||
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||||
use rustc_hir::{self, HirId, Item, ItemKind, TraitItem};
|
use rustc_hir::{self, FnSig, ForeignItem, ForeignItemKind, HirId, Item, ItemKind, TraitItem};
|
||||||
use rustc_hir::{MethodKind, Target};
|
use rustc_hir::{MethodKind, Target};
|
||||||
use rustc_session::lint::builtin::{CONFLICTING_REPR_HINTS, UNUSED_ATTRIBUTES};
|
use rustc_session::lint::builtin::{CONFLICTING_REPR_HINTS, UNUSED_ATTRIBUTES};
|
||||||
use rustc_session::parse::feature_err;
|
use rustc_session::parse::feature_err;
|
||||||
|
@ -43,6 +43,12 @@ pub(crate) fn target_from_impl_item<'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum ItemLike<'tcx> {
|
||||||
|
Item(&'tcx Item<'tcx>),
|
||||||
|
ForeignItem(&'tcx ForeignItem<'tcx>),
|
||||||
|
}
|
||||||
|
|
||||||
struct CheckAttrVisitor<'tcx> {
|
struct CheckAttrVisitor<'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
}
|
}
|
||||||
|
@ -55,7 +61,7 @@ impl CheckAttrVisitor<'tcx> {
|
||||||
attrs: &'hir [Attribute],
|
attrs: &'hir [Attribute],
|
||||||
span: &Span,
|
span: &Span,
|
||||||
target: Target,
|
target: Target,
|
||||||
item: Option<&Item<'_>>,
|
item: Option<ItemLike<'_>>,
|
||||||
) {
|
) {
|
||||||
let mut is_valid = true;
|
let mut is_valid = true;
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
|
@ -75,6 +81,8 @@ impl CheckAttrVisitor<'tcx> {
|
||||||
self.check_no_link(&attr, span, target)
|
self.check_no_link(&attr, span, target)
|
||||||
} else if self.tcx.sess.check_name(attr, sym::export_name) {
|
} else if self.tcx.sess.check_name(attr, sym::export_name) {
|
||||||
self.check_export_name(&attr, span, target)
|
self.check_export_name(&attr, span, target)
|
||||||
|
} else if self.tcx.sess.check_name(attr, sym::rustc_args_required_const) {
|
||||||
|
self.check_rustc_args_required_const(&attr, span, target, item)
|
||||||
} else {
|
} else {
|
||||||
// lint-only checks
|
// lint-only checks
|
||||||
if self.tcx.sess.check_name(attr, sym::cold) {
|
if self.tcx.sess.check_name(attr, sym::cold) {
|
||||||
|
@ -400,6 +408,71 @@ impl CheckAttrVisitor<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if `#[rustc_args_required_const]` is applied to a function and has a valid argument.
|
||||||
|
fn check_rustc_args_required_const(
|
||||||
|
&self,
|
||||||
|
attr: &Attribute,
|
||||||
|
span: &Span,
|
||||||
|
target: Target,
|
||||||
|
item: Option<ItemLike<'_>>,
|
||||||
|
) -> bool {
|
||||||
|
if let Target::Fn | Target::Method(..) | Target::ForeignFn = target {
|
||||||
|
let mut invalid_args = vec![];
|
||||||
|
for meta in attr.meta_item_list().expect("no meta item list") {
|
||||||
|
if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
|
||||||
|
if let Some(ItemLike::Item(Item {
|
||||||
|
kind: ItemKind::Fn(FnSig { decl, .. }, ..),
|
||||||
|
..
|
||||||
|
}))
|
||||||
|
| Some(ItemLike::ForeignItem(ForeignItem {
|
||||||
|
kind: ForeignItemKind::Fn(decl, ..),
|
||||||
|
..
|
||||||
|
})) = item
|
||||||
|
{
|
||||||
|
let arg_count = decl.inputs.len() as u128;
|
||||||
|
if *val >= arg_count {
|
||||||
|
let span = meta.span();
|
||||||
|
self.tcx
|
||||||
|
.sess
|
||||||
|
.struct_span_err(span, "index exceeds number of arguments")
|
||||||
|
.span_label(
|
||||||
|
span,
|
||||||
|
format!(
|
||||||
|
"there {} only {} argument{}",
|
||||||
|
if arg_count != 1 { "are" } else { "is" },
|
||||||
|
arg_count,
|
||||||
|
pluralize!(arg_count)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bug!("should be a function item");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
invalid_args.push(meta.span());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !invalid_args.is_empty() {
|
||||||
|
self.tcx
|
||||||
|
.sess
|
||||||
|
.struct_span_err(invalid_args, "arguments should be non-negative integers")
|
||||||
|
.emit();
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.tcx
|
||||||
|
.sess
|
||||||
|
.struct_span_err(attr.span, "attribute should be applied to a function")
|
||||||
|
.span_label(*span, "not a function")
|
||||||
|
.emit();
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks if `#[link_section]` is applied to a function or static.
|
/// Checks if `#[link_section]` is applied to a function or static.
|
||||||
fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
|
fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
|
||||||
match target {
|
match target {
|
||||||
|
@ -448,7 +521,7 @@ impl CheckAttrVisitor<'tcx> {
|
||||||
attrs: &'hir [Attribute],
|
attrs: &'hir [Attribute],
|
||||||
span: &Span,
|
span: &Span,
|
||||||
target: Target,
|
target: Target,
|
||||||
item: Option<&Item<'_>>,
|
item: Option<ItemLike<'_>>,
|
||||||
hir_id: HirId,
|
hir_id: HirId,
|
||||||
) {
|
) {
|
||||||
// Extract the names of all repr hints, e.g., [foo, bar, align] for:
|
// Extract the names of all repr hints, e.g., [foo, bar, align] for:
|
||||||
|
@ -564,7 +637,14 @@ impl CheckAttrVisitor<'tcx> {
|
||||||
// Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
|
// Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
|
||||||
if (int_reprs > 1)
|
if (int_reprs > 1)
|
||||||
|| (is_simd && is_c)
|
|| (is_simd && is_c)
|
||||||
|| (int_reprs == 1 && is_c && item.map_or(false, |item| is_c_like_enum(item)))
|
|| (int_reprs == 1
|
||||||
|
&& is_c
|
||||||
|
&& item.map_or(false, |item| {
|
||||||
|
if let ItemLike::Item(item) = item {
|
||||||
|
return is_c_like_enum(item);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}))
|
||||||
{
|
{
|
||||||
self.tcx.struct_span_lint_hir(
|
self.tcx.struct_span_lint_hir(
|
||||||
CONFLICTING_REPR_HINTS,
|
CONFLICTING_REPR_HINTS,
|
||||||
|
@ -649,7 +729,13 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
|
||||||
|
|
||||||
fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
|
fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
|
||||||
let target = Target::from_item(item);
|
let target = Target::from_item(item);
|
||||||
self.check_attributes(item.hir_id, item.attrs, &item.span, target, Some(item));
|
self.check_attributes(
|
||||||
|
item.hir_id,
|
||||||
|
item.attrs,
|
||||||
|
&item.span,
|
||||||
|
target,
|
||||||
|
Some(ItemLike::Item(item)),
|
||||||
|
);
|
||||||
intravisit::walk_item(self, item)
|
intravisit::walk_item(self, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -659,9 +745,15 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
|
||||||
intravisit::walk_trait_item(self, trait_item)
|
intravisit::walk_trait_item(self, trait_item)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_foreign_item(&mut self, f_item: &'tcx hir::ForeignItem<'tcx>) {
|
fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
|
||||||
let target = Target::from_foreign_item(f_item);
|
let target = Target::from_foreign_item(f_item);
|
||||||
self.check_attributes(f_item.hir_id, &f_item.attrs, &f_item.span, target, None);
|
self.check_attributes(
|
||||||
|
f_item.hir_id,
|
||||||
|
&f_item.attrs,
|
||||||
|
&f_item.span,
|
||||||
|
target,
|
||||||
|
Some(ItemLike::ForeignItem(f_item)),
|
||||||
|
);
|
||||||
intravisit::walk_foreign_item(self, f_item)
|
intravisit::walk_foreign_item(self, f_item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use super::apple_sdk_base::{opts, AppleOS, Arch};
|
use super::apple_sdk_base::{opts, Arch};
|
||||||
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
|
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
|
||||||
|
|
||||||
pub fn target() -> TargetResult {
|
pub fn target() -> TargetResult {
|
||||||
let base = opts(Arch::Arm64, AppleOS::iOS)?;
|
let base = opts(Arch::Arm64);
|
||||||
Ok(Target {
|
Ok(Target {
|
||||||
llvm_target: "arm64-apple-ios".to_string(),
|
llvm_target: "arm64-apple-ios".to_string(),
|
||||||
target_endian: "little".to_string(),
|
target_endian: "little".to_string(),
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use super::apple_sdk_base::{opts, AppleOS, Arch};
|
use super::apple_sdk_base::{opts, Arch};
|
||||||
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
|
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
|
||||||
|
|
||||||
pub fn target() -> TargetResult {
|
pub fn target() -> TargetResult {
|
||||||
let base = opts(Arch::Arm64, AppleOS::tvOS)?;
|
let base = opts(Arch::Arm64);
|
||||||
Ok(Target {
|
Ok(Target {
|
||||||
llvm_target: "arm64-apple-tvos".to_string(),
|
llvm_target: "arm64-apple-tvos".to_string(),
|
||||||
target_endian: "little".to_string(),
|
target_endian: "little".to_string(),
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions};
|
use crate::spec::TargetOptions;
|
||||||
use std::env;
|
|
||||||
use std::io;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
use Arch::*;
|
use Arch::*;
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
|
@ -16,108 +12,6 @@ pub enum Arch {
|
||||||
X86_64_macabi,
|
X86_64_macabi,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum AppleOS {
|
|
||||||
tvOS,
|
|
||||||
iOS,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Arch {
|
|
||||||
pub fn to_string(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
Armv7 => "armv7",
|
|
||||||
Armv7s => "armv7s",
|
|
||||||
Arm64 => "arm64",
|
|
||||||
I386 => "i386",
|
|
||||||
X86_64 => "x86_64",
|
|
||||||
X86_64_macabi => "x86_64",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_sdk_root(sdk_name: &str) -> Result<String, String> {
|
|
||||||
// Following what clang does
|
|
||||||
// (https://github.com/llvm/llvm-project/blob/
|
|
||||||
// 296a80102a9b72c3eda80558fb78a3ed8849b341/clang/lib/Driver/ToolChains/Darwin.cpp#L1661-L1678)
|
|
||||||
// to allow the SDK path to be set. (For clang, xcrun sets
|
|
||||||
// SDKROOT; for rustc, the user or build system can set it, or we
|
|
||||||
// can fall back to checking for xcrun on PATH.)
|
|
||||||
if let Ok(sdkroot) = env::var("SDKROOT") {
|
|
||||||
let p = Path::new(&sdkroot);
|
|
||||||
match sdk_name {
|
|
||||||
// Ignore `SDKROOT` if it's clearly set for the wrong platform.
|
|
||||||
"appletvos"
|
|
||||||
if sdkroot.contains("TVSimulator.platform")
|
|
||||||
|| sdkroot.contains("MacOSX.platform") => {}
|
|
||||||
"appletvsimulator"
|
|
||||||
if sdkroot.contains("TVOS.platform") || sdkroot.contains("MacOSX.platform") => {}
|
|
||||||
"iphoneos"
|
|
||||||
if sdkroot.contains("iPhoneSimulator.platform")
|
|
||||||
|| sdkroot.contains("MacOSX.platform") => {}
|
|
||||||
"iphonesimulator"
|
|
||||||
if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("MacOSX.platform") => {
|
|
||||||
}
|
|
||||||
"macosx10.15"
|
|
||||||
if sdkroot.contains("iPhoneOS.platform")
|
|
||||||
|| sdkroot.contains("iPhoneSimulator.platform") => {}
|
|
||||||
// Ignore `SDKROOT` if it's not a valid path.
|
|
||||||
_ if !p.is_absolute() || p == Path::new("/") || !p.exists() => {}
|
|
||||||
_ => return Ok(sdkroot),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let res =
|
|
||||||
Command::new("xcrun").arg("--show-sdk-path").arg("-sdk").arg(sdk_name).output().and_then(
|
|
||||||
|output| {
|
|
||||||
if output.status.success() {
|
|
||||||
Ok(String::from_utf8(output.stdout).unwrap())
|
|
||||||
} else {
|
|
||||||
let error = String::from_utf8(output.stderr);
|
|
||||||
let error = format!("process exit with error: {}", error.unwrap());
|
|
||||||
Err(io::Error::new(io::ErrorKind::Other, &error[..]))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
match res {
|
|
||||||
Ok(output) => Ok(output.trim().to_string()),
|
|
||||||
Err(e) => Err(format!("failed to get {} SDK path: {}", sdk_name, e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_pre_link_args(arch: Arch, os: AppleOS) -> Result<LinkArgs, String> {
|
|
||||||
let sdk_name = match (arch, os) {
|
|
||||||
(Arm64, AppleOS::tvOS) => "appletvos",
|
|
||||||
(X86_64, AppleOS::tvOS) => "appletvsimulator",
|
|
||||||
(Armv7, AppleOS::iOS) => "iphoneos",
|
|
||||||
(Armv7s, AppleOS::iOS) => "iphoneos",
|
|
||||||
(Arm64, AppleOS::iOS) => "iphoneos",
|
|
||||||
(I386, AppleOS::iOS) => "iphonesimulator",
|
|
||||||
(X86_64, AppleOS::iOS) => "iphonesimulator",
|
|
||||||
(X86_64_macabi, AppleOS::iOS) => "macosx10.15",
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let arch_name = arch.to_string();
|
|
||||||
|
|
||||||
let sdk_root = get_sdk_root(sdk_name)?;
|
|
||||||
|
|
||||||
let mut args = LinkArgs::new();
|
|
||||||
args.insert(
|
|
||||||
LinkerFlavor::Gcc,
|
|
||||||
vec![
|
|
||||||
"-arch".to_string(),
|
|
||||||
arch_name.to_string(),
|
|
||||||
"-isysroot".to_string(),
|
|
||||||
sdk_root.clone(),
|
|
||||||
"-Wl,-syslibroot".to_string(),
|
|
||||||
sdk_root,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(args)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn target_cpu(arch: Arch) -> String {
|
fn target_cpu(arch: Arch) -> String {
|
||||||
match arch {
|
match arch {
|
||||||
Armv7 => "cortex-a8", // iOS7 is supported on iPhone 4 and higher
|
Armv7 => "cortex-a8", // iOS7 is supported on iPhone 4 and higher
|
||||||
|
@ -137,15 +31,13 @@ fn link_env_remove(arch: Arch) -> Vec<String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn opts(arch: Arch, os: AppleOS) -> Result<TargetOptions, String> {
|
pub fn opts(arch: Arch) -> TargetOptions {
|
||||||
let pre_link_args = build_pre_link_args(arch, os)?;
|
TargetOptions {
|
||||||
Ok(TargetOptions {
|
|
||||||
cpu: target_cpu(arch),
|
cpu: target_cpu(arch),
|
||||||
executables: true,
|
executables: true,
|
||||||
pre_link_args,
|
|
||||||
link_env_remove: link_env_remove(arch),
|
link_env_remove: link_env_remove(arch),
|
||||||
has_elf_tls: false,
|
has_elf_tls: false,
|
||||||
eliminate_frame_pointer: false,
|
eliminate_frame_pointer: false,
|
||||||
..super::apple_base::opts()
|
..super::apple_base::opts()
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use super::apple_sdk_base::{opts, AppleOS, Arch};
|
use super::apple_sdk_base::{opts, Arch};
|
||||||
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
|
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
|
||||||
|
|
||||||
pub fn target() -> TargetResult {
|
pub fn target() -> TargetResult {
|
||||||
let base = opts(Arch::Armv7, AppleOS::iOS)?;
|
let base = opts(Arch::Armv7);
|
||||||
Ok(Target {
|
Ok(Target {
|
||||||
llvm_target: "armv7-apple-ios".to_string(),
|
llvm_target: "armv7-apple-ios".to_string(),
|
||||||
target_endian: "little".to_string(),
|
target_endian: "little".to_string(),
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use super::apple_sdk_base::{opts, AppleOS, Arch};
|
use super::apple_sdk_base::{opts, Arch};
|
||||||
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
|
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
|
||||||
|
|
||||||
pub fn target() -> TargetResult {
|
pub fn target() -> TargetResult {
|
||||||
let base = opts(Arch::Armv7s, AppleOS::iOS)?;
|
let base = opts(Arch::Armv7s);
|
||||||
Ok(Target {
|
Ok(Target {
|
||||||
llvm_target: "armv7s-apple-ios".to_string(),
|
llvm_target: "armv7s-apple-ios".to_string(),
|
||||||
target_endian: "little".to_string(),
|
target_endian: "little".to_string(),
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use super::apple_sdk_base::{opts, AppleOS, Arch};
|
use super::apple_sdk_base::{opts, Arch};
|
||||||
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
|
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
|
||||||
|
|
||||||
pub fn target() -> TargetResult {
|
pub fn target() -> TargetResult {
|
||||||
let base = opts(Arch::I386, AppleOS::iOS)?;
|
let base = opts(Arch::I386);
|
||||||
Ok(Target {
|
Ok(Target {
|
||||||
llvm_target: "i386-apple-ios".to_string(),
|
llvm_target: "i386-apple-ios".to_string(),
|
||||||
target_endian: "little".to_string(),
|
target_endian: "little".to_string(),
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use super::apple_sdk_base::{opts, AppleOS, Arch};
|
use super::apple_sdk_base::{opts, Arch};
|
||||||
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
|
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
|
||||||
|
|
||||||
pub fn target() -> TargetResult {
|
pub fn target() -> TargetResult {
|
||||||
let base = opts(Arch::X86_64, AppleOS::iOS)?;
|
let base = opts(Arch::X86_64);
|
||||||
Ok(Target {
|
Ok(Target {
|
||||||
llvm_target: "x86_64-apple-ios".to_string(),
|
llvm_target: "x86_64-apple-ios".to_string(),
|
||||||
target_endian: "little".to_string(),
|
target_endian: "little".to_string(),
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use super::apple_sdk_base::{opts, AppleOS, Arch};
|
use super::apple_sdk_base::{opts, Arch};
|
||||||
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
|
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
|
||||||
|
|
||||||
pub fn target() -> TargetResult {
|
pub fn target() -> TargetResult {
|
||||||
let base = opts(Arch::X86_64_macabi, AppleOS::iOS)?;
|
let base = opts(Arch::X86_64_macabi);
|
||||||
Ok(Target {
|
Ok(Target {
|
||||||
llvm_target: "x86_64-apple-ios13.0-macabi".to_string(),
|
llvm_target: "x86_64-apple-ios13.0-macabi".to_string(),
|
||||||
target_endian: "little".to_string(),
|
target_endian: "little".to_string(),
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use super::apple_sdk_base::{opts, AppleOS, Arch};
|
use super::apple_sdk_base::{opts, Arch};
|
||||||
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
|
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
|
||||||
|
|
||||||
pub fn target() -> TargetResult {
|
pub fn target() -> TargetResult {
|
||||||
let base = opts(Arch::X86_64, AppleOS::iOS)?;
|
let base = opts(Arch::X86_64);
|
||||||
Ok(Target {
|
Ok(Target {
|
||||||
llvm_target: "x86_64-apple-tvos".to_string(),
|
llvm_target: "x86_64-apple-tvos".to_string(),
|
||||||
target_endian: "little".to_string(),
|
target_endian: "little".to_string(),
|
||||||
|
|
|
@ -23,6 +23,9 @@ use rustc_session::lint;
|
||||||
use rustc_span::def_id::{DefId, LocalDefId};
|
use rustc_span::def_id::{DefId, LocalDefId};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
use std::cmp;
|
||||||
|
|
||||||
|
/// Check if a given constant can be evaluated.
|
||||||
pub fn is_const_evaluatable<'cx, 'tcx>(
|
pub fn is_const_evaluatable<'cx, 'tcx>(
|
||||||
infcx: &InferCtxt<'cx, 'tcx>,
|
infcx: &InferCtxt<'cx, 'tcx>,
|
||||||
def: ty::WithOptConstParam<DefId>,
|
def: ty::WithOptConstParam<DefId>,
|
||||||
|
@ -32,24 +35,88 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
|
||||||
) -> Result<(), ErrorHandled> {
|
) -> Result<(), ErrorHandled> {
|
||||||
debug!("is_const_evaluatable({:?}, {:?})", def, substs);
|
debug!("is_const_evaluatable({:?}, {:?})", def, substs);
|
||||||
if infcx.tcx.features().const_evaluatable_checked {
|
if infcx.tcx.features().const_evaluatable_checked {
|
||||||
if let Some(ct) = AbstractConst::new(infcx.tcx, def, substs)? {
|
let tcx = infcx.tcx;
|
||||||
for pred in param_env.caller_bounds() {
|
match AbstractConst::new(tcx, def, substs)? {
|
||||||
match pred.skip_binders() {
|
// We are looking at a generic abstract constant.
|
||||||
ty::PredicateAtom::ConstEvaluatable(b_def, b_substs) => {
|
Some(ct) => {
|
||||||
debug!("is_const_evaluatable: caller_bound={:?}, {:?}", b_def, b_substs);
|
for pred in param_env.caller_bounds() {
|
||||||
if b_def == def && b_substs == substs {
|
match pred.skip_binders() {
|
||||||
debug!("is_const_evaluatable: caller_bound ~~> ok");
|
ty::PredicateAtom::ConstEvaluatable(b_def, b_substs) => {
|
||||||
return Ok(());
|
debug!(
|
||||||
} else if AbstractConst::new(infcx.tcx, b_def, b_substs)?
|
"is_const_evaluatable: caller_bound={:?}, {:?}",
|
||||||
.map_or(false, |b_ct| try_unify(infcx.tcx, ct, b_ct))
|
b_def, b_substs
|
||||||
{
|
);
|
||||||
debug!("is_const_evaluatable: abstract_const ~~> ok");
|
if b_def == def && b_substs == substs {
|
||||||
return Ok(());
|
debug!("is_const_evaluatable: caller_bound ~~> ok");
|
||||||
|
return Ok(());
|
||||||
|
} else if AbstractConst::new(tcx, b_def, b_substs)?
|
||||||
|
.map_or(false, |b_ct| try_unify(tcx, ct, b_ct))
|
||||||
|
{
|
||||||
|
debug!("is_const_evaluatable: abstract_const ~~> ok");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {} // don't care
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We were unable to unify the abstract constant with
|
||||||
|
// a constant found in the caller bounds, there are
|
||||||
|
// now three possible cases here.
|
||||||
|
//
|
||||||
|
// - The substs are concrete enough that we can simply
|
||||||
|
// try and evaluate the given constant.
|
||||||
|
// - The abstract const still references an inference
|
||||||
|
// variable, in this case we return `TooGeneric`.
|
||||||
|
// - The abstract const references a generic parameter,
|
||||||
|
// this means that we emit an error here.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
enum FailureKind {
|
||||||
|
MentionsInfer,
|
||||||
|
MentionsParam,
|
||||||
|
Concrete,
|
||||||
|
}
|
||||||
|
let mut failure_kind = FailureKind::Concrete;
|
||||||
|
walk_abstract_const(tcx, ct, |node| match node {
|
||||||
|
Node::Leaf(leaf) => {
|
||||||
|
let leaf = leaf.subst(tcx, ct.substs);
|
||||||
|
if leaf.has_infer_types_or_consts() {
|
||||||
|
failure_kind = FailureKind::MentionsInfer;
|
||||||
|
} else if leaf.has_param_types_or_consts() {
|
||||||
|
failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {} // don't care
|
Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => (),
|
||||||
|
});
|
||||||
|
|
||||||
|
match failure_kind {
|
||||||
|
FailureKind::MentionsInfer => {
|
||||||
|
return Err(ErrorHandled::TooGeneric);
|
||||||
|
}
|
||||||
|
FailureKind::MentionsParam => {
|
||||||
|
// FIXME(const_evaluatable_checked): Better error message.
|
||||||
|
infcx
|
||||||
|
.tcx
|
||||||
|
.sess
|
||||||
|
.struct_span_err(span, "unconstrained generic constant")
|
||||||
|
.span_help(
|
||||||
|
tcx.def_span(def.did),
|
||||||
|
"consider adding a `where` bound for this expression",
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
return Err(ErrorHandled::Reported(ErrorReported));
|
||||||
|
}
|
||||||
|
FailureKind::Concrete => {
|
||||||
|
// Dealt with below by the same code which handles this
|
||||||
|
// without the feature gate.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
|
// If we are dealing with a concrete constant, we can
|
||||||
|
// reuse the old code path and try to evaluate
|
||||||
|
// the constant.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +162,36 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(?concrete, "is_const_evaluatable");
|
debug!(?concrete, "is_const_evaluatable");
|
||||||
concrete.map(drop)
|
match concrete {
|
||||||
|
Err(ErrorHandled::TooGeneric) if !substs.has_infer_types_or_consts() => {
|
||||||
|
// FIXME(const_evaluatable_checked): We really should move
|
||||||
|
// emitting this error message to fulfill instead. For
|
||||||
|
// now this is easier.
|
||||||
|
//
|
||||||
|
// This is not a problem without `const_evaluatable_checked` as
|
||||||
|
// all `ConstEvaluatable` predicates have to be fulfilled for compilation
|
||||||
|
// to succeed.
|
||||||
|
//
|
||||||
|
// @lcnr: We already emit an error for things like
|
||||||
|
// `fn test<const N: usize>() -> [0 - N]` eagerly here,
|
||||||
|
// so until we fix this I don't really care.
|
||||||
|
|
||||||
|
let mut err = infcx
|
||||||
|
.tcx
|
||||||
|
.sess
|
||||||
|
.struct_span_err(span, "constant expression depends on a generic parameter");
|
||||||
|
// FIXME(const_generics): we should suggest to the user how they can resolve this
|
||||||
|
// issue. However, this is currently not actually possible
|
||||||
|
// (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083).
|
||||||
|
//
|
||||||
|
// Note that with `feature(const_evaluatable_checked)` this case should not
|
||||||
|
// be reachable.
|
||||||
|
err.note("this may fail depending on what value the parameter takes");
|
||||||
|
err.emit();
|
||||||
|
Err(ErrorHandled::Reported(ErrorReported))
|
||||||
|
}
|
||||||
|
c => c.map(drop),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A tree representing an anonymous constant.
|
/// A tree representing an anonymous constant.
|
||||||
|
@ -421,6 +517,33 @@ pub(super) fn try_unify_abstract_consts<'tcx>(
|
||||||
// on `ErrorReported`.
|
// on `ErrorReported`.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn walk_abstract_const<'tcx, F>(tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, mut f: F)
|
||||||
|
where
|
||||||
|
F: FnMut(Node<'tcx>),
|
||||||
|
{
|
||||||
|
recurse(tcx, ct, &mut f);
|
||||||
|
fn recurse<'tcx>(tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, f: &mut dyn FnMut(Node<'tcx>)) {
|
||||||
|
let root = ct.root();
|
||||||
|
f(root);
|
||||||
|
match root {
|
||||||
|
Node::Leaf(_) => (),
|
||||||
|
Node::Binop(_, l, r) => {
|
||||||
|
recurse(tcx, ct.subtree(l), f);
|
||||||
|
recurse(tcx, ct.subtree(r), f);
|
||||||
|
}
|
||||||
|
Node::UnaryOp(_, v) => {
|
||||||
|
recurse(tcx, ct.subtree(v), f);
|
||||||
|
}
|
||||||
|
Node::FunctionCall(func, args) => {
|
||||||
|
recurse(tcx, ct.subtree(func), f);
|
||||||
|
for &arg in args {
|
||||||
|
recurse(tcx, ct.subtree(arg), f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Tries to unify two abstract constants using structural equality.
|
/// Tries to unify two abstract constants using structural equality.
|
||||||
pub(super) fn try_unify<'tcx>(
|
pub(super) fn try_unify<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
|
|
|
@ -745,25 +745,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
let violations = self.tcx.object_safety_violations(did);
|
let violations = self.tcx.object_safety_violations(did);
|
||||||
report_object_safety_error(self.tcx, span, did, violations)
|
report_object_safety_error(self.tcx, span, did, violations)
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstEvalFailure(ErrorHandled::TooGeneric) => {
|
ConstEvalFailure(ErrorHandled::TooGeneric) => {
|
||||||
// In this instance, we have a const expression containing an unevaluated
|
bug!("too generic should have been handled in `is_const_evaluatable`");
|
||||||
// generic parameter. We have no idea whether this expression is valid or
|
|
||||||
// not (e.g. it might result in an error), but we don't want to just assume
|
|
||||||
// that it's okay, because that might result in post-monomorphisation time
|
|
||||||
// errors. The onus is really on the caller to provide values that it can
|
|
||||||
// prove are well-formed.
|
|
||||||
let mut err = self
|
|
||||||
.tcx
|
|
||||||
.sess
|
|
||||||
.struct_span_err(span, "constant expression depends on a generic parameter");
|
|
||||||
// FIXME(const_generics): we should suggest to the user how they can resolve this
|
|
||||||
// issue. However, this is currently not actually possible
|
|
||||||
// (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083).
|
|
||||||
err.note("this may fail depending on what value the parameter takes");
|
|
||||||
err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Already reported in the query.
|
// Already reported in the query.
|
||||||
ConstEvalFailure(ErrorHandled::Reported(ErrorReported)) => {
|
ConstEvalFailure(ErrorHandled::Reported(ErrorReported)) => {
|
||||||
// FIXME(eddyb) remove this once `ErrorReported` becomes a proof token.
|
// FIXME(eddyb) remove this once `ErrorReported` becomes a proof token.
|
||||||
|
|
|
@ -496,6 +496,13 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
|
||||||
obligation.cause.span,
|
obligation.cause.span,
|
||||||
) {
|
) {
|
||||||
Ok(()) => ProcessResult::Changed(vec![]),
|
Ok(()) => ProcessResult::Changed(vec![]),
|
||||||
|
Err(ErrorHandled::TooGeneric) => {
|
||||||
|
pending_obligation.stalled_on = substs
|
||||||
|
.iter()
|
||||||
|
.filter_map(|ty| TyOrConstInferVar::maybe_from_generic_arg(ty))
|
||||||
|
.collect();
|
||||||
|
ProcessResult::Unchanged
|
||||||
|
}
|
||||||
Err(e) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(e))),
|
Err(e) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(e))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -537,8 +544,10 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
|
||||||
Err(ErrorHandled::TooGeneric) => {
|
Err(ErrorHandled::TooGeneric) => {
|
||||||
stalled_on.append(
|
stalled_on.append(
|
||||||
&mut substs
|
&mut substs
|
||||||
.types()
|
.iter()
|
||||||
.filter_map(|ty| TyOrConstInferVar::maybe_from_ty(ty))
|
.filter_map(|arg| {
|
||||||
|
TyOrConstInferVar::maybe_from_generic_arg(arg)
|
||||||
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
);
|
);
|
||||||
Err(ErrorHandled::TooGeneric)
|
Err(ErrorHandled::TooGeneric)
|
||||||
|
|
|
@ -7,14 +7,19 @@
|
||||||
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
|
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
|
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
|
||||||
|
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||||
use rustc_middle::ty::{self, TypeFoldable};
|
use rustc_middle::ty::{self, TypeFoldable};
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
|
|
||||||
|
use crate::traits::coherence::Conflict;
|
||||||
use crate::traits::{util, SelectionResult};
|
use crate::traits::{util, SelectionResult};
|
||||||
|
use crate::traits::{Overflow, Unimplemented};
|
||||||
|
|
||||||
use super::BuiltinImplConditions;
|
use super::BuiltinImplConditions;
|
||||||
|
use super::IntercrateAmbiguityCause;
|
||||||
|
use super::OverflowError;
|
||||||
use super::SelectionCandidate::{self, *};
|
use super::SelectionCandidate::{self, *};
|
||||||
use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack};
|
use super::{EvaluatedCandidate, SelectionCandidateSet, SelectionContext, TraitObligationStack};
|
||||||
|
|
||||||
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
pub(super) fn candidate_from_obligation<'o>(
|
pub(super) fn candidate_from_obligation<'o>(
|
||||||
|
@ -62,6 +67,161 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
candidate
|
candidate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn candidate_from_obligation_no_cache<'o>(
|
||||||
|
&mut self,
|
||||||
|
stack: &TraitObligationStack<'o, 'tcx>,
|
||||||
|
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
|
||||||
|
if let Some(conflict) = self.is_knowable(stack) {
|
||||||
|
debug!("coherence stage: not knowable");
|
||||||
|
if self.intercrate_ambiguity_causes.is_some() {
|
||||||
|
debug!("evaluate_stack: intercrate_ambiguity_causes is some");
|
||||||
|
// Heuristics: show the diagnostics when there are no candidates in crate.
|
||||||
|
if let Ok(candidate_set) = self.assemble_candidates(stack) {
|
||||||
|
let mut no_candidates_apply = true;
|
||||||
|
|
||||||
|
for c in candidate_set.vec.iter() {
|
||||||
|
if self.evaluate_candidate(stack, &c)?.may_apply() {
|
||||||
|
no_candidates_apply = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !candidate_set.ambiguous && no_candidates_apply {
|
||||||
|
let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
|
||||||
|
let self_ty = trait_ref.self_ty();
|
||||||
|
let (trait_desc, self_desc) = with_no_trimmed_paths(|| {
|
||||||
|
let trait_desc = trait_ref.print_only_trait_path().to_string();
|
||||||
|
let self_desc = if self_ty.has_concrete_skeleton() {
|
||||||
|
Some(self_ty.to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
(trait_desc, self_desc)
|
||||||
|
});
|
||||||
|
let cause = if let Conflict::Upstream = conflict {
|
||||||
|
IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
|
||||||
|
} else {
|
||||||
|
IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
|
||||||
|
};
|
||||||
|
debug!("evaluate_stack: pushing cause = {:?}", cause);
|
||||||
|
self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let candidate_set = self.assemble_candidates(stack)?;
|
||||||
|
|
||||||
|
if candidate_set.ambiguous {
|
||||||
|
debug!("candidate set contains ambig");
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut candidates = candidate_set.vec;
|
||||||
|
|
||||||
|
debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates);
|
||||||
|
|
||||||
|
// At this point, we know that each of the entries in the
|
||||||
|
// candidate set is *individually* applicable. Now we have to
|
||||||
|
// figure out if they contain mutual incompatibilities. This
|
||||||
|
// frequently arises if we have an unconstrained input type --
|
||||||
|
// for example, we are looking for `$0: Eq` where `$0` is some
|
||||||
|
// unconstrained type variable. In that case, we'll get a
|
||||||
|
// candidate which assumes $0 == int, one that assumes `$0 ==
|
||||||
|
// usize`, etc. This spells an ambiguity.
|
||||||
|
|
||||||
|
// If there is more than one candidate, first winnow them down
|
||||||
|
// by considering extra conditions (nested obligations and so
|
||||||
|
// forth). We don't winnow if there is exactly one
|
||||||
|
// candidate. This is a relatively minor distinction but it
|
||||||
|
// can lead to better inference and error-reporting. An
|
||||||
|
// example would be if there was an impl:
|
||||||
|
//
|
||||||
|
// impl<T:Clone> Vec<T> { fn push_clone(...) { ... } }
|
||||||
|
//
|
||||||
|
// and we were to see some code `foo.push_clone()` where `boo`
|
||||||
|
// is a `Vec<Bar>` and `Bar` does not implement `Clone`. If
|
||||||
|
// we were to winnow, we'd wind up with zero candidates.
|
||||||
|
// Instead, we select the right impl now but report "`Bar` does
|
||||||
|
// not implement `Clone`".
|
||||||
|
if candidates.len() == 1 {
|
||||||
|
return self.filter_negative_and_reservation_impls(candidates.pop().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Winnow, but record the exact outcome of evaluation, which
|
||||||
|
// is needed for specialization. Propagate overflow if it occurs.
|
||||||
|
let mut candidates = candidates
|
||||||
|
.into_iter()
|
||||||
|
.map(|c| match self.evaluate_candidate(stack, &c) {
|
||||||
|
Ok(eval) if eval.may_apply() => {
|
||||||
|
Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval }))
|
||||||
|
}
|
||||||
|
Ok(_) => Ok(None),
|
||||||
|
Err(OverflowError) => Err(Overflow),
|
||||||
|
})
|
||||||
|
.flat_map(Result::transpose)
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates);
|
||||||
|
|
||||||
|
let needs_infer = stack.obligation.predicate.needs_infer();
|
||||||
|
|
||||||
|
// If there are STILL multiple candidates, we can further
|
||||||
|
// reduce the list by dropping duplicates -- including
|
||||||
|
// resolving specializations.
|
||||||
|
if candidates.len() > 1 {
|
||||||
|
let mut i = 0;
|
||||||
|
while i < candidates.len() {
|
||||||
|
let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| {
|
||||||
|
self.candidate_should_be_dropped_in_favor_of(
|
||||||
|
&candidates[i],
|
||||||
|
&candidates[j],
|
||||||
|
needs_infer,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
if is_dup {
|
||||||
|
debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]);
|
||||||
|
candidates.swap_remove(i);
|
||||||
|
} else {
|
||||||
|
debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]);
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
// If there are *STILL* multiple candidates, give up
|
||||||
|
// and report ambiguity.
|
||||||
|
if i > 1 {
|
||||||
|
debug!("multiple matches, ambig");
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are *NO* candidates, then there are no impls --
|
||||||
|
// that we know of, anyway. Note that in the case where there
|
||||||
|
// are unbound type variables within the obligation, it might
|
||||||
|
// be the case that you could still satisfy the obligation
|
||||||
|
// from another crate by instantiating the type variables with
|
||||||
|
// a type from another crate that does have an impl. This case
|
||||||
|
// is checked for in `evaluate_stack` (and hence users
|
||||||
|
// who might care about this case, like coherence, should use
|
||||||
|
// that function).
|
||||||
|
if candidates.is_empty() {
|
||||||
|
// If there's an error type, 'downgrade' our result from
|
||||||
|
// `Err(Unimplemented)` to `Ok(None)`. This helps us avoid
|
||||||
|
// emitting additional spurious errors, since we're guaranteed
|
||||||
|
// to have emitted at least one.
|
||||||
|
if stack.obligation.references_error() {
|
||||||
|
debug!("no results for error type, treating as ambiguous");
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
return Err(Unimplemented);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just one candidate left.
|
||||||
|
self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate)
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn assemble_candidates<'o>(
|
pub(super) fn assemble_candidates<'o>(
|
||||||
&mut self,
|
&mut self,
|
||||||
stack: &TraitObligationStack<'o, 'tcx>,
|
stack: &TraitObligationStack<'o, 'tcx>,
|
||||||
|
|
|
@ -1029,161 +1029,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
Ok(Some(candidate))
|
Ok(Some(candidate))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn candidate_from_obligation_no_cache<'o>(
|
|
||||||
&mut self,
|
|
||||||
stack: &TraitObligationStack<'o, 'tcx>,
|
|
||||||
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
|
|
||||||
if let Some(conflict) = self.is_knowable(stack) {
|
|
||||||
debug!("coherence stage: not knowable");
|
|
||||||
if self.intercrate_ambiguity_causes.is_some() {
|
|
||||||
debug!("evaluate_stack: intercrate_ambiguity_causes is some");
|
|
||||||
// Heuristics: show the diagnostics when there are no candidates in crate.
|
|
||||||
if let Ok(candidate_set) = self.assemble_candidates(stack) {
|
|
||||||
let mut no_candidates_apply = true;
|
|
||||||
|
|
||||||
for c in candidate_set.vec.iter() {
|
|
||||||
if self.evaluate_candidate(stack, &c)?.may_apply() {
|
|
||||||
no_candidates_apply = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !candidate_set.ambiguous && no_candidates_apply {
|
|
||||||
let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
|
|
||||||
let self_ty = trait_ref.self_ty();
|
|
||||||
let (trait_desc, self_desc) = with_no_trimmed_paths(|| {
|
|
||||||
let trait_desc = trait_ref.print_only_trait_path().to_string();
|
|
||||||
let self_desc = if self_ty.has_concrete_skeleton() {
|
|
||||||
Some(self_ty.to_string())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
(trait_desc, self_desc)
|
|
||||||
});
|
|
||||||
let cause = if let Conflict::Upstream = conflict {
|
|
||||||
IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
|
|
||||||
} else {
|
|
||||||
IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
|
|
||||||
};
|
|
||||||
debug!("evaluate_stack: pushing cause = {:?}", cause);
|
|
||||||
self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let candidate_set = self.assemble_candidates(stack)?;
|
|
||||||
|
|
||||||
if candidate_set.ambiguous {
|
|
||||||
debug!("candidate set contains ambig");
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut candidates = candidate_set.vec;
|
|
||||||
|
|
||||||
debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates);
|
|
||||||
|
|
||||||
// At this point, we know that each of the entries in the
|
|
||||||
// candidate set is *individually* applicable. Now we have to
|
|
||||||
// figure out if they contain mutual incompatibilities. This
|
|
||||||
// frequently arises if we have an unconstrained input type --
|
|
||||||
// for example, we are looking for `$0: Eq` where `$0` is some
|
|
||||||
// unconstrained type variable. In that case, we'll get a
|
|
||||||
// candidate which assumes $0 == int, one that assumes `$0 ==
|
|
||||||
// usize`, etc. This spells an ambiguity.
|
|
||||||
|
|
||||||
// If there is more than one candidate, first winnow them down
|
|
||||||
// by considering extra conditions (nested obligations and so
|
|
||||||
// forth). We don't winnow if there is exactly one
|
|
||||||
// candidate. This is a relatively minor distinction but it
|
|
||||||
// can lead to better inference and error-reporting. An
|
|
||||||
// example would be if there was an impl:
|
|
||||||
//
|
|
||||||
// impl<T:Clone> Vec<T> { fn push_clone(...) { ... } }
|
|
||||||
//
|
|
||||||
// and we were to see some code `foo.push_clone()` where `boo`
|
|
||||||
// is a `Vec<Bar>` and `Bar` does not implement `Clone`. If
|
|
||||||
// we were to winnow, we'd wind up with zero candidates.
|
|
||||||
// Instead, we select the right impl now but report "`Bar` does
|
|
||||||
// not implement `Clone`".
|
|
||||||
if candidates.len() == 1 {
|
|
||||||
return self.filter_negative_and_reservation_impls(candidates.pop().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Winnow, but record the exact outcome of evaluation, which
|
|
||||||
// is needed for specialization. Propagate overflow if it occurs.
|
|
||||||
let mut candidates = candidates
|
|
||||||
.into_iter()
|
|
||||||
.map(|c| match self.evaluate_candidate(stack, &c) {
|
|
||||||
Ok(eval) if eval.may_apply() => {
|
|
||||||
Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval }))
|
|
||||||
}
|
|
||||||
Ok(_) => Ok(None),
|
|
||||||
Err(OverflowError) => Err(Overflow),
|
|
||||||
})
|
|
||||||
.flat_map(Result::transpose)
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
|
|
||||||
debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates);
|
|
||||||
|
|
||||||
let needs_infer = stack.obligation.predicate.needs_infer();
|
|
||||||
|
|
||||||
// If there are STILL multiple candidates, we can further
|
|
||||||
// reduce the list by dropping duplicates -- including
|
|
||||||
// resolving specializations.
|
|
||||||
if candidates.len() > 1 {
|
|
||||||
let mut i = 0;
|
|
||||||
while i < candidates.len() {
|
|
||||||
let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| {
|
|
||||||
self.candidate_should_be_dropped_in_favor_of(
|
|
||||||
&candidates[i],
|
|
||||||
&candidates[j],
|
|
||||||
needs_infer,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
if is_dup {
|
|
||||||
debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]);
|
|
||||||
candidates.swap_remove(i);
|
|
||||||
} else {
|
|
||||||
debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]);
|
|
||||||
i += 1;
|
|
||||||
|
|
||||||
// If there are *STILL* multiple candidates, give up
|
|
||||||
// and report ambiguity.
|
|
||||||
if i > 1 {
|
|
||||||
debug!("multiple matches, ambig");
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are *NO* candidates, then there are no impls --
|
|
||||||
// that we know of, anyway. Note that in the case where there
|
|
||||||
// are unbound type variables within the obligation, it might
|
|
||||||
// be the case that you could still satisfy the obligation
|
|
||||||
// from another crate by instantiating the type variables with
|
|
||||||
// a type from another crate that does have an impl. This case
|
|
||||||
// is checked for in `evaluate_stack` (and hence users
|
|
||||||
// who might care about this case, like coherence, should use
|
|
||||||
// that function).
|
|
||||||
if candidates.is_empty() {
|
|
||||||
// If there's an error type, 'downgrade' our result from
|
|
||||||
// `Err(Unimplemented)` to `Ok(None)`. This helps us avoid
|
|
||||||
// emitting additional spurious errors, since we're guaranteed
|
|
||||||
// to have emitted at least one.
|
|
||||||
if stack.obligation.references_error() {
|
|
||||||
debug!("no results for error type, treating as ambiguous");
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
return Err(Unimplemented);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Just one candidate left.
|
|
||||||
self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option<Conflict> {
|
fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option<Conflict> {
|
||||||
debug!("is_knowable(intercrate={:?})", self.intercrate);
|
debug!("is_knowable(intercrate={:?})", self.intercrate);
|
||||||
|
|
||||||
|
|
|
@ -145,13 +145,13 @@ pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 {
|
||||||
|
|
||||||
impl Global {
|
impl Global {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>, AllocErr> {
|
fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
match layout.size() {
|
match layout.size() {
|
||||||
0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)),
|
0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)),
|
||||||
// SAFETY: `layout` is non-zero in size,
|
// SAFETY: `layout` is non-zero in size,
|
||||||
size => unsafe {
|
size => unsafe {
|
||||||
let raw_ptr = if zeroed { alloc_zeroed(layout) } else { alloc(layout) };
|
let raw_ptr = if zeroed { alloc_zeroed(layout) } else { alloc(layout) };
|
||||||
let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
|
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
|
||||||
Ok(NonNull::slice_from_raw_parts(ptr, size))
|
Ok(NonNull::slice_from_raw_parts(ptr, size))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ impl Global {
|
||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_layout: Layout,
|
new_layout: Layout,
|
||||||
zeroed: bool,
|
zeroed: bool,
|
||||||
) -> Result<NonNull<[u8]>, AllocErr> {
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
new_layout.size() >= old_layout.size(),
|
new_layout.size() >= old_layout.size(),
|
||||||
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
|
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
|
||||||
|
@ -183,7 +183,7 @@ impl Global {
|
||||||
intrinsics::assume(new_size >= old_layout.size());
|
intrinsics::assume(new_size >= old_layout.size());
|
||||||
|
|
||||||
let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size);
|
let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size);
|
||||||
let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
|
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
|
||||||
if zeroed {
|
if zeroed {
|
||||||
raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
|
raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
|
||||||
}
|
}
|
||||||
|
@ -208,12 +208,12 @@ impl Global {
|
||||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||||
unsafe impl AllocRef for Global {
|
unsafe impl AllocRef for Global {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
|
fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
self.alloc_impl(layout, false)
|
self.alloc_impl(layout, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
|
fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
self.alloc_impl(layout, true)
|
self.alloc_impl(layout, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +232,7 @@ unsafe impl AllocRef for Global {
|
||||||
ptr: NonNull<u8>,
|
ptr: NonNull<u8>,
|
||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_layout: Layout,
|
new_layout: Layout,
|
||||||
) -> Result<NonNull<[u8]>, AllocErr> {
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
// SAFETY: all conditions must be upheld by the caller
|
// SAFETY: all conditions must be upheld by the caller
|
||||||
unsafe { self.grow_impl(ptr, old_layout, new_layout, false) }
|
unsafe { self.grow_impl(ptr, old_layout, new_layout, false) }
|
||||||
}
|
}
|
||||||
|
@ -243,7 +243,7 @@ unsafe impl AllocRef for Global {
|
||||||
ptr: NonNull<u8>,
|
ptr: NonNull<u8>,
|
||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_layout: Layout,
|
new_layout: Layout,
|
||||||
) -> Result<NonNull<[u8]>, AllocErr> {
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
// SAFETY: all conditions must be upheld by the caller
|
// SAFETY: all conditions must be upheld by the caller
|
||||||
unsafe { self.grow_impl(ptr, old_layout, new_layout, true) }
|
unsafe { self.grow_impl(ptr, old_layout, new_layout, true) }
|
||||||
}
|
}
|
||||||
|
@ -254,7 +254,7 @@ unsafe impl AllocRef for Global {
|
||||||
ptr: NonNull<u8>,
|
ptr: NonNull<u8>,
|
||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_layout: Layout,
|
new_layout: Layout,
|
||||||
) -> Result<NonNull<[u8]>, AllocErr> {
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
new_layout.size() <= old_layout.size(),
|
new_layout.size() <= old_layout.size(),
|
||||||
"`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
|
"`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
|
||||||
|
@ -273,7 +273,7 @@ unsafe impl AllocRef for Global {
|
||||||
intrinsics::assume(new_size <= old_layout.size());
|
intrinsics::assume(new_size <= old_layout.size());
|
||||||
|
|
||||||
let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size);
|
let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size);
|
||||||
let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
|
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
|
||||||
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
|
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::cell::Cell;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn allocator_param() {
|
fn allocator_param() {
|
||||||
use crate::alloc::AllocErr;
|
use crate::alloc::AllocError;
|
||||||
|
|
||||||
// Writing a test of integration between third-party
|
// Writing a test of integration between third-party
|
||||||
// allocators and `RawVec` is a little tricky because the `RawVec`
|
// allocators and `RawVec` is a little tricky because the `RawVec`
|
||||||
|
@ -21,10 +21,10 @@ fn allocator_param() {
|
||||||
fuel: Cell<usize>,
|
fuel: Cell<usize>,
|
||||||
}
|
}
|
||||||
unsafe impl AllocRef for BoundedAlloc {
|
unsafe impl AllocRef for BoundedAlloc {
|
||||||
fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
|
fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
let size = layout.size();
|
let size = layout.size();
|
||||||
if size > self.fuel.get() {
|
if size > self.fuel.get() {
|
||||||
return Err(AllocErr);
|
return Err(AllocError);
|
||||||
}
|
}
|
||||||
match Global.alloc(layout) {
|
match Global.alloc(layout) {
|
||||||
ok @ Ok(_) => {
|
ok @ Ok(_) => {
|
||||||
|
|
|
@ -247,7 +247,7 @@ use core::pin::Pin;
|
||||||
use core::ptr::{self, NonNull};
|
use core::ptr::{self, NonNull};
|
||||||
use core::slice::from_raw_parts_mut;
|
use core::slice::from_raw_parts_mut;
|
||||||
|
|
||||||
use crate::alloc::{box_free, handle_alloc_error, AllocErr, AllocRef, Global, Layout};
|
use crate::alloc::{box_free, handle_alloc_error, AllocError, AllocRef, Global, Layout};
|
||||||
use crate::borrow::{Cow, ToOwned};
|
use crate::borrow::{Cow, ToOwned};
|
||||||
use crate::string::String;
|
use crate::string::String;
|
||||||
use crate::vec::Vec;
|
use crate::vec::Vec;
|
||||||
|
@ -996,7 +996,7 @@ impl<T: ?Sized> Rc<T> {
|
||||||
/// and must return back a (potentially fat)-pointer for the `RcBox<T>`.
|
/// and must return back a (potentially fat)-pointer for the `RcBox<T>`.
|
||||||
unsafe fn allocate_for_layout(
|
unsafe fn allocate_for_layout(
|
||||||
value_layout: Layout,
|
value_layout: Layout,
|
||||||
allocate: impl FnOnce(Layout) -> Result<NonNull<[u8]>, AllocErr>,
|
allocate: impl FnOnce(Layout) -> Result<NonNull<[u8]>, AllocError>,
|
||||||
mem_to_rcbox: impl FnOnce(*mut u8) -> *mut RcBox<T>,
|
mem_to_rcbox: impl FnOnce(*mut u8) -> *mut RcBox<T>,
|
||||||
) -> *mut RcBox<T> {
|
) -> *mut RcBox<T> {
|
||||||
// Calculate layout using the given value layout.
|
// Calculate layout using the given value layout.
|
||||||
|
|
|
@ -21,7 +21,7 @@ use core::slice::from_raw_parts_mut;
|
||||||
use core::sync::atomic;
|
use core::sync::atomic;
|
||||||
use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst};
|
use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst};
|
||||||
|
|
||||||
use crate::alloc::{box_free, handle_alloc_error, AllocErr, AllocRef, Global, Layout};
|
use crate::alloc::{box_free, handle_alloc_error, AllocError, AllocRef, Global, Layout};
|
||||||
use crate::borrow::{Cow, ToOwned};
|
use crate::borrow::{Cow, ToOwned};
|
||||||
use crate::boxed::Box;
|
use crate::boxed::Box;
|
||||||
use crate::rc::is_dangling;
|
use crate::rc::is_dangling;
|
||||||
|
@ -969,7 +969,7 @@ impl<T: ?Sized> Arc<T> {
|
||||||
/// and must return back a (potentially fat)-pointer for the `ArcInner<T>`.
|
/// and must return back a (potentially fat)-pointer for the `ArcInner<T>`.
|
||||||
unsafe fn allocate_for_layout(
|
unsafe fn allocate_for_layout(
|
||||||
value_layout: Layout,
|
value_layout: Layout,
|
||||||
allocate: impl FnOnce(Layout) -> Result<NonNull<[u8]>, AllocErr>,
|
allocate: impl FnOnce(Layout) -> Result<NonNull<[u8]>, AllocError>,
|
||||||
mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner<T>,
|
mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner<T>,
|
||||||
) -> *mut ArcInner<T> {
|
) -> *mut ArcInner<T> {
|
||||||
// Calculate layout using the given value layout.
|
// Calculate layout using the given value layout.
|
||||||
|
|
|
@ -13,17 +13,17 @@ pub use self::layout::{Layout, LayoutErr};
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use crate::ptr::{self, NonNull};
|
use crate::ptr::{self, NonNull};
|
||||||
|
|
||||||
/// The `AllocErr` error indicates an allocation failure
|
/// The `AllocError` error indicates an allocation failure
|
||||||
/// that may be due to resource exhaustion or to
|
/// that may be due to resource exhaustion or to
|
||||||
/// something wrong when combining the given input arguments with this
|
/// something wrong when combining the given input arguments with this
|
||||||
/// allocator.
|
/// allocator.
|
||||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct AllocErr;
|
pub struct AllocError;
|
||||||
|
|
||||||
// (we need this for downstream impl of trait Error)
|
// (we need this for downstream impl of trait Error)
|
||||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||||
impl fmt::Display for AllocErr {
|
impl fmt::Display for AllocError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.write_str("memory allocation failed")
|
f.write_str("memory allocation failed")
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ pub unsafe trait AllocRef {
|
||||||
/// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
|
/// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
|
||||||
///
|
///
|
||||||
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
|
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
|
||||||
fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr>;
|
fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
|
||||||
|
|
||||||
/// Behaves like `alloc`, but also ensures that the returned memory is zero-initialized.
|
/// Behaves like `alloc`, but also ensures that the returned memory is zero-initialized.
|
||||||
///
|
///
|
||||||
|
@ -126,7 +126,7 @@ pub unsafe trait AllocRef {
|
||||||
/// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
|
/// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
|
||||||
///
|
///
|
||||||
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
|
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
|
||||||
fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
|
fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
let ptr = self.alloc(layout)?;
|
let ptr = self.alloc(layout)?;
|
||||||
// SAFETY: `alloc` returns a valid memory block
|
// SAFETY: `alloc` returns a valid memory block
|
||||||
unsafe { ptr.as_non_null_ptr().as_ptr().write_bytes(0, ptr.len()) }
|
unsafe { ptr.as_non_null_ptr().as_ptr().write_bytes(0, ptr.len()) }
|
||||||
|
@ -187,7 +187,7 @@ pub unsafe trait AllocRef {
|
||||||
ptr: NonNull<u8>,
|
ptr: NonNull<u8>,
|
||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_layout: Layout,
|
new_layout: Layout,
|
||||||
) -> Result<NonNull<[u8]>, AllocErr> {
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
new_layout.size() >= old_layout.size(),
|
new_layout.size() >= old_layout.size(),
|
||||||
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
|
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
|
||||||
|
@ -248,7 +248,7 @@ pub unsafe trait AllocRef {
|
||||||
ptr: NonNull<u8>,
|
ptr: NonNull<u8>,
|
||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_layout: Layout,
|
new_layout: Layout,
|
||||||
) -> Result<NonNull<[u8]>, AllocErr> {
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
new_layout.size() >= old_layout.size(),
|
new_layout.size() >= old_layout.size(),
|
||||||
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
|
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
|
||||||
|
@ -312,7 +312,7 @@ pub unsafe trait AllocRef {
|
||||||
ptr: NonNull<u8>,
|
ptr: NonNull<u8>,
|
||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_layout: Layout,
|
new_layout: Layout,
|
||||||
) -> Result<NonNull<[u8]>, AllocErr> {
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
new_layout.size() <= old_layout.size(),
|
new_layout.size() <= old_layout.size(),
|
||||||
"`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
|
"`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
|
||||||
|
@ -348,12 +348,12 @@ where
|
||||||
A: AllocRef + ?Sized,
|
A: AllocRef + ?Sized,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
|
fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
(**self).alloc(layout)
|
(**self).alloc(layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
|
fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
(**self).alloc_zeroed(layout)
|
(**self).alloc_zeroed(layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,7 +369,7 @@ where
|
||||||
ptr: NonNull<u8>,
|
ptr: NonNull<u8>,
|
||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_layout: Layout,
|
new_layout: Layout,
|
||||||
) -> Result<NonNull<[u8]>, AllocErr> {
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
// SAFETY: the safety contract must be upheld by the caller
|
// SAFETY: the safety contract must be upheld by the caller
|
||||||
unsafe { (**self).grow(ptr, old_layout, new_layout) }
|
unsafe { (**self).grow(ptr, old_layout, new_layout) }
|
||||||
}
|
}
|
||||||
|
@ -380,7 +380,7 @@ where
|
||||||
ptr: NonNull<u8>,
|
ptr: NonNull<u8>,
|
||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_layout: Layout,
|
new_layout: Layout,
|
||||||
) -> Result<NonNull<[u8]>, AllocErr> {
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
// SAFETY: the safety contract must be upheld by the caller
|
// SAFETY: the safety contract must be upheld by the caller
|
||||||
unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) }
|
unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) }
|
||||||
}
|
}
|
||||||
|
@ -391,7 +391,7 @@ where
|
||||||
ptr: NonNull<u8>,
|
ptr: NonNull<u8>,
|
||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_layout: Layout,
|
new_layout: Layout,
|
||||||
) -> Result<NonNull<[u8]>, AllocErr> {
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
// SAFETY: the safety contract must be upheld by the caller
|
// SAFETY: the safety contract must be upheld by the caller
|
||||||
unsafe { (**self).shrink(ptr, old_layout, new_layout) }
|
unsafe { (**self).shrink(ptr, old_layout, new_layout) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,6 +124,11 @@ where
|
||||||
self.iter.size_hint()
|
self.iter.size_hint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn advance_by(&mut self, n: usize) -> Result<(), usize> {
|
||||||
|
self.iter.advance_back_by(n)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn nth(&mut self, n: usize) -> Option<<I as Iterator>::Item> {
|
fn nth(&mut self, n: usize) -> Option<<I as Iterator>::Item> {
|
||||||
self.iter.nth_back(n)
|
self.iter.nth_back(n)
|
||||||
|
@ -164,6 +169,11 @@ where
|
||||||
self.iter.next()
|
self.iter.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
|
||||||
|
self.iter.advance_by(n)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn nth_back(&mut self, n: usize) -> Option<<I as Iterator>::Item> {
|
fn nth_back(&mut self, n: usize) -> Option<<I as Iterator>::Item> {
|
||||||
self.iter.nth(n)
|
self.iter.nth(n)
|
||||||
|
|
|
@ -91,6 +91,46 @@ pub trait DoubleEndedIterator: Iterator {
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
fn next_back(&mut self) -> Option<Self::Item>;
|
fn next_back(&mut self) -> Option<Self::Item>;
|
||||||
|
|
||||||
|
/// Advances the iterator from the back by `n` elements.
|
||||||
|
///
|
||||||
|
/// `advance_back_by` is the reverse version of [`advance_by`]. This method will
|
||||||
|
/// eagerly skip `n` elements starting from the back by calling [`next_back`] up
|
||||||
|
/// to `n` times until [`None`] is encountered.
|
||||||
|
///
|
||||||
|
/// `advance_back_by(n)` will return [`Ok(())`] if the iterator successfully advances by
|
||||||
|
/// `n` elements, or [`Err(k)`] if [`None`] is encountered, where `k` is the number of
|
||||||
|
/// elements the iterator is advanced by before running out of elements (i.e. the length
|
||||||
|
/// of the iterator). Note that `k` is always less than `n`.
|
||||||
|
///
|
||||||
|
/// Calling `advance_back_by(0)` does not consume any elements and always returns [`Ok(())`].
|
||||||
|
///
|
||||||
|
/// [`advance_by`]: Iterator::advance_by
|
||||||
|
/// [`next_back`]: DoubleEndedIterator::next_back
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Basic usage:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(iter_advance_by)]
|
||||||
|
///
|
||||||
|
/// let a = [3, 4, 5, 6];
|
||||||
|
/// let mut iter = a.iter();
|
||||||
|
///
|
||||||
|
/// assert_eq!(iter.advance_back_by(2), Ok(()));
|
||||||
|
/// assert_eq!(iter.next_back(), Some(&4));
|
||||||
|
/// assert_eq!(iter.advance_back_by(0), Ok(()));
|
||||||
|
/// assert_eq!(iter.advance_back_by(100), Err(1)); // only `&3` was skipped
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
#[unstable(feature = "iter_advance_by", reason = "recently added", issue = "none")]
|
||||||
|
fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
|
||||||
|
for i in 0..n {
|
||||||
|
self.next_back().ok_or(i)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the `n`th element from the end of the iterator.
|
/// Returns the `n`th element from the end of the iterator.
|
||||||
///
|
///
|
||||||
/// This is essentially the reversed version of [`Iterator::nth()`].
|
/// This is essentially the reversed version of [`Iterator::nth()`].
|
||||||
|
@ -134,14 +174,9 @@ pub trait DoubleEndedIterator: Iterator {
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[stable(feature = "iter_nth_back", since = "1.37.0")]
|
#[stable(feature = "iter_nth_back", since = "1.37.0")]
|
||||||
fn nth_back(&mut self, mut n: usize) -> Option<Self::Item> {
|
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
|
||||||
for x in self.rev() {
|
self.advance_back_by(n).ok()?;
|
||||||
if n == 0 {
|
self.next_back()
|
||||||
return Some(x);
|
|
||||||
}
|
|
||||||
n -= 1;
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is the reverse version of [`Iterator::try_fold()`]: it takes
|
/// This is the reverse version of [`Iterator::try_fold()`]: it takes
|
||||||
|
@ -318,6 +353,9 @@ impl<'a, I: DoubleEndedIterator + ?Sized> DoubleEndedIterator for &'a mut I {
|
||||||
fn next_back(&mut self) -> Option<I::Item> {
|
fn next_back(&mut self) -> Option<I::Item> {
|
||||||
(**self).next_back()
|
(**self).next_back()
|
||||||
}
|
}
|
||||||
|
fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
|
||||||
|
(**self).advance_back_by(n)
|
||||||
|
}
|
||||||
fn nth_back(&mut self, n: usize) -> Option<I::Item> {
|
fn nth_back(&mut self, n: usize) -> Option<I::Item> {
|
||||||
(**self).nth_back(n)
|
(**self).nth_back(n)
|
||||||
}
|
}
|
||||||
|
|
|
@ -284,6 +284,44 @@ pub trait Iterator {
|
||||||
self.fold(None, some)
|
self.fold(None, some)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Advances the iterator by `n` elements.
|
||||||
|
///
|
||||||
|
/// This method will eagerly skip `n` elements by calling [`next`] up to `n`
|
||||||
|
/// times until [`None`] is encountered.
|
||||||
|
///
|
||||||
|
/// `advance_by(n)` will return [`Ok(())`] if the iterator successfully advances by
|
||||||
|
/// `n` elements, or [`Err(k)`] if [`None`] is encountered, where `k` is the number
|
||||||
|
/// of elements the iterator is advanced by before running out of elements (i.e. the
|
||||||
|
/// length of the iterator). Note that `k` is always less than `n`.
|
||||||
|
///
|
||||||
|
/// Calling `advance_by(0)` does not consume any elements and always returns [`Ok(())`].
|
||||||
|
///
|
||||||
|
/// [`next`]: Iterator::next
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Basic usage:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(iter_advance_by)]
|
||||||
|
///
|
||||||
|
/// let a = [1, 2, 3, 4];
|
||||||
|
/// let mut iter = a.iter();
|
||||||
|
///
|
||||||
|
/// assert_eq!(iter.advance_by(2), Ok(()));
|
||||||
|
/// assert_eq!(iter.next(), Some(&3));
|
||||||
|
/// assert_eq!(iter.advance_by(0), Ok(()));
|
||||||
|
/// assert_eq!(iter.advance_by(100), Err(1)); // only `&4` was skipped
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
#[unstable(feature = "iter_advance_by", reason = "recently added", issue = "none")]
|
||||||
|
fn advance_by(&mut self, n: usize) -> Result<(), usize> {
|
||||||
|
for i in 0..n {
|
||||||
|
self.next().ok_or(i)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the `n`th element of the iterator.
|
/// Returns the `n`th element of the iterator.
|
||||||
///
|
///
|
||||||
/// Like most indexing operations, the count starts from zero, so `nth(0)`
|
/// Like most indexing operations, the count starts from zero, so `nth(0)`
|
||||||
|
@ -325,14 +363,9 @@ pub trait Iterator {
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
|
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
||||||
while let Some(x) = self.next() {
|
self.advance_by(n).ok()?;
|
||||||
if n == 0 {
|
self.next()
|
||||||
return Some(x);
|
|
||||||
}
|
|
||||||
n -= 1;
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an iterator starting at the same point, but stepping by
|
/// Creates an iterator starting at the same point, but stepping by
|
||||||
|
@ -3265,6 +3298,9 @@ impl<I: Iterator + ?Sized> Iterator for &mut I {
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
(**self).size_hint()
|
(**self).size_hint()
|
||||||
}
|
}
|
||||||
|
fn advance_by(&mut self, n: usize) -> Result<(), usize> {
|
||||||
|
(**self).advance_by(n)
|
||||||
|
}
|
||||||
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
||||||
(**self).nth(n)
|
(**self).nth(n)
|
||||||
}
|
}
|
||||||
|
|
|
@ -448,7 +448,7 @@ impl<T> NonNull<[T]> {
|
||||||
/// // Note that calling `memory.as_mut()` is not allowed here as the content may be uninitialized.
|
/// // Note that calling `memory.as_mut()` is not allowed here as the content may be uninitialized.
|
||||||
/// # #[allow(unused_variables)]
|
/// # #[allow(unused_variables)]
|
||||||
/// let slice: &mut [MaybeUninit<u8>] = unsafe { memory.as_uninit_slice_mut() };
|
/// let slice: &mut [MaybeUninit<u8>] = unsafe { memory.as_uninit_slice_mut() };
|
||||||
/// # Ok::<_, std::alloc::AllocErr>(())
|
/// # Ok::<_, std::alloc::AllocError>(())
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[unstable(feature = "ptr_as_uninit", issue = "75402")]
|
#[unstable(feature = "ptr_as_uninit", issue = "75402")]
|
||||||
|
|
|
@ -1570,6 +1570,66 @@ fn test_iterator_rev_nth() {
|
||||||
assert_eq!(v.iter().rev().nth(v.len()), None);
|
assert_eq!(v.iter().rev().nth(v.len()), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iterator_advance_by() {
|
||||||
|
let v: &[_] = &[0, 1, 2, 3, 4];
|
||||||
|
|
||||||
|
for i in 0..v.len() {
|
||||||
|
let mut iter = v.iter();
|
||||||
|
assert_eq!(iter.advance_by(i), Ok(()));
|
||||||
|
assert_eq!(iter.next().unwrap(), &v[i]);
|
||||||
|
assert_eq!(iter.advance_by(100), Err(v.len() - 1 - i));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(v.iter().advance_by(v.len()), Ok(()));
|
||||||
|
assert_eq!(v.iter().advance_by(100), Err(v.len()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iterator_advance_back_by() {
|
||||||
|
let v: &[_] = &[0, 1, 2, 3, 4];
|
||||||
|
|
||||||
|
for i in 0..v.len() {
|
||||||
|
let mut iter = v.iter();
|
||||||
|
assert_eq!(iter.advance_back_by(i), Ok(()));
|
||||||
|
assert_eq!(iter.next_back().unwrap(), &v[v.len() - 1 - i]);
|
||||||
|
assert_eq!(iter.advance_back_by(100), Err(v.len() - 1 - i));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(v.iter().advance_back_by(v.len()), Ok(()));
|
||||||
|
assert_eq!(v.iter().advance_back_by(100), Err(v.len()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iterator_rev_advance_by() {
|
||||||
|
let v: &[_] = &[0, 1, 2, 3, 4];
|
||||||
|
|
||||||
|
for i in 0..v.len() {
|
||||||
|
let mut iter = v.iter().rev();
|
||||||
|
assert_eq!(iter.advance_by(i), Ok(()));
|
||||||
|
assert_eq!(iter.next().unwrap(), &v[v.len() - 1 - i]);
|
||||||
|
assert_eq!(iter.advance_by(100), Err(v.len() - 1 - i));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(v.iter().rev().advance_by(v.len()), Ok(()));
|
||||||
|
assert_eq!(v.iter().rev().advance_by(100), Err(v.len()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iterator_rev_advance_back_by() {
|
||||||
|
let v: &[_] = &[0, 1, 2, 3, 4];
|
||||||
|
|
||||||
|
for i in 0..v.len() {
|
||||||
|
let mut iter = v.iter().rev();
|
||||||
|
assert_eq!(iter.advance_back_by(i), Ok(()));
|
||||||
|
assert_eq!(iter.next_back().unwrap(), &v[i]);
|
||||||
|
assert_eq!(iter.advance_back_by(100), Err(v.len() - 1 - i));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(v.iter().rev().advance_back_by(v.len()), Ok(()));
|
||||||
|
assert_eq!(v.iter().rev().advance_back_by(100), Err(v.len()));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iterator_last() {
|
fn test_iterator_last() {
|
||||||
let v: &[_] = &[0, 1, 2, 3, 4];
|
let v: &[_] = &[0, 1, 2, 3, 4];
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#![feature(slice_partition_dedup)]
|
#![feature(slice_partition_dedup)]
|
||||||
#![feature(int_error_matching)]
|
#![feature(int_error_matching)]
|
||||||
#![feature(array_value_iter)]
|
#![feature(array_value_iter)]
|
||||||
|
#![feature(iter_advance_by)]
|
||||||
#![feature(iter_partition_in_place)]
|
#![feature(iter_partition_in_place)]
|
||||||
#![feature(iter_is_partitioned)]
|
#![feature(iter_is_partitioned)]
|
||||||
#![feature(iter_order_by)]
|
#![feature(iter_order_by)]
|
||||||
|
|
|
@ -133,7 +133,7 @@ pub struct System;
|
||||||
|
|
||||||
impl System {
|
impl System {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>, AllocErr> {
|
fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
match layout.size() {
|
match layout.size() {
|
||||||
0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)),
|
0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)),
|
||||||
// SAFETY: `layout` is non-zero in size,
|
// SAFETY: `layout` is non-zero in size,
|
||||||
|
@ -143,7 +143,7 @@ impl System {
|
||||||
} else {
|
} else {
|
||||||
GlobalAlloc::alloc(self, layout)
|
GlobalAlloc::alloc(self, layout)
|
||||||
};
|
};
|
||||||
let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
|
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
|
||||||
Ok(NonNull::slice_from_raw_parts(ptr, size))
|
Ok(NonNull::slice_from_raw_parts(ptr, size))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,7 @@ impl System {
|
||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_layout: Layout,
|
new_layout: Layout,
|
||||||
zeroed: bool,
|
zeroed: bool,
|
||||||
) -> Result<NonNull<[u8]>, AllocErr> {
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
new_layout.size() >= old_layout.size(),
|
new_layout.size() >= old_layout.size(),
|
||||||
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
|
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
|
||||||
|
@ -175,7 +175,7 @@ impl System {
|
||||||
intrinsics::assume(new_size >= old_layout.size());
|
intrinsics::assume(new_size >= old_layout.size());
|
||||||
|
|
||||||
let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size);
|
let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size);
|
||||||
let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
|
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
|
||||||
if zeroed {
|
if zeroed {
|
||||||
raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
|
raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
|
||||||
}
|
}
|
||||||
|
@ -202,12 +202,12 @@ impl System {
|
||||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||||
unsafe impl AllocRef for System {
|
unsafe impl AllocRef for System {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
|
fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
self.alloc_impl(layout, false)
|
self.alloc_impl(layout, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
|
fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
self.alloc_impl(layout, true)
|
self.alloc_impl(layout, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ unsafe impl AllocRef for System {
|
||||||
ptr: NonNull<u8>,
|
ptr: NonNull<u8>,
|
||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_layout: Layout,
|
new_layout: Layout,
|
||||||
) -> Result<NonNull<[u8]>, AllocErr> {
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
// SAFETY: all conditions must be upheld by the caller
|
// SAFETY: all conditions must be upheld by the caller
|
||||||
unsafe { self.grow_impl(ptr, old_layout, new_layout, false) }
|
unsafe { self.grow_impl(ptr, old_layout, new_layout, false) }
|
||||||
}
|
}
|
||||||
|
@ -237,7 +237,7 @@ unsafe impl AllocRef for System {
|
||||||
ptr: NonNull<u8>,
|
ptr: NonNull<u8>,
|
||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_layout: Layout,
|
new_layout: Layout,
|
||||||
) -> Result<NonNull<[u8]>, AllocErr> {
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
// SAFETY: all conditions must be upheld by the caller
|
// SAFETY: all conditions must be upheld by the caller
|
||||||
unsafe { self.grow_impl(ptr, old_layout, new_layout, true) }
|
unsafe { self.grow_impl(ptr, old_layout, new_layout, true) }
|
||||||
}
|
}
|
||||||
|
@ -248,7 +248,7 @@ unsafe impl AllocRef for System {
|
||||||
ptr: NonNull<u8>,
|
ptr: NonNull<u8>,
|
||||||
old_layout: Layout,
|
old_layout: Layout,
|
||||||
new_layout: Layout,
|
new_layout: Layout,
|
||||||
) -> Result<NonNull<[u8]>, AllocErr> {
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
new_layout.size() <= old_layout.size(),
|
new_layout.size() <= old_layout.size(),
|
||||||
"`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
|
"`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
|
||||||
|
@ -267,7 +267,7 @@ unsafe impl AllocRef for System {
|
||||||
intrinsics::assume(new_size <= old_layout.size());
|
intrinsics::assume(new_size <= old_layout.size());
|
||||||
|
|
||||||
let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size);
|
let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size);
|
||||||
let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
|
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
|
||||||
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
|
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ mod tests;
|
||||||
use core::array;
|
use core::array;
|
||||||
use core::convert::Infallible;
|
use core::convert::Infallible;
|
||||||
|
|
||||||
use crate::alloc::{AllocErr, LayoutErr};
|
use crate::alloc::{AllocError, LayoutErr};
|
||||||
use crate::any::TypeId;
|
use crate::any::TypeId;
|
||||||
use crate::backtrace::Backtrace;
|
use crate::backtrace::Backtrace;
|
||||||
use crate::borrow::Cow;
|
use crate::borrow::Cow;
|
||||||
|
@ -387,7 +387,7 @@ impl Error for ! {}
|
||||||
reason = "the precise API and guarantees it provides may be tweaked.",
|
reason = "the precise API and guarantees it provides may be tweaked.",
|
||||||
issue = "32838"
|
issue = "32838"
|
||||||
)]
|
)]
|
||||||
impl Error for AllocErr {}
|
impl Error for AllocError {}
|
||||||
|
|
||||||
#[stable(feature = "alloc_layout", since = "1.28.0")]
|
#[stable(feature = "alloc_layout", since = "1.28.0")]
|
||||||
impl Error for LayoutErr {}
|
impl Error for LayoutErr {}
|
||||||
|
|
|
@ -57,7 +57,7 @@ Specifically, these platforms are required to have each of the following:
|
||||||
|
|
||||||
target | std | host | notes
|
target | std | host | notes
|
||||||
-------|-----|------|-------
|
-------|-----|------|-------
|
||||||
`aarch64-apple-ios` | ✓[^apple] | | ARM64 iOS
|
`aarch64-apple-ios` | ✓ | | ARM64 iOS
|
||||||
`aarch64-fuchsia` | ✓ | | ARM64 Fuchsia
|
`aarch64-fuchsia` | ✓ | | ARM64 Fuchsia
|
||||||
`aarch64-linux-android` | ✓ | | ARM64 Android
|
`aarch64-linux-android` | ✓ | | ARM64 Android
|
||||||
`aarch64-pc-windows-msvc` | ✓ | | ARM64 Windows MSVC
|
`aarch64-pc-windows-msvc` | ✓ | | ARM64 Windows MSVC
|
||||||
|
@ -122,7 +122,7 @@ target | std | host | notes
|
||||||
`wasm32-unknown-emscripten` | ✓ | | WebAssembly via Emscripten
|
`wasm32-unknown-emscripten` | ✓ | | WebAssembly via Emscripten
|
||||||
`wasm32-unknown-unknown` | ✓ | | WebAssembly
|
`wasm32-unknown-unknown` | ✓ | | WebAssembly
|
||||||
`wasm32-wasi` | ✓ | | WebAssembly with WASI
|
`wasm32-wasi` | ✓ | | WebAssembly with WASI
|
||||||
`x86_64-apple-ios` | ✓[^apple] | | 64-bit x86 iOS
|
`x86_64-apple-ios` | ✓ | | 64-bit x86 iOS
|
||||||
`x86_64-fortanix-unknown-sgx` | ✓ | | [Fortanix ABI] for 64-bit Intel SGX
|
`x86_64-fortanix-unknown-sgx` | ✓ | | [Fortanix ABI] for 64-bit Intel SGX
|
||||||
`x86_64-fuchsia` | ✓ | | 64-bit Fuchsia
|
`x86_64-fuchsia` | ✓ | | 64-bit Fuchsia
|
||||||
`x86_64-linux-android` | ✓ | | 64-bit x86 Android
|
`x86_64-linux-android` | ✓ | | 64-bit x86 Android
|
||||||
|
@ -146,7 +146,7 @@ not available.
|
||||||
target | std | host | notes
|
target | std | host | notes
|
||||||
-------|-----|------|-------
|
-------|-----|------|-------
|
||||||
`aarch64-apple-darwin` | ? | | ARM64 macOS
|
`aarch64-apple-darwin` | ? | | ARM64 macOS
|
||||||
`aarch64-apple-tvos` | *[^apple] | | ARM64 tvOS
|
`aarch64-apple-tvos` | * | | ARM64 tvOS
|
||||||
`aarch64-unknown-cloudabi` | ✓ | | ARM64 CloudABI
|
`aarch64-unknown-cloudabi` | ✓ | | ARM64 CloudABI
|
||||||
`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD
|
`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD
|
||||||
`aarch64-unknown-hermit` | ? | |
|
`aarch64-unknown-hermit` | ? | |
|
||||||
|
@ -158,16 +158,16 @@ target | std | host | notes
|
||||||
`armv4t-unknown-linux-gnueabi` | ? | |
|
`armv4t-unknown-linux-gnueabi` | ? | |
|
||||||
`armv6-unknown-freebsd` | ✓ | ✓ | ARMv6 FreeBSD
|
`armv6-unknown-freebsd` | ✓ | ✓ | ARMv6 FreeBSD
|
||||||
`armv6-unknown-netbsd-eabihf` | ? | |
|
`armv6-unknown-netbsd-eabihf` | ? | |
|
||||||
`armv7-apple-ios` | ✓[^apple] | | ARMv7 iOS, Cortex-a8
|
`armv7-apple-ios` | ✓ | | ARMv7 iOS, Cortex-a8
|
||||||
`armv7-unknown-cloudabi-eabihf` | ✓ | | ARMv7 CloudABI, hardfloat
|
`armv7-unknown-cloudabi-eabihf` | ✓ | | ARMv7 CloudABI, hardfloat
|
||||||
`armv7-unknown-freebsd` | ✓ | ✓ | ARMv7 FreeBSD
|
`armv7-unknown-freebsd` | ✓ | ✓ | ARMv7 FreeBSD
|
||||||
`armv7-unknown-netbsd-eabihf` | ? | |
|
`armv7-unknown-netbsd-eabihf` | ? | |
|
||||||
`armv7-wrs-vxworks-eabihf` | ? | |
|
`armv7-wrs-vxworks-eabihf` | ? | |
|
||||||
`armv7a-none-eabihf` | * | | ARM Cortex-A, hardfloat
|
`armv7a-none-eabihf` | * | | ARM Cortex-A, hardfloat
|
||||||
`armv7s-apple-ios` | ✓[^apple] | |
|
`armv7s-apple-ios` | ✓ | |
|
||||||
`avr-unknown-gnu-atmega328` | ✗ | | AVR. Requires `-Z build-std=core`
|
`avr-unknown-gnu-atmega328` | ✗ | | AVR. Requires `-Z build-std=core`
|
||||||
`hexagon-unknown-linux-musl` | ? | |
|
`hexagon-unknown-linux-musl` | ? | |
|
||||||
`i386-apple-ios` | ✓[^apple] | | 32-bit x86 iOS
|
`i386-apple-ios` | ✓ | | 32-bit x86 iOS
|
||||||
`i686-apple-darwin` | ✓ | ✓ | 32-bit OSX (10.7+, Lion+)
|
`i686-apple-darwin` | ✓ | ✓ | 32-bit OSX (10.7+, Lion+)
|
||||||
`i686-pc-windows-msvc` | ✓ | | 32-bit Windows XP support
|
`i686-pc-windows-msvc` | ✓ | | 32-bit Windows XP support
|
||||||
`i686-unknown-cloudabi` | ✓ | | 32-bit CloudABI
|
`i686-unknown-cloudabi` | ✓ | | 32-bit CloudABI
|
||||||
|
@ -203,8 +203,8 @@ target | std | host | notes
|
||||||
`thumbv7a-uwp-windows-msvc` | ✓ | |
|
`thumbv7a-uwp-windows-msvc` | ✓ | |
|
||||||
`thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode ARMv7a Linux with NEON, MUSL
|
`thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode ARMv7a Linux with NEON, MUSL
|
||||||
`thumbv4t-none-eabi` | * | | ARMv4T T32
|
`thumbv4t-none-eabi` | * | | ARMv4T T32
|
||||||
`x86_64-apple-ios-macabi` | ✓[^apple] | | Apple Catalyst
|
`x86_64-apple-ios-macabi` | ✓ | | Apple Catalyst
|
||||||
`x86_64-apple-tvos` | *[^apple] | | x86 64-bit tvOS
|
`x86_64-apple-tvos` | * | | x86 64-bit tvOS
|
||||||
`x86_64-linux-kernel` | * | | Linux kernel modules
|
`x86_64-linux-kernel` | * | | Linux kernel modules
|
||||||
`x86_64-pc-solaris` | ? | |
|
`x86_64-pc-solaris` | ? | |
|
||||||
`x86_64-pc-windows-msvc` | ✓ | | 64-bit Windows XP support
|
`x86_64-pc-windows-msvc` | ✓ | | 64-bit Windows XP support
|
||||||
|
@ -221,4 +221,3 @@ target | std | host | notes
|
||||||
`x86_64-wrs-vxworks` | ? | |
|
`x86_64-wrs-vxworks` | ? | |
|
||||||
|
|
||||||
[runs on NVIDIA GPUs]: https://github.com/japaric-archived/nvptx#targets
|
[runs on NVIDIA GPUs]: https://github.com/japaric-archived/nvptx#targets
|
||||||
[^apple]: These targets are only available on macOS.
|
|
||||||
|
|
|
@ -5,10 +5,10 @@ extern crate const_evaluatable_lib;
|
||||||
|
|
||||||
fn user<T>() {
|
fn user<T>() {
|
||||||
let _ = const_evaluatable_lib::test1::<T>();
|
let _ = const_evaluatable_lib::test1::<T>();
|
||||||
//~^ ERROR constant expression depends
|
//~^ ERROR unconstrained generic constant
|
||||||
//~| ERROR constant expression depends
|
//~| ERROR unconstrained generic constant
|
||||||
//~| ERROR constant expression depends
|
//~| ERROR unconstrained generic constant
|
||||||
//~| ERROR constant expression depends
|
//~| ERROR unconstrained generic constant
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,54 +1,50 @@
|
||||||
error: constant expression depends on a generic parameter
|
error: unconstrained generic constant
|
||||||
--> $DIR/cross_crate_predicate.rs:7:13
|
--> $DIR/cross_crate_predicate.rs:7:13
|
||||||
|
|
|
|
||||||
LL | let _ = const_evaluatable_lib::test1::<T>();
|
LL | let _ = const_evaluatable_lib::test1::<T>();
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
::: $DIR/auxiliary/const_evaluatable_lib.rs:6:10
|
help: consider adding a `where` bound for this expression
|
||||||
|
--> $DIR/auxiliary/const_evaluatable_lib.rs:6:10
|
||||||
|
|
|
|
||||||
LL | [u8; std::mem::size_of::<T>() - 1]: Sized,
|
LL | [u8; std::mem::size_of::<T>() - 1]: Sized,
|
||||||
| ---------------------------- required by this bound in `test1`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
= note: this may fail depending on what value the parameter takes
|
|
||||||
|
|
||||||
error: constant expression depends on a generic parameter
|
error: unconstrained generic constant
|
||||||
--> $DIR/cross_crate_predicate.rs:7:13
|
--> $DIR/cross_crate_predicate.rs:7:13
|
||||||
|
|
|
|
||||||
LL | let _ = const_evaluatable_lib::test1::<T>();
|
LL | let _ = const_evaluatable_lib::test1::<T>();
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
::: $DIR/auxiliary/const_evaluatable_lib.rs:4:27
|
help: consider adding a `where` bound for this expression
|
||||||
|
--> $DIR/auxiliary/const_evaluatable_lib.rs:4:27
|
||||||
|
|
|
|
||||||
LL | pub fn test1<T>() -> [u8; std::mem::size_of::<T>() - 1]
|
LL | pub fn test1<T>() -> [u8; std::mem::size_of::<T>() - 1]
|
||||||
| ---------------------------- required by this bound in `test1`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
= note: this may fail depending on what value the parameter takes
|
|
||||||
|
|
||||||
error: constant expression depends on a generic parameter
|
error: unconstrained generic constant
|
||||||
--> $DIR/cross_crate_predicate.rs:7:13
|
--> $DIR/cross_crate_predicate.rs:7:13
|
||||||
|
|
|
|
||||||
LL | let _ = const_evaluatable_lib::test1::<T>();
|
LL | let _ = const_evaluatable_lib::test1::<T>();
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
::: $DIR/auxiliary/const_evaluatable_lib.rs:6:10
|
help: consider adding a `where` bound for this expression
|
||||||
|
--> $DIR/auxiliary/const_evaluatable_lib.rs:6:10
|
||||||
|
|
|
|
||||||
LL | [u8; std::mem::size_of::<T>() - 1]: Sized,
|
LL | [u8; std::mem::size_of::<T>() - 1]: Sized,
|
||||||
| ---------------------------- required by this bound in `test1`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
= note: this may fail depending on what value the parameter takes
|
|
||||||
|
|
||||||
error: constant expression depends on a generic parameter
|
error: unconstrained generic constant
|
||||||
--> $DIR/cross_crate_predicate.rs:7:13
|
--> $DIR/cross_crate_predicate.rs:7:13
|
||||||
|
|
|
|
||||||
LL | let _ = const_evaluatable_lib::test1::<T>();
|
LL | let _ = const_evaluatable_lib::test1::<T>();
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
::: $DIR/auxiliary/const_evaluatable_lib.rs:4:27
|
help: consider adding a `where` bound for this expression
|
||||||
|
--> $DIR/auxiliary/const_evaluatable_lib.rs:4:27
|
||||||
|
|
|
|
||||||
LL | pub fn test1<T>() -> [u8; std::mem::size_of::<T>() - 1]
|
LL | pub fn test1<T>() -> [u8; std::mem::size_of::<T>() - 1]
|
||||||
| ---------------------------- required by this bound in `test1`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
= note: this may fail depending on what value the parameter takes
|
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
// run-pass
|
||||||
|
#![feature(const_generics, const_evaluatable_checked)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
use std::{mem, ptr};
|
||||||
|
|
||||||
|
fn split_first<T, const N: usize>(arr: [T; N]) -> (T, [T; N - 1])
|
||||||
|
where
|
||||||
|
[T; N - 1]: Sized,
|
||||||
|
{
|
||||||
|
let arr = mem::ManuallyDrop::new(arr);
|
||||||
|
unsafe {
|
||||||
|
let head = ptr::read(&arr[0]);
|
||||||
|
let tail = ptr::read(&arr[1..] as *const [T] as *const [T; N - 1]);
|
||||||
|
(head, tail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let arr = [0, 1, 2, 3, 4];
|
||||||
|
let (head, tail) = split_first(arr);
|
||||||
|
assert_eq!(head, 0);
|
||||||
|
assert_eq!(tail, [1, 2, 3, 4]);
|
||||||
|
}
|
|
@ -14,5 +14,4 @@ fn test<T, const P: usize>() where Bool<{core::mem::size_of::<T>() > 4}>: True {
|
||||||
fn main() {
|
fn main() {
|
||||||
test::<2>();
|
test::<2>();
|
||||||
//~^ ERROR wrong number of type
|
//~^ ERROR wrong number of type
|
||||||
//~| ERROR constant expression depends
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,17 +4,6 @@ error[E0107]: wrong number of type arguments: expected 1, found 0
|
||||||
LL | test::<2>();
|
LL | test::<2>();
|
||||||
| ^^^^^^^^^ expected 1 type argument
|
| ^^^^^^^^^ expected 1 type argument
|
||||||
|
|
||||||
error: constant expression depends on a generic parameter
|
error: aborting due to previous error
|
||||||
--> $DIR/issue-76595.rs:15:5
|
|
||||||
|
|
|
||||||
LL | fn test<T, const P: usize>() where Bool<{core::mem::size_of::<T>() > 4}>: True {
|
|
||||||
| ------------------------------- required by this bound in `test`
|
|
||||||
...
|
|
||||||
LL | test::<2>();
|
|
||||||
| ^^^^^^^^^
|
|
||||||
|
|
|
||||||
= note: this may fail depending on what value the parameter takes
|
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0107`.
|
For more information about this error, try `rustc --explain E0107`.
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
|
#[rustc_args_required_const(0)] //~ ERROR index exceeds number of arguments
|
||||||
|
fn foo1() {}
|
||||||
|
|
||||||
|
#[rustc_args_required_const(1)] //~ ERROR index exceeds number of arguments
|
||||||
|
fn foo2(_: u8) {}
|
||||||
|
|
||||||
|
#[rustc_args_required_const(a)] //~ ERROR arguments should be non-negative integers
|
||||||
|
fn foo4() {}
|
||||||
|
|
||||||
|
#[rustc_args_required_const(1, a, 2, b)] //~ ERROR arguments should be non-negative integers
|
||||||
|
fn foo5(_: u8, _: u8, _: u8) {}
|
||||||
|
|
||||||
|
#[rustc_args_required_const(0)] //~ ERROR attribute should be applied to a function
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
#[rustc_args_required_const(0usize)] //~ ERROR suffixed literals are not allowed in attributes
|
||||||
|
fn foo6(_: u8) {}
|
||||||
|
|
||||||
|
extern {
|
||||||
|
#[rustc_args_required_const(1)] //~ ERROR index exceeds number of arguments
|
||||||
|
fn foo7(_: u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,48 @@
|
||||||
|
error: suffixed literals are not allowed in attributes
|
||||||
|
--> $DIR/invalid-rustc_args_required_const-arguments.rs:18:29
|
||||||
|
|
|
||||||
|
LL | #[rustc_args_required_const(0usize)]
|
||||||
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
|
||||||
|
|
||||||
|
error: index exceeds number of arguments
|
||||||
|
--> $DIR/invalid-rustc_args_required_const-arguments.rs:3:29
|
||||||
|
|
|
||||||
|
LL | #[rustc_args_required_const(0)]
|
||||||
|
| ^ there are only 0 arguments
|
||||||
|
|
||||||
|
error: index exceeds number of arguments
|
||||||
|
--> $DIR/invalid-rustc_args_required_const-arguments.rs:6:29
|
||||||
|
|
|
||||||
|
LL | #[rustc_args_required_const(1)]
|
||||||
|
| ^ there is only 1 argument
|
||||||
|
|
||||||
|
error: arguments should be non-negative integers
|
||||||
|
--> $DIR/invalid-rustc_args_required_const-arguments.rs:9:29
|
||||||
|
|
|
||||||
|
LL | #[rustc_args_required_const(a)]
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: arguments should be non-negative integers
|
||||||
|
--> $DIR/invalid-rustc_args_required_const-arguments.rs:12:32
|
||||||
|
|
|
||||||
|
LL | #[rustc_args_required_const(1, a, 2, b)]
|
||||||
|
| ^ ^
|
||||||
|
|
||||||
|
error: attribute should be applied to a function
|
||||||
|
--> $DIR/invalid-rustc_args_required_const-arguments.rs:15:1
|
||||||
|
|
|
||||||
|
LL | #[rustc_args_required_const(0)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
LL | struct S;
|
||||||
|
| --------- not a function
|
||||||
|
|
||||||
|
error: index exceeds number of arguments
|
||||||
|
--> $DIR/invalid-rustc_args_required_const-arguments.rs:22:33
|
||||||
|
|
|
||||||
|
LL | #[rustc_args_required_const(1)]
|
||||||
|
| ^ there is only 1 argument
|
||||||
|
|
||||||
|
error: aborting due to 7 previous errors
|
||||||
|
|
|
@ -3,13 +3,15 @@
|
||||||
struct MyStruct {
|
struct MyStruct {
|
||||||
field: bool,
|
field: bool,
|
||||||
inner_array: [char; 1],
|
inner_array: [char; 1],
|
||||||
|
raw_ptr: *mut u8
|
||||||
}
|
}
|
||||||
impl MyStruct {
|
impl MyStruct {
|
||||||
fn use_mut(&mut self) {}
|
fn use_mut(&mut self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ARRAY: [u8; 1] = [25];
|
const ARRAY: [u8; 1] = [25];
|
||||||
const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
|
const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
|
||||||
|
const RAW_PTR: *mut u8 = 1 as *mut u8;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
ARRAY[0] = 5; //~ WARN attempting to modify
|
ARRAY[0] = 5; //~ WARN attempting to modify
|
||||||
|
@ -18,4 +20,13 @@ fn main() {
|
||||||
MY_STRUCT.use_mut(); //~ WARN taking
|
MY_STRUCT.use_mut(); //~ WARN taking
|
||||||
&mut MY_STRUCT; //~ WARN taking
|
&mut MY_STRUCT; //~ WARN taking
|
||||||
(&mut MY_STRUCT).use_mut(); //~ WARN taking
|
(&mut MY_STRUCT).use_mut(); //~ WARN taking
|
||||||
|
|
||||||
|
// Test that we don't warn when writing through
|
||||||
|
// a raw pointer
|
||||||
|
// This is U.B., but this test is check-pass,
|
||||||
|
// so this never actually executes
|
||||||
|
unsafe {
|
||||||
|
*RAW_PTR = 0;
|
||||||
|
*MY_STRUCT.raw_ptr = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
warning: attempting to modify a `const` item
|
warning: attempting to modify a `const` item
|
||||||
--> $DIR/lint-const-item-mutation.rs:15:5
|
--> $DIR/lint-const-item-mutation.rs:17:5
|
||||||
|
|
|
|
||||||
LL | ARRAY[0] = 5;
|
LL | ARRAY[0] = 5;
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
@ -7,39 +7,39 @@ LL | ARRAY[0] = 5;
|
||||||
= note: `#[warn(const_item_mutation)]` on by default
|
= note: `#[warn(const_item_mutation)]` on by default
|
||||||
= note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified
|
= note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified
|
||||||
note: `const` item defined here
|
note: `const` item defined here
|
||||||
--> $DIR/lint-const-item-mutation.rs:11:1
|
--> $DIR/lint-const-item-mutation.rs:12:1
|
||||||
|
|
|
|
||||||
LL | const ARRAY: [u8; 1] = [25];
|
LL | const ARRAY: [u8; 1] = [25];
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
warning: attempting to modify a `const` item
|
warning: attempting to modify a `const` item
|
||||||
--> $DIR/lint-const-item-mutation.rs:16:5
|
--> $DIR/lint-const-item-mutation.rs:18:5
|
||||||
|
|
|
|
||||||
LL | MY_STRUCT.field = false;
|
LL | MY_STRUCT.field = false;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified
|
= note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified
|
||||||
note: `const` item defined here
|
note: `const` item defined here
|
||||||
--> $DIR/lint-const-item-mutation.rs:12:1
|
--> $DIR/lint-const-item-mutation.rs:13:1
|
||||||
|
|
|
|
||||||
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
|
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
warning: attempting to modify a `const` item
|
warning: attempting to modify a `const` item
|
||||||
--> $DIR/lint-const-item-mutation.rs:17:5
|
--> $DIR/lint-const-item-mutation.rs:19:5
|
||||||
|
|
|
|
||||||
LL | MY_STRUCT.inner_array[0] = 'b';
|
LL | MY_STRUCT.inner_array[0] = 'b';
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified
|
= note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified
|
||||||
note: `const` item defined here
|
note: `const` item defined here
|
||||||
--> $DIR/lint-const-item-mutation.rs:12:1
|
--> $DIR/lint-const-item-mutation.rs:13:1
|
||||||
|
|
|
|
||||||
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
|
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
warning: taking a mutable reference to a `const` item
|
warning: taking a mutable reference to a `const` item
|
||||||
--> $DIR/lint-const-item-mutation.rs:18:5
|
--> $DIR/lint-const-item-mutation.rs:20:5
|
||||||
|
|
|
|
||||||
LL | MY_STRUCT.use_mut();
|
LL | MY_STRUCT.use_mut();
|
||||||
| ^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -47,18 +47,18 @@ LL | MY_STRUCT.use_mut();
|
||||||
= note: each usage of a `const` item creates a new temporary
|
= note: each usage of a `const` item creates a new temporary
|
||||||
= note: the mutable reference will refer to this temporary, not the original `const` item
|
= note: the mutable reference will refer to this temporary, not the original `const` item
|
||||||
note: mutable reference created due to call to this method
|
note: mutable reference created due to call to this method
|
||||||
--> $DIR/lint-const-item-mutation.rs:8:5
|
--> $DIR/lint-const-item-mutation.rs:9:5
|
||||||
|
|
|
|
||||||
LL | fn use_mut(&mut self) {}
|
LL | fn use_mut(&mut self) {}
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
note: `const` item defined here
|
note: `const` item defined here
|
||||||
--> $DIR/lint-const-item-mutation.rs:12:1
|
--> $DIR/lint-const-item-mutation.rs:13:1
|
||||||
|
|
|
|
||||||
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
|
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
warning: taking a mutable reference to a `const` item
|
warning: taking a mutable reference to a `const` item
|
||||||
--> $DIR/lint-const-item-mutation.rs:19:5
|
--> $DIR/lint-const-item-mutation.rs:21:5
|
||||||
|
|
|
|
||||||
LL | &mut MY_STRUCT;
|
LL | &mut MY_STRUCT;
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
@ -66,13 +66,13 @@ LL | &mut MY_STRUCT;
|
||||||
= note: each usage of a `const` item creates a new temporary
|
= note: each usage of a `const` item creates a new temporary
|
||||||
= note: the mutable reference will refer to this temporary, not the original `const` item
|
= note: the mutable reference will refer to this temporary, not the original `const` item
|
||||||
note: `const` item defined here
|
note: `const` item defined here
|
||||||
--> $DIR/lint-const-item-mutation.rs:12:1
|
--> $DIR/lint-const-item-mutation.rs:13:1
|
||||||
|
|
|
|
||||||
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
|
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
warning: taking a mutable reference to a `const` item
|
warning: taking a mutable reference to a `const` item
|
||||||
--> $DIR/lint-const-item-mutation.rs:20:5
|
--> $DIR/lint-const-item-mutation.rs:22:5
|
||||||
|
|
|
|
||||||
LL | (&mut MY_STRUCT).use_mut();
|
LL | (&mut MY_STRUCT).use_mut();
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
@ -80,10 +80,10 @@ LL | (&mut MY_STRUCT).use_mut();
|
||||||
= note: each usage of a `const` item creates a new temporary
|
= note: each usage of a `const` item creates a new temporary
|
||||||
= note: the mutable reference will refer to this temporary, not the original `const` item
|
= note: the mutable reference will refer to this temporary, not the original `const` item
|
||||||
note: `const` item defined here
|
note: `const` item defined here
|
||||||
--> $DIR/lint-const-item-mutation.rs:12:1
|
--> $DIR/lint-const-item-mutation.rs:13:1
|
||||||
|
|
|
|
||||||
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
|
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
warning: 6 warnings emitted
|
warning: 6 warnings emitted
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
const FOO: isize = 10;
|
||||||
|
const ZST: &() = unsafe { std::mem::transmute(FOO) };
|
||||||
|
fn main() {
|
||||||
|
match &() {
|
||||||
|
ZST => 9,
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
// check-pass
|
||||||
|
// edition:2018
|
||||||
|
// compile-flags: -Z span-debug
|
||||||
|
// aux-build:test-macros.rs
|
||||||
|
|
||||||
|
// Tests that we properly pass tokens to proc-macro when nested
|
||||||
|
// nonterminals are involved.
|
||||||
|
|
||||||
|
#![no_std] // Don't load unnecessary hygiene information from std
|
||||||
|
extern crate std;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate test_macros;
|
||||||
|
|
||||||
|
|
||||||
|
macro_rules! wrap {
|
||||||
|
(first, $e:expr) => { wrap!(second, $e + 1) };
|
||||||
|
(second, $e:expr) => { wrap!(third, $e + 2) };
|
||||||
|
(third, $e:expr) => {
|
||||||
|
print_bang!($e + 3);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = wrap!(first, 0);
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
PRINT-BANG INPUT (DISPLAY): 0 + 1 + 2 + 3
|
||||||
|
PRINT-BANG INPUT (DEBUG): TokenStream [
|
||||||
|
Group {
|
||||||
|
delimiter: None,
|
||||||
|
stream: TokenStream [
|
||||||
|
Group {
|
||||||
|
delimiter: None,
|
||||||
|
stream: TokenStream [
|
||||||
|
Group {
|
||||||
|
delimiter: None,
|
||||||
|
stream: TokenStream [
|
||||||
|
Literal {
|
||||||
|
kind: Integer,
|
||||||
|
symbol: "0",
|
||||||
|
suffix: None,
|
||||||
|
span: $DIR/nested-nonterminal-tokens.rs:25:26: 25:27 (#0),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: $DIR/nested-nonterminal-tokens.rs:17:41: 17:43 (#4),
|
||||||
|
},
|
||||||
|
Punct {
|
||||||
|
ch: '+',
|
||||||
|
spacing: Alone,
|
||||||
|
span: $DIR/nested-nonterminal-tokens.rs:17:44: 17:45 (#4),
|
||||||
|
},
|
||||||
|
Literal {
|
||||||
|
kind: Integer,
|
||||||
|
symbol: "1",
|
||||||
|
suffix: None,
|
||||||
|
span: $DIR/nested-nonterminal-tokens.rs:17:46: 17:47 (#4),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: $DIR/nested-nonterminal-tokens.rs:18:41: 18:43 (#5),
|
||||||
|
},
|
||||||
|
Punct {
|
||||||
|
ch: '+',
|
||||||
|
spacing: Alone,
|
||||||
|
span: $DIR/nested-nonterminal-tokens.rs:18:44: 18:45 (#5),
|
||||||
|
},
|
||||||
|
Literal {
|
||||||
|
kind: Integer,
|
||||||
|
symbol: "2",
|
||||||
|
suffix: None,
|
||||||
|
span: $DIR/nested-nonterminal-tokens.rs:18:46: 18:47 (#5),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: $DIR/nested-nonterminal-tokens.rs:20:21: 20:23 (#6),
|
||||||
|
},
|
||||||
|
Punct {
|
||||||
|
ch: '+',
|
||||||
|
spacing: Alone,
|
||||||
|
span: $DIR/nested-nonterminal-tokens.rs:20:24: 20:25 (#6),
|
||||||
|
},
|
||||||
|
Literal {
|
||||||
|
kind: Integer,
|
||||||
|
symbol: "3",
|
||||||
|
suffix: None,
|
||||||
|
span: $DIR/nested-nonterminal-tokens.rs:20:26: 20:27 (#6),
|
||||||
|
},
|
||||||
|
]
|
|
@ -0,0 +1,6 @@
|
||||||
|
fn main() {
|
||||||
|
let a = &[];
|
||||||
|
let b: &Vec<u8> = &vec![];
|
||||||
|
a > b;
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/slice-to-vec-comparison.rs:4:9
|
||||||
|
|
|
||||||
|
LL | a > b;
|
||||||
|
| ^ expected array of 0 elements, found struct `Vec`
|
||||||
|
|
|
||||||
|
= note: expected reference `&[_; 0]`
|
||||||
|
found reference `&Vec<u8>`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
|
@ -1 +1 @@
|
||||||
Subproject commit 05c611ae3c4255b7a2bcf4fcfa65b20286a07839
|
Subproject commit 75615f8e69f748d7ef0df7bc0b064a9b1f5c78b2
|
|
@ -25,8 +25,6 @@ fn main() {
|
||||||
let doc_targets: HashSet<_> = doc_targets_md
|
let doc_targets: HashSet<_> = doc_targets_md
|
||||||
.lines()
|
.lines()
|
||||||
.filter(|line| line.starts_with('`') && line.contains('|'))
|
.filter(|line| line.starts_with('`') && line.contains('|'))
|
||||||
// These platforms only exist on macos.
|
|
||||||
.filter(|line| !line.contains("[^apple]") || cfg!(target_os = "macos"))
|
|
||||||
.map(|line| line.split('`').skip(1).next().expect("expected target code span"))
|
.map(|line| line.split('`').skip(1).next().expect("expected target code span"))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue