mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-25 23:14:12 +00:00
commit
3d5a516710
.github/workflows
Cargo.lockcompiler
rustc_abi/src
rustc_ast/src
rustc_ast_passes/src
rustc_ast_pretty/src/pprust
rustc_attr/src
rustc_builtin_macros/src
rustc_codegen_llvm
rustc_codegen_ssa/src
rustc_driver_impl/src
rustc_error_codes/src/error_codes
rustc_errors/src
rustc_expand/src
rustc_feature/src
rustc_hir/src
rustc_hir_analysis
messages.ftl
src
rustc_hir_typeck/src/fn_ctxt
rustc_infer/src
rustc_interface/src
rustc_lexer/src
rustc_lint/src
rustc_lint_defs/src
rustc_llvm/llvm-wrapper
rustc_metadata/src
rustc_middle/src
rustc_mir_build/src/build/expr
rustc_parse/src
rustc_privacy/src
rustc_resolve/src
rustc_session
rustc_span/src
rustc_target/src/asm
rustc_trait_selection/src/traits
rustc_ty_utils/src
library
18
.github/workflows/ci.yml
vendored
18
.github/workflows/ci.yml
vendored
@ -42,24 +42,20 @@ jobs:
|
||||
TOOLSTATE_REPO: "https://github.com/rust-lang-nursery/rust-toolstate"
|
||||
CACHE_DOMAIN: ci-caches.rust-lang.org
|
||||
if: "github.event_name == 'pull_request'"
|
||||
continue-on-error: "${{ matrix.tidy }}"
|
||||
continue-on-error: "${{ matrix.name == 'mingw-check-tidy' }}"
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: mingw-check
|
||||
tidy: false
|
||||
os: ubuntu-20.04-16core-64gb
|
||||
env: {}
|
||||
- name: mingw-check-tidy
|
||||
tidy: true
|
||||
os: ubuntu-20.04-16core-64gb
|
||||
env: {}
|
||||
- name: x86_64-gnu-llvm-14
|
||||
tidy: false
|
||||
os: ubuntu-20.04-16core-64gb
|
||||
env: {}
|
||||
- name: x86_64-gnu-tools
|
||||
tidy: false
|
||||
os: ubuntu-20.04-16core-64gb
|
||||
env: {}
|
||||
timeout-minutes: 600
|
||||
@ -98,9 +94,6 @@ jobs:
|
||||
- name: show the current environment
|
||||
run: src/ci/scripts/dump-environment.sh
|
||||
if: success() && !env.SKIP_JOB
|
||||
- name: install awscli
|
||||
run: src/ci/scripts/install-awscli.sh
|
||||
if: success() && !env.SKIP_JOB
|
||||
- name: install sccache
|
||||
run: src/ci/scripts/install-sccache.sh
|
||||
if: success() && !env.SKIP_JOB
|
||||
@ -170,6 +163,7 @@ jobs:
|
||||
TOOLSTATE_PUBLISH: 1
|
||||
CACHES_AWS_ACCESS_KEY_ID: AKIA46X5W6CZI5DHEBFL
|
||||
ARTIFACTS_AWS_ACCESS_KEY_ID: AKIA46X5W6CZN24CBO55
|
||||
AWS_REGION: us-west-1
|
||||
CACHE_DOMAIN: ci-caches.rust-lang.org
|
||||
if: "github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust'"
|
||||
strategy:
|
||||
@ -521,9 +515,6 @@ jobs:
|
||||
- name: show the current environment
|
||||
run: src/ci/scripts/dump-environment.sh
|
||||
if: success() && !env.SKIP_JOB
|
||||
- name: install awscli
|
||||
run: src/ci/scripts/install-awscli.sh
|
||||
if: success() && !env.SKIP_JOB
|
||||
- name: install sccache
|
||||
run: src/ci/scripts/install-sccache.sh
|
||||
if: success() && !env.SKIP_JOB
|
||||
@ -593,6 +584,7 @@ jobs:
|
||||
TOOLSTATE_PUBLISH: 1
|
||||
CACHES_AWS_ACCESS_KEY_ID: AKIA46X5W6CZI5DHEBFL
|
||||
ARTIFACTS_AWS_ACCESS_KEY_ID: AKIA46X5W6CZN24CBO55
|
||||
AWS_REGION: us-west-1
|
||||
CACHE_DOMAIN: ci-caches.rust-lang.org
|
||||
if: "github.event_name == 'push' && (github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.repository == 'rust-lang-ci/rust'"
|
||||
strategy:
|
||||
@ -637,9 +629,6 @@ jobs:
|
||||
- name: show the current environment
|
||||
run: src/ci/scripts/dump-environment.sh
|
||||
if: success() && !env.SKIP_JOB
|
||||
- name: install awscli
|
||||
run: src/ci/scripts/install-awscli.sh
|
||||
if: success() && !env.SKIP_JOB
|
||||
- name: install sccache
|
||||
run: src/ci/scripts/install-sccache.sh
|
||||
if: success() && !env.SKIP_JOB
|
||||
@ -706,6 +695,7 @@ jobs:
|
||||
TOOLSTATE_PUBLISH: 1
|
||||
CACHES_AWS_ACCESS_KEY_ID: AKIA46X5W6CZI5DHEBFL
|
||||
ARTIFACTS_AWS_ACCESS_KEY_ID: AKIA46X5W6CZN24CBO55
|
||||
AWS_REGION: us-west-1
|
||||
CACHE_DOMAIN: ci-caches.rust-lang.org
|
||||
if: "github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'rust-lang-ci/rust'"
|
||||
steps:
|
||||
|
@ -577,7 +577,7 @@ checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
|
||||
|
||||
[[package]]
|
||||
name = "clippy"
|
||||
version = "0.1.70"
|
||||
version = "0.1.71"
|
||||
dependencies = [
|
||||
"clap 4.2.1",
|
||||
"clippy_lints",
|
||||
@ -619,7 +619,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clippy_lints"
|
||||
version = "0.1.70"
|
||||
version = "0.1.71"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"cargo_metadata 0.15.3",
|
||||
@ -643,7 +643,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clippy_utils"
|
||||
version = "0.1.70"
|
||||
version = "0.1.71"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"if_chain",
|
||||
@ -969,7 +969,7 @@ checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69"
|
||||
|
||||
[[package]]
|
||||
name = "declare_clippy_lint"
|
||||
version = "0.1.70"
|
||||
version = "0.1.71"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"quote",
|
||||
|
@ -729,42 +729,73 @@ pub trait LayoutCalculator {
|
||||
align = align.max(AbiAndPrefAlign::new(repr_align));
|
||||
}
|
||||
|
||||
let optimize = !repr.inhibit_union_abi_opt();
|
||||
// If all the non-ZST fields have the same ABI and union ABI optimizations aren't
|
||||
// disabled, we can use that common ABI for the union as a whole.
|
||||
struct AbiMismatch;
|
||||
let mut common_non_zst_abi_and_align = if repr.inhibit_union_abi_opt() {
|
||||
// Can't optimize
|
||||
Err(AbiMismatch)
|
||||
} else {
|
||||
Ok(None)
|
||||
};
|
||||
|
||||
let mut size = Size::ZERO;
|
||||
let mut abi = Abi::Aggregate { sized: true };
|
||||
let only_variant = &variants[FIRST_VARIANT];
|
||||
for field in only_variant {
|
||||
assert!(field.0.is_sized());
|
||||
|
||||
align = align.max(field.align());
|
||||
size = cmp::max(size, field.size());
|
||||
|
||||
// If all non-ZST fields have the same ABI, forward this ABI
|
||||
if optimize && !field.0.is_zst() {
|
||||
// Discard valid range information and allow undef
|
||||
let field_abi = match field.abi() {
|
||||
Abi::Scalar(x) => Abi::Scalar(x.to_union()),
|
||||
Abi::ScalarPair(x, y) => Abi::ScalarPair(x.to_union(), y.to_union()),
|
||||
Abi::Vector { element: x, count } => {
|
||||
Abi::Vector { element: x.to_union(), count }
|
||||
}
|
||||
Abi::Uninhabited | Abi::Aggregate { .. } => Abi::Aggregate { sized: true },
|
||||
};
|
||||
|
||||
if size == Size::ZERO {
|
||||
// first non ZST: initialize 'abi'
|
||||
abi = field_abi;
|
||||
} else if abi != field_abi {
|
||||
// different fields have different ABI: reset to Aggregate
|
||||
abi = Abi::Aggregate { sized: true };
|
||||
}
|
||||
if field.0.is_zst() {
|
||||
// Nothing more to do for ZST fields
|
||||
continue;
|
||||
}
|
||||
|
||||
size = cmp::max(size, field.size());
|
||||
if let Ok(common) = common_non_zst_abi_and_align {
|
||||
// Discard valid range information and allow undef
|
||||
let field_abi = field.abi().to_union();
|
||||
|
||||
if let Some((common_abi, common_align)) = common {
|
||||
if common_abi != field_abi {
|
||||
// Different fields have different ABI: disable opt
|
||||
common_non_zst_abi_and_align = Err(AbiMismatch);
|
||||
} else {
|
||||
// Fields with the same non-Aggregate ABI should also
|
||||
// have the same alignment
|
||||
if !matches!(common_abi, Abi::Aggregate { .. }) {
|
||||
assert_eq!(
|
||||
common_align,
|
||||
field.align().abi,
|
||||
"non-Aggregate field with matching ABI but differing alignment"
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// First non-ZST field: record its ABI and alignment
|
||||
common_non_zst_abi_and_align = Ok(Some((field_abi, field.align().abi)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(pack) = repr.pack {
|
||||
align = align.min(AbiAndPrefAlign::new(pack));
|
||||
}
|
||||
|
||||
// If all non-ZST fields have the same ABI, we may forward that ABI
|
||||
// for the union as a whole, unless otherwise inhibited.
|
||||
let abi = match common_non_zst_abi_and_align {
|
||||
Err(AbiMismatch) | Ok(None) => Abi::Aggregate { sized: true },
|
||||
Ok(Some((abi, _))) => {
|
||||
if abi.inherent_align(dl).map(|a| a.abi) != Some(align.abi) {
|
||||
// Mismatched alignment (e.g. union is #[repr(packed)]): disable opt
|
||||
Abi::Aggregate { sized: true }
|
||||
} else {
|
||||
abi
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Some(LayoutS {
|
||||
variants: Variants::Single { index: FIRST_VARIANT },
|
||||
fields: FieldsShape::Union(NonZeroUsize::new(only_variant.len())?),
|
||||
|
@ -1272,6 +1272,50 @@ impl Abi {
|
||||
pub fn is_scalar(&self) -> bool {
|
||||
matches!(*self, Abi::Scalar(_))
|
||||
}
|
||||
|
||||
/// Returns the fixed alignment of this ABI, if any is mandated.
|
||||
pub fn inherent_align<C: HasDataLayout>(&self, cx: &C) -> Option<AbiAndPrefAlign> {
|
||||
Some(match *self {
|
||||
Abi::Scalar(s) => s.align(cx),
|
||||
Abi::ScalarPair(s1, s2) => s1.align(cx).max(s2.align(cx)),
|
||||
Abi::Vector { element, count } => {
|
||||
cx.data_layout().vector_align(element.size(cx) * count)
|
||||
}
|
||||
Abi::Uninhabited | Abi::Aggregate { .. } => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the fixed size of this ABI, if any is mandated.
|
||||
pub fn inherent_size<C: HasDataLayout>(&self, cx: &C) -> Option<Size> {
|
||||
Some(match *self {
|
||||
Abi::Scalar(s) => {
|
||||
// No padding in scalars.
|
||||
s.size(cx)
|
||||
}
|
||||
Abi::ScalarPair(s1, s2) => {
|
||||
// May have some padding between the pair.
|
||||
let field2_offset = s1.size(cx).align_to(s2.align(cx).abi);
|
||||
(field2_offset + s2.size(cx)).align_to(self.inherent_align(cx)?.abi)
|
||||
}
|
||||
Abi::Vector { element, count } => {
|
||||
// No padding in vectors, except possibly for trailing padding
|
||||
// to make the size a multiple of align (e.g. for vectors of size 3).
|
||||
(element.size(cx) * count).align_to(self.inherent_align(cx)?.abi)
|
||||
}
|
||||
Abi::Uninhabited | Abi::Aggregate { .. } => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Discard validity range information and allow undef.
|
||||
pub fn to_union(&self) -> Self {
|
||||
assert!(self.is_sized());
|
||||
match *self {
|
||||
Abi::Scalar(s) => Abi::Scalar(s.to_union()),
|
||||
Abi::ScalarPair(s1, s2) => Abi::ScalarPair(s1.to_union(), s2.to_union()),
|
||||
Abi::Vector { element, count } => Abi::Vector { element: element.to_union(), count },
|
||||
Abi::Uninhabited | Abi::Aggregate { .. } => Abi::Aggregate { sized: true },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
|
@ -1821,6 +1821,8 @@ pub enum LitKind {
|
||||
/// A byte string (`b"foo"`). Not stored as a symbol because it might be
|
||||
/// non-utf8, and symbols only allow utf8 strings.
|
||||
ByteStr(Lrc<[u8]>, StrStyle),
|
||||
/// A C String (`c"foo"`). Guaranteed to only have `\0` at the end.
|
||||
CStr(Lrc<[u8]>, StrStyle),
|
||||
/// A byte char (`b'f'`).
|
||||
Byte(u8),
|
||||
/// A character literal (`'a'`).
|
||||
@ -1875,6 +1877,7 @@ impl LitKind {
|
||||
// unsuffixed variants
|
||||
LitKind::Str(..)
|
||||
| LitKind::ByteStr(..)
|
||||
| LitKind::CStr(..)
|
||||
| LitKind::Byte(..)
|
||||
| LitKind::Char(..)
|
||||
| LitKind::Int(_, LitIntType::Unsuffixed)
|
||||
|
@ -74,6 +74,8 @@ pub enum LitKind {
|
||||
StrRaw(u8), // raw string delimited by `n` hash symbols
|
||||
ByteStr,
|
||||
ByteStrRaw(u8), // raw byte string delimited by `n` hash symbols
|
||||
CStr,
|
||||
CStrRaw(u8),
|
||||
Err,
|
||||
}
|
||||
|
||||
@ -141,6 +143,10 @@ impl fmt::Display for Lit {
|
||||
delim = "#".repeat(n as usize),
|
||||
string = symbol
|
||||
)?,
|
||||
CStr => write!(f, "c\"{symbol}\"")?,
|
||||
CStrRaw(n) => {
|
||||
write!(f, "cr{delim}\"{symbol}\"{delim}", delim = "#".repeat(n as usize))?
|
||||
}
|
||||
Integer | Float | Bool | Err => write!(f, "{symbol}")?,
|
||||
}
|
||||
|
||||
@ -170,6 +176,7 @@ impl LitKind {
|
||||
Float => "float",
|
||||
Str | StrRaw(..) => "string",
|
||||
ByteStr | ByteStrRaw(..) => "byte string",
|
||||
CStr | CStrRaw(..) => "C string",
|
||||
Err => "error",
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,13 @@
|
||||
|
||||
use crate::ast::{self, LitKind, MetaItemLit, StrStyle};
|
||||
use crate::token::{self, Token};
|
||||
use rustc_lexer::unescape::{byte_from_char, unescape_byte, unescape_char, unescape_literal, Mode};
|
||||
use rustc_lexer::unescape::{
|
||||
byte_from_char, unescape_byte, unescape_c_string, unescape_char, unescape_literal, CStrUnit,
|
||||
Mode,
|
||||
};
|
||||
use rustc_span::symbol::{kw, sym, Symbol};
|
||||
use rustc_span::Span;
|
||||
use std::ops::Range;
|
||||
use std::{ascii, fmt, str};
|
||||
|
||||
// Escapes a string, represented as a symbol. Reuses the original symbol,
|
||||
@ -35,6 +39,7 @@ pub enum LitError {
|
||||
InvalidFloatSuffix,
|
||||
NonDecimalFloat(u32),
|
||||
IntTooLarge(u32),
|
||||
NulInCStr(Range<usize>),
|
||||
}
|
||||
|
||||
impl LitKind {
|
||||
@ -158,6 +163,52 @@ impl LitKind {
|
||||
|
||||
LitKind::ByteStr(bytes.into(), StrStyle::Raw(n))
|
||||
}
|
||||
token::CStr => {
|
||||
let s = symbol.as_str();
|
||||
let mut buf = Vec::with_capacity(s.len());
|
||||
let mut error = Ok(());
|
||||
unescape_c_string(s, Mode::CStr, &mut |span, c| match c {
|
||||
Ok(CStrUnit::Byte(0) | CStrUnit::Char('\0')) => {
|
||||
error = Err(LitError::NulInCStr(span));
|
||||
}
|
||||
Ok(CStrUnit::Byte(b)) => buf.push(b),
|
||||
Ok(CStrUnit::Char(c)) if c.len_utf8() == 1 => buf.push(c as u8),
|
||||
Ok(CStrUnit::Char(c)) => {
|
||||
buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes())
|
||||
}
|
||||
Err(err) => {
|
||||
if err.is_fatal() {
|
||||
error = Err(LitError::LexerError);
|
||||
}
|
||||
}
|
||||
});
|
||||
error?;
|
||||
buf.push(0);
|
||||
LitKind::CStr(buf.into(), StrStyle::Cooked)
|
||||
}
|
||||
token::CStrRaw(n) => {
|
||||
let s = symbol.as_str();
|
||||
let mut buf = Vec::with_capacity(s.len());
|
||||
let mut error = Ok(());
|
||||
unescape_c_string(s, Mode::RawCStr, &mut |span, c| match c {
|
||||
Ok(CStrUnit::Byte(0) | CStrUnit::Char('\0')) => {
|
||||
error = Err(LitError::NulInCStr(span));
|
||||
}
|
||||
Ok(CStrUnit::Byte(b)) => buf.push(b),
|
||||
Ok(CStrUnit::Char(c)) if c.len_utf8() == 1 => buf.push(c as u8),
|
||||
Ok(CStrUnit::Char(c)) => {
|
||||
buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes())
|
||||
}
|
||||
Err(err) => {
|
||||
if err.is_fatal() {
|
||||
error = Err(LitError::LexerError);
|
||||
}
|
||||
}
|
||||
});
|
||||
error?;
|
||||
buf.push(0);
|
||||
LitKind::CStr(buf.into(), StrStyle::Raw(n))
|
||||
}
|
||||
token::Err => LitKind::Err,
|
||||
})
|
||||
}
|
||||
@ -191,6 +242,14 @@ impl fmt::Display for LitKind {
|
||||
string = symbol
|
||||
)?;
|
||||
}
|
||||
LitKind::CStr(ref bytes, StrStyle::Cooked) => {
|
||||
write!(f, "c\"{}\"", escape_byte_str_symbol(bytes))?
|
||||
}
|
||||
LitKind::CStr(ref bytes, StrStyle::Raw(n)) => {
|
||||
// This can only be valid UTF-8.
|
||||
let symbol = str::from_utf8(bytes).unwrap();
|
||||
write!(f, "cr{delim}\"{symbol}\"{delim}", delim = "#".repeat(n as usize),)?;
|
||||
}
|
||||
LitKind::Int(n, ty) => {
|
||||
write!(f, "{n}")?;
|
||||
match ty {
|
||||
@ -237,6 +296,8 @@ impl MetaItemLit {
|
||||
LitKind::Str(_, ast::StrStyle::Raw(n)) => token::StrRaw(n),
|
||||
LitKind::ByteStr(_, ast::StrStyle::Cooked) => token::ByteStr,
|
||||
LitKind::ByteStr(_, ast::StrStyle::Raw(n)) => token::ByteStrRaw(n),
|
||||
LitKind::CStr(_, ast::StrStyle::Cooked) => token::CStr,
|
||||
LitKind::CStr(_, ast::StrStyle::Raw(n)) => token::CStrRaw(n),
|
||||
LitKind::Byte(_) => token::Byte,
|
||||
LitKind::Char(_) => token::Char,
|
||||
LitKind::Int(..) => token::Integer,
|
||||
|
@ -572,6 +572,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
|
||||
}
|
||||
};
|
||||
}
|
||||
gate_all!(c_str_literals, "`c\"..\"` literals are experimental");
|
||||
gate_all!(
|
||||
if_let_guard,
|
||||
"`if let` guards are experimental",
|
||||
|
@ -210,6 +210,10 @@ pub fn literal_to_string(lit: token::Lit) -> String {
|
||||
token::ByteStrRaw(n) => {
|
||||
format!("br{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol)
|
||||
}
|
||||
token::CStr => format!("c\"{symbol}\""),
|
||||
token::CStrRaw(n) => {
|
||||
format!("cr{delim}\"{symbol}\"{delim}", delim = "#".repeat(n as usize))
|
||||
}
|
||||
token::Integer | token::Float | token::Bool | token::Err => symbol.to_string(),
|
||||
};
|
||||
|
||||
|
@ -5,6 +5,7 @@ use rustc_ast::{Attribute, LitKind, MetaItem, MetaItemKind, MetaItemLit, NestedM
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
|
||||
use rustc_macros::HashStable_Generic;
|
||||
use rustc_session::config::ExpectedValues;
|
||||
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
|
||||
use rustc_session::lint::BuiltinLintDiagnostics;
|
||||
use rustc_session::parse::{feature_err, ParseSess};
|
||||
@ -581,32 +582,32 @@ pub fn cfg_matches(
|
||||
) -> bool {
|
||||
eval_condition(cfg, sess, features, &mut |cfg| {
|
||||
try_gate_cfg(cfg.name, cfg.span, sess, features);
|
||||
if let Some(names_valid) = &sess.check_config.names_valid {
|
||||
if !names_valid.contains(&cfg.name) {
|
||||
match sess.check_config.expecteds.get(&cfg.name) {
|
||||
Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
|
||||
sess.buffer_lint_with_diagnostic(
|
||||
UNEXPECTED_CFGS,
|
||||
cfg.span,
|
||||
lint_node_id,
|
||||
"unexpected `cfg` condition value",
|
||||
BuiltinLintDiagnostics::UnexpectedCfgValue(
|
||||
(cfg.name, cfg.name_span),
|
||||
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
|
||||
),
|
||||
);
|
||||
}
|
||||
None if sess.check_config.exhaustive_names => {
|
||||
sess.buffer_lint_with_diagnostic(
|
||||
UNEXPECTED_CFGS,
|
||||
cfg.span,
|
||||
lint_node_id,
|
||||
"unexpected `cfg` condition name",
|
||||
BuiltinLintDiagnostics::UnexpectedCfg((cfg.name, cfg.name_span), None),
|
||||
BuiltinLintDiagnostics::UnexpectedCfgName(
|
||||
(cfg.name, cfg.name_span),
|
||||
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(value) = cfg.value {
|
||||
if let Some(values) = &sess.check_config.values_valid.get(&cfg.name) {
|
||||
if !values.contains(&value) {
|
||||
sess.buffer_lint_with_diagnostic(
|
||||
UNEXPECTED_CFGS,
|
||||
cfg.span,
|
||||
lint_node_id,
|
||||
"unexpected `cfg` condition value",
|
||||
BuiltinLintDiagnostics::UnexpectedCfg(
|
||||
(cfg.name, cfg.name_span),
|
||||
cfg.value_span.map(|vs| (value, vs)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => { /* not unexpected */ }
|
||||
}
|
||||
sess.config.contains(&(cfg.name, cfg.value))
|
||||
})
|
||||
|
@ -32,6 +32,10 @@ pub fn expand_concat(
|
||||
Ok(ast::LitKind::Bool(b)) => {
|
||||
accumulator.push_str(&b.to_string());
|
||||
}
|
||||
Ok(ast::LitKind::CStr(..)) => {
|
||||
cx.span_err(e.span, "cannot concatenate a C string literal");
|
||||
has_errors = true;
|
||||
}
|
||||
Ok(ast::LitKind::Byte(..) | ast::LitKind::ByteStr(..)) => {
|
||||
cx.emit_err(errors::ConcatBytestr { span: e.span });
|
||||
has_errors = true;
|
||||
|
@ -18,6 +18,11 @@ fn invalid_type_err(
|
||||
};
|
||||
let snippet = cx.sess.source_map().span_to_snippet(span).ok();
|
||||
match ast::LitKind::from_token_lit(token_lit) {
|
||||
Ok(ast::LitKind::CStr(_, _)) => {
|
||||
// FIXME(c_str_literals): should concatenation of C string literals
|
||||
// include the null bytes in the end?
|
||||
cx.span_err(span, "cannot concatenate C string literals");
|
||||
}
|
||||
Ok(ast::LitKind::Char(_)) => {
|
||||
let sugg =
|
||||
snippet.map(|snippet| ConcatBytesInvalidSuggestion::CharLit { span, snippet });
|
||||
|
@ -24,7 +24,7 @@ codegen_llvm_error_writing_def_file =
|
||||
Error writing .DEF file: {$error}
|
||||
|
||||
codegen_llvm_error_calling_dlltool =
|
||||
Error calling dlltool: {$error}
|
||||
Error calling dlltool '{$dlltool_path}': {$error}
|
||||
|
||||
codegen_llvm_dlltool_fail_import_library =
|
||||
Dlltool could not create import library: {$stdout}
|
||||
@ -82,7 +82,7 @@ codegen_llvm_prepare_thin_lto_module_with_llvm_err = failed to prepare thin LTO
|
||||
codegen_llvm_parse_bitcode = failed to parse bitcode for LTO module
|
||||
codegen_llvm_parse_bitcode_with_llvm_err = failed to parse bitcode for LTO module: {$llvm_err}
|
||||
|
||||
codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name}: {$message}
|
||||
codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message}
|
||||
codegen_llvm_from_llvm_diag = {$message}
|
||||
|
||||
codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err}
|
||||
|
@ -238,7 +238,9 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
||||
InlineAsmArch::Hexagon => {}
|
||||
InlineAsmArch::LoongArch64 => {}
|
||||
InlineAsmArch::Mips | InlineAsmArch::Mips64 => {}
|
||||
InlineAsmArch::S390x => {}
|
||||
InlineAsmArch::S390x => {
|
||||
constraints.push("~{cc}".to_string());
|
||||
}
|
||||
InlineAsmArch::SpirV => {}
|
||||
InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => {}
|
||||
InlineAsmArch::Bpf => {}
|
||||
|
@ -198,7 +198,7 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
|
||||
"arm" => ("arm", "--32"),
|
||||
_ => panic!("unsupported arch {}", sess.target.arch),
|
||||
};
|
||||
let result = std::process::Command::new(dlltool)
|
||||
let result = std::process::Command::new(&dlltool)
|
||||
.args([
|
||||
"-d",
|
||||
def_file_path.to_str().unwrap(),
|
||||
@ -218,9 +218,13 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
|
||||
|
||||
match result {
|
||||
Err(e) => {
|
||||
sess.emit_fatal(ErrorCallingDllTool { error: e });
|
||||
sess.emit_fatal(ErrorCallingDllTool {
|
||||
dlltool_path: dlltool.to_string_lossy(),
|
||||
error: e,
|
||||
});
|
||||
}
|
||||
Ok(output) if !output.status.success() => {
|
||||
// dlltool returns '0' on failure, so check for error output instead.
|
||||
Ok(output) if !output.stderr.is_empty() => {
|
||||
sess.emit_fatal(DlltoolFailImportLibrary {
|
||||
stdout: String::from_utf8_lossy(&output.stdout),
|
||||
stderr: String::from_utf8_lossy(&output.stderr),
|
||||
@ -431,7 +435,7 @@ fn string_to_io_error(s: String) -> io::Error {
|
||||
|
||||
fn find_binutils_dlltool(sess: &Session) -> OsString {
|
||||
assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc);
|
||||
if let Some(dlltool_path) = &sess.opts.unstable_opts.dlltool {
|
||||
if let Some(dlltool_path) = &sess.opts.cg.dlltool {
|
||||
return dlltool_path.clone().into_os_string();
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ use rustc_span::symbol::sym;
|
||||
use rustc_span::InnerSpan;
|
||||
use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo};
|
||||
|
||||
use crate::llvm::diagnostic::OptimizationDiagnosticKind;
|
||||
use libc::{c_char, c_int, c_uint, c_void, size_t};
|
||||
use std::ffi::CString;
|
||||
use std::fs;
|
||||
@ -363,6 +364,15 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void
|
||||
line: opt.line,
|
||||
column: opt.column,
|
||||
pass_name: &opt.pass_name,
|
||||
kind: match opt.kind {
|
||||
OptimizationDiagnosticKind::OptimizationRemark => "success",
|
||||
OptimizationDiagnosticKind::OptimizationMissed
|
||||
| OptimizationDiagnosticKind::OptimizationFailure => "missed",
|
||||
OptimizationDiagnosticKind::OptimizationAnalysis
|
||||
| OptimizationDiagnosticKind::OptimizationAnalysisFPCommute
|
||||
| OptimizationDiagnosticKind::OptimizationAnalysisAliasing => "analysis",
|
||||
OptimizationDiagnosticKind::OptimizationRemarkOther => "other",
|
||||
},
|
||||
message: &opt.message,
|
||||
});
|
||||
}
|
||||
|
@ -322,7 +322,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let def_id = instance.def_id();
|
||||
let containing_scope = get_containing_scope(self, instance);
|
||||
let (containing_scope, is_method) = get_containing_scope(self, instance);
|
||||
let span = tcx.def_span(def_id);
|
||||
let loc = self.lookup_debug_loc(span.lo());
|
||||
let file_metadata = file_metadata(self, &loc.file);
|
||||
@ -378,8 +378,29 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
return llvm::LLVMRustDIBuilderCreateFunction(
|
||||
// When we're adding a method to a type DIE, we only want a DW_AT_declaration there, because
|
||||
// LLVM LTO can't unify type definitions when a child DIE is a full subprogram definition.
|
||||
// When we use this `decl` below, the subprogram definition gets created at the CU level
|
||||
// with a DW_AT_specification pointing back to the type's declaration.
|
||||
let decl = is_method.then(|| unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateMethod(
|
||||
DIB(self),
|
||||
containing_scope,
|
||||
name.as_ptr().cast(),
|
||||
name.len(),
|
||||
linkage_name.as_ptr().cast(),
|
||||
linkage_name.len(),
|
||||
file_metadata,
|
||||
loc.line,
|
||||
function_type_metadata,
|
||||
flags,
|
||||
spflags & !DISPFlags::SPFlagDefinition,
|
||||
template_parameters,
|
||||
)
|
||||
});
|
||||
|
||||
return unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateFunction(
|
||||
DIB(self),
|
||||
containing_scope,
|
||||
name.as_ptr().cast(),
|
||||
@ -394,9 +415,9 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
spflags,
|
||||
maybe_definition_llfn,
|
||||
template_parameters,
|
||||
None,
|
||||
);
|
||||
}
|
||||
decl,
|
||||
)
|
||||
};
|
||||
|
||||
fn get_function_signature<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
@ -493,14 +514,16 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
names
|
||||
}
|
||||
|
||||
/// Returns a scope, plus `true` if that's a type scope for "class" methods,
|
||||
/// otherwise `false` for plain namespace scopes.
|
||||
fn get_containing_scope<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
instance: Instance<'tcx>,
|
||||
) -> &'ll DIScope {
|
||||
) -> (&'ll DIScope, bool) {
|
||||
// First, let's see if this is a method within an inherent impl. Because
|
||||
// if yes, we want to make the result subroutine DIE a child of the
|
||||
// subroutine's self-type.
|
||||
let self_type = cx.tcx.impl_of_method(instance.def_id()).and_then(|impl_def_id| {
|
||||
if let Some(impl_def_id) = cx.tcx.impl_of_method(instance.def_id()) {
|
||||
// If the method does *not* belong to a trait, proceed
|
||||
if cx.tcx.trait_id_of_impl(impl_def_id).is_none() {
|
||||
let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions(
|
||||
@ -511,39 +534,33 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
|
||||
// Only "class" methods are generally understood by LLVM,
|
||||
// so avoid methods on other types (e.g., `<*mut T>::null`).
|
||||
match impl_self_ty.kind() {
|
||||
ty::Adt(def, ..) if !def.is_box() => {
|
||||
// Again, only create type information if full debuginfo is enabled
|
||||
if cx.sess().opts.debuginfo == DebugInfo::Full
|
||||
&& !impl_self_ty.has_param()
|
||||
{
|
||||
Some(type_di_node(cx, impl_self_ty))
|
||||
} else {
|
||||
Some(namespace::item_namespace(cx, def.did()))
|
||||
}
|
||||
if let ty::Adt(def, ..) = impl_self_ty.kind() && !def.is_box() {
|
||||
// Again, only create type information if full debuginfo is enabled
|
||||
if cx.sess().opts.debuginfo == DebugInfo::Full && !impl_self_ty.has_param()
|
||||
{
|
||||
return (type_di_node(cx, impl_self_ty), true);
|
||||
} else {
|
||||
return (namespace::item_namespace(cx, def.did()), false);
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
// For trait method impls we still use the "parallel namespace"
|
||||
// strategy
|
||||
None
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
self_type.unwrap_or_else(|| {
|
||||
namespace::item_namespace(
|
||||
cx,
|
||||
DefId {
|
||||
krate: instance.def_id().krate,
|
||||
index: cx
|
||||
.tcx
|
||||
.def_key(instance.def_id())
|
||||
.parent
|
||||
.expect("get_containing_scope: missing parent?"),
|
||||
},
|
||||
)
|
||||
})
|
||||
let scope = namespace::item_namespace(
|
||||
cx,
|
||||
DefId {
|
||||
krate: instance.def_id().krate,
|
||||
index: cx
|
||||
.tcx
|
||||
.def_key(instance.def_id())
|
||||
.parent
|
||||
.expect("get_containing_scope: missing parent?"),
|
||||
},
|
||||
);
|
||||
(scope, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,8 @@ pub(crate) struct ErrorWritingDEFFile {
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_error_calling_dlltool)]
|
||||
pub(crate) struct ErrorCallingDllTool {
|
||||
pub(crate) struct ErrorCallingDllTool<'a> {
|
||||
pub dlltool_path: Cow<'a, str>,
|
||||
pub error: std::io::Error,
|
||||
}
|
||||
|
||||
@ -195,6 +196,7 @@ pub(crate) struct FromLlvmOptimizationDiag<'a> {
|
||||
pub line: std::ffi::c_uint,
|
||||
pub column: std::ffi::c_uint,
|
||||
pub pass_name: &'a str,
|
||||
pub kind: &'a str,
|
||||
pub message: &'a str,
|
||||
}
|
||||
|
||||
|
@ -1987,6 +1987,21 @@ extern "C" {
|
||||
Decl: Option<&'a DIDescriptor>,
|
||||
) -> &'a DISubprogram;
|
||||
|
||||
pub fn LLVMRustDIBuilderCreateMethod<'a>(
|
||||
Builder: &DIBuilder<'a>,
|
||||
Scope: &'a DIDescriptor,
|
||||
Name: *const c_char,
|
||||
NameLen: size_t,
|
||||
LinkageName: *const c_char,
|
||||
LinkageNameLen: size_t,
|
||||
File: &'a DIFile,
|
||||
LineNo: c_uint,
|
||||
Ty: &'a DIType,
|
||||
Flags: DIFlags,
|
||||
SPFlags: DISPFlags,
|
||||
TParam: &'a DIArray,
|
||||
) -> &'a DISubprogram;
|
||||
|
||||
pub fn LLVMRustDIBuilderCreateBasicType<'a>(
|
||||
Builder: &DIBuilder<'a>,
|
||||
Name: *const c_char,
|
||||
@ -2249,7 +2264,7 @@ extern "C" {
|
||||
|
||||
pub fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool;
|
||||
|
||||
pub fn LLVMRustPrintTargetCPUs(T: &TargetMachine);
|
||||
pub fn LLVMRustPrintTargetCPUs(T: &TargetMachine, cpu: *const c_char);
|
||||
pub fn LLVMRustGetTargetFeaturesCount(T: &TargetMachine) -> size_t;
|
||||
pub fn LLVMRustGetTargetFeature(
|
||||
T: &TargetMachine,
|
||||
|
@ -329,7 +329,14 @@ pub(crate) fn print(req: PrintRequest, sess: &Session) {
|
||||
require_inited();
|
||||
let tm = create_informational_target_machine(sess);
|
||||
match req {
|
||||
PrintRequest::TargetCPUs => unsafe { llvm::LLVMRustPrintTargetCPUs(tm) },
|
||||
PrintRequest::TargetCPUs => {
|
||||
// SAFETY generate a C compatible string from a byte slice to pass
|
||||
// the target CPU name into LLVM, the lifetime of the reference is
|
||||
// at least as long as the C function
|
||||
let cpu_cstring = CString::new(handle_native(sess.target.cpu.as_ref()))
|
||||
.unwrap_or_else(|e| bug!("failed to convert to cstring: {}", e));
|
||||
unsafe { llvm::LLVMRustPrintTargetCPUs(tm, cpu_cstring.as_ptr()) };
|
||||
}
|
||||
PrintRequest::TargetFeatures => print_target_features(sess, tm),
|
||||
_ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req),
|
||||
}
|
||||
|
@ -1821,9 +1821,15 @@ impl SharedEmitterMain {
|
||||
let source = sess
|
||||
.source_map()
|
||||
.new_source_file(FileName::inline_asm_source_code(&buffer), buffer);
|
||||
let source_span = Span::with_root_ctxt(source.start_pos, source.end_pos);
|
||||
let spans: Vec<_> =
|
||||
spans.iter().map(|sp| source_span.from_inner(*sp)).collect();
|
||||
let spans: Vec<_> = spans
|
||||
.iter()
|
||||
.map(|sp| {
|
||||
Span::with_root_ctxt(
|
||||
source.normalized_byte_pos(sp.start as u32),
|
||||
source.normalized_byte_pos(sp.end as u32),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
err.span_note(spans, "instantiated into assembly here");
|
||||
}
|
||||
|
||||
|
@ -592,15 +592,6 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
|
||||
fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
|
||||
use rustc_ast::{LitIntType, LitKind, MetaItemLit};
|
||||
if !tcx.features().raw_dylib && tcx.sess.target.arch == "x86" {
|
||||
feature_err(
|
||||
&tcx.sess.parse_sess,
|
||||
sym::raw_dylib,
|
||||
attr.span,
|
||||
"`#[link_ordinal]` is unstable on x86",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
let meta_item_list = attr.meta_item_list();
|
||||
let meta_item_list = meta_item_list.as_deref();
|
||||
let sole_meta_list = match meta_item_list {
|
||||
|
@ -2,6 +2,7 @@ use super::place::PlaceRef;
|
||||
use super::{FunctionCx, LocalRef};
|
||||
|
||||
use crate::base;
|
||||
use crate::common::TypeKind;
|
||||
use crate::glue;
|
||||
use crate::traits::*;
|
||||
use crate::MemFlags;
|
||||
@ -236,19 +237,47 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
||||
};
|
||||
|
||||
match (&mut val, field.abi) {
|
||||
(OperandValue::Immediate(llval), _) => {
|
||||
(
|
||||
OperandValue::Immediate(llval),
|
||||
Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. },
|
||||
) => {
|
||||
// Bools in union fields needs to be truncated.
|
||||
*llval = bx.to_immediate(*llval, field);
|
||||
// HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
|
||||
*llval = bx.bitcast(*llval, bx.cx().immediate_backend_type(field));
|
||||
let ty = bx.cx().immediate_backend_type(field);
|
||||
if bx.type_kind(ty) == TypeKind::Pointer {
|
||||
*llval = bx.pointercast(*llval, ty);
|
||||
}
|
||||
}
|
||||
(OperandValue::Pair(a, b), Abi::ScalarPair(a_abi, b_abi)) => {
|
||||
// Bools in union fields needs to be truncated.
|
||||
*a = bx.to_immediate_scalar(*a, a_abi);
|
||||
*b = bx.to_immediate_scalar(*b, b_abi);
|
||||
// HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
|
||||
*a = bx.bitcast(*a, bx.cx().scalar_pair_element_backend_type(field, 0, true));
|
||||
*b = bx.bitcast(*b, bx.cx().scalar_pair_element_backend_type(field, 1, true));
|
||||
let a_ty = bx.cx().scalar_pair_element_backend_type(field, 0, true);
|
||||
let b_ty = bx.cx().scalar_pair_element_backend_type(field, 1, true);
|
||||
if bx.type_kind(a_ty) == TypeKind::Pointer {
|
||||
*a = bx.pointercast(*a, a_ty);
|
||||
}
|
||||
if bx.type_kind(b_ty) == TypeKind::Pointer {
|
||||
*b = bx.pointercast(*b, b_ty);
|
||||
}
|
||||
}
|
||||
// Newtype vector of array, e.g. #[repr(simd)] struct S([i32; 4]);
|
||||
(OperandValue::Immediate(llval), Abi::Aggregate { sized: true }) => {
|
||||
assert!(matches!(self.layout.abi, Abi::Vector { .. }));
|
||||
|
||||
let llty = bx.cx().backend_type(self.layout);
|
||||
let llfield_ty = bx.cx().backend_type(field);
|
||||
|
||||
// Can't bitcast an aggregate, so round trip through memory.
|
||||
let lltemp = bx.alloca(llfield_ty, field.align.abi);
|
||||
let llptr = bx.pointercast(lltemp, bx.cx().type_ptr_to(llty));
|
||||
bx.store(*llval, llptr, field.align.abi);
|
||||
*llval = bx.load(llfield_ty, lltemp, field.align.abi);
|
||||
}
|
||||
(OperandValue::Immediate(_), Abi::Uninhabited | Abi::Aggregate { sized: false }) => {
|
||||
bug!()
|
||||
}
|
||||
(OperandValue::Pair(..), _) => bug!(),
|
||||
(OperandValue::Ref(..), _) => bug!(),
|
||||
|
@ -25,7 +25,7 @@ use rustc_data_structures::profiling::{
|
||||
use rustc_data_structures::sync::SeqCst;
|
||||
use rustc_errors::registry::{InvalidErrorCode, Registry};
|
||||
use rustc_errors::{
|
||||
DiagnosticMessage, ErrorGuaranteed, PResult, SubdiagnosticMessage, TerminalUrl,
|
||||
DiagnosticMessage, ErrorGuaranteed, Handler, PResult, SubdiagnosticMessage, TerminalUrl,
|
||||
};
|
||||
use rustc_feature::find_gated_cfg;
|
||||
use rustc_fluent_macro::fluent_messages;
|
||||
@ -55,7 +55,7 @@ use std::panic::{self, catch_unwind};
|
||||
use std::path::PathBuf;
|
||||
use std::process::{self, Command, Stdio};
|
||||
use std::str;
|
||||
use std::sync::LazyLock;
|
||||
use std::sync::OnceLock;
|
||||
use std::time::Instant;
|
||||
|
||||
// This import blocks the use of panicking `print` and `println` in all the code
|
||||
@ -119,7 +119,7 @@ pub const EXIT_SUCCESS: i32 = 0;
|
||||
/// Exit status code used for compilation failures and invalid flags.
|
||||
pub const EXIT_FAILURE: i32 = 1;
|
||||
|
||||
const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\
|
||||
pub const DEFAULT_BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\
|
||||
?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
|
||||
|
||||
const ICE_REPORT_COMPILER_FLAGS: &[&str] = &["-Z", "-C", "--crate-type"];
|
||||
@ -1178,6 +1178,7 @@ fn extra_compiler_flags() -> Option<(Vec<String>, bool)> {
|
||||
pub fn catch_fatal_errors<F: FnOnce() -> R, R>(f: F) -> Result<R, ErrorGuaranteed> {
|
||||
catch_unwind(panic::AssertUnwindSafe(f)).map_err(|value| {
|
||||
if value.is::<rustc_errors::FatalErrorMarker>() {
|
||||
#[allow(deprecated)]
|
||||
ErrorGuaranteed::unchecked_claim_error_was_emitted()
|
||||
} else {
|
||||
panic::resume_unwind(value);
|
||||
@ -1195,35 +1196,58 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
|
||||
LazyLock::new(|| {
|
||||
let hook = panic::take_hook();
|
||||
panic::set_hook(Box::new(|info| {
|
||||
// If the error was caused by a broken pipe then this is not a bug.
|
||||
// Write the error and return immediately. See #98700.
|
||||
#[cfg(windows)]
|
||||
if let Some(msg) = info.payload().downcast_ref::<String>() {
|
||||
if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)")
|
||||
{
|
||||
early_error_no_abort(ErrorOutputType::default(), &msg);
|
||||
return;
|
||||
}
|
||||
};
|
||||
/// Stores the default panic hook, from before [`install_ice_hook`] was called.
|
||||
static DEFAULT_HOOK: OnceLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
|
||||
OnceLock::new();
|
||||
|
||||
// Invoke the default handler, which prints the actual panic message and optionally a backtrace
|
||||
// Don't do this for delayed bugs, which already emit their own more useful backtrace.
|
||||
if !info.payload().is::<rustc_errors::DelayedBugPanic>() {
|
||||
(*DEFAULT_HOOK)(info);
|
||||
/// Installs a panic hook that will print the ICE message on unexpected panics.
|
||||
///
|
||||
/// The hook is intended to be useable even by external tools. You can pass a custom
|
||||
/// `bug_report_url`, or report arbitrary info in `extra_info`. Note that `extra_info` is called in
|
||||
/// a context where *the thread is currently panicking*, so it must not panic or the process will
|
||||
/// abort.
|
||||
///
|
||||
/// If you have no extra info to report, pass the empty closure `|_| ()` as the argument to
|
||||
/// extra_info.
|
||||
///
|
||||
/// A custom rustc driver can skip calling this to set up a custom ICE hook.
|
||||
pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) {
|
||||
// If the user has not explicitly overridden "RUST_BACKTRACE", then produce
|
||||
// full backtraces. When a compiler ICE happens, we want to gather
|
||||
// as much information as possible to present in the issue opened
|
||||
// by the user. Compiler developers and other rustc users can
|
||||
// opt in to less-verbose backtraces by manually setting "RUST_BACKTRACE"
|
||||
// (e.g. `RUST_BACKTRACE=1`)
|
||||
if std::env::var("RUST_BACKTRACE").is_err() {
|
||||
std::env::set_var("RUST_BACKTRACE", "full");
|
||||
}
|
||||
|
||||
// Separate the output with an empty line
|
||||
eprintln!();
|
||||
let default_hook = DEFAULT_HOOK.get_or_init(panic::take_hook);
|
||||
|
||||
panic::set_hook(Box::new(move |info| {
|
||||
// If the error was caused by a broken pipe then this is not a bug.
|
||||
// Write the error and return immediately. See #98700.
|
||||
#[cfg(windows)]
|
||||
if let Some(msg) = info.payload().downcast_ref::<String>() {
|
||||
if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)") {
|
||||
early_error_no_abort(ErrorOutputType::default(), &msg);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Print the ICE message
|
||||
report_ice(info, BUG_REPORT_URL);
|
||||
}));
|
||||
hook
|
||||
});
|
||||
// Invoke the default handler, which prints the actual panic message and optionally a backtrace
|
||||
// Don't do this for delayed bugs, which already emit their own more useful backtrace.
|
||||
if !info.payload().is::<rustc_errors::DelayedBugPanic>() {
|
||||
(*default_hook)(info);
|
||||
|
||||
// Separate the output with an empty line
|
||||
eprintln!();
|
||||
}
|
||||
|
||||
// Print the ICE message
|
||||
report_ice(info, bug_report_url, extra_info);
|
||||
}));
|
||||
}
|
||||
|
||||
/// Prints the ICE message, including query stack, but without backtrace.
|
||||
///
|
||||
@ -1231,7 +1255,7 @@ static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send +
|
||||
///
|
||||
/// When `install_ice_hook` is called, this function will be called as the panic
|
||||
/// hook.
|
||||
pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
|
||||
pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: fn(&Handler)) {
|
||||
let fallback_bundle =
|
||||
rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
|
||||
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
|
||||
@ -1276,6 +1300,10 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
|
||||
|
||||
interface::try_print_query_stack(&handler, num_frames);
|
||||
|
||||
// We don't trust this callback not to panic itself, so run it at the end after we're sure we've
|
||||
// printed all the relevant info.
|
||||
extra_info(&handler);
|
||||
|
||||
#[cfg(windows)]
|
||||
if env::var("RUSTC_BREAK_ON_ICE").is_ok() {
|
||||
// Trigger a debugger if we crashed during bootstrap
|
||||
@ -1283,22 +1311,6 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Installs a panic hook that will print the ICE message on unexpected panics.
|
||||
///
|
||||
/// A custom rustc driver can skip calling this to set up a custom ICE hook.
|
||||
pub fn install_ice_hook() {
|
||||
// If the user has not explicitly overridden "RUST_BACKTRACE", then produce
|
||||
// full backtraces. When a compiler ICE happens, we want to gather
|
||||
// as much information as possible to present in the issue opened
|
||||
// by the user. Compiler developers and other rustc users can
|
||||
// opt in to less-verbose backtraces by manually setting "RUST_BACKTRACE"
|
||||
// (e.g. `RUST_BACKTRACE=1`)
|
||||
if std::env::var("RUST_BACKTRACE").is_err() {
|
||||
std::env::set_var("RUST_BACKTRACE", "full");
|
||||
}
|
||||
LazyLock::force(&DEFAULT_HOOK);
|
||||
}
|
||||
|
||||
/// This allows tools to enable rust logging without having to magically match rustc's
|
||||
/// tracing crate version.
|
||||
pub fn init_rustc_env_logger() {
|
||||
@ -1369,7 +1381,7 @@ pub fn main() -> ! {
|
||||
init_rustc_env_logger();
|
||||
signal_handler::install();
|
||||
let mut callbacks = TimePassesCallbacks::default();
|
||||
install_ice_hook();
|
||||
install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ());
|
||||
let exit_code = catch_with_exit_code(|| {
|
||||
let args = env::args_os()
|
||||
.enumerate()
|
||||
|
@ -25,8 +25,8 @@ block_on(future);
|
||||
|
||||
Specify desired lifetime of parameter `content` or indicate the anonymous
|
||||
lifetime like `content: Content<'_>`. The anonymous lifetime tells the Rust
|
||||
compiler that `content` is only needed until create function is done with
|
||||
it's execution.
|
||||
compiler that `content` is only needed until the `create` function is done with
|
||||
its execution.
|
||||
|
||||
The `implicit elision` meaning the omission of suggested lifetime that is
|
||||
`pub async fn create<'a>(content: Content<'a>) {}` is not allowed here as
|
||||
|
@ -192,6 +192,7 @@ impl EmissionGuarantee for ErrorGuaranteed {
|
||||
became non-error ({:?}), after original `.emit()`",
|
||||
db.inner.diagnostic.level,
|
||||
);
|
||||
#[allow(deprecated)]
|
||||
ErrorGuaranteed::unchecked_claim_error_was_emitted()
|
||||
}
|
||||
}
|
||||
|
@ -1069,26 +1069,29 @@ impl Handler {
|
||||
}
|
||||
|
||||
pub fn has_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow().has_errors().then(ErrorGuaranteed::unchecked_claim_error_was_emitted)
|
||||
self.inner.borrow().has_errors().then(|| {
|
||||
#[allow(deprecated)]
|
||||
ErrorGuaranteed::unchecked_claim_error_was_emitted()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn has_errors_or_lint_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner
|
||||
.borrow()
|
||||
.has_errors_or_lint_errors()
|
||||
.then(ErrorGuaranteed::unchecked_claim_error_was_emitted)
|
||||
self.inner.borrow().has_errors_or_lint_errors().then(|| {
|
||||
#[allow(deprecated)]
|
||||
ErrorGuaranteed::unchecked_claim_error_was_emitted()
|
||||
})
|
||||
}
|
||||
pub fn has_errors_or_delayed_span_bugs(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner
|
||||
.borrow()
|
||||
.has_errors_or_delayed_span_bugs()
|
||||
.then(ErrorGuaranteed::unchecked_claim_error_was_emitted)
|
||||
self.inner.borrow().has_errors_or_delayed_span_bugs().then(|| {
|
||||
#[allow(deprecated)]
|
||||
ErrorGuaranteed::unchecked_claim_error_was_emitted()
|
||||
})
|
||||
}
|
||||
pub fn is_compilation_going_to_fail(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner
|
||||
.borrow()
|
||||
.is_compilation_going_to_fail()
|
||||
.then(ErrorGuaranteed::unchecked_claim_error_was_emitted)
|
||||
self.inner.borrow().is_compilation_going_to_fail().then(|| {
|
||||
#[allow(deprecated)]
|
||||
ErrorGuaranteed::unchecked_claim_error_was_emitted()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn print_error_count(&self, registry: &Registry) {
|
||||
@ -1333,6 +1336,7 @@ impl HandlerInner {
|
||||
.push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace));
|
||||
|
||||
if !self.flags.report_delayed_bugs {
|
||||
#[allow(deprecated)]
|
||||
return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted());
|
||||
}
|
||||
}
|
||||
@ -1411,7 +1415,10 @@ impl HandlerInner {
|
||||
self.bump_err_count();
|
||||
}
|
||||
|
||||
guaranteed = Some(ErrorGuaranteed::unchecked_claim_error_was_emitted());
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
guaranteed = Some(ErrorGuaranteed::unchecked_claim_error_was_emitted());
|
||||
}
|
||||
} else {
|
||||
self.bump_warn_count();
|
||||
}
|
||||
|
@ -61,6 +61,8 @@ impl FromInternal<token::LitKind> for LitKind {
|
||||
token::StrRaw(n) => LitKind::StrRaw(n),
|
||||
token::ByteStr => LitKind::ByteStr,
|
||||
token::ByteStrRaw(n) => LitKind::ByteStrRaw(n),
|
||||
token::CStr => LitKind::CStr,
|
||||
token::CStrRaw(n) => LitKind::CStrRaw(n),
|
||||
token::Err => LitKind::Err,
|
||||
token::Bool => unreachable!(),
|
||||
}
|
||||
@ -78,6 +80,8 @@ impl ToInternal<token::LitKind> for LitKind {
|
||||
LitKind::StrRaw(n) => token::StrRaw(n),
|
||||
LitKind::ByteStr => token::ByteStr,
|
||||
LitKind::ByteStrRaw(n) => token::ByteStrRaw(n),
|
||||
LitKind::CStr => token::CStr,
|
||||
LitKind::CStrRaw(n) => token::CStrRaw(n),
|
||||
LitKind::Err => token::Err,
|
||||
}
|
||||
}
|
||||
@ -436,6 +440,8 @@ impl server::FreeFunctions for Rustc<'_, '_> {
|
||||
| token::LitKind::StrRaw(_)
|
||||
| token::LitKind::ByteStr
|
||||
| token::LitKind::ByteStrRaw(_)
|
||||
| token::LitKind::CStr
|
||||
| token::LitKind::CStrRaw(_)
|
||||
| token::LitKind::Err => return Err(()),
|
||||
token::LitKind::Integer | token::LitKind::Float => {}
|
||||
}
|
||||
|
@ -280,6 +280,8 @@ declare_features! (
|
||||
(accepted, pub_restricted, "1.18.0", Some(32409), None),
|
||||
/// Allows use of the postfix `?` operator in expressions.
|
||||
(accepted, question_mark, "1.13.0", Some(31436), None),
|
||||
/// Allows the use of raw-dylibs (RFC 2627).
|
||||
(accepted, raw_dylib, "CURRENT_RUSTC_VERSION", Some(58713), None),
|
||||
/// Allows keywords to be escaped for use as identifiers.
|
||||
(accepted, raw_identifiers, "1.30.0", Some(48589), None),
|
||||
/// Allows relaxing the coherence rules such that
|
||||
|
@ -313,6 +313,8 @@ declare_features! (
|
||||
(active, async_closure, "1.37.0", Some(62290), None),
|
||||
/// Allows async functions to be declared, implemented, and used in traits.
|
||||
(active, async_fn_in_trait, "1.66.0", Some(91611), None),
|
||||
/// Allows `c"foo"` literals.
|
||||
(active, c_str_literals, "CURRENT_RUSTC_VERSION", Some(105723), None),
|
||||
/// Treat `extern "C"` function as nounwind.
|
||||
(active, c_unwind, "1.52.0", Some(74990), None),
|
||||
/// Allows using C-variadics.
|
||||
@ -487,8 +489,6 @@ declare_features! (
|
||||
(active, precise_pointer_size_matching, "1.32.0", Some(56354), None),
|
||||
/// Allows macro attributes on expressions, statements and non-inline modules.
|
||||
(active, proc_macro_hygiene, "1.30.0", Some(54727), None),
|
||||
/// Allows the use of raw-dylibs (RFC 2627).
|
||||
(active, raw_dylib, "1.65.0", Some(58713), None),
|
||||
/// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions.
|
||||
(active, raw_ref_op, "1.41.0", Some(64490), None),
|
||||
/// Allows using the `#[register_tool]` attribute.
|
||||
|
@ -333,6 +333,7 @@ language_item_table! {
|
||||
RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
|
||||
|
||||
String, sym::String, string, Target::Struct, GenericRequirement::None;
|
||||
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
|
||||
}
|
||||
|
||||
pub enum GenericRequirement {
|
||||
|
@ -192,7 +192,11 @@ hir_analysis_return_type_notation_equality_bound =
|
||||
return type notation is not allowed to use type equality
|
||||
|
||||
hir_analysis_return_type_notation_missing_method =
|
||||
cannot find associated function `{$assoc_name}` in trait `{$trait_name}`
|
||||
cannot find associated function `{$assoc_name}` for `{$ty_name}`
|
||||
|
||||
hir_analysis_return_type_notation_conflicting_bound =
|
||||
ambiguous associated function `{$assoc_name}` for `{$ty_name}`
|
||||
.note = `{$assoc_name}` is declared in two supertraits: `{$first_bound}` and `{$second_bound}`
|
||||
|
||||
hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind}
|
||||
.label = not allowed in type signatures
|
||||
|
@ -1062,7 +1062,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
|
||||
/// Convert the bounds in `ast_bounds` that refer to traits which define an associated type
|
||||
/// named `assoc_name` into ty::Bounds. Ignore the rest.
|
||||
pub(crate) fn compute_bounds_that_match_assoc_type(
|
||||
pub(crate) fn compute_bounds_that_match_assoc_item(
|
||||
&self,
|
||||
param_ty: Ty<'tcx>,
|
||||
ast_bounds: &[hir::GenericBound<'_>],
|
||||
@ -1073,7 +1073,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
for ast_bound in ast_bounds {
|
||||
if let Some(trait_ref) = ast_bound.trait_ref()
|
||||
&& let Some(trait_did) = trait_ref.trait_def_id()
|
||||
&& self.tcx().trait_may_define_assoc_type(trait_did, assoc_name)
|
||||
&& self.tcx().trait_may_define_assoc_item(trait_did, assoc_name)
|
||||
{
|
||||
result.push(ast_bound.clone());
|
||||
}
|
||||
@ -1141,11 +1141,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
) {
|
||||
trait_ref
|
||||
} else {
|
||||
return Err(tcx.sess.emit_err(crate::errors::ReturnTypeNotationMissingMethod {
|
||||
span: binding.span,
|
||||
trait_name: tcx.item_name(trait_ref.def_id()),
|
||||
assoc_name: binding.item_name.name,
|
||||
}));
|
||||
self.one_bound_for_assoc_method(
|
||||
traits::supertraits(tcx, trait_ref),
|
||||
trait_ref.print_only_trait_path(),
|
||||
binding.item_name,
|
||||
path_span,
|
||||
)?
|
||||
}
|
||||
} else if self.trait_defines_associated_item_named(
|
||||
trait_ref.def_id(),
|
||||
@ -1946,7 +1947,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
let param_name = tcx.hir().ty_param_name(ty_param_def_id);
|
||||
self.one_bound_for_assoc_type(
|
||||
|| {
|
||||
traits::transitive_bounds_that_define_assoc_type(
|
||||
traits::transitive_bounds_that_define_assoc_item(
|
||||
tcx,
|
||||
predicates.iter().filter_map(|(p, _)| {
|
||||
Some(p.to_opt_poly_trait_pred()?.map_bound(|t| t.trait_ref))
|
||||
@ -2081,6 +2082,46 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
Ok(bound)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, all_candidates, ty_name), ret)]
|
||||
fn one_bound_for_assoc_method(
|
||||
&self,
|
||||
all_candidates: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
|
||||
ty_name: impl Display,
|
||||
assoc_name: Ident,
|
||||
span: Span,
|
||||
) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> {
|
||||
let mut matching_candidates = all_candidates.filter(|r| {
|
||||
self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Fn, assoc_name)
|
||||
});
|
||||
|
||||
let candidate = match matching_candidates.next() {
|
||||
Some(candidate) => candidate,
|
||||
None => {
|
||||
return Err(self.tcx().sess.emit_err(
|
||||
crate::errors::ReturnTypeNotationMissingMethod {
|
||||
span,
|
||||
ty_name: ty_name.to_string(),
|
||||
assoc_name: assoc_name.name,
|
||||
},
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(conflicting_candidate) = matching_candidates.next() {
|
||||
return Err(self.tcx().sess.emit_err(
|
||||
crate::errors::ReturnTypeNotationConflictingBound {
|
||||
span,
|
||||
ty_name: ty_name.to_string(),
|
||||
assoc_name: assoc_name.name,
|
||||
first_bound: candidate.print_only_trait_path(),
|
||||
second_bound: conflicting_candidate.print_only_trait_path(),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
Ok(candidate)
|
||||
}
|
||||
|
||||
// Create a type from a path to an associated type or to an enum variant.
|
||||
// For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C`
|
||||
// and item_segment is the path segment for `D`. We return a type and a def for
|
||||
|
@ -1,12 +1,14 @@
|
||||
// FIXME(@lcnr): Move this module out of `rustc_hir_analysis`.
|
||||
//
|
||||
// We don't do any drop checking during hir typeck.
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{struct_span_err, ErrorGuaranteed};
|
||||
use rustc_middle::ty::error::TypeError;
|
||||
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
|
||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
|
||||
use rustc_middle::ty::subst::SubstsRef;
|
||||
use rustc_middle::ty::util::IgnoreRegions;
|
||||
use rustc_middle::ty::{self, Predicate, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_trait_selection::traits::{self, ObligationCtxt};
|
||||
|
||||
use crate::errors;
|
||||
use crate::hir::def_id::{DefId, LocalDefId};
|
||||
@ -43,21 +45,20 @@ pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), Erro
|
||||
}
|
||||
}
|
||||
let dtor_self_type = tcx.type_of(drop_impl_did).subst_identity();
|
||||
let dtor_predicates = tcx.predicates_of(drop_impl_did);
|
||||
match dtor_self_type.kind() {
|
||||
ty::Adt(adt_def, self_to_impl_substs) => {
|
||||
ty::Adt(adt_def, adt_to_impl_substs) => {
|
||||
ensure_drop_params_and_item_params_correspond(
|
||||
tcx,
|
||||
drop_impl_did.expect_local(),
|
||||
adt_def.did(),
|
||||
self_to_impl_substs,
|
||||
adt_to_impl_substs,
|
||||
)?;
|
||||
|
||||
ensure_drop_predicates_are_implied_by_item_defn(
|
||||
tcx,
|
||||
dtor_predicates,
|
||||
drop_impl_did.expect_local(),
|
||||
adt_def.did().expect_local(),
|
||||
self_to_impl_substs,
|
||||
adt_to_impl_substs,
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
@ -78,9 +79,9 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
drop_impl_did: LocalDefId,
|
||||
self_type_did: DefId,
|
||||
drop_impl_substs: SubstsRef<'tcx>,
|
||||
adt_to_impl_substs: SubstsRef<'tcx>,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let Err(arg) = tcx.uses_unique_generic_params(drop_impl_substs, IgnoreRegions::No) else {
|
||||
let Err(arg) = tcx.uses_unique_generic_params(adt_to_impl_substs, IgnoreRegions::No) else {
|
||||
return Ok(())
|
||||
};
|
||||
|
||||
@ -111,237 +112,94 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>(
|
||||
/// implied by assuming the predicates attached to self_type_did.
|
||||
fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
dtor_predicates: ty::GenericPredicates<'tcx>,
|
||||
self_type_did: LocalDefId,
|
||||
self_to_impl_substs: SubstsRef<'tcx>,
|
||||
drop_impl_def_id: LocalDefId,
|
||||
adt_def_id: LocalDefId,
|
||||
adt_to_impl_substs: SubstsRef<'tcx>,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let mut result = Ok(());
|
||||
let infcx = tcx.infer_ctxt().build();
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
|
||||
// Here is an example, analogous to that from
|
||||
// `compare_impl_method`.
|
||||
// Take the param-env of the adt and substitute the substs that show up in
|
||||
// the implementation's self type. This gives us the assumptions that the
|
||||
// self ty of the implementation is allowed to know just from it being a
|
||||
// well-formed adt, since that's all we're allowed to assume while proving
|
||||
// the Drop implementation is not specialized.
|
||||
//
|
||||
// Consider a struct type:
|
||||
//
|
||||
// struct Type<'c, 'b:'c, 'a> {
|
||||
// x: &'a Contents // (contents are irrelevant;
|
||||
// y: &'c Cell<&'b Contents>, // only the bounds matter for our purposes.)
|
||||
// }
|
||||
//
|
||||
// and a Drop impl:
|
||||
//
|
||||
// impl<'z, 'y:'z, 'x:'y> Drop for P<'z, 'y, 'x> {
|
||||
// fn drop(&mut self) { self.y.set(self.x); } // (only legal if 'x: 'y)
|
||||
// }
|
||||
//
|
||||
// We start out with self_to_impl_substs, that maps the generic
|
||||
// parameters of Type to that of the Drop impl.
|
||||
//
|
||||
// self_to_impl_substs = {'c => 'z, 'b => 'y, 'a => 'x}
|
||||
//
|
||||
// Applying this to the predicates (i.e., assumptions) provided by the item
|
||||
// definition yields the instantiated assumptions:
|
||||
//
|
||||
// ['y : 'z]
|
||||
//
|
||||
// We then check all of the predicates of the Drop impl:
|
||||
//
|
||||
// ['y:'z, 'x:'y]
|
||||
//
|
||||
// and ensure each is in the list of instantiated
|
||||
// assumptions. Here, `'y:'z` is present, but `'x:'y` is
|
||||
// absent. So we report an error that the Drop impl injected a
|
||||
// predicate that is not present on the struct definition.
|
||||
// We don't need to normalize this param-env or anything, since we're only
|
||||
// substituting it with free params, so no additional param-env normalization
|
||||
// can occur on top of what has been done in the param_env query itself.
|
||||
let param_env = ty::EarlyBinder(tcx.param_env(adt_def_id))
|
||||
.subst(tcx, adt_to_impl_substs)
|
||||
.with_constness(tcx.constness(drop_impl_def_id));
|
||||
|
||||
// We can assume the predicates attached to struct/enum definition
|
||||
// hold.
|
||||
let generic_assumptions = tcx.predicates_of(self_type_did);
|
||||
for (pred, span) in tcx.predicates_of(drop_impl_def_id).instantiate_identity(tcx) {
|
||||
let normalize_cause = traits::ObligationCause::misc(span, adt_def_id);
|
||||
let pred = ocx.normalize(&normalize_cause, param_env, pred);
|
||||
let cause = traits::ObligationCause::new(span, adt_def_id, traits::DropImpl);
|
||||
ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, pred));
|
||||
}
|
||||
|
||||
let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs);
|
||||
let assumptions_in_impl_context = assumptions_in_impl_context.predicates;
|
||||
// All of the custom error reporting logic is to preserve parity with the old
|
||||
// error messages.
|
||||
//
|
||||
// They can probably get removed with better treatment of the new `DropImpl`
|
||||
// obligation cause code, and perhaps some custom logic in `report_region_errors`.
|
||||
|
||||
debug!(?assumptions_in_impl_context, ?dtor_predicates.predicates);
|
||||
|
||||
let self_param_env = tcx.param_env(self_type_did);
|
||||
|
||||
// An earlier version of this code attempted to do this checking
|
||||
// via the traits::fulfill machinery. However, it ran into trouble
|
||||
// since the fulfill machinery merely turns outlives-predicates
|
||||
// 'a:'b and T:'b into region inference constraints. It is simpler
|
||||
// just to look for all the predicates directly.
|
||||
|
||||
assert_eq!(dtor_predicates.parent, None);
|
||||
for &(predicate, predicate_sp) in dtor_predicates.predicates {
|
||||
// (We do not need to worry about deep analysis of type
|
||||
// expressions etc because the Drop impls are already forced
|
||||
// to take on a structure that is roughly an alpha-renaming of
|
||||
// the generic parameters of the item definition.)
|
||||
|
||||
// This path now just checks *all* predicates via an instantiation of
|
||||
// the `SimpleEqRelation`, which simply forwards to the `relate` machinery
|
||||
// after taking care of anonymizing late bound regions.
|
||||
//
|
||||
// However, it may be more efficient in the future to batch
|
||||
// the analysis together via the fulfill (see comment above regarding
|
||||
// the usage of the fulfill machinery), rather than the
|
||||
// repeated `.iter().any(..)` calls.
|
||||
|
||||
// This closure is a more robust way to check `Predicate` equality
|
||||
// than simple `==` checks (which were the previous implementation).
|
||||
// It relies on `ty::relate` for `TraitPredicate`, `ProjectionPredicate`,
|
||||
// `ConstEvaluatable` and `TypeOutlives` (which implement the Relate trait),
|
||||
// while delegating on simple equality for the other `Predicate`.
|
||||
// This implementation solves (Issue #59497) and (Issue #58311).
|
||||
// It is unclear to me at the moment whether the approach based on `relate`
|
||||
// could be extended easily also to the other `Predicate`.
|
||||
let predicate_matches_closure = |p: Predicate<'tcx>| {
|
||||
let mut relator: SimpleEqRelation<'tcx> = SimpleEqRelation::new(tcx, self_param_env);
|
||||
let predicate = predicate.kind();
|
||||
let p = p.kind();
|
||||
match (predicate.skip_binder(), p.skip_binder()) {
|
||||
(
|
||||
ty::PredicateKind::Clause(ty::Clause::Trait(a)),
|
||||
ty::PredicateKind::Clause(ty::Clause::Trait(b)),
|
||||
) => relator.relate(predicate.rebind(a), p.rebind(b)).is_ok(),
|
||||
(
|
||||
ty::PredicateKind::Clause(ty::Clause::Projection(a)),
|
||||
ty::PredicateKind::Clause(ty::Clause::Projection(b)),
|
||||
) => relator.relate(predicate.rebind(a), p.rebind(b)).is_ok(),
|
||||
(
|
||||
ty::PredicateKind::ConstEvaluatable(a),
|
||||
ty::PredicateKind::ConstEvaluatable(b),
|
||||
) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(),
|
||||
(
|
||||
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
|
||||
ty_a,
|
||||
lt_a,
|
||||
))),
|
||||
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
|
||||
ty_b,
|
||||
lt_b,
|
||||
))),
|
||||
) => {
|
||||
relator.relate(predicate.rebind(ty_a), p.rebind(ty_b)).is_ok()
|
||||
&& relator.relate(predicate.rebind(lt_a), p.rebind(lt_b)).is_ok()
|
||||
}
|
||||
(ty::PredicateKind::WellFormed(arg_a), ty::PredicateKind::WellFormed(arg_b)) => {
|
||||
relator.relate(predicate.rebind(arg_a), p.rebind(arg_b)).is_ok()
|
||||
}
|
||||
_ => predicate == p,
|
||||
let errors = ocx.select_all_or_error();
|
||||
if !errors.is_empty() {
|
||||
let mut guar = None;
|
||||
let mut root_predicates = FxHashSet::default();
|
||||
for error in errors {
|
||||
let root_predicate = error.root_obligation.predicate;
|
||||
if root_predicates.insert(root_predicate) {
|
||||
let item_span = tcx.def_span(adt_def_id);
|
||||
let self_descr = tcx.def_descr(adt_def_id.to_def_id());
|
||||
guar = Some(
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
error.root_obligation.cause.span,
|
||||
E0367,
|
||||
"`Drop` impl requires `{root_predicate}` \
|
||||
but the {self_descr} it is implemented for does not",
|
||||
)
|
||||
.span_note(item_span, "the implementor must specify the same requirement")
|
||||
.emit(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if !assumptions_in_impl_context.iter().copied().any(predicate_matches_closure) {
|
||||
let item_span = tcx.def_span(self_type_did);
|
||||
let self_descr = tcx.def_descr(self_type_did.to_def_id());
|
||||
let reported = struct_span_err!(
|
||||
tcx.sess,
|
||||
predicate_sp,
|
||||
E0367,
|
||||
"`Drop` impl requires `{predicate}` but the {self_descr} it is implemented for does not",
|
||||
)
|
||||
.span_note(item_span, "the implementor must specify the same requirement")
|
||||
.emit();
|
||||
result = Err(reported);
|
||||
}
|
||||
return Err(guar.unwrap());
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// This is an implementation of the [`TypeRelation`] trait with the
|
||||
/// aim of simply comparing for equality (without side-effects).
|
||||
///
|
||||
/// It is not intended to be used anywhere else other than here.
|
||||
pub(crate) struct SimpleEqRelation<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> SimpleEqRelation<'tcx> {
|
||||
fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> SimpleEqRelation<'tcx> {
|
||||
SimpleEqRelation { tcx, param_env }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeRelation<'tcx> for SimpleEqRelation<'tcx> {
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
self.param_env
|
||||
}
|
||||
|
||||
fn tag(&self) -> &'static str {
|
||||
"dropck::SimpleEqRelation"
|
||||
}
|
||||
|
||||
fn a_is_expected(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn relate_with_variance<T: Relate<'tcx>>(
|
||||
&mut self,
|
||||
_: ty::Variance,
|
||||
_info: ty::VarianceDiagInfo<'tcx>,
|
||||
a: T,
|
||||
b: T,
|
||||
) -> RelateResult<'tcx, T> {
|
||||
// Here we ignore variance because we require drop impl's types
|
||||
// to be *exactly* the same as to the ones in the struct definition.
|
||||
self.relate(a, b)
|
||||
}
|
||||
|
||||
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
|
||||
debug!("SimpleEqRelation::tys(a={:?}, b={:?})", a, b);
|
||||
ty::relate::super_relate_tys(self, a, b)
|
||||
}
|
||||
|
||||
fn regions(
|
||||
&mut self,
|
||||
a: ty::Region<'tcx>,
|
||||
b: ty::Region<'tcx>,
|
||||
) -> RelateResult<'tcx, ty::Region<'tcx>> {
|
||||
debug!("SimpleEqRelation::regions(a={:?}, b={:?})", a, b);
|
||||
|
||||
// We can just equate the regions because LBRs have been
|
||||
// already anonymized.
|
||||
if a == b {
|
||||
Ok(a)
|
||||
} else {
|
||||
// I'm not sure is this `TypeError` is the right one, but
|
||||
// it should not matter as it won't be checked (the dropck
|
||||
// will emit its own, more informative and higher-level errors
|
||||
// in case anything goes wrong).
|
||||
Err(TypeError::RegionsPlaceholderMismatch)
|
||||
let errors = ocx.infcx.resolve_regions(&OutlivesEnvironment::new(param_env));
|
||||
if !errors.is_empty() {
|
||||
let mut guar = None;
|
||||
for error in errors {
|
||||
let item_span = tcx.def_span(adt_def_id);
|
||||
let self_descr = tcx.def_descr(adt_def_id.to_def_id());
|
||||
let outlives = match error {
|
||||
RegionResolutionError::ConcreteFailure(_, a, b) => format!("{b}: {a}"),
|
||||
RegionResolutionError::GenericBoundFailure(_, generic, r) => {
|
||||
format!("{generic}: {r}")
|
||||
}
|
||||
RegionResolutionError::SubSupConflict(_, _, _, a, _, b, _) => format!("{b}: {a}"),
|
||||
RegionResolutionError::UpperBoundUniverseConflict(a, _, _, _, b) => {
|
||||
format!("{b}: {a}", a = tcx.mk_re_var(a))
|
||||
}
|
||||
};
|
||||
guar = Some(
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
error.origin().span(),
|
||||
E0367,
|
||||
"`Drop` impl requires `{outlives}` \
|
||||
but the {self_descr} it is implemented for does not",
|
||||
)
|
||||
.span_note(item_span, "the implementor must specify the same requirement")
|
||||
.emit(),
|
||||
);
|
||||
}
|
||||
return Err(guar.unwrap());
|
||||
}
|
||||
|
||||
fn consts(
|
||||
&mut self,
|
||||
a: ty::Const<'tcx>,
|
||||
b: ty::Const<'tcx>,
|
||||
) -> RelateResult<'tcx, ty::Const<'tcx>> {
|
||||
debug!("SimpleEqRelation::consts(a={:?}, b={:?})", a, b);
|
||||
ty::relate::super_relate_consts(self, a, b)
|
||||
}
|
||||
|
||||
fn binders<T>(
|
||||
&mut self,
|
||||
a: ty::Binder<'tcx, T>,
|
||||
b: ty::Binder<'tcx, T>,
|
||||
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
|
||||
where
|
||||
T: Relate<'tcx>,
|
||||
{
|
||||
debug!("SimpleEqRelation::binders({:?}: {:?}", a, b);
|
||||
|
||||
// Anonymizing the LBRs is necessary to solve (Issue #59497).
|
||||
// After we do so, it should be totally fine to skip the binders.
|
||||
let anon_a = self.tcx.anonymize_bound_vars(a);
|
||||
let anon_b = self.tcx.anonymize_bound_vars(b);
|
||||
self.relate(anon_a.skip_binder(), anon_b.skip_binder())?;
|
||||
|
||||
Ok(a)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -64,8 +64,8 @@ pub fn provide(providers: &mut Providers) {
|
||||
explicit_predicates_of: predicates_of::explicit_predicates_of,
|
||||
super_predicates_of: predicates_of::super_predicates_of,
|
||||
implied_predicates_of: predicates_of::implied_predicates_of,
|
||||
super_predicates_that_define_assoc_type:
|
||||
predicates_of::super_predicates_that_define_assoc_type,
|
||||
super_predicates_that_define_assoc_item:
|
||||
predicates_of::super_predicates_that_define_assoc_item,
|
||||
trait_explicit_predicates_and_bounds: predicates_of::trait_explicit_predicates_and_bounds,
|
||||
type_param_predicates: predicates_of::type_param_predicates,
|
||||
trait_def,
|
||||
|
@ -565,7 +565,7 @@ pub(super) fn super_predicates_of(
|
||||
implied_predicates_with_filter(tcx, trait_def_id.to_def_id(), PredicateFilter::SelfOnly)
|
||||
}
|
||||
|
||||
pub(super) fn super_predicates_that_define_assoc_type(
|
||||
pub(super) fn super_predicates_that_define_assoc_item(
|
||||
tcx: TyCtxt<'_>,
|
||||
(trait_def_id, assoc_name): (DefId, Ident),
|
||||
) -> ty::GenericPredicates<'_> {
|
||||
@ -640,7 +640,7 @@ pub(super) fn implied_predicates_with_filter(
|
||||
),
|
||||
PredicateFilter::SelfThatDefines(assoc_name) => (
|
||||
// Convert the bounds that follow the colon (or equal) that reference the associated name
|
||||
icx.astconv().compute_bounds_that_match_assoc_type(self_param_ty, bounds, assoc_name),
|
||||
icx.astconv().compute_bounds_that_match_assoc_item(self_param_ty, bounds, assoc_name),
|
||||
// Include where clause bounds for `Self` that reference the associated name
|
||||
icx.type_parameter_bounds_in_generics(
|
||||
generics,
|
||||
@ -819,7 +819,7 @@ impl<'tcx> ItemCtxt<'tcx> {
|
||||
hir::GenericBound::Trait(poly_trait_ref, _) => {
|
||||
let trait_ref = &poly_trait_ref.trait_ref;
|
||||
if let Some(trait_did) = trait_ref.trait_def_id() {
|
||||
self.tcx.trait_may_define_assoc_type(trait_did, assoc_name)
|
||||
self.tcx.trait_may_define_assoc_item(trait_did, assoc_name)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -1652,17 +1652,16 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
||||
if binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation {
|
||||
let bound_vars = if let Some(type_def_id) = type_def_id
|
||||
&& self.tcx.def_kind(type_def_id) == DefKind::Trait
|
||||
// FIXME(return_type_notation): We could bound supertrait methods.
|
||||
&& let Some(assoc_fn) = self
|
||||
.tcx
|
||||
.associated_items(type_def_id)
|
||||
.find_by_name_and_kind(self.tcx, binding.ident, ty::AssocKind::Fn, type_def_id)
|
||||
&& let Some((mut bound_vars, assoc_fn)) =
|
||||
BoundVarContext::supertrait_hrtb_vars(
|
||||
self.tcx,
|
||||
type_def_id,
|
||||
binding.ident,
|
||||
ty::AssocKind::Fn,
|
||||
)
|
||||
{
|
||||
self.tcx
|
||||
.generics_of(assoc_fn.def_id)
|
||||
.params
|
||||
.iter()
|
||||
.map(|param| match param.kind {
|
||||
bound_vars.extend(self.tcx.generics_of(assoc_fn.def_id).params.iter().map(
|
||||
|param| match param.kind {
|
||||
ty::GenericParamDefKind::Lifetime => ty::BoundVariableKind::Region(
|
||||
ty::BoundRegionKind::BrNamed(param.def_id, param.name),
|
||||
),
|
||||
@ -1670,9 +1669,11 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
||||
ty::BoundTyKind::Param(param.def_id, param.name),
|
||||
),
|
||||
ty::GenericParamDefKind::Const { .. } => ty::BoundVariableKind::Const,
|
||||
})
|
||||
.chain(self.tcx.fn_sig(assoc_fn.def_id).subst_identity().bound_vars())
|
||||
.collect()
|
||||
},
|
||||
));
|
||||
bound_vars
|
||||
.extend(self.tcx.fn_sig(assoc_fn.def_id).subst_identity().bound_vars());
|
||||
bound_vars
|
||||
} else {
|
||||
self.tcx.sess.delay_span_bug(
|
||||
binding.ident.span,
|
||||
@ -1689,8 +1690,13 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
||||
});
|
||||
});
|
||||
} else if let Some(type_def_id) = type_def_id {
|
||||
let bound_vars =
|
||||
BoundVarContext::supertrait_hrtb_vars(self.tcx, type_def_id, binding.ident);
|
||||
let bound_vars = BoundVarContext::supertrait_hrtb_vars(
|
||||
self.tcx,
|
||||
type_def_id,
|
||||
binding.ident,
|
||||
ty::AssocKind::Type,
|
||||
)
|
||||
.map(|(bound_vars, _)| bound_vars);
|
||||
self.with(scope, |this| {
|
||||
let scope = Scope::Supertrait {
|
||||
bound_vars: bound_vars.unwrap_or_default(),
|
||||
@ -1720,11 +1726,15 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
assoc_name: Ident,
|
||||
) -> Option<Vec<ty::BoundVariableKind>> {
|
||||
let trait_defines_associated_type_named = |trait_def_id: DefId| {
|
||||
tcx.associated_items(trait_def_id)
|
||||
.find_by_name_and_kind(tcx, assoc_name, ty::AssocKind::Type, trait_def_id)
|
||||
.is_some()
|
||||
assoc_kind: ty::AssocKind,
|
||||
) -> Option<(Vec<ty::BoundVariableKind>, &'tcx ty::AssocItem)> {
|
||||
let trait_defines_associated_item_named = |trait_def_id: DefId| {
|
||||
tcx.associated_items(trait_def_id).find_by_name_and_kind(
|
||||
tcx,
|
||||
assoc_name,
|
||||
assoc_kind,
|
||||
trait_def_id,
|
||||
)
|
||||
};
|
||||
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
@ -1742,10 +1752,10 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
||||
_ => break None,
|
||||
}
|
||||
|
||||
if trait_defines_associated_type_named(def_id) {
|
||||
break Some(bound_vars.into_iter().collect());
|
||||
if let Some(assoc_item) = trait_defines_associated_item_named(def_id) {
|
||||
break Some((bound_vars.into_iter().collect(), assoc_item));
|
||||
}
|
||||
let predicates = tcx.super_predicates_that_define_assoc_type((def_id, assoc_name));
|
||||
let predicates = tcx.super_predicates_that_define_assoc_item((def_id, assoc_name));
|
||||
let obligations = predicates.predicates.iter().filter_map(|&(pred, _)| {
|
||||
let bound_predicate = pred.kind();
|
||||
match bound_predicate.skip_binder() {
|
||||
|
@ -6,7 +6,7 @@ use rustc_errors::{
|
||||
MultiSpan,
|
||||
};
|
||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::{self, print::TraitRefPrintOnlyTraitPath, Ty};
|
||||
use rustc_span::{symbol::Ident, Span, Symbol};
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
@ -512,10 +512,22 @@ pub(crate) struct ReturnTypeNotationEqualityBound {
|
||||
pub(crate) struct ReturnTypeNotationMissingMethod {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub trait_name: Symbol,
|
||||
pub ty_name: String,
|
||||
pub assoc_name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_return_type_notation_conflicting_bound)]
|
||||
#[note]
|
||||
pub(crate) struct ReturnTypeNotationConflictingBound<'tcx> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub ty_name: String,
|
||||
pub assoc_name: Symbol,
|
||||
pub first_bound: ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
|
||||
pub second_bound: ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_placeholder_not_allowed_item_signatures, code = "E0121")]
|
||||
pub(crate) struct PlaceholderNotAllowedItemSignatures {
|
||||
@ -657,7 +669,6 @@ pub enum ImplNotMarkedDefault {
|
||||
#[note]
|
||||
Err {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
span: Span,
|
||||
cname: Symbol,
|
||||
ident: Symbol,
|
||||
|
@ -854,9 +854,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let result = self
|
||||
.resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id)
|
||||
.or_else(|error| {
|
||||
let guar = self
|
||||
.tcx
|
||||
.sess
|
||||
.delay_span_bug(span, "method resolution should've emitted an error");
|
||||
let result = match error {
|
||||
method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)),
|
||||
_ => Err(ErrorGuaranteed::unchecked_claim_error_was_emitted()),
|
||||
_ => Err(guar),
|
||||
};
|
||||
|
||||
// If we have a path like `MyTrait::missing_method`, then don't register
|
||||
|
@ -1300,6 +1300,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
opt_ty.unwrap_or_else(|| self.next_float_var())
|
||||
}
|
||||
ast::LitKind::Bool(_) => tcx.types.bool,
|
||||
ast::LitKind::CStr(_, _) => tcx.mk_imm_ref(
|
||||
tcx.lifetimes.re_static,
|
||||
tcx.type_of(tcx.require_lang_item(hir::LangItem::CStr, Some(lit.span)))
|
||||
.skip_binder(),
|
||||
),
|
||||
ast::LitKind::Err => tcx.ty_error_misc(),
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,8 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
R: ObligationEmittingRelation<'tcx>,
|
||||
{
|
||||
let a_is_expected = relation.a_is_expected();
|
||||
debug_assert!(!a.has_escaping_bound_vars());
|
||||
debug_assert!(!b.has_escaping_bound_vars());
|
||||
|
||||
match (a.kind(), b.kind()) {
|
||||
// Relate integral variables to other types
|
||||
@ -163,6 +165,8 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
R: ObligationEmittingRelation<'tcx>,
|
||||
{
|
||||
debug!("{}.consts({:?}, {:?})", relation.tag(), a, b);
|
||||
debug_assert!(!a.has_escaping_bound_vars());
|
||||
debug_assert!(!b.has_escaping_bound_vars());
|
||||
if a == b {
|
||||
return Ok(a);
|
||||
}
|
||||
@ -238,22 +242,12 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
(_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
|
||||
return self.unify_const_variable(vid, a);
|
||||
}
|
||||
(ty::ConstKind::Unevaluated(..), _) if self.tcx.lazy_normalization() => {
|
||||
// FIXME(#59490): Need to remove the leak check to accommodate
|
||||
// escaping bound variables here.
|
||||
if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() {
|
||||
relation.register_const_equate_obligation(a, b);
|
||||
}
|
||||
(ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
|
||||
if self.tcx.lazy_normalization() =>
|
||||
{
|
||||
relation.register_const_equate_obligation(a, b);
|
||||
return Ok(b);
|
||||
}
|
||||
(_, ty::ConstKind::Unevaluated(..)) if self.tcx.lazy_normalization() => {
|
||||
// FIXME(#59490): Need to remove the leak check to accommodate
|
||||
// escaping bound variables here.
|
||||
if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() {
|
||||
relation.register_const_equate_obligation(a, b);
|
||||
}
|
||||
return Ok(a);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
@ -102,6 +102,17 @@ pub enum RegionResolutionError<'tcx> {
|
||||
),
|
||||
}
|
||||
|
||||
impl<'tcx> RegionResolutionError<'tcx> {
|
||||
pub fn origin(&self) -> &SubregionOrigin<'tcx> {
|
||||
match self {
|
||||
RegionResolutionError::ConcreteFailure(origin, _, _)
|
||||
| RegionResolutionError::GenericBoundFailure(origin, _, _)
|
||||
| RegionResolutionError::SubSupConflict(_, _, origin, _, _, _, _)
|
||||
| RegionResolutionError::UpperBoundUniverseConflict(_, _, _, origin, _) => origin,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RegionAndOrigin<'tcx> {
|
||||
region: Region<'tcx>,
|
||||
origin: SubregionOrigin<'tcx>,
|
||||
|
@ -380,11 +380,11 @@ pub fn transitive_bounds<'tcx>(
|
||||
}
|
||||
|
||||
/// A specialized variant of `elaborate` that only elaborates trait references that may
|
||||
/// define the given associated type `assoc_name`. It uses the
|
||||
/// `super_predicates_that_define_assoc_type` query to avoid enumerating super-predicates that
|
||||
/// define the given associated item with the name `assoc_name`. It uses the
|
||||
/// `super_predicates_that_define_assoc_item` query to avoid enumerating super-predicates that
|
||||
/// aren't related to `assoc_item`. This is used when resolving types like `Self::Item` or
|
||||
/// `T::Item` and helps to avoid cycle errors (see e.g. #35237).
|
||||
pub fn transitive_bounds_that_define_assoc_type<'tcx>(
|
||||
pub fn transitive_bounds_that_define_assoc_item<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
bounds: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
|
||||
assoc_name: Ident,
|
||||
@ -397,7 +397,7 @@ pub fn transitive_bounds_that_define_assoc_type<'tcx>(
|
||||
let anon_trait_ref = tcx.anonymize_bound_vars(trait_ref);
|
||||
if visited.insert(anon_trait_ref) {
|
||||
let super_predicates =
|
||||
tcx.super_predicates_that_define_assoc_type((trait_ref.def_id(), assoc_name));
|
||||
tcx.super_predicates_that_define_assoc_item((trait_ref.def_id(), assoc_name));
|
||||
for (super_predicate, _) in super_predicates.predicates {
|
||||
let subst_predicate = super_predicate.subst_supertrait(tcx, &trait_ref);
|
||||
if let Some(binder) = subst_predicate.to_opt_poly_trait_pred() {
|
||||
|
@ -9,11 +9,12 @@ use rustc_data_structures::OnDrop;
|
||||
use rustc_errors::registry::Registry;
|
||||
use rustc_errors::{ErrorGuaranteed, Handler};
|
||||
use rustc_lint::LintStore;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::{bug, ty};
|
||||
use rustc_parse::maybe_new_parser_from_source_str;
|
||||
use rustc_query_impl::QueryCtxt;
|
||||
use rustc_query_system::query::print_query_stack;
|
||||
use rustc_session::config::{self, CheckCfg, ErrorOutputType, Input, OutputFilenames};
|
||||
use rustc_session::config::{self, ErrorOutputType, Input, OutputFilenames};
|
||||
use rustc_session::config::{CheckCfg, ExpectedValues};
|
||||
use rustc_session::lint;
|
||||
use rustc_session::parse::{CrateConfig, ParseSess};
|
||||
use rustc_session::Session;
|
||||
@ -121,9 +122,9 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String
|
||||
/// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`.
|
||||
pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
|
||||
rustc_span::create_default_session_if_not_set_then(move |_| {
|
||||
let mut cfg = CheckCfg::default();
|
||||
let mut check_cfg = CheckCfg::default();
|
||||
|
||||
'specs: for s in specs {
|
||||
for s in specs {
|
||||
let sess = ParseSess::with_silent_emitter(Some(format!(
|
||||
"this error occurred on the command line: `--check-cfg={s}`"
|
||||
)));
|
||||
@ -137,40 +138,54 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
|
||||
concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"),
|
||||
s
|
||||
),
|
||||
);
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
let expected_error = || {
|
||||
error!(
|
||||
"expected `names(name1, name2, ... nameN)` or \
|
||||
`values(name, \"value1\", \"value2\", ... \"valueN\")`"
|
||||
)
|
||||
};
|
||||
|
||||
match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) {
|
||||
Ok(mut parser) => match parser.parse_meta_item() {
|
||||
Ok(meta_item) if parser.token == token::Eof => {
|
||||
if let Some(args) = meta_item.meta_item_list() {
|
||||
if meta_item.has_name(sym::names) {
|
||||
let names_valid =
|
||||
cfg.names_valid.get_or_insert_with(|| FxHashSet::default());
|
||||
check_cfg.exhaustive_names = true;
|
||||
for arg in args {
|
||||
if arg.is_word() && arg.ident().is_some() {
|
||||
let ident = arg.ident().expect("multi-segment cfg key");
|
||||
names_valid.insert(ident.name.to_string());
|
||||
check_cfg
|
||||
.expecteds
|
||||
.entry(ident.name.to_string())
|
||||
.or_insert(ExpectedValues::Any);
|
||||
} else {
|
||||
error!("`names()` arguments must be simple identifiers");
|
||||
}
|
||||
}
|
||||
continue 'specs;
|
||||
} else if meta_item.has_name(sym::values) {
|
||||
if let Some((name, values)) = args.split_first() {
|
||||
if name.is_word() && name.ident().is_some() {
|
||||
let ident = name.ident().expect("multi-segment cfg key");
|
||||
let ident_values = cfg
|
||||
.values_valid
|
||||
let expected_values = check_cfg
|
||||
.expecteds
|
||||
.entry(ident.name.to_string())
|
||||
.or_insert_with(|| FxHashSet::default());
|
||||
.or_insert_with(|| {
|
||||
ExpectedValues::Some(FxHashSet::default())
|
||||
});
|
||||
|
||||
let ExpectedValues::Some(expected_values) = expected_values else {
|
||||
bug!("shoudn't be possible")
|
||||
};
|
||||
|
||||
for val in values {
|
||||
if let Some(LitKind::Str(s, _)) =
|
||||
val.lit().map(|lit| &lit.kind)
|
||||
{
|
||||
ident_values.insert(s.to_string());
|
||||
expected_values.insert(Some(s.to_string()));
|
||||
} else {
|
||||
error!(
|
||||
"`values()` arguments must be string literals"
|
||||
@ -178,35 +193,40 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
|
||||
}
|
||||
}
|
||||
|
||||
continue 'specs;
|
||||
if values.is_empty() {
|
||||
expected_values.insert(None);
|
||||
}
|
||||
} else {
|
||||
error!(
|
||||
"`values()` first argument must be a simple identifier"
|
||||
);
|
||||
}
|
||||
} else if args.is_empty() {
|
||||
cfg.well_known_values = true;
|
||||
continue 'specs;
|
||||
check_cfg.exhaustive_values = true;
|
||||
} else {
|
||||
expected_error();
|
||||
}
|
||||
} else {
|
||||
expected_error();
|
||||
}
|
||||
} else {
|
||||
expected_error();
|
||||
}
|
||||
}
|
||||
Ok(..) => {}
|
||||
Err(err) => err.cancel(),
|
||||
Ok(..) => expected_error(),
|
||||
Err(err) => {
|
||||
err.cancel();
|
||||
expected_error();
|
||||
}
|
||||
},
|
||||
Err(errs) => drop(errs),
|
||||
Err(errs) => {
|
||||
drop(errs);
|
||||
expected_error();
|
||||
}
|
||||
}
|
||||
|
||||
error!(
|
||||
"expected `names(name1, name2, ... nameN)` or \
|
||||
`values(name, \"value1\", \"value2\", ... \"valueN\")`"
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(names_valid) = &mut cfg.names_valid {
|
||||
names_valid.extend(cfg.values_valid.keys().cloned());
|
||||
}
|
||||
cfg
|
||||
check_cfg
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,7 @@ where
|
||||
is_private_dep: false,
|
||||
add_prelude: true,
|
||||
nounused_dep: false,
|
||||
force: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -547,6 +548,7 @@ fn test_codegen_options_tracking_hash() {
|
||||
untracked!(ar, String::from("abc"));
|
||||
untracked!(codegen_units, Some(42));
|
||||
untracked!(default_linker_libraries, true);
|
||||
untracked!(dlltool, Some(PathBuf::from("custom_dlltool.exe")));
|
||||
untracked!(extra_filename, String::from("extra-filename"));
|
||||
untracked!(incremental, Some(String::from("abc")));
|
||||
// `link_arg` is omitted because it just forwards to `link_args`.
|
||||
@ -651,7 +653,6 @@ fn test_unstable_options_tracking_hash() {
|
||||
untracked!(assert_incr_state, Some(String::from("loaded")));
|
||||
untracked!(deduplicate_diagnostics, false);
|
||||
untracked!(dep_tasks, true);
|
||||
untracked!(dlltool, Some(PathBuf::from("custom_dlltool.exe")));
|
||||
untracked!(dont_buffer_diagnostics, true);
|
||||
untracked!(dump_dep_graph, true);
|
||||
untracked!(dump_drop_tracking_cfg, Some("cfg.dot".to_string()));
|
||||
|
@ -186,12 +186,16 @@ pub enum LiteralKind {
|
||||
Str { terminated: bool },
|
||||
/// "b"abc"", "b"abc"
|
||||
ByteStr { terminated: bool },
|
||||
/// `c"abc"`, `c"abc`
|
||||
CStr { terminated: bool },
|
||||
/// "r"abc"", "r#"abc"#", "r####"ab"###"c"####", "r#"a". `None` indicates
|
||||
/// an invalid literal.
|
||||
RawStr { n_hashes: Option<u8> },
|
||||
/// "br"abc"", "br#"abc"#", "br####"ab"###"c"####", "br#"a". `None`
|
||||
/// indicates an invalid literal.
|
||||
RawByteStr { n_hashes: Option<u8> },
|
||||
/// `cr"abc"`, "cr#"abc"#", `cr#"a`. `None` indicates an invalid literal.
|
||||
RawCStr { n_hashes: Option<u8> },
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
@ -357,39 +361,18 @@ impl Cursor<'_> {
|
||||
},
|
||||
|
||||
// Byte literal, byte string literal, raw byte string literal or identifier.
|
||||
'b' => match (self.first(), self.second()) {
|
||||
('\'', _) => {
|
||||
self.bump();
|
||||
let terminated = self.single_quoted_string();
|
||||
let suffix_start = self.pos_within_token();
|
||||
if terminated {
|
||||
self.eat_literal_suffix();
|
||||
}
|
||||
let kind = Byte { terminated };
|
||||
Literal { kind, suffix_start }
|
||||
}
|
||||
('"', _) => {
|
||||
self.bump();
|
||||
let terminated = self.double_quoted_string();
|
||||
let suffix_start = self.pos_within_token();
|
||||
if terminated {
|
||||
self.eat_literal_suffix();
|
||||
}
|
||||
let kind = ByteStr { terminated };
|
||||
Literal { kind, suffix_start }
|
||||
}
|
||||
('r', '"') | ('r', '#') => {
|
||||
self.bump();
|
||||
let res = self.raw_double_quoted_string(2);
|
||||
let suffix_start = self.pos_within_token();
|
||||
if res.is_ok() {
|
||||
self.eat_literal_suffix();
|
||||
}
|
||||
let kind = RawByteStr { n_hashes: res.ok() };
|
||||
Literal { kind, suffix_start }
|
||||
}
|
||||
_ => self.ident_or_unknown_prefix(),
|
||||
},
|
||||
'b' => self.c_or_byte_string(
|
||||
|terminated| ByteStr { terminated },
|
||||
|n_hashes| RawByteStr { n_hashes },
|
||||
Some(|terminated| Byte { terminated }),
|
||||
),
|
||||
|
||||
// c-string literal, raw c-string literal or identifier.
|
||||
'c' => self.c_or_byte_string(
|
||||
|terminated| CStr { terminated },
|
||||
|n_hashes| RawCStr { n_hashes },
|
||||
None,
|
||||
),
|
||||
|
||||
// Identifier (this should be checked after other variant that can
|
||||
// start as identifier).
|
||||
@ -553,6 +536,47 @@ impl Cursor<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn c_or_byte_string(
|
||||
&mut self,
|
||||
mk_kind: impl FnOnce(bool) -> LiteralKind,
|
||||
mk_kind_raw: impl FnOnce(Option<u8>) -> LiteralKind,
|
||||
single_quoted: Option<fn(bool) -> LiteralKind>,
|
||||
) -> TokenKind {
|
||||
match (self.first(), self.second(), single_quoted) {
|
||||
('\'', _, Some(mk_kind)) => {
|
||||
self.bump();
|
||||
let terminated = self.single_quoted_string();
|
||||
let suffix_start = self.pos_within_token();
|
||||
if terminated {
|
||||
self.eat_literal_suffix();
|
||||
}
|
||||
let kind = mk_kind(terminated);
|
||||
Literal { kind, suffix_start }
|
||||
}
|
||||
('"', _, _) => {
|
||||
self.bump();
|
||||
let terminated = self.double_quoted_string();
|
||||
let suffix_start = self.pos_within_token();
|
||||
if terminated {
|
||||
self.eat_literal_suffix();
|
||||
}
|
||||
let kind = mk_kind(terminated);
|
||||
Literal { kind, suffix_start }
|
||||
}
|
||||
('r', '"', _) | ('r', '#', _) => {
|
||||
self.bump();
|
||||
let res = self.raw_double_quoted_string(2);
|
||||
let suffix_start = self.pos_within_token();
|
||||
if res.is_ok() {
|
||||
self.eat_literal_suffix();
|
||||
}
|
||||
let kind = mk_kind_raw(res.ok());
|
||||
Literal { kind, suffix_start }
|
||||
}
|
||||
_ => self.ident_or_unknown_prefix(),
|
||||
}
|
||||
}
|
||||
|
||||
fn number(&mut self, first_digit: char) -> LiteralKind {
|
||||
debug_assert!('0' <= self.prev() && self.prev() <= '9');
|
||||
let mut base = Base::Decimal;
|
||||
|
@ -86,10 +86,45 @@ where
|
||||
let res = unescape_char_or_byte(&mut chars, mode == Mode::Byte);
|
||||
callback(0..(src.len() - chars.as_str().len()), res);
|
||||
}
|
||||
Mode::Str | Mode::ByteStr => unescape_str_or_byte_str(src, mode == Mode::ByteStr, callback),
|
||||
Mode::Str | Mode::ByteStr => unescape_str_common(src, mode, callback),
|
||||
|
||||
Mode::RawStr | Mode::RawByteStr => {
|
||||
unescape_raw_str_or_raw_byte_str(src, mode == Mode::RawByteStr, callback)
|
||||
}
|
||||
Mode::CStr | Mode::RawCStr => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A unit within CStr. Must not be a nul character.
|
||||
pub enum CStrUnit {
|
||||
Byte(u8),
|
||||
Char(char),
|
||||
}
|
||||
|
||||
impl From<u8> for CStrUnit {
|
||||
fn from(value: u8) -> Self {
|
||||
CStrUnit::Byte(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<char> for CStrUnit {
|
||||
fn from(value: char) -> Self {
|
||||
CStrUnit::Char(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unescape_c_string<F>(src: &str, mode: Mode, callback: &mut F)
|
||||
where
|
||||
F: FnMut(Range<usize>, Result<CStrUnit, EscapeError>),
|
||||
{
|
||||
if mode == Mode::RawCStr {
|
||||
unescape_raw_str_or_raw_byte_str(
|
||||
src,
|
||||
mode.characters_should_be_ascii(),
|
||||
&mut |r, result| callback(r, result.map(CStrUnit::Char)),
|
||||
);
|
||||
} else {
|
||||
unescape_str_common(src, mode, callback);
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,34 +149,69 @@ pub enum Mode {
|
||||
ByteStr,
|
||||
RawStr,
|
||||
RawByteStr,
|
||||
CStr,
|
||||
RawCStr,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
pub fn in_double_quotes(self) -> bool {
|
||||
match self {
|
||||
Mode::Str | Mode::ByteStr | Mode::RawStr | Mode::RawByteStr => true,
|
||||
Mode::Str
|
||||
| Mode::ByteStr
|
||||
| Mode::RawStr
|
||||
| Mode::RawByteStr
|
||||
| Mode::CStr
|
||||
| Mode::RawCStr => true,
|
||||
Mode::Char | Mode::Byte => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_byte(self) -> bool {
|
||||
/// Non-byte literals should have `\xXX` escapes that are within the ASCII range.
|
||||
pub fn ascii_escapes_should_be_ascii(self) -> bool {
|
||||
match self {
|
||||
Mode::Char | Mode::Str | Mode::RawStr => true,
|
||||
Mode::Byte | Mode::ByteStr | Mode::RawByteStr | Mode::CStr | Mode::RawCStr => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether characters within the literal must be within the ASCII range
|
||||
pub fn characters_should_be_ascii(self) -> bool {
|
||||
match self {
|
||||
Mode::Byte | Mode::ByteStr | Mode::RawByteStr => true,
|
||||
Mode::Char | Mode::Str | Mode::RawStr => false,
|
||||
Mode::Char | Mode::Str | Mode::RawStr | Mode::CStr | Mode::RawCStr => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Byte literals do not allow unicode escape.
|
||||
pub fn is_unicode_escape_disallowed(self) -> bool {
|
||||
match self {
|
||||
Mode::Byte | Mode::ByteStr | Mode::RawByteStr => true,
|
||||
Mode::Char | Mode::Str | Mode::RawStr | Mode::CStr | Mode::RawCStr => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prefix_noraw(self) -> &'static str {
|
||||
match self {
|
||||
Mode::Byte | Mode::ByteStr | Mode::RawByteStr => "b",
|
||||
Mode::CStr | Mode::RawCStr => "c",
|
||||
Mode::Char | Mode::Str | Mode::RawStr => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn scan_escape(chars: &mut Chars<'_>, is_byte: bool) -> Result<char, EscapeError> {
|
||||
fn scan_escape<T: From<u8> + From<char>>(
|
||||
chars: &mut Chars<'_>,
|
||||
mode: Mode,
|
||||
) -> Result<T, EscapeError> {
|
||||
// Previous character was '\\', unescape what follows.
|
||||
let res = match chars.next().ok_or(EscapeError::LoneSlash)? {
|
||||
'"' => '"',
|
||||
'n' => '\n',
|
||||
'r' => '\r',
|
||||
't' => '\t',
|
||||
'\\' => '\\',
|
||||
'\'' => '\'',
|
||||
'0' => '\0',
|
||||
'"' => b'"',
|
||||
'n' => b'\n',
|
||||
'r' => b'\r',
|
||||
't' => b'\t',
|
||||
'\\' => b'\\',
|
||||
'\'' => b'\'',
|
||||
'0' => b'\0',
|
||||
|
||||
'x' => {
|
||||
// Parse hexadecimal character code.
|
||||
@ -154,76 +224,78 @@ fn scan_escape(chars: &mut Chars<'_>, is_byte: bool) -> Result<char, EscapeError
|
||||
|
||||
let value = hi * 16 + lo;
|
||||
|
||||
// For a non-byte literal verify that it is within ASCII range.
|
||||
if !is_byte && !is_ascii(value) {
|
||||
if mode.ascii_escapes_should_be_ascii() && !is_ascii(value) {
|
||||
return Err(EscapeError::OutOfRangeHexEscape);
|
||||
}
|
||||
let value = value as u8;
|
||||
|
||||
value as char
|
||||
value as u8
|
||||
}
|
||||
|
||||
'u' => {
|
||||
// We've parsed '\u', now we have to parse '{..}'.
|
||||
|
||||
if chars.next() != Some('{') {
|
||||
return Err(EscapeError::NoBraceInUnicodeEscape);
|
||||
}
|
||||
|
||||
// First character must be a hexadecimal digit.
|
||||
let mut n_digits = 1;
|
||||
let mut value: u32 = match chars.next().ok_or(EscapeError::UnclosedUnicodeEscape)? {
|
||||
'_' => return Err(EscapeError::LeadingUnderscoreUnicodeEscape),
|
||||
'}' => return Err(EscapeError::EmptyUnicodeEscape),
|
||||
c => c.to_digit(16).ok_or(EscapeError::InvalidCharInUnicodeEscape)?,
|
||||
};
|
||||
|
||||
// First character is valid, now parse the rest of the number
|
||||
// and closing brace.
|
||||
loop {
|
||||
match chars.next() {
|
||||
None => return Err(EscapeError::UnclosedUnicodeEscape),
|
||||
Some('_') => continue,
|
||||
Some('}') => {
|
||||
if n_digits > 6 {
|
||||
return Err(EscapeError::OverlongUnicodeEscape);
|
||||
}
|
||||
|
||||
// Incorrect syntax has higher priority for error reporting
|
||||
// than unallowed value for a literal.
|
||||
if is_byte {
|
||||
return Err(EscapeError::UnicodeEscapeInByte);
|
||||
}
|
||||
|
||||
break std::char::from_u32(value).ok_or_else(|| {
|
||||
if value > 0x10FFFF {
|
||||
EscapeError::OutOfRangeUnicodeEscape
|
||||
} else {
|
||||
EscapeError::LoneSurrogateUnicodeEscape
|
||||
}
|
||||
})?;
|
||||
}
|
||||
Some(c) => {
|
||||
let digit: u32 =
|
||||
c.to_digit(16).ok_or(EscapeError::InvalidCharInUnicodeEscape)?;
|
||||
n_digits += 1;
|
||||
if n_digits > 6 {
|
||||
// Stop updating value since we're sure that it's incorrect already.
|
||||
continue;
|
||||
}
|
||||
value = value * 16 + digit;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
'u' => return scan_unicode(chars, mode.is_unicode_escape_disallowed()).map(Into::into),
|
||||
_ => return Err(EscapeError::InvalidEscape),
|
||||
};
|
||||
Ok(res)
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
fn scan_unicode(
|
||||
chars: &mut Chars<'_>,
|
||||
is_unicode_escape_disallowed: bool,
|
||||
) -> Result<char, EscapeError> {
|
||||
// We've parsed '\u', now we have to parse '{..}'.
|
||||
|
||||
if chars.next() != Some('{') {
|
||||
return Err(EscapeError::NoBraceInUnicodeEscape);
|
||||
}
|
||||
|
||||
// First character must be a hexadecimal digit.
|
||||
let mut n_digits = 1;
|
||||
let mut value: u32 = match chars.next().ok_or(EscapeError::UnclosedUnicodeEscape)? {
|
||||
'_' => return Err(EscapeError::LeadingUnderscoreUnicodeEscape),
|
||||
'}' => return Err(EscapeError::EmptyUnicodeEscape),
|
||||
c => c.to_digit(16).ok_or(EscapeError::InvalidCharInUnicodeEscape)?,
|
||||
};
|
||||
|
||||
// First character is valid, now parse the rest of the number
|
||||
// and closing brace.
|
||||
loop {
|
||||
match chars.next() {
|
||||
None => return Err(EscapeError::UnclosedUnicodeEscape),
|
||||
Some('_') => continue,
|
||||
Some('}') => {
|
||||
if n_digits > 6 {
|
||||
return Err(EscapeError::OverlongUnicodeEscape);
|
||||
}
|
||||
|
||||
// Incorrect syntax has higher priority for error reporting
|
||||
// than unallowed value for a literal.
|
||||
if is_unicode_escape_disallowed {
|
||||
return Err(EscapeError::UnicodeEscapeInByte);
|
||||
}
|
||||
|
||||
break std::char::from_u32(value).ok_or_else(|| {
|
||||
if value > 0x10FFFF {
|
||||
EscapeError::OutOfRangeUnicodeEscape
|
||||
} else {
|
||||
EscapeError::LoneSurrogateUnicodeEscape
|
||||
}
|
||||
});
|
||||
}
|
||||
Some(c) => {
|
||||
let digit: u32 = c.to_digit(16).ok_or(EscapeError::InvalidCharInUnicodeEscape)?;
|
||||
n_digits += 1;
|
||||
if n_digits > 6 {
|
||||
// Stop updating value since we're sure that it's incorrect already.
|
||||
continue;
|
||||
}
|
||||
value = value * 16 + digit;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ascii_check(c: char, is_byte: bool) -> Result<char, EscapeError> {
|
||||
if is_byte && !c.is_ascii() {
|
||||
fn ascii_check(c: char, characters_should_be_ascii: bool) -> Result<char, EscapeError> {
|
||||
if characters_should_be_ascii && !c.is_ascii() {
|
||||
// Byte literal can't be a non-ascii character.
|
||||
Err(EscapeError::NonAsciiCharInByte)
|
||||
} else {
|
||||
@ -234,7 +306,7 @@ fn ascii_check(c: char, is_byte: bool) -> Result<char, EscapeError> {
|
||||
fn unescape_char_or_byte(chars: &mut Chars<'_>, is_byte: bool) -> Result<char, EscapeError> {
|
||||
let c = chars.next().ok_or(EscapeError::ZeroChars)?;
|
||||
let res = match c {
|
||||
'\\' => scan_escape(chars, is_byte),
|
||||
'\\' => scan_escape(chars, if is_byte { Mode::Byte } else { Mode::Char }),
|
||||
'\n' | '\t' | '\'' => Err(EscapeError::EscapeOnlyChar),
|
||||
'\r' => Err(EscapeError::BareCarriageReturn),
|
||||
_ => ascii_check(c, is_byte),
|
||||
@ -247,9 +319,9 @@ fn unescape_char_or_byte(chars: &mut Chars<'_>, is_byte: bool) -> Result<char, E
|
||||
|
||||
/// Takes a contents of a string literal (without quotes) and produces a
|
||||
/// sequence of escaped characters or errors.
|
||||
fn unescape_str_or_byte_str<F>(src: &str, is_byte: bool, callback: &mut F)
|
||||
fn unescape_str_common<F, T: From<u8> + From<char>>(src: &str, mode: Mode, callback: &mut F)
|
||||
where
|
||||
F: FnMut(Range<usize>, Result<char, EscapeError>),
|
||||
F: FnMut(Range<usize>, Result<T, EscapeError>),
|
||||
{
|
||||
let mut chars = src.chars();
|
||||
|
||||
@ -266,47 +338,49 @@ where
|
||||
// if unescaped '\' character is followed by '\n'.
|
||||
// For details see [Rust language reference]
|
||||
// (https://doc.rust-lang.org/reference/tokens.html#string-literals).
|
||||
skip_ascii_whitespace(&mut chars, start, callback);
|
||||
skip_ascii_whitespace(&mut chars, start, &mut |range, err| {
|
||||
callback(range, Err(err))
|
||||
});
|
||||
continue;
|
||||
}
|
||||
_ => scan_escape(&mut chars, is_byte),
|
||||
_ => scan_escape::<T>(&mut chars, mode),
|
||||
}
|
||||
}
|
||||
'\n' => Ok('\n'),
|
||||
'\t' => Ok('\t'),
|
||||
'\n' => Ok(b'\n'.into()),
|
||||
'\t' => Ok(b'\t'.into()),
|
||||
'"' => Err(EscapeError::EscapeOnlyChar),
|
||||
'\r' => Err(EscapeError::BareCarriageReturn),
|
||||
_ => ascii_check(c, is_byte),
|
||||
_ => ascii_check(c, mode.characters_should_be_ascii()).map(Into::into),
|
||||
};
|
||||
let end = src.len() - chars.as_str().len();
|
||||
callback(start..end, res);
|
||||
callback(start..end, res.map(Into::into));
|
||||
}
|
||||
}
|
||||
|
||||
fn skip_ascii_whitespace<F>(chars: &mut Chars<'_>, start: usize, callback: &mut F)
|
||||
where
|
||||
F: FnMut(Range<usize>, Result<char, EscapeError>),
|
||||
{
|
||||
let tail = chars.as_str();
|
||||
let first_non_space = tail
|
||||
.bytes()
|
||||
.position(|b| b != b' ' && b != b'\t' && b != b'\n' && b != b'\r')
|
||||
.unwrap_or(tail.len());
|
||||
if tail[1..first_non_space].contains('\n') {
|
||||
// The +1 accounts for the escaping slash.
|
||||
let end = start + first_non_space + 1;
|
||||
callback(start..end, Err(EscapeError::MultipleSkippedLinesWarning));
|
||||
}
|
||||
let tail = &tail[first_non_space..];
|
||||
if let Some(c) = tail.chars().nth(0) {
|
||||
if c.is_whitespace() {
|
||||
// For error reporting, we would like the span to contain the character that was not
|
||||
// skipped. The +1 is necessary to account for the leading \ that started the escape.
|
||||
let end = start + first_non_space + c.len_utf8() + 1;
|
||||
callback(start..end, Err(EscapeError::UnskippedWhitespaceWarning));
|
||||
}
|
||||
}
|
||||
*chars = tail.chars();
|
||||
fn skip_ascii_whitespace<F>(chars: &mut Chars<'_>, start: usize, callback: &mut F)
|
||||
where
|
||||
F: FnMut(Range<usize>, EscapeError),
|
||||
{
|
||||
let tail = chars.as_str();
|
||||
let first_non_space = tail
|
||||
.bytes()
|
||||
.position(|b| b != b' ' && b != b'\t' && b != b'\n' && b != b'\r')
|
||||
.unwrap_or(tail.len());
|
||||
if tail[1..first_non_space].contains('\n') {
|
||||
// The +1 accounts for the escaping slash.
|
||||
let end = start + first_non_space + 1;
|
||||
callback(start..end, EscapeError::MultipleSkippedLinesWarning);
|
||||
}
|
||||
let tail = &tail[first_non_space..];
|
||||
if let Some(c) = tail.chars().nth(0) {
|
||||
if c.is_whitespace() {
|
||||
// For error reporting, we would like the span to contain the character that was not
|
||||
// skipped. The +1 is necessary to account for the leading \ that started the escape.
|
||||
let end = start + first_non_space + c.len_utf8() + 1;
|
||||
callback(start..end, EscapeError::UnskippedWhitespaceWarning);
|
||||
}
|
||||
}
|
||||
*chars = tail.chars();
|
||||
}
|
||||
|
||||
/// Takes a contents of a string literal (without quotes) and produces a
|
||||
|
@ -63,6 +63,7 @@ use rustc_middle::ty::layout::{LayoutError, LayoutOf};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, VariantDef};
|
||||
use rustc_session::config::ExpectedValues;
|
||||
use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason};
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::source_map::Spanned;
|
||||
@ -116,8 +117,7 @@ impl EarlyLintPass for WhileTrue {
|
||||
#[inline]
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
|
||||
if let ast::ExprKind::While(cond, _, label) = &e.kind
|
||||
&& let cond = pierce_parens(cond)
|
||||
&& let ast::ExprKind::Lit(token_lit) = cond.kind
|
||||
&& let ast::ExprKind::Lit(token_lit) = pierce_parens(cond).kind
|
||||
&& let token::Lit { kind: token::Bool, symbol: kw::True, .. } = token_lit
|
||||
&& !cond.span.from_expansion()
|
||||
{
|
||||
@ -546,32 +546,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
|
||||
match it.kind {
|
||||
hir::ItemKind::Trait(..) => {
|
||||
// Issue #11592: traits are always considered exported, even when private.
|
||||
if cx.tcx.visibility(it.owner_id)
|
||||
== ty::Visibility::Restricted(
|
||||
cx.tcx.parent_module_from_def_id(it.owner_id.def_id).to_def_id(),
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
hir::ItemKind::TyAlias(..)
|
||||
| hir::ItemKind::Fn(..)
|
||||
| hir::ItemKind::Macro(..)
|
||||
| hir::ItemKind::Mod(..)
|
||||
| hir::ItemKind::Enum(..)
|
||||
| hir::ItemKind::Struct(..)
|
||||
| hir::ItemKind::Union(..)
|
||||
| hir::ItemKind::Const(..)
|
||||
| hir::ItemKind::Static(..) => {}
|
||||
|
||||
_ => return,
|
||||
};
|
||||
// Previously the Impl and Use types have been excluded from missing docs,
|
||||
// so we will continue to exclude them for compatibility
|
||||
if let hir::ItemKind::Impl(..) | hir::ItemKind::Use(..) = it.kind {
|
||||
return;
|
||||
}
|
||||
|
||||
let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id());
|
||||
|
||||
self.check_missing_docs_attrs(cx, it.owner_id.def_id, article, desc);
|
||||
}
|
||||
|
||||
@ -3306,16 +3287,15 @@ impl EarlyLintPass for UnexpectedCfgs {
|
||||
let cfg = &cx.sess().parse_sess.config;
|
||||
let check_cfg = &cx.sess().parse_sess.check_config;
|
||||
for &(name, value) in cfg {
|
||||
if let Some(names_valid) = &check_cfg.names_valid && !names_valid.contains(&name){
|
||||
cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigName {
|
||||
name,
|
||||
});
|
||||
}
|
||||
if let Some(value) = value && let Some(values) = check_cfg.values_valid.get(&name) && !values.contains(&value) {
|
||||
cx.emit_lint(
|
||||
UNEXPECTED_CFGS,
|
||||
BuiltinUnexpectedCliConfigValue { name, value },
|
||||
);
|
||||
match check_cfg.expecteds.get(&name) {
|
||||
Some(ExpectedValues::Some(values)) if !values.contains(&value) => {
|
||||
let value = value.unwrap_or(kw::Empty);
|
||||
cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigValue { name, value });
|
||||
}
|
||||
None if check_cfg.exhaustive_names => {
|
||||
cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigName { name });
|
||||
}
|
||||
_ => { /* expected */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ use rustc_middle::middle::stability;
|
||||
use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, print::Printer, subst::GenericArg, RegisteredTools, Ty, TyCtxt};
|
||||
use rustc_session::config::ExpectedValues;
|
||||
use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId};
|
||||
use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
|
||||
use rustc_session::Session;
|
||||
@ -768,22 +769,52 @@ pub trait LintContext: Sized {
|
||||
db.help(help);
|
||||
db.note("see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information");
|
||||
},
|
||||
BuiltinLintDiagnostics::UnexpectedCfg((name, name_span), None) => {
|
||||
let Some(names_valid) = &sess.parse_sess.check_config.names_valid else {
|
||||
bug!("it shouldn't be possible to have a diagnostic on a name if name checking is not enabled");
|
||||
};
|
||||
let possibilities: Vec<Symbol> = names_valid.iter().map(|s| *s).collect();
|
||||
BuiltinLintDiagnostics::UnexpectedCfgName((name, name_span), value) => {
|
||||
let possibilities: Vec<Symbol> = sess.parse_sess.check_config.expecteds.keys().map(|s| *s).collect();
|
||||
|
||||
// Suggest the most probable if we found one
|
||||
if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) {
|
||||
db.span_suggestion(name_span, "did you mean", best_match, Applicability::MaybeIncorrect);
|
||||
if let Some(ExpectedValues::Some(best_match_values)) =
|
||||
sess.parse_sess.check_config.expecteds.get(&best_match) {
|
||||
let mut possibilities = best_match_values.iter()
|
||||
.flatten()
|
||||
.map(Symbol::as_str)
|
||||
.collect::<Vec<_>>();
|
||||
possibilities.sort();
|
||||
|
||||
if let Some((value, value_span)) = value {
|
||||
if best_match_values.contains(&Some(value)) {
|
||||
db.span_suggestion(name_span, "there is a config with a similar name and value", best_match, Applicability::MaybeIncorrect);
|
||||
} else if best_match_values.contains(&None) {
|
||||
db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and no value", best_match, Applicability::MaybeIncorrect);
|
||||
} else if let Some(first_value) = possibilities.first() {
|
||||
db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and different values", format!("{best_match} = \"{first_value}\""), Applicability::MaybeIncorrect);
|
||||
} else {
|
||||
db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and different values", best_match, Applicability::MaybeIncorrect);
|
||||
};
|
||||
} else {
|
||||
db.span_suggestion(name_span, "there is a config with a similar name", best_match, Applicability::MaybeIncorrect);
|
||||
}
|
||||
|
||||
if !possibilities.is_empty() {
|
||||
let possibilities = possibilities.join("`, `");
|
||||
db.help(format!("expected values for `{best_match}` are: `{possibilities}`"));
|
||||
}
|
||||
} else {
|
||||
db.span_suggestion(name_span, "there is a config with a similar name", best_match, Applicability::MaybeIncorrect);
|
||||
}
|
||||
}
|
||||
},
|
||||
BuiltinLintDiagnostics::UnexpectedCfg((name, name_span), Some((value, value_span))) => {
|
||||
let Some(values) = &sess.parse_sess.check_config.values_valid.get(&name) else {
|
||||
BuiltinLintDiagnostics::UnexpectedCfgValue((name, name_span), value) => {
|
||||
let Some(ExpectedValues::Some(values)) = &sess.parse_sess.check_config.expecteds.get(&name) else {
|
||||
bug!("it shouldn't be possible to have a diagnostic on a value whose name is not in values");
|
||||
};
|
||||
let possibilities: Vec<Symbol> = values.iter().map(|&s| s).collect();
|
||||
let mut have_none_possibility = false;
|
||||
let possibilities: Vec<Symbol> = values.iter()
|
||||
.inspect(|a| have_none_possibility |= a.is_none())
|
||||
.copied()
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
// Show the full list if all possible values for a given name, but don't do it
|
||||
// for names as the possibilities could be very long
|
||||
@ -792,17 +823,24 @@ pub trait LintContext: Sized {
|
||||
let mut possibilities = possibilities.iter().map(Symbol::as_str).collect::<Vec<_>>();
|
||||
possibilities.sort();
|
||||
|
||||
let possibilities = possibilities.join(", ");
|
||||
db.note(format!("expected values for `{name}` are: {possibilities}"));
|
||||
let possibilities = possibilities.join("`, `");
|
||||
let none = if have_none_possibility { "(none), " } else { "" };
|
||||
|
||||
db.note(format!("expected values for `{name}` are: {none}`{possibilities}`"));
|
||||
}
|
||||
|
||||
// Suggest the most probable if we found one
|
||||
if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) {
|
||||
db.span_suggestion(value_span, "did you mean", format!("\"{best_match}\""), Applicability::MaybeIncorrect);
|
||||
if let Some((value, value_span)) = value {
|
||||
// Suggest the most probable if we found one
|
||||
if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) {
|
||||
db.span_suggestion(value_span, "there is a expected value with a similar name", format!("\"{best_match}\""), Applicability::MaybeIncorrect);
|
||||
|
||||
}
|
||||
} else if let &[first_possibility] = &possibilities[..] {
|
||||
db.span_suggestion(name_span.shrink_to_hi(), "specify a config value", format!(" = \"{first_possibility}\""), Applicability::MaybeIncorrect);
|
||||
}
|
||||
} else {
|
||||
} else if have_none_possibility {
|
||||
db.note(format!("no expected value for `{name}`"));
|
||||
if name != sym::feature {
|
||||
if let Some((_value, value_span)) = value {
|
||||
db.span_suggestion(name_span.shrink_to_hi().to(value_span), "remove the value", "", Applicability::MaybeIncorrect);
|
||||
}
|
||||
}
|
||||
|
@ -478,8 +478,10 @@ impl EarlyLintPass for Diagnostics {
|
||||
}
|
||||
if !segments.iter().all(|(name, args)| {
|
||||
let arg = match name.as_str() {
|
||||
"struct_span_err" | "span_note" | "span_label" | "span_help" => &args[1],
|
||||
"note" | "help" => &args[0],
|
||||
"struct_span_err" | "span_note" | "span_label" | "span_help" if args.len() == 2 => {
|
||||
&args[1]
|
||||
}
|
||||
"note" | "help" if args.len() == 1 => &args[0],
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
|
@ -496,7 +496,8 @@ pub enum BuiltinLintDiagnostics {
|
||||
BreakWithLabelAndLoop(Span),
|
||||
NamedAsmLabel(String),
|
||||
UnicodeTextFlow(Span, String),
|
||||
UnexpectedCfg((Symbol, Span), Option<(Symbol, Span)>),
|
||||
UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>),
|
||||
UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>),
|
||||
DeprecatedWhereclauseLocation(Span, String),
|
||||
SingleUseLifetime {
|
||||
/// Span of the parameter which declares this lifetime.
|
||||
|
@ -297,7 +297,6 @@ static Reloc::Model fromRust(LLVMRustRelocModel RustReloc) {
|
||||
report_fatal_error("Bad RelocModel.");
|
||||
}
|
||||
|
||||
#ifdef LLVM_RUSTLLVM
|
||||
/// getLongestEntryLength - Return the length of the longest entry in the table.
|
||||
template<typename KV>
|
||||
static size_t getLongestEntryLength(ArrayRef<KV> Table) {
|
||||
@ -307,56 +306,68 @@ static size_t getLongestEntryLength(ArrayRef<KV> Table) {
|
||||
return MaxLen;
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM) {
|
||||
extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, const char* TargetCPU) {
|
||||
const TargetMachine *Target = unwrap(TM);
|
||||
const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
|
||||
const Triple::ArchType HostArch = Triple(sys::getDefaultTargetTriple()).getArch();
|
||||
const Triple::ArchType TargetArch = Target->getTargetTriple().getArch();
|
||||
|
||||
#if LLVM_VERSION_GE(17, 0)
|
||||
const ArrayRef<SubtargetSubTypeKV> CPUTable = MCInfo->getAllProcessorDescriptions();
|
||||
#elif defined(LLVM_RUSTLLVM)
|
||||
const ArrayRef<SubtargetSubTypeKV> CPUTable = MCInfo->getCPUTable();
|
||||
#else
|
||||
printf("Full target CPU help is not supported by this LLVM version.\n\n");
|
||||
SubtargetSubTypeKV TargetCPUKV = { TargetCPU, {{}}, {{}} };
|
||||
const ArrayRef<SubtargetSubTypeKV> CPUTable = TargetCPUKV;
|
||||
#endif
|
||||
unsigned MaxCPULen = getLongestEntryLength(CPUTable);
|
||||
|
||||
printf("Available CPUs for this target:\n");
|
||||
// Don't print the "native" entry when the user specifies --target with a
|
||||
// different arch since that could be wrong or misleading.
|
||||
if (HostArch == TargetArch) {
|
||||
MaxCPULen = std::max(MaxCPULen, (unsigned) std::strlen("native"));
|
||||
const StringRef HostCPU = sys::getHostCPUName();
|
||||
printf(" %-*s - Select the CPU of the current host (currently %.*s).\n",
|
||||
MaxCPULen, "native", (int)HostCPU.size(), HostCPU.data());
|
||||
}
|
||||
for (auto &CPU : CPUTable)
|
||||
printf(" %-*s\n", MaxCPULen, CPU.Key);
|
||||
printf("\n");
|
||||
for (auto &CPU : CPUTable) {
|
||||
// Compare cpu against current target to label the default
|
||||
if (strcmp(CPU.Key, TargetCPU) == 0) {
|
||||
printf(" %-*s - This is the default target CPU"
|
||||
" for the current build target (currently %s).",
|
||||
MaxCPULen, CPU.Key, Target->getTargetTriple().str().c_str());
|
||||
}
|
||||
else {
|
||||
printf(" %-*s", MaxCPULen, CPU.Key);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) {
|
||||
#ifdef LLVM_RUSTLLVM
|
||||
const TargetMachine *Target = unwrap(TM);
|
||||
const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
|
||||
const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getFeatureTable();
|
||||
return FeatTable.size();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef TM, size_t Index,
|
||||
const char** Feature, const char** Desc) {
|
||||
#ifdef LLVM_RUSTLLVM
|
||||
const TargetMachine *Target = unwrap(TM);
|
||||
const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
|
||||
const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getFeatureTable();
|
||||
const SubtargetFeatureKV Feat = FeatTable[Index];
|
||||
*Feature = Feat.Key;
|
||||
*Desc = Feat.Desc;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef) {
|
||||
printf("Target CPU help is not supported by this LLVM version.\n\n");
|
||||
}
|
||||
|
||||
extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef, const char**, const char**) {}
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" const char* LLVMRustGetHostCPUName(size_t *len) {
|
||||
StringRef Name = sys::getHostCPUName();
|
||||
|
@ -831,6 +831,28 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction(
|
||||
return wrap(Sub);
|
||||
}
|
||||
|
||||
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMethod(
|
||||
LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
|
||||
const char *Name, size_t NameLen,
|
||||
const char *LinkageName, size_t LinkageNameLen,
|
||||
LLVMMetadataRef File, unsigned LineNo,
|
||||
LLVMMetadataRef Ty, LLVMRustDIFlags Flags,
|
||||
LLVMRustDISPFlags SPFlags, LLVMMetadataRef TParam) {
|
||||
DITemplateParameterArray TParams =
|
||||
DITemplateParameterArray(unwrap<MDTuple>(TParam));
|
||||
DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags);
|
||||
DINode::DIFlags llvmFlags = fromRust(Flags);
|
||||
DISubprogram *Sub = Builder->createMethod(
|
||||
unwrapDI<DIScope>(Scope),
|
||||
StringRef(Name, NameLen),
|
||||
StringRef(LinkageName, LinkageNameLen),
|
||||
unwrapDI<DIFile>(File), LineNo,
|
||||
unwrapDI<DISubroutineType>(Ty),
|
||||
0, 0, nullptr, // VTable params aren't used
|
||||
llvmFlags, llvmSPFlags, TParams);
|
||||
return wrap(Sub);
|
||||
}
|
||||
|
||||
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateBasicType(
|
||||
LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen,
|
||||
uint64_t SizeInBits, unsigned Encoding) {
|
||||
|
@ -865,6 +865,17 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn inject_forced_externs(&mut self) {
|
||||
for (name, entry) in self.sess.opts.externs.iter() {
|
||||
if entry.force {
|
||||
let name_interned = Symbol::intern(name);
|
||||
if !self.used_extern_options.contains(&name_interned) {
|
||||
self.resolve_crate(name_interned, DUMMY_SP, CrateDepKind::Explicit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn inject_dependency_if(
|
||||
&self,
|
||||
krate: CrateNum,
|
||||
@ -913,7 +924,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
|
||||
// Don't worry about pathless `--extern foo` sysroot references
|
||||
continue;
|
||||
}
|
||||
if entry.nounused_dep {
|
||||
if entry.nounused_dep || entry.force {
|
||||
// We're not worried about this one
|
||||
continue;
|
||||
}
|
||||
@ -942,6 +953,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
|
||||
}
|
||||
|
||||
pub fn postprocess(&mut self, krate: &ast::Crate) {
|
||||
self.inject_forced_externs();
|
||||
self.inject_profiler_runtime(krate);
|
||||
self.inject_allocator_crate(krate);
|
||||
self.inject_panic_runtime(krate);
|
||||
|
@ -161,14 +161,6 @@ impl<'tcx> Collector<'tcx> {
|
||||
"raw-dylib" => {
|
||||
if !sess.target.is_like_windows {
|
||||
sess.emit_err(errors::FrameworkOnlyWindows { span });
|
||||
} else if !features.raw_dylib && sess.target.arch == "x86" {
|
||||
feature_err(
|
||||
&sess.parse_sess,
|
||||
sym::raw_dylib,
|
||||
span,
|
||||
"link kind `raw-dylib` is unstable on x86",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
NativeLibKind::RawDylib
|
||||
}
|
||||
@ -251,16 +243,6 @@ impl<'tcx> Collector<'tcx> {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if !features.raw_dylib {
|
||||
let span = item.name_value_literal_span().unwrap();
|
||||
feature_err(
|
||||
&sess.parse_sess,
|
||||
sym::raw_dylib,
|
||||
span,
|
||||
"import name type is unstable",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
import_name_type = Some((link_import_name_type, item.span()));
|
||||
}
|
||||
_ => {
|
||||
|
@ -64,7 +64,7 @@ impl EffectiveVisibility {
|
||||
self.at_level(level).is_public()
|
||||
}
|
||||
|
||||
pub fn from_vis(vis: Visibility) -> EffectiveVisibility {
|
||||
pub const fn from_vis(vis: Visibility) -> EffectiveVisibility {
|
||||
EffectiveVisibility {
|
||||
direct: vis,
|
||||
reexported: vis,
|
||||
@ -72,6 +72,18 @@ impl EffectiveVisibility {
|
||||
reachable_through_impl_trait: vis,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn min(mut self, lhs: EffectiveVisibility, tcx: TyCtxt<'_>) -> Self {
|
||||
for l in Level::all_levels() {
|
||||
let rhs_vis = self.at_level_mut(l);
|
||||
let lhs_vis = *lhs.at_level(l);
|
||||
if rhs_vis.is_at_least(lhs_vis, tcx) {
|
||||
*rhs_vis = lhs_vis;
|
||||
};
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds a map of effective visibilities for reachable HIR nodes.
|
||||
@ -137,24 +149,6 @@ impl EffectiveVisibilities {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn set_public_at_level(
|
||||
&mut self,
|
||||
id: LocalDefId,
|
||||
lazy_private_vis: impl FnOnce() -> Visibility,
|
||||
level: Level,
|
||||
) {
|
||||
let mut effective_vis = self
|
||||
.effective_vis(id)
|
||||
.copied()
|
||||
.unwrap_or_else(|| EffectiveVisibility::from_vis(lazy_private_vis()));
|
||||
for l in Level::all_levels() {
|
||||
if l <= level {
|
||||
*effective_vis.at_level_mut(l) = Visibility::Public;
|
||||
}
|
||||
}
|
||||
self.map.insert(id, effective_vis);
|
||||
}
|
||||
|
||||
pub fn check_invariants(&self, tcx: TyCtxt<'_>, early: bool) {
|
||||
if !cfg!(debug_assertions) {
|
||||
return;
|
||||
@ -219,7 +213,7 @@ impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
|
||||
pub fn update(
|
||||
&mut self,
|
||||
id: Id,
|
||||
nominal_vis: Visibility,
|
||||
nominal_vis: Option<Visibility>,
|
||||
lazy_private_vis: impl FnOnce() -> Visibility,
|
||||
inherited_effective_vis: EffectiveVisibility,
|
||||
level: Level,
|
||||
@ -243,12 +237,11 @@ impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
|
||||
if !(inherited_effective_vis_at_prev_level == inherited_effective_vis_at_level
|
||||
&& level != l)
|
||||
{
|
||||
calculated_effective_vis =
|
||||
if nominal_vis.is_at_least(inherited_effective_vis_at_level, tcx) {
|
||||
inherited_effective_vis_at_level
|
||||
} else {
|
||||
nominal_vis
|
||||
};
|
||||
calculated_effective_vis = if let Some(nominal_vis) = nominal_vis && !nominal_vis.is_at_least(inherited_effective_vis_at_level, tcx) {
|
||||
nominal_vis
|
||||
} else {
|
||||
inherited_effective_vis_at_level
|
||||
}
|
||||
}
|
||||
// effective visibility can't be decreased at next update call for the
|
||||
// same id
|
||||
|
@ -569,7 +569,7 @@ rustc_queries! {
|
||||
/// returns the full set of predicates. If `Some<Ident>`, then the query returns only the
|
||||
/// subset of super-predicates that reference traits that define the given associated type.
|
||||
/// This is used to avoid cycles in resolving types like `T::Item`.
|
||||
query super_predicates_that_define_assoc_type(key: (DefId, rustc_span::symbol::Ident)) -> ty::GenericPredicates<'tcx> {
|
||||
query super_predicates_that_define_assoc_item(key: (DefId, rustc_span::symbol::Ident)) -> ty::GenericPredicates<'tcx> {
|
||||
desc { |tcx| "computing the super traits of `{}` with associated type name `{}`",
|
||||
tcx.def_path_str(key.0),
|
||||
key.1
|
||||
|
@ -444,6 +444,10 @@ pub enum ObligationCauseCode<'tcx> {
|
||||
AscribeUserTypeProvePredicate(Span),
|
||||
|
||||
RustCall,
|
||||
|
||||
/// Obligations to prove that a `std::ops::Drop` impl is not stronger than
|
||||
/// the ADT it's being implemented for.
|
||||
DropImpl,
|
||||
}
|
||||
|
||||
/// The 'location' at which we try to perform HIR-based wf checking.
|
||||
|
@ -1567,11 +1567,11 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
|
||||
/// Given the def_id of a Trait `trait_def_id` and the name of an associated item `assoc_name`
|
||||
/// returns true if the `trait_def_id` defines an associated item of name `assoc_name`.
|
||||
pub fn trait_may_define_assoc_type(self, trait_def_id: DefId, assoc_name: Ident) -> bool {
|
||||
pub fn trait_may_define_assoc_item(self, trait_def_id: DefId, assoc_name: Ident) -> bool {
|
||||
self.super_traits_of(trait_def_id).any(|trait_did| {
|
||||
self.associated_items(trait_did)
|
||||
.find_by_name_and_kind(self, assoc_name, ty::AssocKind::Type, trait_did)
|
||||
.is_some()
|
||||
.filter_by_name_unhygienic(assoc_name.name)
|
||||
.any(|item| self.hygienic_eq(assoc_name, item.ident(self), trait_did))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -385,7 +385,7 @@ impl<'tcx> Instance<'tcx> {
|
||||
/// couldn't complete due to errors elsewhere - this is distinct
|
||||
/// from `Ok(None)` to avoid misleading diagnostics when an error
|
||||
/// has already been/will be emitted, for the original cause
|
||||
#[instrument(level = "debug", skip(tcx))]
|
||||
#[instrument(level = "debug", skip(tcx), ret)]
|
||||
pub fn resolve(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
@ -2633,6 +2633,12 @@ macro_rules! define_print_and_forward_display {
|
||||
#[derive(Copy, Clone, TypeFoldable, TypeVisitable, Lift)]
|
||||
pub struct TraitRefPrintOnlyTraitPath<'tcx>(ty::TraitRef<'tcx>);
|
||||
|
||||
impl<'tcx> rustc_errors::IntoDiagnosticArg for TraitRefPrintOnlyTraitPath<'tcx> {
|
||||
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
|
||||
self.to_string().into_diagnostic_arg()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> fmt::Debug for TraitRefPrintOnlyTraitPath<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
|
@ -146,6 +146,12 @@ pub(crate) fn lit_to_mir_constant<'tcx>(
|
||||
let id = tcx.allocate_bytes(data);
|
||||
ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx))
|
||||
}
|
||||
(ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().c_str()) =>
|
||||
{
|
||||
let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]);
|
||||
let allocation = tcx.mk_const_alloc(allocation);
|
||||
ConstValue::Slice { data: allocation, start: 0, end: data.len() }
|
||||
}
|
||||
(ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
|
||||
ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1)))
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::errors;
|
||||
use crate::lexer::unicode_chars::UNICODE_ARRAY;
|
||||
use crate::make_unclosed_delims_error;
|
||||
@ -6,7 +8,7 @@ use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::util::unicode::contains_text_flow_control_chars;
|
||||
use rustc_errors::{error_code, Applicability, Diagnostic, DiagnosticBuilder, StashKey};
|
||||
use rustc_lexer::unescape::{self, Mode};
|
||||
use rustc_lexer::unescape::{self, EscapeError, Mode};
|
||||
use rustc_lexer::Cursor;
|
||||
use rustc_lexer::{Base, DocStyle, RawStrError};
|
||||
use rustc_session::lint::builtin::{
|
||||
@ -204,6 +206,9 @@ impl<'a> StringReader<'a> {
|
||||
rustc_lexer::TokenKind::Literal { kind, suffix_start } => {
|
||||
let suffix_start = start + BytePos(suffix_start);
|
||||
let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind);
|
||||
if let token::LitKind::CStr | token::LitKind::CStrRaw(_) = kind {
|
||||
self.sess.gated_spans.gate(sym::c_str_literals, self.mk_sp(start, self.pos));
|
||||
}
|
||||
let suffix = if suffix_start < self.pos {
|
||||
let string = self.str_from(suffix_start);
|
||||
if string == "_" {
|
||||
@ -415,6 +420,16 @@ impl<'a> StringReader<'a> {
|
||||
}
|
||||
self.cook_quoted(token::ByteStr, Mode::ByteStr, start, end, 2, 1) // b" "
|
||||
}
|
||||
rustc_lexer::LiteralKind::CStr { terminated } => {
|
||||
if !terminated {
|
||||
self.sess.span_diagnostic.span_fatal_with_code(
|
||||
self.mk_sp(start + BytePos(1), end),
|
||||
"unterminated C string",
|
||||
error_code!(E0767),
|
||||
)
|
||||
}
|
||||
self.cook_c_string(token::CStr, Mode::CStr, start, end, 2, 1) // c" "
|
||||
}
|
||||
rustc_lexer::LiteralKind::RawStr { n_hashes } => {
|
||||
if let Some(n_hashes) = n_hashes {
|
||||
let n = u32::from(n_hashes);
|
||||
@ -433,6 +448,15 @@ impl<'a> StringReader<'a> {
|
||||
self.report_raw_str_error(start, 2);
|
||||
}
|
||||
}
|
||||
rustc_lexer::LiteralKind::RawCStr { n_hashes } => {
|
||||
if let Some(n_hashes) = n_hashes {
|
||||
let n = u32::from(n_hashes);
|
||||
let kind = token::CStrRaw(n_hashes);
|
||||
self.cook_c_string(kind, Mode::RawCStr, start, end, 3 + n, 1 + n) // cr##" "##
|
||||
} else {
|
||||
self.report_raw_str_error(start, 2);
|
||||
}
|
||||
}
|
||||
rustc_lexer::LiteralKind::Int { base, empty_int } => {
|
||||
if empty_int {
|
||||
let span = self.mk_sp(start, end);
|
||||
@ -648,7 +672,7 @@ impl<'a> StringReader<'a> {
|
||||
self.sess.emit_fatal(errors::TooManyHashes { span: self.mk_sp(start, self.pos), num });
|
||||
}
|
||||
|
||||
fn cook_quoted(
|
||||
fn cook_common(
|
||||
&self,
|
||||
kind: token::LitKind,
|
||||
mode: Mode,
|
||||
@ -656,12 +680,13 @@ impl<'a> StringReader<'a> {
|
||||
end: BytePos,
|
||||
prefix_len: u32,
|
||||
postfix_len: u32,
|
||||
unescape: fn(&str, Mode, &mut dyn FnMut(Range<usize>, Result<(), EscapeError>)),
|
||||
) -> (token::LitKind, Symbol) {
|
||||
let mut has_fatal_err = false;
|
||||
let content_start = start + BytePos(prefix_len);
|
||||
let content_end = end - BytePos(postfix_len);
|
||||
let lit_content = self.str_from_to(content_start, content_end);
|
||||
unescape::unescape_literal(lit_content, mode, &mut |range, result| {
|
||||
unescape(lit_content, mode, &mut |range, result| {
|
||||
// Here we only check for errors. The actual unescaping is done later.
|
||||
if let Err(err) = result {
|
||||
let span_with_quotes = self.mk_sp(start, end);
|
||||
@ -692,6 +717,38 @@ impl<'a> StringReader<'a> {
|
||||
(token::Err, self.symbol_from_to(start, end))
|
||||
}
|
||||
}
|
||||
|
||||
fn cook_quoted(
|
||||
&self,
|
||||
kind: token::LitKind,
|
||||
mode: Mode,
|
||||
start: BytePos,
|
||||
end: BytePos,
|
||||
prefix_len: u32,
|
||||
postfix_len: u32,
|
||||
) -> (token::LitKind, Symbol) {
|
||||
self.cook_common(kind, mode, start, end, prefix_len, postfix_len, |src, mode, callback| {
|
||||
unescape::unescape_literal(src, mode, &mut |span, result| {
|
||||
callback(span, result.map(drop))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn cook_c_string(
|
||||
&self,
|
||||
kind: token::LitKind,
|
||||
mode: Mode,
|
||||
start: BytePos,
|
||||
end: BytePos,
|
||||
prefix_len: u32,
|
||||
postfix_len: u32,
|
||||
) -> (token::LitKind, Symbol) {
|
||||
self.cook_common(kind, mode, start, end, prefix_len, postfix_len, |src, mode, callback| {
|
||||
unescape::unescape_c_string(src, mode, &mut |span, result| {
|
||||
callback(span, result.map(drop))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nfc_normalize(string: &str) -> Symbol {
|
||||
|
@ -78,8 +78,7 @@ pub(crate) fn emit_unescape_error(
|
||||
}
|
||||
};
|
||||
let sugg = sugg.unwrap_or_else(|| {
|
||||
let is_byte = mode.is_byte();
|
||||
let prefix = if is_byte { "b" } else { "" };
|
||||
let prefix = mode.prefix_noraw();
|
||||
let mut escaped = String::with_capacity(lit.len());
|
||||
let mut chrs = lit.chars().peekable();
|
||||
while let Some(first) = chrs.next() {
|
||||
@ -97,7 +96,11 @@ pub(crate) fn emit_unescape_error(
|
||||
};
|
||||
}
|
||||
let sugg = format!("{prefix}\"{escaped}\"");
|
||||
MoreThanOneCharSugg::Quotes { span: span_with_quotes, is_byte, sugg }
|
||||
MoreThanOneCharSugg::Quotes {
|
||||
span: span_with_quotes,
|
||||
is_byte: mode == Mode::Byte,
|
||||
sugg,
|
||||
}
|
||||
});
|
||||
handler.emit_err(UnescapeError::MoreThanOneChar {
|
||||
span: span_with_quotes,
|
||||
@ -112,7 +115,7 @@ pub(crate) fn emit_unescape_error(
|
||||
char_span,
|
||||
escaped_sugg: c.escape_default().to_string(),
|
||||
escaped_msg: escaped_char(c),
|
||||
byte: mode.is_byte(),
|
||||
byte: mode == Mode::Byte,
|
||||
});
|
||||
}
|
||||
EscapeError::BareCarriageReturn => {
|
||||
@ -126,12 +129,15 @@ pub(crate) fn emit_unescape_error(
|
||||
EscapeError::InvalidEscape => {
|
||||
let (c, span) = last_char();
|
||||
|
||||
let label =
|
||||
if mode.is_byte() { "unknown byte escape" } else { "unknown character escape" };
|
||||
let label = if mode == Mode::Byte || mode == Mode::ByteStr {
|
||||
"unknown byte escape"
|
||||
} else {
|
||||
"unknown character escape"
|
||||
};
|
||||
let ec = escaped_char(c);
|
||||
let mut diag = handler.struct_span_err(span, format!("{}: `{}`", label, ec));
|
||||
diag.span_label(span, label);
|
||||
if c == '{' || c == '}' && !mode.is_byte() {
|
||||
if c == '{' || c == '}' && matches!(mode, Mode::Str | Mode::RawStr) {
|
||||
diag.help(
|
||||
"if used in a formatting string, curly braces are escaped with `{{` and `}}`",
|
||||
);
|
||||
@ -141,7 +147,7 @@ pub(crate) fn emit_unescape_error(
|
||||
version control settings",
|
||||
);
|
||||
} else {
|
||||
if !mode.is_byte() {
|
||||
if mode == Mode::Str || mode == Mode::Char {
|
||||
diag.span_suggestion(
|
||||
span_with_quotes,
|
||||
"if you meant to write a literal backslash (perhaps escaping in a regular expression), consider a raw string literal",
|
||||
|
@ -1448,8 +1448,19 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
fn parse_expr_path_start(&mut self) -> PResult<'a, P<Expr>> {
|
||||
let maybe_eq_tok = self.prev_token.clone();
|
||||
let (qself, path) = if self.eat_lt() {
|
||||
let (qself, path) = self.parse_qpath(PathStyle::Expr)?;
|
||||
let lt_span = self.prev_token.span;
|
||||
let (qself, path) = self.parse_qpath(PathStyle::Expr).map_err(|mut err| {
|
||||
// Suggests using '<=' if there is an error parsing qpath when the previous token
|
||||
// is an '=' token. Only emits suggestion if the '<' token and '=' token are
|
||||
// directly adjacent (i.e. '=<')
|
||||
if maybe_eq_tok.kind == TokenKind::Eq && maybe_eq_tok.span.hi() == lt_span.lo() {
|
||||
let eq_lt = maybe_eq_tok.span.to(lt_span);
|
||||
err.span_suggestion(eq_lt, "did you mean", "<=", Applicability::Unspecified);
|
||||
}
|
||||
err
|
||||
})?;
|
||||
(Some(qself), path)
|
||||
} else {
|
||||
(None, self.parse_path(PathStyle::Expr)?)
|
||||
@ -1870,6 +1881,7 @@ impl<'a> Parser<'a> {
|
||||
let recovered = self.recover_after_dot();
|
||||
let token = recovered.as_ref().unwrap_or(&self.token);
|
||||
let span = token.span;
|
||||
|
||||
token::Lit::from_token(token).map(|token_lit| {
|
||||
self.bump();
|
||||
(token_lit, span)
|
||||
|
@ -25,7 +25,7 @@ use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{AssocItemKind, HirIdSet, ItemId, Node, PatKind};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::middle::privacy::{EffectiveVisibilities, Level};
|
||||
use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::subst::InternalSubsts;
|
||||
@ -38,7 +38,7 @@ use rustc_span::Span;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::ControlFlow;
|
||||
use std::{cmp, fmt, mem};
|
||||
use std::{fmt, mem};
|
||||
|
||||
use errors::{
|
||||
FieldIsPrivate, FieldIsPrivateLabel, FromPrivateDependencyInPublicInterface, InPublicInterface,
|
||||
@ -375,8 +375,9 @@ impl VisibilityLike for ty::Visibility {
|
||||
min(find.tcx.local_visibility(def_id), find.min, find.tcx)
|
||||
}
|
||||
}
|
||||
impl VisibilityLike for Option<Level> {
|
||||
const MAX: Self = Some(Level::Direct);
|
||||
|
||||
impl VisibilityLike for Option<EffectiveVisibility> {
|
||||
const MAX: Self = Some(EffectiveVisibility::from_vis(ty::Visibility::Public));
|
||||
// Type inference is very smart sometimes.
|
||||
// It can make an impl reachable even some components of its type or trait are unreachable.
|
||||
// E.g. methods of `impl ReachableTrait<UnreachableTy> for ReachableTy<UnreachableTy> { ... }`
|
||||
@ -388,7 +389,13 @@ impl VisibilityLike for Option<Level> {
|
||||
// (which require reaching the `DefId`s in them).
|
||||
const SHALLOW: bool = true;
|
||||
fn new_min(find: &FindMin<'_, '_, Self>, def_id: LocalDefId) -> Self {
|
||||
cmp::min(find.effective_visibilities.public_at_level(def_id), find.min)
|
||||
if let Some(min) = find.min {
|
||||
return find
|
||||
.effective_visibilities
|
||||
.effective_vis(def_id)
|
||||
.map(|eff_vis| min.min(*eff_vis, find.tcx));
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@ -414,49 +421,79 @@ struct EmbargoVisitor<'tcx> {
|
||||
/// n::p::f()
|
||||
/// }
|
||||
macro_reachable: FxHashSet<(LocalDefId, LocalDefId)>,
|
||||
/// Previous visibility level; `None` means unreachable.
|
||||
prev_level: Option<Level>,
|
||||
/// Has something changed in the level map?
|
||||
changed: bool,
|
||||
}
|
||||
|
||||
struct ReachEverythingInTheInterfaceVisitor<'a, 'tcx> {
|
||||
level: Option<Level>,
|
||||
effective_vis: Option<EffectiveVisibility>,
|
||||
item_def_id: LocalDefId,
|
||||
ev: &'a mut EmbargoVisitor<'tcx>,
|
||||
level: Level,
|
||||
}
|
||||
|
||||
impl<'tcx> EmbargoVisitor<'tcx> {
|
||||
fn get(&self, def_id: LocalDefId) -> Option<Level> {
|
||||
self.effective_visibilities.public_at_level(def_id)
|
||||
fn get(&self, def_id: LocalDefId) -> Option<EffectiveVisibility> {
|
||||
self.effective_visibilities.effective_vis(def_id).copied()
|
||||
}
|
||||
|
||||
/// Updates node level and returns the updated level.
|
||||
fn update(&mut self, def_id: LocalDefId, level: Option<Level>) -> Option<Level> {
|
||||
let old_level = self.get(def_id);
|
||||
// Visibility levels can only grow.
|
||||
if level > old_level {
|
||||
self.effective_visibilities.set_public_at_level(
|
||||
def_id,
|
||||
|| ty::Visibility::Restricted(self.tcx.parent_module_from_def_id(def_id)),
|
||||
level.unwrap(),
|
||||
);
|
||||
self.changed = true;
|
||||
level
|
||||
} else {
|
||||
old_level
|
||||
// Updates node effective visibility.
|
||||
fn update(
|
||||
&mut self,
|
||||
def_id: LocalDefId,
|
||||
inherited_effective_vis: Option<EffectiveVisibility>,
|
||||
level: Level,
|
||||
) {
|
||||
let nominal_vis = self.tcx.local_visibility(def_id);
|
||||
self.update_eff_vis(def_id, inherited_effective_vis, Some(nominal_vis), level);
|
||||
}
|
||||
|
||||
fn update_eff_vis(
|
||||
&mut self,
|
||||
def_id: LocalDefId,
|
||||
inherited_effective_vis: Option<EffectiveVisibility>,
|
||||
nominal_vis: Option<ty::Visibility>,
|
||||
level: Level,
|
||||
) {
|
||||
if let Some(inherited_effective_vis) = inherited_effective_vis {
|
||||
let private_vis =
|
||||
ty::Visibility::Restricted(self.tcx.parent_module_from_def_id(def_id));
|
||||
if Some(private_vis) != nominal_vis {
|
||||
self.changed |= self.effective_visibilities.update(
|
||||
def_id,
|
||||
nominal_vis,
|
||||
|| private_vis,
|
||||
inherited_effective_vis,
|
||||
level,
|
||||
self.tcx,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reach(
|
||||
&mut self,
|
||||
def_id: LocalDefId,
|
||||
level: Option<Level>,
|
||||
effective_vis: Option<EffectiveVisibility>,
|
||||
) -> ReachEverythingInTheInterfaceVisitor<'_, 'tcx> {
|
||||
ReachEverythingInTheInterfaceVisitor {
|
||||
level: cmp::min(level, Some(Level::Reachable)),
|
||||
effective_vis,
|
||||
item_def_id: def_id,
|
||||
ev: self,
|
||||
level: Level::Reachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn reach_through_impl_trait(
|
||||
&mut self,
|
||||
def_id: LocalDefId,
|
||||
effective_vis: Option<EffectiveVisibility>,
|
||||
) -> ReachEverythingInTheInterfaceVisitor<'_, 'tcx> {
|
||||
ReachEverythingInTheInterfaceVisitor {
|
||||
effective_vis,
|
||||
item_def_id: def_id,
|
||||
ev: self,
|
||||
level: Level::ReachableThroughImplTrait,
|
||||
}
|
||||
}
|
||||
|
||||
@ -477,16 +514,18 @@ impl<'tcx> EmbargoVisitor<'tcx> {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.get(local_def_id).is_none() {
|
||||
if self.effective_visibilities.public_at_level(local_def_id).is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Since we are starting from an externally visible module,
|
||||
// all the parents in the loop below are also guaranteed to be modules.
|
||||
let mut module_def_id = macro_module_def_id;
|
||||
let macro_ev = self.get(local_def_id);
|
||||
assert!(macro_ev.is_some());
|
||||
loop {
|
||||
let changed_reachability =
|
||||
self.update_macro_reachable(module_def_id, macro_module_def_id);
|
||||
self.update_macro_reachable(module_def_id, macro_module_def_id, macro_ev);
|
||||
if changed_reachability || module_def_id == CRATE_DEF_ID {
|
||||
break;
|
||||
}
|
||||
@ -500,21 +539,33 @@ impl<'tcx> EmbargoVisitor<'tcx> {
|
||||
&mut self,
|
||||
module_def_id: LocalDefId,
|
||||
defining_mod: LocalDefId,
|
||||
macro_ev: Option<EffectiveVisibility>,
|
||||
) -> bool {
|
||||
if self.macro_reachable.insert((module_def_id, defining_mod)) {
|
||||
self.update_macro_reachable_mod(module_def_id, defining_mod);
|
||||
self.update_macro_reachable_mod(module_def_id, defining_mod, macro_ev);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn update_macro_reachable_mod(&mut self, module_def_id: LocalDefId, defining_mod: LocalDefId) {
|
||||
fn update_macro_reachable_mod(
|
||||
&mut self,
|
||||
module_def_id: LocalDefId,
|
||||
defining_mod: LocalDefId,
|
||||
macro_ev: Option<EffectiveVisibility>,
|
||||
) {
|
||||
let module = self.tcx.hir().get_module(module_def_id).0;
|
||||
for item_id in module.item_ids {
|
||||
let def_kind = self.tcx.def_kind(item_id.owner_id);
|
||||
let vis = self.tcx.local_visibility(item_id.owner_id.def_id);
|
||||
self.update_macro_reachable_def(item_id.owner_id.def_id, def_kind, vis, defining_mod);
|
||||
self.update_macro_reachable_def(
|
||||
item_id.owner_id.def_id,
|
||||
def_kind,
|
||||
vis,
|
||||
defining_mod,
|
||||
macro_ev,
|
||||
);
|
||||
}
|
||||
for child in self.tcx.module_children_local(module_def_id) {
|
||||
// FIXME: Use module children for the logic above too.
|
||||
@ -523,7 +574,7 @@ impl<'tcx> EmbargoVisitor<'tcx> {
|
||||
&& let Res::Def(def_kind, def_id) = child.res
|
||||
&& let Some(def_id) = def_id.as_local() {
|
||||
let vis = self.tcx.local_visibility(def_id);
|
||||
self.update_macro_reachable_def(def_id, def_kind, vis, defining_mod);
|
||||
self.update_macro_reachable_def(def_id, def_kind, vis, defining_mod, macro_ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -534,16 +585,14 @@ impl<'tcx> EmbargoVisitor<'tcx> {
|
||||
def_kind: DefKind,
|
||||
vis: ty::Visibility,
|
||||
module: LocalDefId,
|
||||
macro_ev: Option<EffectiveVisibility>,
|
||||
) {
|
||||
let level = Some(Level::Reachable);
|
||||
if vis.is_public() {
|
||||
self.update(def_id, level);
|
||||
}
|
||||
self.update(def_id, macro_ev, Level::Reachable);
|
||||
match def_kind {
|
||||
// No type privacy, so can be directly marked as reachable.
|
||||
DefKind::Const | DefKind::Static(_) | DefKind::TraitAlias | DefKind::TyAlias => {
|
||||
if vis.is_accessible_from(module, self.tcx) {
|
||||
self.update(def_id, level);
|
||||
self.update(def_id, macro_ev, Level::Reachable);
|
||||
}
|
||||
}
|
||||
|
||||
@ -555,7 +604,7 @@ impl<'tcx> EmbargoVisitor<'tcx> {
|
||||
let item = self.tcx.hir().expect_item(def_id);
|
||||
if let hir::ItemKind::Macro(MacroDef { macro_rules: false, .. }, _) = item.kind {
|
||||
if vis.is_accessible_from(module, self.tcx) {
|
||||
self.update(def_id, level);
|
||||
self.update(def_id, macro_ev, Level::Reachable);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -566,26 +615,24 @@ impl<'tcx> EmbargoVisitor<'tcx> {
|
||||
// the module, however may be reachable.
|
||||
DefKind::Mod => {
|
||||
if vis.is_accessible_from(module, self.tcx) {
|
||||
self.update_macro_reachable(def_id, module);
|
||||
self.update_macro_reachable(def_id, module, macro_ev);
|
||||
}
|
||||
}
|
||||
|
||||
DefKind::Struct | DefKind::Union => {
|
||||
// While structs and unions have type privacy, their fields do not.
|
||||
if vis.is_public() {
|
||||
let item = self.tcx.hir().expect_item(def_id);
|
||||
if let hir::ItemKind::Struct(ref struct_def, _)
|
||||
| hir::ItemKind::Union(ref struct_def, _) = item.kind
|
||||
{
|
||||
for field in struct_def.fields() {
|
||||
let field_vis = self.tcx.local_visibility(field.def_id);
|
||||
if field_vis.is_accessible_from(module, self.tcx) {
|
||||
self.reach(field.def_id, level).ty();
|
||||
}
|
||||
let item = self.tcx.hir().expect_item(def_id);
|
||||
if let hir::ItemKind::Struct(ref struct_def, _)
|
||||
| hir::ItemKind::Union(ref struct_def, _) = item.kind
|
||||
{
|
||||
for field in struct_def.fields() {
|
||||
let field_vis = self.tcx.local_visibility(field.def_id);
|
||||
if field_vis.is_accessible_from(module, self.tcx) {
|
||||
self.reach(field.def_id, macro_ev).ty();
|
||||
}
|
||||
} else {
|
||||
bug!("item {:?} with DefKind {:?}", item, def_kind);
|
||||
}
|
||||
} else {
|
||||
bug!("item {:?} with DefKind {:?}", item, def_kind);
|
||||
}
|
||||
}
|
||||
|
||||
@ -629,14 +676,16 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
|
||||
let item_level = match item.kind {
|
||||
let item_ev = match item.kind {
|
||||
hir::ItemKind::Impl { .. } => {
|
||||
let impl_level = Option::<Level>::of_impl(
|
||||
let impl_ev = Option::<EffectiveVisibility>::of_impl(
|
||||
item.owner_id.def_id,
|
||||
self.tcx,
|
||||
&self.effective_visibilities,
|
||||
);
|
||||
self.update(item.owner_id.def_id, impl_level)
|
||||
|
||||
self.update_eff_vis(item.owner_id.def_id, impl_ev, None, Level::Direct);
|
||||
impl_ev
|
||||
}
|
||||
_ => self.get(item.owner_id.def_id),
|
||||
};
|
||||
@ -645,38 +694,32 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
|
||||
match item.kind {
|
||||
hir::ItemKind::Enum(ref def, _) => {
|
||||
for variant in def.variants {
|
||||
let variant_level = self.update(variant.def_id, item_level);
|
||||
self.update(variant.def_id, item_ev, Level::Reachable);
|
||||
let variant_ev = self.get(variant.def_id);
|
||||
if let Some(ctor_def_id) = variant.data.ctor_def_id() {
|
||||
self.update(ctor_def_id, item_level);
|
||||
self.update(ctor_def_id, variant_ev, Level::Reachable);
|
||||
}
|
||||
for field in variant.data.fields() {
|
||||
self.update(field.def_id, variant_level);
|
||||
self.update(field.def_id, variant_ev, Level::Reachable);
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::ItemKind::Impl(ref impl_) => {
|
||||
for impl_item_ref in impl_.items {
|
||||
if impl_.of_trait.is_some()
|
||||
|| self.tcx.visibility(impl_item_ref.id.owner_id).is_public()
|
||||
{
|
||||
self.update(impl_item_ref.id.owner_id.def_id, item_level);
|
||||
}
|
||||
self.update(impl_item_ref.id.owner_id.def_id, item_ev, Level::Direct);
|
||||
}
|
||||
}
|
||||
hir::ItemKind::Trait(.., trait_item_refs) => {
|
||||
for trait_item_ref in trait_item_refs {
|
||||
self.update(trait_item_ref.id.owner_id.def_id, item_level);
|
||||
self.update(trait_item_ref.id.owner_id.def_id, item_ev, Level::Reachable);
|
||||
}
|
||||
}
|
||||
hir::ItemKind::Struct(ref def, _) | hir::ItemKind::Union(ref def, _) => {
|
||||
if let Some(ctor_def_id) = def.ctor_def_id() {
|
||||
self.update(ctor_def_id, item_level);
|
||||
self.update(ctor_def_id, item_ev, Level::Reachable);
|
||||
}
|
||||
for field in def.fields() {
|
||||
let vis = self.tcx.visibility(field.def_id);
|
||||
if vis.is_public() {
|
||||
self.update(field.def_id, item_level);
|
||||
}
|
||||
self.update(field.def_id, item_ev, Level::Reachable);
|
||||
}
|
||||
}
|
||||
hir::ItemKind::Macro(ref macro_def, _) => {
|
||||
@ -684,9 +727,7 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
|
||||
}
|
||||
hir::ItemKind::ForeignMod { items, .. } => {
|
||||
for foreign_item in items {
|
||||
if self.tcx.visibility(foreign_item.id.owner_id).is_public() {
|
||||
self.update(foreign_item.id.owner_id.def_id, item_level);
|
||||
}
|
||||
self.update(foreign_item.id.owner_id.def_id, item_ev, Level::Reachable);
|
||||
}
|
||||
}
|
||||
|
||||
@ -721,8 +762,11 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
|
||||
// FIXME: This is some serious pessimization intended to workaround deficiencies
|
||||
// in the reachability pass (`middle/reachable.rs`). Types are marked as link-time
|
||||
// reachable if they are returned via `impl Trait`, even from private functions.
|
||||
let exist_level = cmp::max(item_level, Some(Level::ReachableThroughImplTrait));
|
||||
self.reach(item.owner_id.def_id, exist_level).generics().predicates().ty();
|
||||
let exist_ev = Some(EffectiveVisibility::from_vis(ty::Visibility::Public));
|
||||
self.reach_through_impl_trait(item.owner_id.def_id, exist_ev)
|
||||
.generics()
|
||||
.predicates()
|
||||
.ty();
|
||||
}
|
||||
}
|
||||
// Visit everything.
|
||||
@ -730,17 +774,18 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
|
||||
| hir::ItemKind::Static(..)
|
||||
| hir::ItemKind::Fn(..)
|
||||
| hir::ItemKind::TyAlias(..) => {
|
||||
if item_level.is_some() {
|
||||
self.reach(item.owner_id.def_id, item_level).generics().predicates().ty();
|
||||
if item_ev.is_some() {
|
||||
self.reach(item.owner_id.def_id, item_ev).generics().predicates().ty();
|
||||
}
|
||||
}
|
||||
hir::ItemKind::Trait(.., trait_item_refs) => {
|
||||
if item_level.is_some() {
|
||||
self.reach(item.owner_id.def_id, item_level).generics().predicates();
|
||||
if item_ev.is_some() {
|
||||
self.reach(item.owner_id.def_id, item_ev).generics().predicates();
|
||||
|
||||
for trait_item_ref in trait_item_refs {
|
||||
let tcx = self.tcx;
|
||||
let mut reach = self.reach(trait_item_ref.id.owner_id.def_id, item_level);
|
||||
let mut reach = self.reach(trait_item_ref.id.owner_id.def_id, item_ev);
|
||||
|
||||
reach.generics().predicates();
|
||||
|
||||
if trait_item_ref.kind == AssocItemKind::Type
|
||||
@ -754,23 +799,24 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
|
||||
}
|
||||
}
|
||||
hir::ItemKind::TraitAlias(..) => {
|
||||
if item_level.is_some() {
|
||||
self.reach(item.owner_id.def_id, item_level).generics().predicates();
|
||||
if item_ev.is_some() {
|
||||
self.reach(item.owner_id.def_id, item_ev).generics().predicates();
|
||||
}
|
||||
}
|
||||
// Visit everything except for private impl items.
|
||||
hir::ItemKind::Impl(ref impl_) => {
|
||||
if item_level.is_some() {
|
||||
self.reach(item.owner_id.def_id, item_level)
|
||||
if item_ev.is_some() {
|
||||
self.reach(item.owner_id.def_id, item_ev)
|
||||
.generics()
|
||||
.predicates()
|
||||
.ty()
|
||||
.trait_ref();
|
||||
|
||||
for impl_item_ref in impl_.items {
|
||||
let impl_item_level = self.get(impl_item_ref.id.owner_id.def_id);
|
||||
if impl_item_level.is_some() {
|
||||
self.reach(impl_item_ref.id.owner_id.def_id, impl_item_level)
|
||||
let impl_item_ev = self.get(impl_item_ref.id.owner_id.def_id);
|
||||
|
||||
if impl_item_ev.is_some() {
|
||||
self.reach(impl_item_ref.id.owner_id.def_id, impl_item_ev)
|
||||
.generics()
|
||||
.predicates()
|
||||
.ty();
|
||||
@ -781,23 +827,23 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
|
||||
|
||||
// Visit everything, but enum variants have their own levels.
|
||||
hir::ItemKind::Enum(ref def, _) => {
|
||||
if item_level.is_some() {
|
||||
self.reach(item.owner_id.def_id, item_level).generics().predicates();
|
||||
if item_ev.is_some() {
|
||||
self.reach(item.owner_id.def_id, item_ev).generics().predicates();
|
||||
}
|
||||
for variant in def.variants {
|
||||
let variant_level = self.get(variant.def_id);
|
||||
if variant_level.is_some() {
|
||||
let variant_ev = self.get(variant.def_id);
|
||||
if variant_ev.is_some() {
|
||||
for field in variant.data.fields() {
|
||||
self.reach(field.def_id, variant_level).ty();
|
||||
self.reach(field.def_id, variant_ev).ty();
|
||||
}
|
||||
// Corner case: if the variant is reachable, but its
|
||||
// enum is not, make the enum reachable as well.
|
||||
self.reach(item.owner_id.def_id, variant_level).ty();
|
||||
self.reach(item.owner_id.def_id, variant_ev).ty();
|
||||
}
|
||||
if let Some(ctor_def_id) = variant.data.ctor_def_id() {
|
||||
let ctor_level = self.get(ctor_def_id);
|
||||
if ctor_level.is_some() {
|
||||
self.reach(item.owner_id.def_id, ctor_level).ty();
|
||||
let ctor_ev = self.get(ctor_def_id);
|
||||
if ctor_ev.is_some() {
|
||||
self.reach(item.owner_id.def_id, ctor_ev).ty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -805,9 +851,9 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
|
||||
// Visit everything, but foreign items have their own levels.
|
||||
hir::ItemKind::ForeignMod { items, .. } => {
|
||||
for foreign_item in items {
|
||||
let foreign_item_level = self.get(foreign_item.id.owner_id.def_id);
|
||||
if foreign_item_level.is_some() {
|
||||
self.reach(foreign_item.id.owner_id.def_id, foreign_item_level)
|
||||
let foreign_item_ev = self.get(foreign_item.id.owner_id.def_id);
|
||||
if foreign_item_ev.is_some() {
|
||||
self.reach(foreign_item.id.owner_id.def_id, foreign_item_ev)
|
||||
.generics()
|
||||
.predicates()
|
||||
.ty();
|
||||
@ -816,36 +862,32 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
|
||||
}
|
||||
// Visit everything except for private fields.
|
||||
hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => {
|
||||
if item_level.is_some() {
|
||||
self.reach(item.owner_id.def_id, item_level).generics().predicates();
|
||||
if item_ev.is_some() {
|
||||
self.reach(item.owner_id.def_id, item_ev).generics().predicates();
|
||||
for field in struct_def.fields() {
|
||||
let field_level = self.get(field.def_id);
|
||||
if field_level.is_some() {
|
||||
self.reach(field.def_id, field_level).ty();
|
||||
let field_ev = self.get(field.def_id);
|
||||
if field_ev.is_some() {
|
||||
self.reach(field.def_id, field_ev).ty();
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(ctor_def_id) = struct_def.ctor_def_id() {
|
||||
let ctor_level = self.get(ctor_def_id);
|
||||
if ctor_level.is_some() {
|
||||
self.reach(item.owner_id.def_id, ctor_level).ty();
|
||||
let ctor_ev = self.get(ctor_def_id);
|
||||
if ctor_ev.is_some() {
|
||||
self.reach(item.owner_id.def_id, ctor_ev).ty();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let orig_level = mem::replace(&mut self.prev_level, item_level);
|
||||
intravisit::walk_item(self, item);
|
||||
self.prev_level = orig_level;
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) {
|
||||
// Blocks can have public items, for example impls, but they always
|
||||
// start as completely private regardless of publicity of a function,
|
||||
// constant, type, field, etc., in which this block resides.
|
||||
let orig_level = mem::replace(&mut self.prev_level, None);
|
||||
intravisit::walk_block(self, b);
|
||||
self.prev_level = orig_level;
|
||||
}
|
||||
}
|
||||
|
||||
@ -899,11 +941,7 @@ impl<'tcx> DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx>
|
||||
_descr: &dyn fmt::Display,
|
||||
) -> ControlFlow<Self::BreakTy> {
|
||||
if let Some(def_id) = def_id.as_local() {
|
||||
if let (ty::Visibility::Public, _) | (_, Some(Level::ReachableThroughImplTrait)) =
|
||||
(self.tcx().visibility(def_id.to_def_id()), self.level)
|
||||
{
|
||||
self.ev.update(def_id, self.level);
|
||||
}
|
||||
self.ev.update_eff_vis(def_id, self.effective_vis, None, self.level);
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
@ -2131,7 +2169,6 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
|
||||
tcx,
|
||||
effective_visibilities: tcx.resolutions(()).effective_visibilities.clone(),
|
||||
macro_reachable: Default::default(),
|
||||
prev_level: Some(Level::Direct),
|
||||
changed: false,
|
||||
};
|
||||
|
||||
|
@ -199,7 +199,7 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
|
||||
let tcx = self.r.tcx;
|
||||
self.changed |= self.import_effective_visibilities.update(
|
||||
binding,
|
||||
nominal_vis,
|
||||
Some(nominal_vis),
|
||||
|| cheap_private_vis.unwrap_or_else(|| self.r.private_vis_import(binding)),
|
||||
inherited_eff_vis,
|
||||
parent_id.level(),
|
||||
@ -213,7 +213,7 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
|
||||
let tcx = self.r.tcx;
|
||||
self.changed |= self.def_effective_visibilities.update(
|
||||
def_id,
|
||||
nominal_vis,
|
||||
Some(nominal_vis),
|
||||
|| cheap_private_vis.unwrap_or_else(|| self.r.private_vis_def(def_id)),
|
||||
inherited_eff_vis,
|
||||
parent_id.level(),
|
||||
|
@ -101,3 +101,5 @@ session_invalid_int_literal_width = invalid width `{$width}` for integer literal
|
||||
.help = valid widths are 8, 16, 32, 64 and 128
|
||||
|
||||
session_optimization_fuel_exhausted = optimization-fuel-exhausted: {$msg}
|
||||
|
||||
session_nul_in_c_str = null characters in C string literals are not supported
|
||||
|
@ -518,6 +518,12 @@ pub struct ExternEntry {
|
||||
/// `--extern nounused:std=/path/to/lib/libstd.rlib`. This is used to
|
||||
/// suppress `unused-crate-dependencies` warnings.
|
||||
pub nounused_dep: bool,
|
||||
/// If the extern entry is not referenced in the crate, force it to be resolved anyway.
|
||||
///
|
||||
/// Allows a dependency satisfying, for instance, a missing panic handler to be injected
|
||||
/// without modifying source:
|
||||
/// `--extern force:extras=/path/to/lib/libstd.rlib`
|
||||
pub force: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -556,7 +562,13 @@ impl Externs {
|
||||
|
||||
impl ExternEntry {
|
||||
fn new(location: ExternLocation) -> ExternEntry {
|
||||
ExternEntry { location, is_private_dep: false, add_prelude: false, nounused_dep: false }
|
||||
ExternEntry {
|
||||
location,
|
||||
is_private_dep: false,
|
||||
add_prelude: false,
|
||||
nounused_dep: false,
|
||||
force: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn files(&self) -> Option<impl Iterator<Item = &CanonicalizedPath>> {
|
||||
@ -1064,37 +1076,76 @@ pub fn to_crate_config(cfg: FxHashSet<(String, Option<String>)>) -> CrateConfig
|
||||
|
||||
/// The parsed `--check-cfg` options
|
||||
pub struct CheckCfg<T = String> {
|
||||
/// The set of all `names()`, if None no name checking is performed
|
||||
pub names_valid: Option<FxHashSet<T>>,
|
||||
/// Is well known names activated
|
||||
pub exhaustive_names: bool,
|
||||
/// Is well known values activated
|
||||
pub well_known_values: bool,
|
||||
/// The set of all `values()`
|
||||
pub values_valid: FxHashMap<T, FxHashSet<T>>,
|
||||
pub exhaustive_values: bool,
|
||||
/// All the expected values for a config name
|
||||
pub expecteds: FxHashMap<T, ExpectedValues<T>>,
|
||||
}
|
||||
|
||||
impl<T> Default for CheckCfg<T> {
|
||||
fn default() -> Self {
|
||||
CheckCfg {
|
||||
names_valid: Default::default(),
|
||||
values_valid: Default::default(),
|
||||
well_known_values: false,
|
||||
exhaustive_names: false,
|
||||
exhaustive_values: false,
|
||||
expecteds: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> CheckCfg<T> {
|
||||
fn map_data<O: Eq + Hash>(&self, f: impl Fn(&T) -> O) -> CheckCfg<O> {
|
||||
fn map_data<O: Eq + Hash>(self, f: impl Fn(T) -> O) -> CheckCfg<O> {
|
||||
CheckCfg {
|
||||
names_valid: self
|
||||
.names_valid
|
||||
.as_ref()
|
||||
.map(|names_valid| names_valid.iter().map(|a| f(a)).collect()),
|
||||
values_valid: self
|
||||
.values_valid
|
||||
.iter()
|
||||
.map(|(a, b)| (f(a), b.iter().map(|b| f(b)).collect()))
|
||||
exhaustive_names: self.exhaustive_names,
|
||||
exhaustive_values: self.exhaustive_values,
|
||||
expecteds: self
|
||||
.expecteds
|
||||
.into_iter()
|
||||
.map(|(name, values)| {
|
||||
(
|
||||
f(name),
|
||||
match values {
|
||||
ExpectedValues::Some(values) => ExpectedValues::Some(
|
||||
values.into_iter().map(|b| b.map(|b| f(b))).collect(),
|
||||
),
|
||||
ExpectedValues::Any => ExpectedValues::Any,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
well_known_values: self.well_known_values,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ExpectedValues<T> {
|
||||
Some(FxHashSet<Option<T>>),
|
||||
Any,
|
||||
}
|
||||
|
||||
impl<T: Eq + Hash> ExpectedValues<T> {
|
||||
fn insert(&mut self, value: T) -> bool {
|
||||
match self {
|
||||
ExpectedValues::Some(expecteds) => expecteds.insert(Some(value)),
|
||||
ExpectedValues::Any => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq + Hash> Extend<T> for ExpectedValues<T> {
|
||||
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
|
||||
match self {
|
||||
ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(Some)),
|
||||
ExpectedValues::Any => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Eq + Hash + Copy + 'a> Extend<&'a T> for ExpectedValues<T> {
|
||||
fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
|
||||
match self {
|
||||
ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(|a| Some(*a))),
|
||||
ExpectedValues::Any => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1103,58 +1154,27 @@ impl<T> CheckCfg<T> {
|
||||
/// `rustc_interface::interface::Config` accepts this in the compiler configuration,
|
||||
/// but the symbol interner is not yet set up then, so we must convert it later.
|
||||
pub fn to_crate_check_config(cfg: CheckCfg) -> CrateCheckConfig {
|
||||
cfg.map_data(|s| Symbol::intern(s))
|
||||
cfg.map_data(|s| Symbol::intern(&s))
|
||||
}
|
||||
|
||||
impl CrateCheckConfig {
|
||||
/// Fills a `CrateCheckConfig` with well-known configuration names.
|
||||
fn fill_well_known_names(&mut self) {
|
||||
// NOTE: This should be kept in sync with `default_configuration` and
|
||||
// `fill_well_known_values`
|
||||
const WELL_KNOWN_NAMES: &[Symbol] = &[
|
||||
// rustc
|
||||
sym::unix,
|
||||
sym::windows,
|
||||
sym::target_os,
|
||||
sym::target_family,
|
||||
sym::target_arch,
|
||||
sym::target_endian,
|
||||
sym::target_pointer_width,
|
||||
sym::target_env,
|
||||
sym::target_abi,
|
||||
sym::target_vendor,
|
||||
sym::target_thread_local,
|
||||
sym::target_has_atomic_load_store,
|
||||
sym::target_has_atomic,
|
||||
sym::target_has_atomic_equal_alignment,
|
||||
sym::target_feature,
|
||||
sym::panic,
|
||||
sym::sanitize,
|
||||
sym::debug_assertions,
|
||||
sym::proc_macro,
|
||||
sym::test,
|
||||
sym::feature,
|
||||
// rustdoc
|
||||
sym::doc,
|
||||
sym::doctest,
|
||||
// miri
|
||||
sym::miri,
|
||||
];
|
||||
|
||||
// We only insert well-known names if `names()` was activated
|
||||
if let Some(names_valid) = &mut self.names_valid {
|
||||
names_valid.extend(WELL_KNOWN_NAMES);
|
||||
}
|
||||
}
|
||||
|
||||
/// Fills a `CrateCheckConfig` with well-known configuration values.
|
||||
fn fill_well_known_values(&mut self, current_target: &Target) {
|
||||
if !self.well_known_values {
|
||||
pub fn fill_well_known(&mut self, current_target: &Target) {
|
||||
if !self.exhaustive_values && !self.exhaustive_names {
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: This should be kept in sync with `default_configuration` and
|
||||
// `fill_well_known_names`
|
||||
let no_values = || {
|
||||
let mut values = FxHashSet::default();
|
||||
values.insert(None);
|
||||
ExpectedValues::Some(values)
|
||||
};
|
||||
|
||||
let empty_values = || {
|
||||
let values = FxHashSet::default();
|
||||
ExpectedValues::Some(values)
|
||||
};
|
||||
|
||||
// NOTE: This should be kept in sync with `default_configuration`
|
||||
|
||||
let panic_values = &PanicStrategy::all();
|
||||
|
||||
@ -1174,6 +1194,9 @@ impl CrateCheckConfig {
|
||||
// Unknown possible values:
|
||||
// - `feature`
|
||||
// - `target_feature`
|
||||
for name in [sym::feature, sym::target_feature] {
|
||||
self.expecteds.entry(name).or_insert(ExpectedValues::Any);
|
||||
}
|
||||
|
||||
// No-values
|
||||
for name in [
|
||||
@ -1187,20 +1210,23 @@ impl CrateCheckConfig {
|
||||
sym::debug_assertions,
|
||||
sym::target_thread_local,
|
||||
] {
|
||||
self.values_valid.entry(name).or_default();
|
||||
self.expecteds.entry(name).or_insert_with(no_values);
|
||||
}
|
||||
|
||||
// Pre-defined values
|
||||
self.values_valid.entry(sym::panic).or_default().extend(panic_values);
|
||||
self.values_valid.entry(sym::sanitize).or_default().extend(sanitize_values);
|
||||
self.values_valid.entry(sym::target_has_atomic).or_default().extend(atomic_values);
|
||||
self.values_valid
|
||||
.entry(sym::target_has_atomic_load_store)
|
||||
.or_default()
|
||||
self.expecteds.entry(sym::panic).or_insert_with(empty_values).extend(panic_values);
|
||||
self.expecteds.entry(sym::sanitize).or_insert_with(empty_values).extend(sanitize_values);
|
||||
self.expecteds
|
||||
.entry(sym::target_has_atomic)
|
||||
.or_insert_with(no_values)
|
||||
.extend(atomic_values);
|
||||
self.values_valid
|
||||
self.expecteds
|
||||
.entry(sym::target_has_atomic_load_store)
|
||||
.or_insert_with(no_values)
|
||||
.extend(atomic_values);
|
||||
self.expecteds
|
||||
.entry(sym::target_has_atomic_equal_alignment)
|
||||
.or_default()
|
||||
.or_insert_with(no_values)
|
||||
.extend(atomic_values);
|
||||
|
||||
// Target specific values
|
||||
@ -1218,47 +1244,50 @@ impl CrateCheckConfig {
|
||||
|
||||
// Initialize (if not already initialized)
|
||||
for &e in VALUES {
|
||||
self.values_valid.entry(e).or_default();
|
||||
let entry = self.expecteds.entry(e);
|
||||
if !self.exhaustive_values {
|
||||
entry.or_insert(ExpectedValues::Any);
|
||||
} else {
|
||||
entry.or_insert_with(empty_values);
|
||||
}
|
||||
}
|
||||
|
||||
// Get all values map at once otherwise it would be costly.
|
||||
// (8 values * 220 targets ~= 1760 times, at the time of writing this comment).
|
||||
let [
|
||||
values_target_os,
|
||||
values_target_family,
|
||||
values_target_arch,
|
||||
values_target_endian,
|
||||
values_target_env,
|
||||
values_target_abi,
|
||||
values_target_vendor,
|
||||
values_target_pointer_width,
|
||||
] = self
|
||||
.values_valid
|
||||
.get_many_mut(VALUES)
|
||||
.expect("unable to get all the check-cfg values buckets");
|
||||
if self.exhaustive_values {
|
||||
// Get all values map at once otherwise it would be costly.
|
||||
// (8 values * 220 targets ~= 1760 times, at the time of writing this comment).
|
||||
let [
|
||||
values_target_os,
|
||||
values_target_family,
|
||||
values_target_arch,
|
||||
values_target_endian,
|
||||
values_target_env,
|
||||
values_target_abi,
|
||||
values_target_vendor,
|
||||
values_target_pointer_width,
|
||||
] = self
|
||||
.expecteds
|
||||
.get_many_mut(VALUES)
|
||||
.expect("unable to get all the check-cfg values buckets");
|
||||
|
||||
for target in TARGETS
|
||||
.iter()
|
||||
.map(|target| Target::expect_builtin(&TargetTriple::from_triple(target)))
|
||||
.chain(iter::once(current_target.clone()))
|
||||
{
|
||||
values_target_os.insert(Symbol::intern(&target.options.os));
|
||||
values_target_family
|
||||
.extend(target.options.families.iter().map(|family| Symbol::intern(family)));
|
||||
values_target_arch.insert(Symbol::intern(&target.arch));
|
||||
values_target_endian.insert(Symbol::intern(target.options.endian.as_str()));
|
||||
values_target_env.insert(Symbol::intern(&target.options.env));
|
||||
values_target_abi.insert(Symbol::intern(&target.options.abi));
|
||||
values_target_vendor.insert(Symbol::intern(&target.options.vendor));
|
||||
values_target_pointer_width.insert(sym::integer(target.pointer_width));
|
||||
for target in TARGETS
|
||||
.iter()
|
||||
.map(|target| Target::expect_builtin(&TargetTriple::from_triple(target)))
|
||||
.chain(iter::once(current_target.clone()))
|
||||
{
|
||||
values_target_os.insert(Symbol::intern(&target.options.os));
|
||||
values_target_family.extend(
|
||||
target.options.families.iter().map(|family| Symbol::intern(family)),
|
||||
);
|
||||
values_target_arch.insert(Symbol::intern(&target.arch));
|
||||
values_target_endian.insert(Symbol::intern(target.options.endian.as_str()));
|
||||
values_target_env.insert(Symbol::intern(&target.options.env));
|
||||
values_target_abi.insert(Symbol::intern(&target.options.abi));
|
||||
values_target_vendor.insert(Symbol::intern(&target.options.vendor));
|
||||
values_target_pointer_width.insert(sym::integer(target.pointer_width));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fill_well_known(&mut self, current_target: &Target) {
|
||||
self.fill_well_known_names();
|
||||
self.fill_well_known_values(current_target);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateConfig {
|
||||
@ -2244,6 +2273,7 @@ pub fn parse_externs(
|
||||
let mut is_private_dep = false;
|
||||
let mut add_prelude = true;
|
||||
let mut nounused_dep = false;
|
||||
let mut force = false;
|
||||
if let Some(opts) = options {
|
||||
if !is_unstable_enabled {
|
||||
early_error(
|
||||
@ -2266,6 +2296,7 @@ pub fn parse_externs(
|
||||
}
|
||||
}
|
||||
"nounused" => nounused_dep = true,
|
||||
"force" => force = true,
|
||||
_ => early_error(error_format, &format!("unknown --extern option `{opt}`")),
|
||||
}
|
||||
}
|
||||
@ -2276,6 +2307,8 @@ pub fn parse_externs(
|
||||
entry.is_private_dep |= is_private_dep;
|
||||
// likewise `nounused`
|
||||
entry.nounused_dep |= nounused_dep;
|
||||
// and `force`
|
||||
entry.force |= force;
|
||||
// If any flag is missing `noprelude`, then add to the prelude.
|
||||
entry.add_prelude |= add_prelude;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use rustc_ast::token;
|
||||
use rustc_ast::util::literal::LitError;
|
||||
use rustc_errors::{error_code, DiagnosticMessage, EmissionGuarantee, IntoDiagnostic, MultiSpan};
|
||||
use rustc_macros::Diagnostic;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_span::{BytePos, Span, Symbol};
|
||||
use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTriple};
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
@ -323,6 +323,13 @@ pub(crate) struct BinaryFloatLiteralNotSupported {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(session_nul_in_c_str)]
|
||||
pub(crate) struct NulInCStr {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span: Span) {
|
||||
// Checks if `s` looks like i32 or u1234 etc.
|
||||
fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool {
|
||||
@ -401,6 +408,12 @@ pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span:
|
||||
};
|
||||
sess.emit_err(IntLiteralTooLarge { span, limit });
|
||||
}
|
||||
LitError::NulInCStr(range) => {
|
||||
let lo = BytePos(span.lo().0 + range.start as u32 + 2);
|
||||
let hi = BytePos(span.lo().0 + range.end as u32 + 2);
|
||||
let span = span.with_lo(lo).with_hi(hi);
|
||||
sess.emit_err(NulInCStr { span });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1235,6 +1235,8 @@ options! {
|
||||
line-tables-only, limited, or full; default: 0)"),
|
||||
default_linker_libraries: bool = (false, parse_bool, [UNTRACKED],
|
||||
"allow the linker to link its default libraries (default: no)"),
|
||||
dlltool: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
|
||||
"import library generation tool (ignored except when targeting windows-gnu)"),
|
||||
embed_bitcode: bool = (true, parse_bool, [TRACKED],
|
||||
"emit bitcode in rlibs (default: yes)"),
|
||||
extra_filename: String = (String::new(), parse_string, [UNTRACKED],
|
||||
@ -1391,8 +1393,6 @@ options! {
|
||||
(default: no)"),
|
||||
diagnostic_width: Option<usize> = (None, parse_opt_number, [UNTRACKED],
|
||||
"set the current output width for diagnostic truncation"),
|
||||
dlltool: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
|
||||
"import library generation tool (windows-gnu only)"),
|
||||
dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED],
|
||||
"emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) \
|
||||
(default: no)"),
|
||||
|
@ -1744,6 +1744,28 @@ impl SourceFile {
|
||||
BytePos::from_u32(pos.0 - self.start_pos.0 + diff)
|
||||
}
|
||||
|
||||
/// Calculates a normalized byte position from a byte offset relative to the
|
||||
/// start of the file.
|
||||
///
|
||||
/// When we get an inline assembler error from LLVM during codegen, we
|
||||
/// import the expanded assembly code as a new `SourceFile`, which can then
|
||||
/// be used for error reporting with spans. However the byte offsets given
|
||||
/// to us by LLVM are relative to the start of the original buffer, not the
|
||||
/// normalized one. Hence we need to convert those offsets to the normalized
|
||||
/// form when constructing spans.
|
||||
pub fn normalized_byte_pos(&self, offset: u32) -> BytePos {
|
||||
let diff = match self
|
||||
.normalized_pos
|
||||
.binary_search_by(|np| (np.pos.0 + np.diff).cmp(&(self.start_pos.0 + offset)))
|
||||
{
|
||||
Ok(i) => self.normalized_pos[i].diff,
|
||||
Err(i) if i == 0 => 0,
|
||||
Err(i) => self.normalized_pos[i - 1].diff,
|
||||
};
|
||||
|
||||
BytePos::from_u32(self.start_pos.0 + offset - diff)
|
||||
}
|
||||
|
||||
/// Converts an absolute `BytePos` to a `CharPos` relative to the `SourceFile`.
|
||||
pub fn bytepos_to_file_charpos(&self, bpos: BytePos) -> CharPos {
|
||||
// The number of extra bytes due to multibyte chars in the `SourceFile`.
|
||||
@ -2199,6 +2221,7 @@ pub struct ErrorGuaranteed(());
|
||||
impl ErrorGuaranteed {
|
||||
/// To be used only if you really know what you are doing... ideally, we would find a way to
|
||||
/// eliminate all calls to this method.
|
||||
#[deprecated = "`Session::delay_span_bug` should be preferred over this function"]
|
||||
pub fn unchecked_claim_error_was_emitted() -> Self {
|
||||
ErrorGuaranteed(())
|
||||
}
|
||||
|
@ -441,6 +441,7 @@ symbols! {
|
||||
bridge,
|
||||
bswap,
|
||||
c_str,
|
||||
c_str_literals,
|
||||
c_unwind,
|
||||
c_variadic,
|
||||
c_void,
|
||||
@ -1206,6 +1207,7 @@ symbols! {
|
||||
require,
|
||||
residual,
|
||||
result,
|
||||
resume,
|
||||
return_position_impl_trait_in_trait,
|
||||
return_type_notation,
|
||||
rhs,
|
||||
|
@ -839,6 +839,7 @@ pub enum InlineAsmClobberAbi {
|
||||
AArch64,
|
||||
AArch64NoX18,
|
||||
RiscV,
|
||||
LoongArch,
|
||||
}
|
||||
|
||||
impl InlineAsmClobberAbi {
|
||||
@ -880,6 +881,10 @@ impl InlineAsmClobberAbi {
|
||||
"C" | "system" | "efiapi" => Ok(InlineAsmClobberAbi::RiscV),
|
||||
_ => Err(&["C", "system", "efiapi"]),
|
||||
},
|
||||
InlineAsmArch::LoongArch64 => match name {
|
||||
"C" | "system" | "efiapi" => Ok(InlineAsmClobberAbi::LoongArch),
|
||||
_ => Err(&["C", "system", "efiapi"]),
|
||||
},
|
||||
_ => Err(&[]),
|
||||
}
|
||||
}
|
||||
@ -1022,6 +1027,21 @@ impl InlineAsmClobberAbi {
|
||||
v24, v25, v26, v27, v28, v29, v30, v31,
|
||||
}
|
||||
},
|
||||
InlineAsmClobberAbi::LoongArch => clobbered_regs! {
|
||||
LoongArch LoongArchInlineAsmReg {
|
||||
// ra
|
||||
r1,
|
||||
// a0-a7
|
||||
r4, r5, r6, r7, r8, r9, r10, r11,
|
||||
// t0-t8
|
||||
r12, r13, r14, r15, r16, r17, r18, r19, r20,
|
||||
// fa0-fa7
|
||||
f0, f1, f2, f3, f4, f5, f6, f7,
|
||||
// ft0-ft15
|
||||
f8, f9, f10, f11, f12, f13, f14, f15,
|
||||
f16, f17, f18, f19, f20, f21, f22, f23,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,102 +0,0 @@
|
||||
use crate::infer::InferCtxt;
|
||||
|
||||
use rustc_infer::infer::ObligationEmittingRelation;
|
||||
use rustc_infer::traits::PredicateObligations;
|
||||
use rustc_middle::ty::error::TypeError;
|
||||
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
|
||||
pub struct CollectAllMismatches<'a, 'tcx> {
|
||||
pub infcx: &'a InferCtxt<'tcx>,
|
||||
pub param_env: ty::ParamEnv<'tcx>,
|
||||
pub errors: Vec<TypeError<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TypeRelation<'tcx> for CollectAllMismatches<'a, 'tcx> {
|
||||
fn tag(&self) -> &'static str {
|
||||
"CollectAllMismatches"
|
||||
}
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.infcx.tcx
|
||||
}
|
||||
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
self.param_env
|
||||
}
|
||||
|
||||
fn a_is_expected(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn relate_with_variance<T: Relate<'tcx>>(
|
||||
&mut self,
|
||||
_: ty::Variance,
|
||||
_: ty::VarianceDiagInfo<'tcx>,
|
||||
a: T,
|
||||
b: T,
|
||||
) -> RelateResult<'tcx, T> {
|
||||
self.relate(a, b)
|
||||
}
|
||||
|
||||
fn regions(
|
||||
&mut self,
|
||||
a: ty::Region<'tcx>,
|
||||
_b: ty::Region<'tcx>,
|
||||
) -> RelateResult<'tcx, ty::Region<'tcx>> {
|
||||
Ok(a)
|
||||
}
|
||||
|
||||
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
|
||||
self.infcx.probe(|_| {
|
||||
if a.is_ty_var() || b.is_ty_var() {
|
||||
Ok(a)
|
||||
} else {
|
||||
self.infcx.super_combine_tys(self, a, b).or_else(|e| {
|
||||
self.errors.push(e);
|
||||
Ok(a)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn consts(
|
||||
&mut self,
|
||||
a: ty::Const<'tcx>,
|
||||
b: ty::Const<'tcx>,
|
||||
) -> RelateResult<'tcx, ty::Const<'tcx>> {
|
||||
self.infcx.probe(|_| {
|
||||
if a.is_ct_infer() || b.is_ct_infer() {
|
||||
Ok(a)
|
||||
} else {
|
||||
relate::super_relate_consts(self, a, b) // could do something similar here for constants!
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn binders<T: Relate<'tcx>>(
|
||||
&mut self,
|
||||
a: ty::Binder<'tcx, T>,
|
||||
b: ty::Binder<'tcx, T>,
|
||||
) -> RelateResult<'tcx, ty::Binder<'tcx, T>> {
|
||||
Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ObligationEmittingRelation<'tcx> for CollectAllMismatches<'_, 'tcx> {
|
||||
fn alias_relate_direction(&self) -> ty::AliasRelationDirection {
|
||||
// FIXME(deferred_projection_equality): We really should get rid of this relation.
|
||||
ty::AliasRelationDirection::Equate
|
||||
}
|
||||
|
||||
fn register_obligations(&mut self, _obligations: PredicateObligations<'tcx>) {
|
||||
// FIXME(deferred_projection_equality)
|
||||
}
|
||||
|
||||
fn register_predicates(
|
||||
&mut self,
|
||||
_obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>,
|
||||
) {
|
||||
// FIXME(deferred_projection_equality)
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
mod ambiguity;
|
||||
pub mod method_chain;
|
||||
pub mod on_unimplemented;
|
||||
pub mod suggestions;
|
||||
|
||||
@ -559,6 +558,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
suggest_increasing_limit,
|
||||
|err| {
|
||||
self.note_obligation_cause_code(
|
||||
obligation.cause.body_id,
|
||||
err,
|
||||
predicate,
|
||||
obligation.param_env,
|
||||
@ -1431,6 +1431,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
| ObligationCauseCode::ExprItemObligation(..) = code
|
||||
{
|
||||
self.note_obligation_cause_code(
|
||||
error.obligation.cause.body_id,
|
||||
&mut diag,
|
||||
error.obligation.predicate,
|
||||
error.obligation.param_env,
|
||||
@ -2544,6 +2545,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
// message, and fall back to regular note otherwise.
|
||||
if !self.maybe_note_obligation_cause_for_async_await(err, obligation) {
|
||||
self.note_obligation_cause_code(
|
||||
obligation.cause.body_id,
|
||||
err,
|
||||
obligation.predicate,
|
||||
obligation.param_env,
|
||||
|
@ -25,10 +25,9 @@ use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
|
||||
use rustc_hir::{Expr, HirId};
|
||||
use rustc_infer::infer::error_reporting::TypeErrCtxt;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_infer::infer::{InferOk, LateBoundRegionConversionTime};
|
||||
use rustc_infer::infer::{DefineOpaqueTypes, InferOk, LateBoundRegionConversionTime};
|
||||
use rustc_middle::hir::map;
|
||||
use rustc_middle::ty::error::TypeError::{self, Sorts};
|
||||
use rustc_middle::ty::relate::TypeRelation;
|
||||
use rustc_middle::ty::{
|
||||
self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind,
|
||||
GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, InternalSubsts,
|
||||
@ -39,9 +38,9 @@ use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::symbol::{sym, Ident, Symbol};
|
||||
use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP};
|
||||
use rustc_target::spec::abi;
|
||||
use std::iter;
|
||||
use std::ops::Deref;
|
||||
|
||||
use super::method_chain::CollectAllMismatches;
|
||||
use super::InferCtxtPrivExt;
|
||||
use crate::infer::InferCtxtExt as _;
|
||||
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
@ -319,6 +318,7 @@ pub trait TypeErrCtxtExt<'tcx> {
|
||||
|
||||
fn note_obligation_cause_code<T>(
|
||||
&self,
|
||||
body_id: LocalDefId,
|
||||
err: &mut Diagnostic,
|
||||
predicate: T,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
@ -359,8 +359,9 @@ pub trait TypeErrCtxtExt<'tcx> {
|
||||
);
|
||||
fn note_function_argument_obligation(
|
||||
&self,
|
||||
arg_hir_id: HirId,
|
||||
body_id: LocalDefId,
|
||||
err: &mut Diagnostic,
|
||||
arg_hir_id: HirId,
|
||||
parent_code: &ObligationCauseCode<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
predicate: ty::Predicate<'tcx>,
|
||||
@ -2742,6 +2743,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
// bound that introduced the obligation (e.g. `T: Send`).
|
||||
debug!(?next_code);
|
||||
self.note_obligation_cause_code(
|
||||
obligation.cause.body_id,
|
||||
err,
|
||||
obligation.predicate,
|
||||
obligation.param_env,
|
||||
@ -2753,6 +2755,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
|
||||
fn note_obligation_cause_code<T>(
|
||||
&self,
|
||||
body_id: LocalDefId,
|
||||
err: &mut Diagnostic,
|
||||
predicate: T,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
@ -2790,7 +2793,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
| ObligationCauseCode::LetElse
|
||||
| ObligationCauseCode::BinOp { .. }
|
||||
| ObligationCauseCode::AscribeUserTypeProvePredicate(..)
|
||||
| ObligationCauseCode::RustCall => {}
|
||||
| ObligationCauseCode::RustCall
|
||||
| ObligationCauseCode::DropImpl => {}
|
||||
ObligationCauseCode::SliceOrArrayElem => {
|
||||
err.note("slice and array elements must have `Sized` type");
|
||||
}
|
||||
@ -3152,6 +3156,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
// #74711: avoid a stack overflow
|
||||
ensure_sufficient_stack(|| {
|
||||
self.note_obligation_cause_code(
|
||||
body_id,
|
||||
err,
|
||||
parent_predicate,
|
||||
param_env,
|
||||
@ -3163,6 +3168,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
} else {
|
||||
ensure_sufficient_stack(|| {
|
||||
self.note_obligation_cause_code(
|
||||
body_id,
|
||||
err,
|
||||
parent_predicate,
|
||||
param_env,
|
||||
@ -3292,6 +3298,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
// #74711: avoid a stack overflow
|
||||
ensure_sufficient_stack(|| {
|
||||
self.note_obligation_cause_code(
|
||||
body_id,
|
||||
err,
|
||||
parent_predicate,
|
||||
param_env,
|
||||
@ -3307,6 +3314,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
// #74711: avoid a stack overflow
|
||||
ensure_sufficient_stack(|| {
|
||||
self.note_obligation_cause_code(
|
||||
body_id,
|
||||
err,
|
||||
parent_predicate,
|
||||
param_env,
|
||||
@ -3323,8 +3331,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
..
|
||||
} => {
|
||||
self.note_function_argument_obligation(
|
||||
arg_hir_id,
|
||||
body_id,
|
||||
err,
|
||||
arg_hir_id,
|
||||
parent_code,
|
||||
param_env,
|
||||
predicate,
|
||||
@ -3332,6 +3341,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
);
|
||||
ensure_sufficient_stack(|| {
|
||||
self.note_obligation_cause_code(
|
||||
body_id,
|
||||
err,
|
||||
predicate,
|
||||
param_env,
|
||||
@ -3553,8 +3563,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
}
|
||||
fn note_function_argument_obligation(
|
||||
&self,
|
||||
arg_hir_id: HirId,
|
||||
body_id: LocalDefId,
|
||||
err: &mut Diagnostic,
|
||||
arg_hir_id: HirId,
|
||||
parent_code: &ObligationCauseCode<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
failed_pred: ty::Predicate<'tcx>,
|
||||
@ -3587,7 +3598,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
// to an associated type (as seen from `trait_pred`) in the predicate. Like in
|
||||
// trait_pred `S: Sum<<Self as Iterator>::Item>` and predicate `i32: Sum<&()>`
|
||||
let mut type_diffs = vec![];
|
||||
|
||||
if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = parent_code.deref()
|
||||
&& let Some(node_substs) = typeck_results.node_substs_opt(call_hir_id)
|
||||
&& let where_clauses = self.tcx.predicates_of(def_id).instantiate(self.tcx, node_substs)
|
||||
@ -3596,14 +3606,26 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
if let Some(where_pred) = where_pred.to_opt_poly_trait_pred()
|
||||
&& let Some(failed_pred) = failed_pred.to_opt_poly_trait_pred()
|
||||
{
|
||||
let mut c = CollectAllMismatches {
|
||||
infcx: self.infcx,
|
||||
param_env,
|
||||
errors: vec![],
|
||||
let where_pred = self.instantiate_binder_with_placeholders(where_pred);
|
||||
let failed_pred = self.instantiate_binder_with_fresh_vars(
|
||||
expr.span,
|
||||
LateBoundRegionConversionTime::FnCall,
|
||||
failed_pred
|
||||
);
|
||||
|
||||
let zipped =
|
||||
iter::zip(where_pred.trait_ref.substs, failed_pred.trait_ref.substs);
|
||||
for (expected, actual) in zipped {
|
||||
self.probe(|_| {
|
||||
match self
|
||||
.at(&ObligationCause::misc(expr.span, body_id), param_env)
|
||||
.eq(DefineOpaqueTypes::No, expected, actual)
|
||||
{
|
||||
Ok(_) => (), // We ignore nested obligations here for now.
|
||||
Err(err) => type_diffs.push(err),
|
||||
}
|
||||
})
|
||||
};
|
||||
if let Ok(_) = c.relate(where_pred, failed_pred) {
|
||||
type_diffs = c.errors;
|
||||
}
|
||||
} else if let Some(where_pred) = where_pred.to_opt_poly_projection_pred()
|
||||
&& let Some(failed_pred) = failed_pred.to_opt_poly_projection_pred()
|
||||
&& let Some(found) = failed_pred.skip_binder().term.ty()
|
||||
|
@ -62,7 +62,7 @@ pub use self::util::elaborate;
|
||||
pub use self::util::{expand_trait_aliases, TraitAliasExpander};
|
||||
pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
|
||||
pub use self::util::{
|
||||
supertrait_def_ids, supertraits, transitive_bounds, transitive_bounds_that_define_assoc_type,
|
||||
supertrait_def_ids, supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item,
|
||||
SupertraitDefIds,
|
||||
};
|
||||
|
||||
|
@ -177,15 +177,55 @@ fn resolve_associated_item<'tcx>(
|
||||
|
||||
Some(ty::Instance::new(leaf_def.item.def_id, substs))
|
||||
}
|
||||
traits::ImplSource::Generator(generator_data) => Some(Instance {
|
||||
def: ty::InstanceDef::Item(generator_data.generator_def_id),
|
||||
substs: generator_data.substs,
|
||||
}),
|
||||
traits::ImplSource::Future(future_data) => Some(Instance {
|
||||
def: ty::InstanceDef::Item(future_data.generator_def_id),
|
||||
substs: future_data.substs,
|
||||
}),
|
||||
traits::ImplSource::Generator(generator_data) => {
|
||||
if cfg!(debug_assertions) && tcx.item_name(trait_item_id) != sym::resume {
|
||||
// For compiler developers who'd like to add new items to `Generator`,
|
||||
// you either need to generate a shim body, or perhaps return
|
||||
// `InstanceDef::Item` pointing to a trait default method body if
|
||||
// it is given a default implementation by the trait.
|
||||
span_bug!(
|
||||
tcx.def_span(generator_data.generator_def_id),
|
||||
"no definition for `{trait_ref}::{}` for built-in generator type",
|
||||
tcx.item_name(trait_item_id)
|
||||
)
|
||||
}
|
||||
Some(Instance {
|
||||
def: ty::InstanceDef::Item(generator_data.generator_def_id),
|
||||
substs: generator_data.substs,
|
||||
})
|
||||
}
|
||||
traits::ImplSource::Future(future_data) => {
|
||||
if cfg!(debug_assertions) && tcx.item_name(trait_item_id) != sym::poll {
|
||||
// For compiler developers who'd like to add new items to `Future`,
|
||||
// you either need to generate a shim body, or perhaps return
|
||||
// `InstanceDef::Item` pointing to a trait default method body if
|
||||
// it is given a default implementation by the trait.
|
||||
span_bug!(
|
||||
tcx.def_span(future_data.generator_def_id),
|
||||
"no definition for `{trait_ref}::{}` for built-in async generator type",
|
||||
tcx.item_name(trait_item_id)
|
||||
)
|
||||
}
|
||||
Some(Instance {
|
||||
def: ty::InstanceDef::Item(future_data.generator_def_id),
|
||||
substs: future_data.substs,
|
||||
})
|
||||
}
|
||||
traits::ImplSource::Closure(closure_data) => {
|
||||
if cfg!(debug_assertions)
|
||||
&& ![sym::call, sym::call_mut, sym::call_once]
|
||||
.contains(&tcx.item_name(trait_item_id))
|
||||
{
|
||||
// For compiler developers who'd like to add new items to `Fn`/`FnMut`/`FnOnce`,
|
||||
// you either need to generate a shim body, or perhaps return
|
||||
// `InstanceDef::Item` pointing to a trait default method body if
|
||||
// it is given a default implementation by the trait.
|
||||
span_bug!(
|
||||
tcx.def_span(closure_data.closure_def_id),
|
||||
"no definition for `{trait_ref}::{}` for built-in closure type",
|
||||
tcx.item_name(trait_item_id)
|
||||
)
|
||||
}
|
||||
let trait_closure_kind = tcx.fn_trait_kind_from_def_id(trait_id).unwrap();
|
||||
Instance::resolve_closure(
|
||||
tcx,
|
||||
@ -195,11 +235,29 @@ fn resolve_associated_item<'tcx>(
|
||||
)
|
||||
}
|
||||
traits::ImplSource::FnPointer(ref data) => match data.fn_ty.kind() {
|
||||
ty::FnDef(..) | ty::FnPtr(..) => Some(Instance {
|
||||
def: ty::InstanceDef::FnPtrShim(trait_item_id, data.fn_ty),
|
||||
substs: rcvr_substs,
|
||||
}),
|
||||
_ => None,
|
||||
ty::FnDef(..) | ty::FnPtr(..) => {
|
||||
if cfg!(debug_assertions)
|
||||
&& ![sym::call, sym::call_mut, sym::call_once]
|
||||
.contains(&tcx.item_name(trait_item_id))
|
||||
{
|
||||
// For compiler developers who'd like to add new items to `Fn`/`FnMut`/`FnOnce`,
|
||||
// you either need to generate a shim body, or perhaps return
|
||||
// `InstanceDef::Item` pointing to a trait default method body if
|
||||
// it is given a default implementation by the trait.
|
||||
bug!(
|
||||
"no definition for `{trait_ref}::{}` for built-in fn type",
|
||||
tcx.item_name(trait_item_id)
|
||||
)
|
||||
}
|
||||
Some(Instance {
|
||||
def: ty::InstanceDef::FnPtrShim(trait_item_id, data.fn_ty),
|
||||
substs: rcvr_substs,
|
||||
})
|
||||
}
|
||||
_ => bug!(
|
||||
"no built-in definition for `{trait_ref}::{}` for non-fn type",
|
||||
tcx.item_name(trait_item_id)
|
||||
),
|
||||
},
|
||||
traits::ImplSource::Object(ref data) => {
|
||||
traits::get_vtable_index_of_object_method(tcx, data, trait_item_id).map(|index| {
|
||||
|
@ -4,7 +4,7 @@ use rustc_middle::ty::{
|
||||
};
|
||||
use rustc_target::abi::*;
|
||||
|
||||
use std::cmp;
|
||||
use std::assert_matches::assert_matches;
|
||||
|
||||
/// Enforce some basic invariants on layouts.
|
||||
pub(super) fn sanity_check_layout<'tcx>(
|
||||
@ -68,21 +68,31 @@ pub(super) fn sanity_check_layout<'tcx>(
|
||||
}
|
||||
|
||||
fn check_layout_abi<'tcx>(cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, layout: &TyAndLayout<'tcx>) {
|
||||
// Verify the ABI mandated alignment and size.
|
||||
let align = layout.abi.inherent_align(cx).map(|align| align.abi);
|
||||
let size = layout.abi.inherent_size(cx);
|
||||
let Some((align, size)) = align.zip(size) else {
|
||||
assert_matches!(
|
||||
layout.layout.abi(),
|
||||
Abi::Uninhabited | Abi::Aggregate { .. },
|
||||
"ABI unexpectedly missing alignment and/or size in {layout:#?}"
|
||||
);
|
||||
return
|
||||
};
|
||||
assert_eq!(
|
||||
layout.layout.align().abi,
|
||||
align,
|
||||
"alignment mismatch between ABI and layout in {layout:#?}"
|
||||
);
|
||||
assert_eq!(
|
||||
layout.layout.size(),
|
||||
size,
|
||||
"size mismatch between ABI and layout in {layout:#?}"
|
||||
);
|
||||
|
||||
// Verify per-ABI invariants
|
||||
match layout.layout.abi() {
|
||||
Abi::Scalar(scalar) => {
|
||||
// No padding in scalars.
|
||||
let size = scalar.size(cx);
|
||||
let align = scalar.align(cx).abi;
|
||||
assert_eq!(
|
||||
layout.layout.size(),
|
||||
size,
|
||||
"size mismatch between ABI and layout in {layout:#?}"
|
||||
);
|
||||
assert_eq!(
|
||||
layout.layout.align().abi,
|
||||
align,
|
||||
"alignment mismatch between ABI and layout in {layout:#?}"
|
||||
);
|
||||
Abi::Scalar(_) => {
|
||||
// Check that this matches the underlying field.
|
||||
let inner = skip_newtypes(cx, layout);
|
||||
assert!(
|
||||
@ -135,24 +145,6 @@ pub(super) fn sanity_check_layout<'tcx>(
|
||||
}
|
||||
}
|
||||
Abi::ScalarPair(scalar1, scalar2) => {
|
||||
// Sanity-check scalar pairs. Computing the expected size and alignment is a bit of work.
|
||||
let size1 = scalar1.size(cx);
|
||||
let align1 = scalar1.align(cx).abi;
|
||||
let size2 = scalar2.size(cx);
|
||||
let align2 = scalar2.align(cx).abi;
|
||||
let align = cmp::max(align1, align2);
|
||||
let field2_offset = size1.align_to(align2);
|
||||
let size = (field2_offset + size2).align_to(align);
|
||||
assert_eq!(
|
||||
layout.layout.size(),
|
||||
size,
|
||||
"size mismatch between ABI and layout in {layout:#?}"
|
||||
);
|
||||
assert_eq!(
|
||||
layout.layout.align().abi,
|
||||
align,
|
||||
"alignment mismatch between ABI and layout in {layout:#?}",
|
||||
);
|
||||
// Check that the underlying pair of fields matches.
|
||||
let inner = skip_newtypes(cx, layout);
|
||||
assert!(
|
||||
@ -189,8 +181,9 @@ pub(super) fn sanity_check_layout<'tcx>(
|
||||
"`ScalarPair` layout for type with less than two non-ZST fields: {inner:#?}"
|
||||
)
|
||||
});
|
||||
assert!(
|
||||
fields.next().is_none(),
|
||||
assert_matches!(
|
||||
fields.next(),
|
||||
None,
|
||||
"`ScalarPair` layout for type with at least three non-ZST fields: {inner:#?}"
|
||||
);
|
||||
// The fields might be in opposite order.
|
||||
@ -200,6 +193,10 @@ pub(super) fn sanity_check_layout<'tcx>(
|
||||
(offset2, field2, offset1, field1)
|
||||
};
|
||||
// The fields should be at the right offset, and match the `scalar` layout.
|
||||
let size1 = scalar1.size(cx);
|
||||
let align1 = scalar1.align(cx).abi;
|
||||
let size2 = scalar2.size(cx);
|
||||
let align2 = scalar2.align(cx).abi;
|
||||
assert_eq!(
|
||||
offset1,
|
||||
Size::ZERO,
|
||||
@ -213,10 +210,12 @@ pub(super) fn sanity_check_layout<'tcx>(
|
||||
field1.align.abi, align1,
|
||||
"`ScalarPair` first field with bad align in {inner:#?}",
|
||||
);
|
||||
assert!(
|
||||
matches!(field1.abi, Abi::Scalar(_)),
|
||||
assert_matches!(
|
||||
field1.abi,
|
||||
Abi::Scalar(_),
|
||||
"`ScalarPair` first field with bad ABI in {inner:#?}",
|
||||
);
|
||||
let field2_offset = size1.align_to(align2);
|
||||
assert_eq!(
|
||||
offset2, field2_offset,
|
||||
"`ScalarPair` second field at bad offset in {inner:#?}",
|
||||
@ -229,27 +228,14 @@ pub(super) fn sanity_check_layout<'tcx>(
|
||||
field2.align.abi, align2,
|
||||
"`ScalarPair` second field with bad align in {inner:#?}",
|
||||
);
|
||||
assert!(
|
||||
matches!(field2.abi, Abi::Scalar(_)),
|
||||
assert_matches!(
|
||||
field2.abi,
|
||||
Abi::Scalar(_),
|
||||
"`ScalarPair` second field with bad ABI in {inner:#?}",
|
||||
);
|
||||
}
|
||||
Abi::Vector { count, element } => {
|
||||
// No padding in vectors, except possibly for trailing padding to make the size a multiple of align.
|
||||
let size = element.size(cx) * count;
|
||||
let align = cx.data_layout().vector_align(size).abi;
|
||||
let size = size.align_to(align); // needed e.g. for vectors of size 3
|
||||
Abi::Vector { element, .. } => {
|
||||
assert!(align >= element.align(cx).abi); // just sanity-checking `vector_align`.
|
||||
assert_eq!(
|
||||
layout.layout.size(),
|
||||
size,
|
||||
"size mismatch between ABI and layout in {layout:#?}"
|
||||
);
|
||||
assert_eq!(
|
||||
layout.layout.align().abi,
|
||||
align,
|
||||
"alignment mismatch between ABI and layout in {layout:#?}"
|
||||
);
|
||||
// FIXME: Do some kind of check of the inner type, like for Scalar and ScalarPair.
|
||||
}
|
||||
Abi::Uninhabited | Abi::Aggregate { .. } => {} // Nothing to check.
|
||||
|
@ -5,6 +5,7 @@
|
||||
//! This API is completely unstable and subject to change.
|
||||
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(iterator_try_collect)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(never_type)]
|
||||
|
@ -3079,8 +3079,8 @@ impl<'a, K, V, A> CursorMut<'a, K, V, A> {
|
||||
unsafe { self.root.reborrow() }
|
||||
.as_mut()?
|
||||
.borrow_mut()
|
||||
.first_leaf_edge()
|
||||
.next_kv()
|
||||
.last_leaf_edge()
|
||||
.next_back_kv()
|
||||
.ok()?
|
||||
.into_kv_valmut()
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use crate::testing::crash_test::{CrashTestDummy, Panic};
|
||||
use crate::testing::ord_chaos::{Cyclic3, Governed, Governor};
|
||||
use crate::testing::rng::DeterministicRng;
|
||||
use crate::vec::Vec;
|
||||
use core::assert_matches::assert_matches;
|
||||
use std::cmp::Ordering;
|
||||
use std::iter;
|
||||
use std::mem;
|
||||
@ -2448,3 +2449,21 @@ fn test_cursor_mut_insert_after_4() {
|
||||
let mut cur = map.upper_bound_mut(Bound::Included(&2));
|
||||
cur.insert_after(4, 'd');
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cursor_peek_prev_agrees_with_cursor_mut() {
|
||||
let mut map = BTreeMap::from([(1, 1), (2, 2), (3, 3)]);
|
||||
|
||||
let cursor = map.lower_bound(Bound::Excluded(&3));
|
||||
assert!(cursor.key().is_none());
|
||||
|
||||
let prev = cursor.peek_prev();
|
||||
assert_matches!(prev, Some((&3, _)));
|
||||
|
||||
// Shadow names so the two parts of this test match.
|
||||
let mut cursor = map.lower_bound_mut(Bound::Excluded(&3));
|
||||
assert!(cursor.key().is_none());
|
||||
|
||||
let prev = cursor.peek_prev();
|
||||
assert_matches!(prev, Some((&3, _)));
|
||||
}
|
||||
|
@ -4,10 +4,23 @@ use crate::ascii;
|
||||
impl<const N: usize> [u8; N] {
|
||||
/// Converts this array of bytes into a array of ASCII characters,
|
||||
/// or returns `None` if any of the characters is non-ASCII.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(ascii_char)]
|
||||
/// #![feature(const_option)]
|
||||
///
|
||||
/// const HEX_DIGITS: [std::ascii::Char; 16] =
|
||||
/// *b"0123456789abcdef".as_ascii().unwrap();
|
||||
///
|
||||
/// assert_eq!(HEX_DIGITS[1].as_str(), "1");
|
||||
/// assert_eq!(HEX_DIGITS[10].as_str(), "a");
|
||||
/// ```
|
||||
#[unstable(feature = "ascii_char", issue = "110998")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn as_ascii(&self) -> Option<&[ascii::Char; N]> {
|
||||
pub const fn as_ascii(&self) -> Option<&[ascii::Char; N]> {
|
||||
if self.is_ascii() {
|
||||
// SAFETY: Just checked that it's ASCII
|
||||
Some(unsafe { self.as_ascii_unchecked() })
|
||||
|
@ -79,9 +79,9 @@ use crate::str;
|
||||
///
|
||||
/// [str]: prim@str "str"
|
||||
#[derive(Hash)]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "CStr")]
|
||||
#[stable(feature = "core_c_str", since = "1.64.0")]
|
||||
#[rustc_has_incoherent_inherent_impls]
|
||||
#[cfg_attr(not(bootstrap), lang = "CStr")]
|
||||
// FIXME:
|
||||
// `fn from` in `impl From<&CStr> for Box<CStr>` current implementation relies
|
||||
// on `CStr` being layout-compatible with `[u8]`.
|
||||
|
@ -310,6 +310,7 @@ where
|
||||
/// Real logic of both `Flatten` and `FlatMap` which simply delegate to
|
||||
/// this type.
|
||||
#[derive(Clone, Debug)]
|
||||
#[unstable(feature = "trusted_len", issue = "37572")]
|
||||
struct FlattenCompat<I, U> {
|
||||
iter: Fuse<I>,
|
||||
frontiter: Option<U>,
|
||||
@ -463,6 +464,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "trusted_len", issue = "37572")]
|
||||
impl<I, U> Iterator for FlattenCompat<I, U>
|
||||
where
|
||||
I: Iterator<Item: IntoIterator<IntoIter = U, Item = U::Item>>,
|
||||
@ -577,6 +579,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "trusted_len", issue = "37572")]
|
||||
impl<I, U> DoubleEndedIterator for FlattenCompat<I, U>
|
||||
where
|
||||
I: DoubleEndedIterator<Item: IntoIterator<IntoIter = U, Item = U::Item>>,
|
||||
@ -646,6 +649,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "trusted_len", issue = "37572")]
|
||||
unsafe impl<const N: usize, I, T> TrustedLen
|
||||
for FlattenCompat<I, <[T; N] as IntoIterator>::IntoIter>
|
||||
where
|
||||
@ -653,6 +657,7 @@ where
|
||||
{
|
||||
}
|
||||
|
||||
#[unstable(feature = "trusted_len", issue = "37572")]
|
||||
unsafe impl<'a, const N: usize, I, T> TrustedLen
|
||||
for FlattenCompat<I, <&'a [T; N] as IntoIterator>::IntoIter>
|
||||
where
|
||||
@ -660,6 +665,7 @@ where
|
||||
{
|
||||
}
|
||||
|
||||
#[unstable(feature = "trusted_len", issue = "37572")]
|
||||
unsafe impl<'a, const N: usize, I, T> TrustedLen
|
||||
for FlattenCompat<I, <&'a mut [T; N] as IntoIterator>::IntoIter>
|
||||
where
|
||||
|
@ -150,6 +150,7 @@
|
||||
#![feature(const_slice_from_raw_parts_mut)]
|
||||
#![feature(const_slice_from_ref)]
|
||||
#![feature(const_slice_index)]
|
||||
#![feature(const_slice_is_ascii)]
|
||||
#![feature(const_slice_ptr_len)]
|
||||
#![feature(const_slice_split_at_mut)]
|
||||
#![feature(const_str_from_utf8_unchecked_mut)]
|
||||
|
@ -1287,7 +1287,7 @@ impl<T, const N: usize> MaybeUninit<[T; N]> {
|
||||
#[inline]
|
||||
pub const fn transpose(self) -> [MaybeUninit<T>; N] {
|
||||
// SAFETY: T and MaybeUninit<T> have the same layout
|
||||
unsafe { super::transmute_copy(&ManuallyDrop::new(self)) }
|
||||
unsafe { intrinsics::transmute_unchecked(self) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1307,6 +1307,6 @@ impl<T, const N: usize> [MaybeUninit<T>; N] {
|
||||
#[inline]
|
||||
pub const fn transpose(self) -> MaybeUninit<[T; N]> {
|
||||
// SAFETY: T and MaybeUninit<T> have the same layout
|
||||
unsafe { super::transmute_copy(&ManuallyDrop::new(self)) }
|
||||
unsafe { intrinsics::transmute_unchecked(self) }
|
||||
}
|
||||
}
|
||||
|
@ -122,6 +122,7 @@ impl SocketAddr {
|
||||
#[stable(feature = "ip_addr", since = "1.7.0")]
|
||||
#[must_use]
|
||||
#[rustc_const_stable(feature = "const_socketaddr", since = "1.69.0")]
|
||||
#[inline]
|
||||
pub const fn new(ip: IpAddr, port: u16) -> SocketAddr {
|
||||
match ip {
|
||||
IpAddr::V4(a) => SocketAddr::V4(SocketAddrV4::new(a, port)),
|
||||
@ -142,6 +143,7 @@ impl SocketAddr {
|
||||
#[must_use]
|
||||
#[stable(feature = "ip_addr", since = "1.7.0")]
|
||||
#[rustc_const_stable(feature = "const_socketaddr", since = "1.69.0")]
|
||||
#[inline]
|
||||
pub const fn ip(&self) -> IpAddr {
|
||||
match *self {
|
||||
SocketAddr::V4(ref a) => IpAddr::V4(*a.ip()),
|
||||
@ -161,6 +163,7 @@ impl SocketAddr {
|
||||
/// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(10, 10, 0, 1)));
|
||||
/// ```
|
||||
#[stable(feature = "sockaddr_setters", since = "1.9.0")]
|
||||
#[inline]
|
||||
pub fn set_ip(&mut self, new_ip: IpAddr) {
|
||||
// `match (*self, new_ip)` would have us mutate a copy of self only to throw it away.
|
||||
match (self, new_ip) {
|
||||
@ -183,6 +186,7 @@ impl SocketAddr {
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_socketaddr", since = "1.69.0")]
|
||||
#[inline]
|
||||
pub const fn port(&self) -> u16 {
|
||||
match *self {
|
||||
SocketAddr::V4(ref a) => a.port(),
|
||||
@ -202,6 +206,7 @@ impl SocketAddr {
|
||||
/// assert_eq!(socket.port(), 1025);
|
||||
/// ```
|
||||
#[stable(feature = "sockaddr_setters", since = "1.9.0")]
|
||||
#[inline]
|
||||
pub fn set_port(&mut self, new_port: u16) {
|
||||
match *self {
|
||||
SocketAddr::V4(ref mut a) => a.set_port(new_port),
|
||||
@ -227,6 +232,7 @@ impl SocketAddr {
|
||||
#[must_use]
|
||||
#[stable(feature = "sockaddr_checker", since = "1.16.0")]
|
||||
#[rustc_const_stable(feature = "const_socketaddr", since = "1.69.0")]
|
||||
#[inline]
|
||||
pub const fn is_ipv4(&self) -> bool {
|
||||
matches!(*self, SocketAddr::V4(_))
|
||||
}
|
||||
@ -249,6 +255,7 @@ impl SocketAddr {
|
||||
#[must_use]
|
||||
#[stable(feature = "sockaddr_checker", since = "1.16.0")]
|
||||
#[rustc_const_stable(feature = "const_socketaddr", since = "1.69.0")]
|
||||
#[inline]
|
||||
pub const fn is_ipv6(&self) -> bool {
|
||||
matches!(*self, SocketAddr::V6(_))
|
||||
}
|
||||
@ -269,6 +276,7 @@ impl SocketAddrV4 {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[must_use]
|
||||
#[rustc_const_stable(feature = "const_socketaddr", since = "1.69.0")]
|
||||
#[inline]
|
||||
pub const fn new(ip: Ipv4Addr, port: u16) -> SocketAddrV4 {
|
||||
SocketAddrV4 { ip, port }
|
||||
}
|
||||
@ -286,6 +294,7 @@ impl SocketAddrV4 {
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_socketaddr", since = "1.69.0")]
|
||||
#[inline]
|
||||
pub const fn ip(&self) -> &Ipv4Addr {
|
||||
&self.ip
|
||||
}
|
||||
@ -302,6 +311,7 @@ impl SocketAddrV4 {
|
||||
/// assert_eq!(socket.ip(), &Ipv4Addr::new(192, 168, 0, 1));
|
||||
/// ```
|
||||
#[stable(feature = "sockaddr_setters", since = "1.9.0")]
|
||||
#[inline]
|
||||
pub fn set_ip(&mut self, new_ip: Ipv4Addr) {
|
||||
self.ip = new_ip;
|
||||
}
|
||||
@ -319,6 +329,7 @@ impl SocketAddrV4 {
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_socketaddr", since = "1.69.0")]
|
||||
#[inline]
|
||||
pub const fn port(&self) -> u16 {
|
||||
self.port
|
||||
}
|
||||
@ -335,6 +346,7 @@ impl SocketAddrV4 {
|
||||
/// assert_eq!(socket.port(), 4242);
|
||||
/// ```
|
||||
#[stable(feature = "sockaddr_setters", since = "1.9.0")]
|
||||
#[inline]
|
||||
pub fn set_port(&mut self, new_port: u16) {
|
||||
self.port = new_port;
|
||||
}
|
||||
@ -360,6 +372,7 @@ impl SocketAddrV6 {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[must_use]
|
||||
#[rustc_const_stable(feature = "const_socketaddr", since = "1.69.0")]
|
||||
#[inline]
|
||||
pub const fn new(ip: Ipv6Addr, port: u16, flowinfo: u32, scope_id: u32) -> SocketAddrV6 {
|
||||
SocketAddrV6 { ip, port, flowinfo, scope_id }
|
||||
}
|
||||
@ -377,6 +390,7 @@ impl SocketAddrV6 {
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_socketaddr", since = "1.69.0")]
|
||||
#[inline]
|
||||
pub const fn ip(&self) -> &Ipv6Addr {
|
||||
&self.ip
|
||||
}
|
||||
@ -393,6 +407,7 @@ impl SocketAddrV6 {
|
||||
/// assert_eq!(socket.ip(), &Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0));
|
||||
/// ```
|
||||
#[stable(feature = "sockaddr_setters", since = "1.9.0")]
|
||||
#[inline]
|
||||
pub fn set_ip(&mut self, new_ip: Ipv6Addr) {
|
||||
self.ip = new_ip;
|
||||
}
|
||||
@ -410,6 +425,7 @@ impl SocketAddrV6 {
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_socketaddr", since = "1.69.0")]
|
||||
#[inline]
|
||||
pub const fn port(&self) -> u16 {
|
||||
self.port
|
||||
}
|
||||
@ -426,6 +442,7 @@ impl SocketAddrV6 {
|
||||
/// assert_eq!(socket.port(), 4242);
|
||||
/// ```
|
||||
#[stable(feature = "sockaddr_setters", since = "1.9.0")]
|
||||
#[inline]
|
||||
pub fn set_port(&mut self, new_port: u16) {
|
||||
self.port = new_port;
|
||||
}
|
||||
@ -453,6 +470,7 @@ impl SocketAddrV6 {
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_socketaddr", since = "1.69.0")]
|
||||
#[inline]
|
||||
pub const fn flowinfo(&self) -> u32 {
|
||||
self.flowinfo
|
||||
}
|
||||
@ -471,6 +489,7 @@ impl SocketAddrV6 {
|
||||
/// assert_eq!(socket.flowinfo(), 56);
|
||||
/// ```
|
||||
#[stable(feature = "sockaddr_setters", since = "1.9.0")]
|
||||
#[inline]
|
||||
pub fn set_flowinfo(&mut self, new_flowinfo: u32) {
|
||||
self.flowinfo = new_flowinfo;
|
||||
}
|
||||
@ -493,6 +512,7 @@ impl SocketAddrV6 {
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_socketaddr", since = "1.69.0")]
|
||||
#[inline]
|
||||
pub const fn scope_id(&self) -> u32 {
|
||||
self.scope_id
|
||||
}
|
||||
@ -511,6 +531,7 @@ impl SocketAddrV6 {
|
||||
/// assert_eq!(socket.scope_id(), 42);
|
||||
/// ```
|
||||
#[stable(feature = "sockaddr_setters", since = "1.9.0")]
|
||||
#[inline]
|
||||
pub fn set_scope_id(&mut self, new_scope_id: u32) {
|
||||
self.scope_id = new_scope_id;
|
||||
}
|
||||
@ -519,6 +540,7 @@ impl SocketAddrV6 {
|
||||
#[stable(feature = "ip_from_ip", since = "1.16.0")]
|
||||
impl From<SocketAddrV4> for SocketAddr {
|
||||
/// Converts a [`SocketAddrV4`] into a [`SocketAddr::V4`].
|
||||
#[inline]
|
||||
fn from(sock4: SocketAddrV4) -> SocketAddr {
|
||||
SocketAddr::V4(sock4)
|
||||
}
|
||||
@ -527,6 +549,7 @@ impl From<SocketAddrV4> for SocketAddr {
|
||||
#[stable(feature = "ip_from_ip", since = "1.16.0")]
|
||||
impl From<SocketAddrV6> for SocketAddr {
|
||||
/// Converts a [`SocketAddrV6`] into a [`SocketAddr::V6`].
|
||||
#[inline]
|
||||
fn from(sock6: SocketAddrV6) -> SocketAddr {
|
||||
SocketAddr::V6(sock6)
|
||||
}
|
||||
@ -624,6 +647,7 @@ impl fmt::Debug for SocketAddrV6 {
|
||||
|
||||
#[stable(feature = "socketaddr_ordering", since = "1.45.0")]
|
||||
impl PartialOrd for SocketAddrV4 {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &SocketAddrV4) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
@ -631,6 +655,7 @@ impl PartialOrd for SocketAddrV4 {
|
||||
|
||||
#[stable(feature = "socketaddr_ordering", since = "1.45.0")]
|
||||
impl PartialOrd for SocketAddrV6 {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &SocketAddrV6) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
@ -638,6 +663,7 @@ impl PartialOrd for SocketAddrV6 {
|
||||
|
||||
#[stable(feature = "socketaddr_ordering", since = "1.45.0")]
|
||||
impl Ord for SocketAddrV4 {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &SocketAddrV4) -> Ordering {
|
||||
self.ip().cmp(other.ip()).then(self.port().cmp(&other.port()))
|
||||
}
|
||||
@ -645,6 +671,7 @@ impl Ord for SocketAddrV4 {
|
||||
|
||||
#[stable(feature = "socketaddr_ordering", since = "1.45.0")]
|
||||
impl Ord for SocketAddrV6 {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &SocketAddrV6) -> Ordering {
|
||||
self.ip().cmp(other.ip()).then(self.port().cmp(&other.port()))
|
||||
}
|
||||
|
@ -1641,10 +1641,8 @@ impl<T> Option<T> {
|
||||
where
|
||||
F: FnOnce() -> T,
|
||||
{
|
||||
if let None = *self {
|
||||
// the compiler isn't smart enough to know that we are not dropping a `T`
|
||||
// here and wants us to ensure `T` can be dropped at compile time.
|
||||
mem::forget(mem::replace(self, Some(f())))
|
||||
if let None = self {
|
||||
*self = Some(f());
|
||||
}
|
||||
|
||||
// SAFETY: a `None` variant for `self` would have been replaced by a `Some`
|
||||
|
@ -10,9 +10,10 @@ use crate::ops;
|
||||
impl [u8] {
|
||||
/// Checks if all bytes in this slice are within the ASCII range.
|
||||
#[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
|
||||
#[rustc_const_unstable(feature = "const_slice_is_ascii", issue = "111090")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn is_ascii(&self) -> bool {
|
||||
pub const fn is_ascii(&self) -> bool {
|
||||
is_ascii(self)
|
||||
}
|
||||
|
||||
@ -21,7 +22,7 @@ impl [u8] {
|
||||
#[unstable(feature = "ascii_char", issue = "110998")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn as_ascii(&self) -> Option<&[ascii::Char]> {
|
||||
pub const fn as_ascii(&self) -> Option<&[ascii::Char]> {
|
||||
if self.is_ascii() {
|
||||
// SAFETY: Just checked that it's ASCII
|
||||
Some(unsafe { self.as_ascii_unchecked() })
|
||||
@ -262,11 +263,29 @@ impl<'a> fmt::Debug for EscapeAscii<'a> {
|
||||
/// Returns `true` if any byte in the word `v` is nonascii (>= 128). Snarfed
|
||||
/// from `../str/mod.rs`, which does something similar for utf8 validation.
|
||||
#[inline]
|
||||
fn contains_nonascii(v: usize) -> bool {
|
||||
const fn contains_nonascii(v: usize) -> bool {
|
||||
const NONASCII_MASK: usize = usize::repeat_u8(0x80);
|
||||
(NONASCII_MASK & v) != 0
|
||||
}
|
||||
|
||||
/// ASCII test *without* the chunk-at-a-time optimizations.
|
||||
///
|
||||
/// This is carefully structured to produce nice small code -- it's smaller in
|
||||
/// `-O` than what the "obvious" ways produces under `-C opt-level=s`. If you
|
||||
/// touch it, be sure to run (and update if needed) the assembly test.
|
||||
#[unstable(feature = "str_internals", issue = "none")]
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub const fn is_ascii_simple(mut bytes: &[u8]) -> bool {
|
||||
while let [rest @ .., last] = bytes {
|
||||
if !last.is_ascii() {
|
||||
break;
|
||||
}
|
||||
bytes = rest;
|
||||
}
|
||||
bytes.is_empty()
|
||||
}
|
||||
|
||||
/// Optimized ASCII test that will use usize-at-a-time operations instead of
|
||||
/// byte-at-a-time operations (when possible).
|
||||
///
|
||||
@ -280,7 +299,7 @@ fn contains_nonascii(v: usize) -> bool {
|
||||
/// If any of these loads produces something for which `contains_nonascii`
|
||||
/// (above) returns true, then we know the answer is false.
|
||||
#[inline]
|
||||
fn is_ascii(s: &[u8]) -> bool {
|
||||
const fn is_ascii(s: &[u8]) -> bool {
|
||||
const USIZE_SIZE: usize = mem::size_of::<usize>();
|
||||
|
||||
let len = s.len();
|
||||
@ -292,7 +311,7 @@ fn is_ascii(s: &[u8]) -> bool {
|
||||
// We also do this for architectures where `size_of::<usize>()` isn't
|
||||
// sufficient alignment for `usize`, because it's a weird edge case.
|
||||
if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < mem::align_of::<usize>() {
|
||||
return s.iter().all(|b| b.is_ascii());
|
||||
return is_ascii_simple(s);
|
||||
}
|
||||
|
||||
// We always read the first word unaligned, which means `align_offset` is
|
||||
@ -321,18 +340,26 @@ fn is_ascii(s: &[u8]) -> bool {
|
||||
// Paranoia check about alignment, since we're about to do a bunch of
|
||||
// unaligned loads. In practice this should be impossible barring a bug in
|
||||
// `align_offset` though.
|
||||
debug_assert_eq!(word_ptr.addr() % mem::align_of::<usize>(), 0);
|
||||
// While this method is allowed to spuriously fail in CTFE, if it doesn't
|
||||
// have alignment information it should have given a `usize::MAX` for
|
||||
// `align_offset` earlier, sending things through the scalar path instead of
|
||||
// this one, so this check should pass if it's reachable.
|
||||
debug_assert!(word_ptr.is_aligned_to(mem::align_of::<usize>()));
|
||||
|
||||
// Read subsequent words until the last aligned word, excluding the last
|
||||
// aligned word by itself to be done in tail check later, to ensure that
|
||||
// tail is always one `usize` at most to extra branch `byte_pos == len`.
|
||||
while byte_pos < len - USIZE_SIZE {
|
||||
debug_assert!(
|
||||
// Sanity check that the read is in bounds
|
||||
(word_ptr.addr() + USIZE_SIZE) <= start.addr().wrapping_add(len) &&
|
||||
// And that our assumptions about `byte_pos` hold.
|
||||
(word_ptr.addr() - start.addr()) == byte_pos
|
||||
);
|
||||
// Sanity check that the read is in bounds
|
||||
debug_assert!(byte_pos + USIZE_SIZE <= len);
|
||||
// And that our assumptions about `byte_pos` hold.
|
||||
debug_assert!(matches!(
|
||||
word_ptr.cast::<u8>().guaranteed_eq(start.wrapping_add(byte_pos)),
|
||||
// These are from the same allocation, so will hopefully always be
|
||||
// known to match even in CTFE, but if it refuses to compare them
|
||||
// that's ok since it's just a debug check anyway.
|
||||
None | Some(true),
|
||||
));
|
||||
|
||||
// SAFETY: We know `word_ptr` is properly aligned (because of
|
||||
// `align_offset`), and we know that we have enough bytes between `word_ptr` and the end
|
||||
|
@ -44,6 +44,10 @@ mod raw;
|
||||
mod rotate;
|
||||
mod specialize;
|
||||
|
||||
#[unstable(feature = "str_internals", issue = "none")]
|
||||
#[doc(hidden)]
|
||||
pub use ascii::is_ascii_simple;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use iter::{Chunks, ChunksMut, Windows};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
|
@ -2358,9 +2358,10 @@ impl str {
|
||||
/// assert!(!non_ascii.is_ascii());
|
||||
/// ```
|
||||
#[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
|
||||
#[rustc_const_unstable(feature = "const_slice_is_ascii", issue = "111090")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn is_ascii(&self) -> bool {
|
||||
pub const fn is_ascii(&self) -> bool {
|
||||
// We can treat each byte as character here: all multibyte characters
|
||||
// start with a byte that is not in the ASCII range, so we will stop
|
||||
// there already.
|
||||
@ -2372,7 +2373,7 @@ impl str {
|
||||
#[unstable(feature = "ascii_char", issue = "110998")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn as_ascii(&self) -> Option<&[ascii::Char]> {
|
||||
pub const fn as_ascii(&self) -> Option<&[ascii::Char]> {
|
||||
// Like in `is_ascii`, we can work on the bytes directly.
|
||||
self.as_bytes().as_ascii()
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
// See src/libstd/primitive_docs.rs for documentation.
|
||||
|
||||
use crate::cmp::Ordering::{self, *};
|
||||
use crate::mem::transmute;
|
||||
|
||||
// Recursive macro for implementing n-ary tuple functions and operations
|
||||
//
|
||||
@ -142,16 +141,13 @@ macro_rules! maybe_tuple_doc {
|
||||
#[inline]
|
||||
const fn ordering_is_some(c: Option<Ordering>, x: Ordering) -> bool {
|
||||
// FIXME: Just use `==` once that's const-stable on `Option`s.
|
||||
// This isn't using `match` because that optimizes worse due to
|
||||
// making a two-step check (`Some` *then* the inner value).
|
||||
|
||||
// SAFETY: There's no public guarantee for `Option<Ordering>`,
|
||||
// but we're core so we know that it's definitely a byte.
|
||||
unsafe {
|
||||
let c: i8 = transmute(c);
|
||||
let x: i8 = transmute(Some(x));
|
||||
c == x
|
||||
}
|
||||
// This is mapping `None` to 2 and then doing the comparison afterwards
|
||||
// because it optimizes better (`None::<Ordering>` is represented as 2).
|
||||
x as i8
|
||||
== match c {
|
||||
Some(c) => c as i8,
|
||||
None => 2,
|
||||
}
|
||||
}
|
||||
|
||||
// Constructs an expression that performs a lexical ordering using method `$rel`.
|
||||
|
@ -337,6 +337,8 @@ pub enum LitKind {
|
||||
StrRaw(u8),
|
||||
ByteStr,
|
||||
ByteStrRaw(u8),
|
||||
CStr,
|
||||
CStrRaw(u8),
|
||||
Err,
|
||||
}
|
||||
|
||||
@ -350,6 +352,8 @@ rpc_encode_decode!(
|
||||
StrRaw(n),
|
||||
ByteStr,
|
||||
ByteStrRaw(n),
|
||||
CStr,
|
||||
CStrRaw(n),
|
||||
Err,
|
||||
}
|
||||
);
|
||||
|
@ -40,11 +40,7 @@ impl Timespec {
|
||||
}
|
||||
|
||||
fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
|
||||
let mut secs = other
|
||||
.as_secs()
|
||||
.try_into() // <- target type would be `libc::time_t`
|
||||
.ok()
|
||||
.and_then(|secs| self.t.tv_sec.checked_add(secs))?;
|
||||
let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?;
|
||||
|
||||
// Nano calculations can't overflow because nanos are <1B which fit
|
||||
// in a u32.
|
||||
@ -57,11 +53,7 @@ impl Timespec {
|
||||
}
|
||||
|
||||
fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
|
||||
let mut secs = other
|
||||
.as_secs()
|
||||
.try_into() // <- target type would be `libc::time_t`
|
||||
.ok()
|
||||
.and_then(|secs| self.t.tv_sec.checked_sub(secs))?;
|
||||
let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?;
|
||||
|
||||
// Similar to above, nanos can't overflow.
|
||||
let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;
|
||||
|
@ -26,7 +26,7 @@ IMAGE_BASE:
|
||||
.Lxsave_clear:
|
||||
.org .+24
|
||||
.Lxsave_mxcsr:
|
||||
.short 0x1f80
|
||||
.short 0x1fbf
|
||||
|
||||
/* We can store a bunch of data in the gap between MXCSR and the XSAVE header */
|
||||
|
||||
@ -178,6 +178,7 @@ sgx_entry:
|
||||
mov $-1, %rax
|
||||
mov $-1, %rdx
|
||||
xrstor .Lxsave_clear(%rip)
|
||||
lfence
|
||||
mov %r10, %rdx
|
||||
|
||||
/* check if returning from usercall */
|
||||
@ -311,6 +312,9 @@ usercall:
|
||||
movq $0,%gs:tcsls_last_rsp
|
||||
/* restore callee-saved state, cf. "save" above */
|
||||
mov %r11,%rsp
|
||||
/* MCDT mitigation requires an lfence after ldmxcsr _before_ any of the affected */
|
||||
/* vector instructions is used. We omit the lfence here as one is required before */
|
||||
/* the jmp instruction anyway. */
|
||||
ldmxcsr (%rsp)
|
||||
fldcw 4(%rsp)
|
||||
add $8, %rsp
|
||||
|
@ -47,10 +47,10 @@ impl SystemTime {
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
Some(SystemTime(self.0.checked_add(other.as_secs().try_into().ok()?)?))
|
||||
Some(SystemTime(self.0.checked_add_unsigned(other.as_secs())?))
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
Some(SystemTime(self.0.checked_sub(other.as_secs().try_into().ok()?)?))
|
||||
Some(SystemTime(self.0.checked_sub_unsigned(other.as_secs())?))
|
||||
}
|
||||
}
|
||||
|
@ -326,6 +326,25 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> {
|
||||
} else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd"))] {
|
||||
use crate::ptr;
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
{
|
||||
let mut set: libc::cpuset_t = unsafe { mem::zeroed() };
|
||||
unsafe {
|
||||
if libc::cpuset_getaffinity(
|
||||
libc::CPU_LEVEL_WHICH,
|
||||
libc::CPU_WHICH_PID,
|
||||
-1,
|
||||
mem::size_of::<libc::cpuset_t>(),
|
||||
&mut set,
|
||||
) == 0 {
|
||||
let count = libc::CPU_COUNT(&set) as usize;
|
||||
if count > 0 {
|
||||
return Ok(NonZeroUsize::new_unchecked(count));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut cpus: libc::c_uint = 0;
|
||||
let mut cpus_size = crate::mem::size_of_val(&cpus);
|
||||
|
||||
|
@ -113,11 +113,7 @@ impl Timespec {
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
|
||||
let mut secs = other
|
||||
.as_secs()
|
||||
.try_into() // <- target type would be `i64`
|
||||
.ok()
|
||||
.and_then(|secs| self.tv_sec.checked_add(secs))?;
|
||||
let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?;
|
||||
|
||||
// Nano calculations can't overflow because nanos are <1B which fit
|
||||
// in a u32.
|
||||
@ -126,15 +122,11 @@ impl Timespec {
|
||||
nsec -= NSEC_PER_SEC as u32;
|
||||
secs = secs.checked_add(1)?;
|
||||
}
|
||||
Some(Timespec::new(secs, nsec as i64))
|
||||
Some(Timespec::new(secs, nsec.into()))
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
|
||||
let mut secs = other
|
||||
.as_secs()
|
||||
.try_into() // <- target type would be `i64`
|
||||
.ok()
|
||||
.and_then(|secs| self.tv_sec.checked_sub(secs))?;
|
||||
let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?;
|
||||
|
||||
// Similar to above, nanos can't overflow.
|
||||
let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32;
|
||||
@ -142,7 +134,7 @@ impl Timespec {
|
||||
nsec += NSEC_PER_SEC as i32;
|
||||
secs = secs.checked_sub(1)?;
|
||||
}
|
||||
Some(Timespec::new(secs, nsec as i64))
|
||||
Some(Timespec::new(secs, nsec.into()))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user