commit
0d225bcf1e
440 changed files with 8460 additions and 5513 deletions
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
|
|
@ -37,6 +37,7 @@ jobs:
|
|||
name: PR
|
||||
env:
|
||||
CI_JOB_NAME: "${{ matrix.name }}"
|
||||
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
|
||||
SCCACHE_BUCKET: rust-lang-ci-sccache2
|
||||
TOOLSTATE_REPO: "https://github.com/rust-lang-nursery/rust-toolstate"
|
||||
CACHE_DOMAIN: ci-caches.rust-lang.org
|
||||
|
|
@ -162,6 +163,7 @@ jobs:
|
|||
name: auto
|
||||
env:
|
||||
CI_JOB_NAME: "${{ matrix.name }}"
|
||||
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
|
||||
SCCACHE_BUCKET: rust-lang-ci-sccache2
|
||||
DEPLOY_BUCKET: rust-lang-ci2
|
||||
TOOLSTATE_REPO: "https://github.com/rust-lang-nursery/rust-toolstate"
|
||||
|
|
@ -584,6 +586,7 @@ jobs:
|
|||
name: try
|
||||
env:
|
||||
CI_JOB_NAME: "${{ matrix.name }}"
|
||||
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
|
||||
SCCACHE_BUCKET: rust-lang-ci-sccache2
|
||||
DEPLOY_BUCKET: rust-lang-ci2
|
||||
TOOLSTATE_REPO: "https://github.com/rust-lang-nursery/rust-toolstate"
|
||||
|
|
|
|||
1
.mailmap
1
.mailmap
|
|
@ -166,6 +166,7 @@ Eduard-Mihai Burtescu <edy.burt@gmail.com>
|
|||
Eduardo Bautista <me@eduardobautista.com> <=>
|
||||
Eduardo Bautista <me@eduardobautista.com> <mail@eduardobautista.com>
|
||||
Eduardo Broto <ebroto@tutanota.com>
|
||||
Edward Shen <code@eddie.sh> <xes@meta.com>
|
||||
Elliott Slaughter <elliottslaughter@gmail.com> <eslaughter@mozilla.com>
|
||||
Elly Fong-Jones <elly@leptoquark.net>
|
||||
Eric Holk <eric.holk@gmail.com> <eholk@cs.indiana.edu>
|
||||
|
|
|
|||
25
Cargo.lock
25
Cargo.lock
|
|
@ -3567,6 +3567,7 @@ dependencies = [
|
|||
"jemalloc-sys",
|
||||
"rustc_codegen_ssa",
|
||||
"rustc_driver",
|
||||
"rustc_driver_impl",
|
||||
"rustc_smir",
|
||||
]
|
||||
|
||||
|
|
@ -3688,7 +3689,6 @@ dependencies = [
|
|||
name = "rustc_ast_lowering"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rustc_arena",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_data_structures",
|
||||
|
|
@ -3697,7 +3697,6 @@ dependencies = [
|
|||
"rustc_index",
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_query_system",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
|
|
@ -3730,7 +3729,6 @@ name = "rustc_ast_pretty"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rustc_ast",
|
||||
"rustc_parse_format",
|
||||
"rustc_span",
|
||||
]
|
||||
|
||||
|
|
@ -3837,7 +3835,6 @@ dependencies = [
|
|||
"rustc_metadata",
|
||||
"rustc_middle",
|
||||
"rustc_query_system",
|
||||
"rustc_serialize",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_symbol_mangling",
|
||||
|
|
@ -3863,7 +3860,6 @@ dependencies = [
|
|||
"rustc_arena",
|
||||
"rustc_ast",
|
||||
"rustc_attr",
|
||||
"rustc_const_eval",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_fs_util",
|
||||
|
|
@ -3904,7 +3900,6 @@ dependencies = [
|
|||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_mir_dataflow",
|
||||
"rustc_query_system",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
|
|
@ -3946,6 +3941,13 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "rustc_driver"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rustc_driver_impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_driver_impl"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rustc_ast",
|
||||
|
|
@ -4092,15 +4094,12 @@ dependencies = [
|
|||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
"rustc_graphviz",
|
||||
"rustc_hir",
|
||||
"rustc_hir_pretty",
|
||||
"rustc_index",
|
||||
"rustc_infer",
|
||||
"rustc_lint",
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_serialize",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
|
|
@ -4187,7 +4186,6 @@ dependencies = [
|
|||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_serialize",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
"smallvec",
|
||||
|
|
@ -4230,7 +4228,6 @@ dependencies = [
|
|||
"rustc_privacy",
|
||||
"rustc_query_impl",
|
||||
"rustc_resolve",
|
||||
"rustc_serialize",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_symbol_mangling",
|
||||
|
|
@ -4396,7 +4393,6 @@ dependencies = [
|
|||
"rustc_apfloat",
|
||||
"rustc_arena",
|
||||
"rustc_ast",
|
||||
"rustc_attr",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_hir",
|
||||
|
|
@ -4428,7 +4424,6 @@ dependencies = [
|
|||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_serialize",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
"smallvec",
|
||||
|
|
@ -4559,7 +4554,6 @@ dependencies = [
|
|||
"rustc_middle",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_trait_selection",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
|
@ -4580,7 +4574,6 @@ dependencies = [
|
|||
"rustc_serialize",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
"thin-vec",
|
||||
"tracing",
|
||||
]
|
||||
|
|
@ -4781,7 +4774,6 @@ dependencies = [
|
|||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_infer",
|
||||
"rustc_lint_defs",
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_parse_format",
|
||||
|
|
@ -4803,7 +4795,6 @@ dependencies = [
|
|||
"chalk-ir",
|
||||
"chalk-solve",
|
||||
"rustc_ast",
|
||||
"rustc_attr",
|
||||
"rustc_data_structures",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
rustc_driver = { path = "../rustc_driver" }
|
||||
rustc_driver_impl = { path = "../rustc_driver_impl" }
|
||||
|
||||
# Make sure rustc_codegen_ssa ends up in the sysroot, because this
|
||||
# crate is intended to be used by codegen backends, which may not be in-tree.
|
||||
|
|
@ -20,6 +21,6 @@ features = ['unprefixed_malloc_on_supported_platforms']
|
|||
|
||||
[features]
|
||||
jemalloc = ['jemalloc-sys']
|
||||
llvm = ['rustc_driver/llvm']
|
||||
max_level_info = ['rustc_driver/max_level_info']
|
||||
rustc_use_parallel_compiler = ['rustc_driver/rustc_use_parallel_compiler']
|
||||
llvm = ['rustc_driver_impl/llvm']
|
||||
max_level_info = ['rustc_driver_impl/max_level_info']
|
||||
rustc_use_parallel_compiler = ['rustc_driver_impl/rustc_use_parallel_compiler']
|
||||
|
|
|
|||
|
|
@ -1439,21 +1439,12 @@ impl<V: Idx> fmt::Debug for LayoutS<V> {
|
|||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum PointerKind {
|
||||
/// Most general case, we know no restrictions to tell LLVM.
|
||||
SharedMutable,
|
||||
|
||||
/// `&T` where `T` contains no `UnsafeCell`, is `dereferenceable`, `noalias` and `readonly`.
|
||||
Frozen,
|
||||
|
||||
/// `&mut T` which is `dereferenceable` and `noalias` but not `readonly`.
|
||||
UniqueBorrowed,
|
||||
|
||||
/// `&mut !Unpin`, which is `dereferenceable` but neither `noalias` nor `readonly`.
|
||||
UniqueBorrowedPinned,
|
||||
|
||||
/// `Box<T>`, which is `noalias` (even on return types, unlike the above) but neither `readonly`
|
||||
/// nor `dereferenceable`.
|
||||
UniqueOwned,
|
||||
/// Shared reference. `frozen` indicates the absence of any `UnsafeCell`.
|
||||
SharedRef { frozen: bool },
|
||||
/// Mutable reference. `unpin` indicates the absence of any pinned data.
|
||||
MutableRef { unpin: bool },
|
||||
/// Box. `unpin` indicates the absence of any pinned data.
|
||||
Box { unpin: bool },
|
||||
}
|
||||
|
||||
/// Note that this information is advisory only, and backends are free to ignore it.
|
||||
|
|
|
|||
|
|
@ -1826,6 +1826,13 @@ pub enum LitKind {
|
|||
}
|
||||
|
||||
impl LitKind {
|
||||
pub fn str(&self) -> Option<Symbol> {
|
||||
match *self {
|
||||
LitKind::Str(s, _) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this literal is a string.
|
||||
pub fn is_str(&self) -> bool {
|
||||
matches!(self, LitKind::Str(..))
|
||||
|
|
|
|||
|
|
@ -140,17 +140,14 @@ impl Attribute {
|
|||
|
||||
pub fn value_str(&self) -> Option<Symbol> {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(normal) => normal.item.meta_kind().and_then(|kind| kind.value_str()),
|
||||
AttrKind::Normal(normal) => normal.item.value_str(),
|
||||
AttrKind::DocComment(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(normal) => match normal.item.meta_kind() {
|
||||
Some(MetaItemKind::List(list)) => Some(list),
|
||||
_ => None,
|
||||
},
|
||||
AttrKind::Normal(normal) => normal.item.meta_item_list(),
|
||||
AttrKind::DocComment(..) => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -216,6 +213,20 @@ impl MetaItem {
|
|||
}
|
||||
}
|
||||
|
||||
impl AttrArgsEq {
|
||||
fn value_str(&self) -> Option<Symbol> {
|
||||
match self {
|
||||
AttrArgsEq::Ast(expr) => match expr.kind {
|
||||
ExprKind::Lit(token_lit) => {
|
||||
LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
AttrArgsEq::Hir(lit) => lit.kind.str(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AttrItem {
|
||||
pub fn span(&self) -> Span {
|
||||
self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))
|
||||
|
|
@ -228,6 +239,22 @@ impl AttrItem {
|
|||
pub fn meta_kind(&self) -> Option<MetaItemKind> {
|
||||
MetaItemKind::from_attr_args(&self.args)
|
||||
}
|
||||
|
||||
fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
|
||||
match &self.args {
|
||||
AttrArgs::Delimited(args) if args.delim == MacDelimiter::Parenthesis => {
|
||||
MetaItemKind::list_from_tokens(args.tokens.clone())
|
||||
}
|
||||
AttrArgs::Delimited(_) | AttrArgs::Eq(..) | AttrArgs::Empty => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn value_str(&self) -> Option<Symbol> {
|
||||
match &self.args {
|
||||
AttrArgs::Eq(_, args) => args.value_str(),
|
||||
AttrArgs::Delimited(_) | AttrArgs::Empty => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Attribute {
|
||||
|
|
@ -247,13 +274,11 @@ impl Attribute {
|
|||
/// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
|
||||
/// * `#[doc(...)]` returns `None`.
|
||||
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
|
||||
match self.kind {
|
||||
AttrKind::DocComment(kind, data) => Some((data, kind)),
|
||||
AttrKind::Normal(ref normal) if normal.item.path == sym::doc => normal
|
||||
.item
|
||||
.meta_kind()
|
||||
.and_then(|kind| kind.value_str())
|
||||
.map(|data| (data, CommentKind::Line)),
|
||||
match &self.kind {
|
||||
AttrKind::DocComment(kind, data) => Some((*data, *kind)),
|
||||
AttrKind::Normal(normal) if normal.item.path == sym::doc => {
|
||||
normal.item.value_str().map(|s| (s, CommentKind::Line))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -265,9 +290,7 @@ impl Attribute {
|
|||
pub fn doc_str(&self) -> Option<Symbol> {
|
||||
match &self.kind {
|
||||
AttrKind::DocComment(.., data) => Some(*data),
|
||||
AttrKind::Normal(normal) if normal.item.path == sym::doc => {
|
||||
normal.item.meta_kind().and_then(|kind| kind.value_str())
|
||||
}
|
||||
AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -508,15 +531,12 @@ impl MetaItem {
|
|||
impl MetaItemKind {
|
||||
pub fn value_str(&self) -> Option<Symbol> {
|
||||
match self {
|
||||
MetaItemKind::NameValue(v) => match v.kind {
|
||||
LitKind::Str(s, _) => Some(s),
|
||||
_ => None,
|
||||
},
|
||||
MetaItemKind::NameValue(v) => v.kind.str(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn list_from_tokens(tokens: TokenStream) -> Option<MetaItemKind> {
|
||||
fn list_from_tokens(tokens: TokenStream) -> Option<Vec<NestedMetaItem>> {
|
||||
let mut tokens = tokens.into_trees().peekable();
|
||||
let mut result = Vec::new();
|
||||
while tokens.peek().is_some() {
|
||||
|
|
@ -527,7 +547,7 @@ impl MetaItemKind {
|
|||
_ => return None,
|
||||
}
|
||||
}
|
||||
Some(MetaItemKind::List(result))
|
||||
Some(result)
|
||||
}
|
||||
|
||||
fn name_value_from_tokens(
|
||||
|
|
@ -551,7 +571,7 @@ impl MetaItemKind {
|
|||
dspan: _,
|
||||
delim: MacDelimiter::Parenthesis,
|
||||
tokens,
|
||||
}) => MetaItemKind::list_from_tokens(tokens.clone()),
|
||||
}) => MetaItemKind::list_from_tokens(tokens.clone()).map(MetaItemKind::List),
|
||||
AttrArgs::Delimited(..) => None,
|
||||
AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => match expr.kind {
|
||||
ExprKind::Lit(token_lit) => {
|
||||
|
|
@ -573,7 +593,7 @@ impl MetaItemKind {
|
|||
Some(TokenTree::Delimited(_, Delimiter::Parenthesis, inner_tokens)) => {
|
||||
let inner_tokens = inner_tokens.clone();
|
||||
tokens.next();
|
||||
MetaItemKind::list_from_tokens(inner_tokens)
|
||||
MetaItemKind::list_from_tokens(inner_tokens).map(MetaItemKind::List)
|
||||
}
|
||||
Some(TokenTree::Delimited(..)) => None,
|
||||
Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ use std::{fmt, iter};
|
|||
/// Nothing special happens to misnamed or misplaced `SubstNt`s.
|
||||
#[derive(Debug, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
|
||||
pub enum TokenTree {
|
||||
/// A single token.
|
||||
/// A single token. Should never be `OpenDelim` or `CloseDelim`, because
|
||||
/// delimiters are implicitly represented by `Delimited`.
|
||||
Token(Token, Spacing),
|
||||
/// A delimited sequence of token trees.
|
||||
Delimited(DelimSpan, Delimiter, TokenStream),
|
||||
|
|
@ -388,12 +389,12 @@ impl TokenStream {
|
|||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn trees(&self) -> CursorRef<'_> {
|
||||
CursorRef::new(self)
|
||||
pub fn trees(&self) -> RefTokenTreeCursor<'_> {
|
||||
RefTokenTreeCursor::new(self)
|
||||
}
|
||||
|
||||
pub fn into_trees(self) -> Cursor {
|
||||
Cursor::new(self)
|
||||
pub fn into_trees(self) -> TokenTreeCursor {
|
||||
TokenTreeCursor::new(self)
|
||||
}
|
||||
|
||||
/// Compares two `TokenStream`s, checking equality without regarding span information.
|
||||
|
|
@ -551,16 +552,17 @@ impl TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
/// By-reference iterator over a [`TokenStream`].
|
||||
/// By-reference iterator over a [`TokenStream`], that produces `&TokenTree`
|
||||
/// items.
|
||||
#[derive(Clone)]
|
||||
pub struct CursorRef<'t> {
|
||||
pub struct RefTokenTreeCursor<'t> {
|
||||
stream: &'t TokenStream,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'t> CursorRef<'t> {
|
||||
impl<'t> RefTokenTreeCursor<'t> {
|
||||
fn new(stream: &'t TokenStream) -> Self {
|
||||
CursorRef { stream, index: 0 }
|
||||
RefTokenTreeCursor { stream, index: 0 }
|
||||
}
|
||||
|
||||
pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
|
||||
|
|
@ -568,7 +570,7 @@ impl<'t> CursorRef<'t> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'t> Iterator for CursorRef<'t> {
|
||||
impl<'t> Iterator for RefTokenTreeCursor<'t> {
|
||||
type Item = &'t TokenTree;
|
||||
|
||||
fn next(&mut self) -> Option<&'t TokenTree> {
|
||||
|
|
@ -579,15 +581,16 @@ impl<'t> Iterator for CursorRef<'t> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Owning by-value iterator over a [`TokenStream`].
|
||||
/// Owning by-value iterator over a [`TokenStream`], that produces `TokenTree`
|
||||
/// items.
|
||||
// FIXME: Many uses of this can be replaced with by-reference iterator to avoid clones.
|
||||
#[derive(Clone)]
|
||||
pub struct Cursor {
|
||||
pub struct TokenTreeCursor {
|
||||
pub stream: TokenStream,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl Iterator for Cursor {
|
||||
impl Iterator for TokenTreeCursor {
|
||||
type Item = TokenTree;
|
||||
|
||||
fn next(&mut self) -> Option<TokenTree> {
|
||||
|
|
@ -598,9 +601,9 @@ impl Iterator for Cursor {
|
|||
}
|
||||
}
|
||||
|
||||
impl Cursor {
|
||||
impl TokenTreeCursor {
|
||||
fn new(stream: TokenStream) -> Self {
|
||||
Cursor { stream, index: 0 }
|
||||
TokenTreeCursor { stream, index: 0 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -614,6 +617,15 @@ impl Cursor {
|
|||
pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
|
||||
self.stream.0.get(self.index + n)
|
||||
}
|
||||
|
||||
// Replace the previously obtained token tree with `tts`, and rewind to
|
||||
// just before them.
|
||||
pub fn replace_prev_and_rewind(&mut self, tts: Vec<TokenTree>) {
|
||||
assert!(self.index > 0);
|
||||
self.index -= 1;
|
||||
let stream = Lrc::make_mut(&mut self.stream.0);
|
||||
stream.splice(self.index..self.index + 1, tts);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
|
||||
|
|
|
|||
|
|
@ -403,8 +403,8 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
|
|||
walk_list!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Ref);
|
||||
visitor.visit_ty(&mutable_type.ty)
|
||||
}
|
||||
TyKind::Tup(tys) => {
|
||||
walk_list!(visitor, visit_ty, tys);
|
||||
TyKind::Tup(tuple_element_types) => {
|
||||
walk_list!(visitor, visit_ty, tuple_element_types);
|
||||
}
|
||||
TyKind::BareFn(function_declaration) => {
|
||||
walk_list!(visitor, visit_generic_param, &function_declaration.generic_params);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ edition = "2021"
|
|||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
rustc_arena = { path = "../rustc_arena" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
|
|
@ -16,7 +15,6 @@ rustc_hir = { path = "../rustc_hir" }
|
|||
rustc_index = { path = "../rustc_index" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_query_system = { path = "../rustc_query_system" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
|
|
|
|||
|
|
@ -7,5 +7,4 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_parse_format = { path = "../rustc_parse_format" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ pub fn print_crate<'a>(
|
|||
|
||||
// Currently, in Rust 2018 we don't have `extern crate std;` at the crate
|
||||
// root, so this is not needed, and actually breaks things.
|
||||
if edition.rust_2015() {
|
||||
if edition.is_rust_2015() {
|
||||
// `#![no_std]`
|
||||
let fake_attr = attr::mk_attr_word(g, ast::AttrStyle::Inner, sym::no_std, DUMMY_SP);
|
||||
s.print_attribute(&fake_attr);
|
||||
|
|
|
|||
|
|
@ -2645,6 +2645,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
operands,
|
||||
) = rvalue
|
||||
{
|
||||
let def_id = def_id.expect_local();
|
||||
for operand in operands {
|
||||
let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
|
||||
continue;
|
||||
|
|
@ -2667,7 +2668,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
// into a place then we should annotate the closure in
|
||||
// case it ends up being assigned into the return place.
|
||||
annotated_closure =
|
||||
self.annotate_fn_sig(*def_id, substs.as_closure().sig());
|
||||
self.annotate_fn_sig(def_id, substs.as_closure().sig());
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: \
|
||||
annotated_closure={:?} assigned_from_local={:?} \
|
||||
|
|
|
|||
|
|
@ -817,6 +817,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
&& let AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) = **kind
|
||||
{
|
||||
debug!("move_spans: def_id={:?} places={:?}", def_id, places);
|
||||
let def_id = def_id.expect_local();
|
||||
if let Some((args_span, generator_kind, capture_kind_span, path_span)) =
|
||||
self.closure_span(def_id, moved_place, places)
|
||||
{
|
||||
|
|
@ -945,6 +946,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
box AggregateKind::Generator(def_id, _, _) => (def_id, true),
|
||||
_ => continue,
|
||||
};
|
||||
let def_id = def_id.expect_local();
|
||||
|
||||
debug!(
|
||||
"borrow_spans: def_id={:?} is_generator={:?} places={:?}",
|
||||
|
|
|
|||
|
|
@ -606,12 +606,63 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
Some((false, err_label_span, message)) => {
|
||||
err.span_label(
|
||||
err_label_span,
|
||||
&format!(
|
||||
"consider changing this binding's type to be: `{message}`"
|
||||
),
|
||||
);
|
||||
struct BindingFinder {
|
||||
span: Span,
|
||||
hir_id: Option<hir::HirId>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for BindingFinder {
|
||||
fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
|
||||
if let hir::StmtKind::Local(local) = s.kind {
|
||||
if local.pat.span == self.span {
|
||||
self.hir_id = Some(local.hir_id);
|
||||
}
|
||||
}
|
||||
hir::intravisit::walk_stmt(self, s);
|
||||
}
|
||||
}
|
||||
let hir_map = self.infcx.tcx.hir();
|
||||
let def_id = self.body.source.def_id();
|
||||
let hir_id = hir_map.local_def_id_to_hir_id(def_id.expect_local());
|
||||
let node = hir_map.find(hir_id);
|
||||
let hir_id = if let Some(hir::Node::Item(item)) = node
|
||||
&& let hir::ItemKind::Fn(.., body_id) = item.kind
|
||||
{
|
||||
let body = hir_map.body(body_id);
|
||||
let mut v = BindingFinder {
|
||||
span: err_label_span,
|
||||
hir_id: None,
|
||||
};
|
||||
v.visit_body(body);
|
||||
v.hir_id
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(hir_id) = hir_id
|
||||
&& let Some(hir::Node::Local(local)) = hir_map.find(hir_id)
|
||||
{
|
||||
let (changing, span, sugg) = match local.ty {
|
||||
Some(ty) => ("changing", ty.span, message),
|
||||
None => (
|
||||
"specifying",
|
||||
local.pat.span.shrink_to_hi(),
|
||||
format!(": {message}"),
|
||||
),
|
||||
};
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
&format!("consider {changing} this binding's type"),
|
||||
sugg,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
} else {
|
||||
err.span_label(
|
||||
err_label_span,
|
||||
&format!(
|
||||
"consider changing this binding's type to be: `{message}`"
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -583,10 +583,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
let err = FnMutError {
|
||||
span: *span,
|
||||
ty_err: match output_ty.kind() {
|
||||
ty::Closure(_, _) => FnMutReturnTypeErr::ReturnClosure { span: *span },
|
||||
ty::Generator(def, ..) if self.infcx.tcx.generator_is_async(*def) => {
|
||||
FnMutReturnTypeErr::ReturnAsyncBlock { span: *span }
|
||||
}
|
||||
_ if output_ty.contains_closure() => {
|
||||
FnMutReturnTypeErr::ReturnClosure { span: *span }
|
||||
}
|
||||
_ => FnMutReturnTypeErr::ReturnRef { span: *span },
|
||||
},
|
||||
};
|
||||
|
|
@ -997,7 +999,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
fn suggest_move_on_borrowing_closure(&self, diag: &mut Diagnostic) {
|
||||
let map = self.infcx.tcx.hir();
|
||||
let body_id = map.body_owned_by(self.mir_def_id());
|
||||
let expr = &map.body(body_id).value;
|
||||
let expr = &map.body(body_id).value.peel_blocks();
|
||||
let mut closure_span = None::<rustc_span::Span>;
|
||||
match expr.kind {
|
||||
hir::ExprKind::MethodCall(.., args, _) => {
|
||||
|
|
@ -1012,20 +1014,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
hir::ExprKind::Block(blk, _) => {
|
||||
if let Some(expr) = blk.expr {
|
||||
// only when the block is a closure
|
||||
if let hir::ExprKind::Closure(hir::Closure {
|
||||
capture_clause: hir::CaptureBy::Ref,
|
||||
body,
|
||||
..
|
||||
}) = expr.kind
|
||||
{
|
||||
let body = map.body(*body);
|
||||
if !matches!(body.generator_kind, Some(hir::GeneratorKind::Async(..))) {
|
||||
closure_span = Some(expr.span.shrink_to_lo());
|
||||
}
|
||||
}
|
||||
hir::ExprKind::Closure(hir::Closure {
|
||||
capture_clause: hir::CaptureBy::Ref,
|
||||
body,
|
||||
..
|
||||
}) => {
|
||||
let body = map.body(*body);
|
||||
if !matches!(body.generator_kind, Some(hir::GeneratorKind::Async(..))) {
|
||||
closure_span = Some(expr.span.shrink_to_lo());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
|||
|
|
@ -1278,6 +1278,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
// in order to populate our used_mut set.
|
||||
match **aggregate_kind {
|
||||
AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) => {
|
||||
let def_id = def_id.expect_local();
|
||||
let BorrowCheckResult { used_mut_upvars, .. } =
|
||||
self.infcx.tcx.mir_borrowck(def_id);
|
||||
debug!("{:?} used_mut_upvars={:?}", def_id, used_mut_upvars);
|
||||
|
|
|
|||
|
|
@ -1484,7 +1484,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
None => {
|
||||
if !sig.output().is_privately_uninhabited(self.tcx(), self.param_env) {
|
||||
// The signature in this call can reference region variables,
|
||||
// so erase them before calling a query.
|
||||
let output_ty = self.tcx().erase_regions(sig.output());
|
||||
if !output_ty.is_privately_uninhabited(self.tcx(), self.param_env) {
|
||||
span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
|
||||
}
|
||||
}
|
||||
|
|
@ -2533,7 +2536,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
// clauses on the struct.
|
||||
AggregateKind::Closure(def_id, substs)
|
||||
| AggregateKind::Generator(def_id, substs, _) => {
|
||||
(def_id.to_def_id(), self.prove_closure_bounds(tcx, def_id, substs, location))
|
||||
(def_id, self.prove_closure_bounds(tcx, def_id.expect_local(), substs, location))
|
||||
}
|
||||
|
||||
AggregateKind::Array(_) | AggregateKind::Tuple => {
|
||||
|
|
|
|||
|
|
@ -76,6 +76,21 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
|
|||
// The number of fields that can be handled without an array.
|
||||
const CUTOFF: usize = 5;
|
||||
|
||||
fn expr_for_field(
|
||||
cx: &ExtCtxt<'_>,
|
||||
field: &FieldInfo,
|
||||
index: usize,
|
||||
len: usize,
|
||||
) -> ast::ptr::P<ast::Expr> {
|
||||
if index < len - 1 {
|
||||
field.self_expr.clone()
|
||||
} else {
|
||||
// Unsized types need an extra indirection, but only the last field
|
||||
// may be unsized.
|
||||
cx.expr_addr_of(field.span, field.self_expr.clone())
|
||||
}
|
||||
}
|
||||
|
||||
if fields.is_empty() {
|
||||
// Special case for no fields.
|
||||
let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
|
||||
|
|
@ -98,8 +113,8 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
|
|||
let name = cx.expr_str(field.span, field.name.unwrap().name);
|
||||
args.push(name);
|
||||
}
|
||||
// Use an extra indirection to make sure this works for unsized types.
|
||||
let field = cx.expr_addr_of(field.span, field.self_expr.clone());
|
||||
|
||||
let field = expr_for_field(cx, field, i, fields.len());
|
||||
args.push(field);
|
||||
}
|
||||
let expr = cx.expr_call_global(span, fn_path_debug, args);
|
||||
|
|
@ -109,13 +124,13 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
|
|||
let mut name_exprs = Vec::with_capacity(fields.len());
|
||||
let mut value_exprs = Vec::with_capacity(fields.len());
|
||||
|
||||
for field in fields {
|
||||
for i in 0..fields.len() {
|
||||
let field = &fields[i];
|
||||
if is_struct {
|
||||
name_exprs.push(cx.expr_str(field.span, field.name.unwrap().name));
|
||||
}
|
||||
|
||||
// Use an extra indirection to make sure this works for unsized types.
|
||||
let field = cx.expr_addr_of(field.span, field.self_expr.clone());
|
||||
let field = expr_for_field(cx, field, i, fields.len());
|
||||
value_exprs.push(field);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ rustc_macros = { path = "../rustc_macros" }
|
|||
rustc_metadata = { path = "../rustc_metadata" }
|
||||
rustc_query_system = { path = "../rustc_query_system" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_serialize = { path = "../rustc_serialize" }
|
||||
rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ rustc_metadata = { path = "../rustc_metadata" }
|
|||
rustc_query_system = { path = "../rustc_query_system" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_const_eval = { path = "../rustc_const_eval" }
|
||||
|
||||
[dependencies.object]
|
||||
version = "0.30.1"
|
||||
|
|
|
|||
|
|
@ -270,10 +270,9 @@ pub fn each_linked_rlib(
|
|||
|
||||
/// Create an 'rlib'.
|
||||
///
|
||||
/// An rlib in its current incarnation is essentially a renamed .a file. The rlib primarily contains
|
||||
/// the object file of the crate, but it also contains all of the object files from native
|
||||
/// libraries. This is done by unzipping native libraries and inserting all of the contents into
|
||||
/// this archive.
|
||||
/// An rlib in its current incarnation is essentially a renamed .a file (with "dummy" object files).
|
||||
/// The rlib primarily contains the object file of the crate, but it also some of the object files
|
||||
/// from native libraries.
|
||||
fn link_rlib<'a>(
|
||||
sess: &'a Session,
|
||||
archive_builder_builder: &dyn ArchiveBuilderBuilder,
|
||||
|
|
@ -347,44 +346,23 @@ fn link_rlib<'a>(
|
|||
// loaded from the libraries found here and then encode that into the
|
||||
// metadata of the rlib we're generating somehow.
|
||||
for lib in codegen_results.crate_info.used_libraries.iter() {
|
||||
match lib.kind {
|
||||
NativeLibKind::Static { bundle: None | Some(true), whole_archive: Some(true) }
|
||||
if flavor == RlibFlavor::Normal && sess.opts.unstable_opts.packed_bundled_libs => {}
|
||||
NativeLibKind::Static { bundle: None | Some(true), whole_archive: Some(true) }
|
||||
if flavor == RlibFlavor::Normal =>
|
||||
{
|
||||
// Don't allow mixing +bundle with +whole_archive since an rlib may contain
|
||||
// multiple native libs, some of which are +whole-archive and some of which are
|
||||
// -whole-archive and it isn't clear how we can currently handle such a
|
||||
// situation correctly.
|
||||
// See https://github.com/rust-lang/rust/issues/88085#issuecomment-901050897
|
||||
sess.emit_err(errors::IncompatibleLinkingModifiers);
|
||||
}
|
||||
NativeLibKind::Static { bundle: None | Some(true), .. } => {}
|
||||
NativeLibKind::Static { bundle: Some(false), .. }
|
||||
| NativeLibKind::Dylib { .. }
|
||||
| NativeLibKind::Framework { .. }
|
||||
| NativeLibKind::RawDylib
|
||||
| NativeLibKind::LinkArg
|
||||
| NativeLibKind::Unspecified => continue,
|
||||
let NativeLibKind::Static { bundle: None | Some(true), whole_archive } = lib.kind else {
|
||||
continue;
|
||||
};
|
||||
if whole_archive == Some(true) && !codegen_results.crate_info.feature_packed_bundled_libs {
|
||||
sess.emit_err(errors::IncompatibleLinkingModifiers);
|
||||
}
|
||||
if let Some(name) = lib.name {
|
||||
let location =
|
||||
if flavor == RlibFlavor::Normal && let Some(filename) = lib.filename {
|
||||
let path = find_native_static_library(filename.as_str(), true, &lib_search_paths, sess);
|
||||
let src = read(path).map_err(|e| sess.emit_fatal(errors::ReadFileError {message: e }))?;
|
||||
let (data, _) = create_wrapper_file(sess, b".bundled_lib".to_vec(), &src);
|
||||
let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str());
|
||||
packed_bundled_libs.push(wrapper_file);
|
||||
} else if let Some(name) = lib.name {
|
||||
let path =
|
||||
find_native_static_library(name.as_str(), lib.verbatim, &lib_search_paths, sess);
|
||||
if sess.opts.unstable_opts.packed_bundled_libs && flavor == RlibFlavor::Normal {
|
||||
let filename = lib.filename.unwrap();
|
||||
let lib_path =
|
||||
find_native_static_library(filename.as_str(), true, &lib_search_paths, sess);
|
||||
let src = read(lib_path)
|
||||
.map_err(|e| sess.emit_fatal(errors::ReadFileError { message: e }))?;
|
||||
let (data, _) = create_wrapper_file(sess, b".bundled_lib".to_vec(), &src);
|
||||
let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str());
|
||||
packed_bundled_libs.push(wrapper_file);
|
||||
continue;
|
||||
}
|
||||
ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|error| {
|
||||
sess.emit_fatal(errors::AddNativeLibrary { library_path: location, error });
|
||||
});
|
||||
ab.add_archive(&path, Box::new(|_| false)).unwrap_or_else(|error| {
|
||||
sess.emit_fatal(errors::AddNativeLibrary { library_path: path, error })});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -516,36 +494,14 @@ fn link_staticlib<'a>(
|
|||
&codegen_results.crate_info,
|
||||
Some(CrateType::Staticlib),
|
||||
&mut |cnum, path| {
|
||||
let name = codegen_results.crate_info.crate_name[&cnum];
|
||||
let native_libs = &codegen_results.crate_info.native_libraries[&cnum];
|
||||
|
||||
// Here when we include the rlib into our staticlib we need to make a
|
||||
// decision whether to include the extra object files along the way.
|
||||
// These extra object files come from statically included native
|
||||
// libraries, but they may be cfg'd away with #[link(cfg(..))].
|
||||
//
|
||||
// This unstable feature, though, only needs liblibc to work. The only
|
||||
// use case there is where musl is statically included in liblibc.rlib,
|
||||
// so if we don't want the included version we just need to skip it. As
|
||||
// a result the logic here is that if *any* linked library is cfg'd away
|
||||
// we just skip all object files.
|
||||
//
|
||||
// Clearly this is not sufficient for a general purpose feature, and
|
||||
// we'd want to read from the library's metadata to determine which
|
||||
// object files come from where and selectively skip them.
|
||||
let skip_object_files = native_libs.iter().any(|lib| {
|
||||
matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. })
|
||||
&& !relevant_lib(sess, lib)
|
||||
});
|
||||
|
||||
let lto = are_upstream_rust_objects_already_included(sess)
|
||||
&& !ignored_for_lto(sess, &codegen_results.crate_info, cnum);
|
||||
|
||||
// Ignoring obj file starting with the crate name
|
||||
// as simple comparison is not enough - there
|
||||
// might be also an extra name suffix
|
||||
let obj_start = name.as_str().to_owned();
|
||||
let native_libs = codegen_results.crate_info.native_libraries[&cnum].iter();
|
||||
let relevant = native_libs.clone().filter(|lib| relevant_lib(sess, &lib));
|
||||
let relevant_libs: FxHashSet<_> = relevant.filter_map(|lib| lib.filename).collect();
|
||||
|
||||
let bundled_libs: FxHashSet<_> = native_libs.filter_map(|lib| lib.filename).collect();
|
||||
ab.add_archive(
|
||||
path,
|
||||
Box::new(move |fname: &str| {
|
||||
|
|
@ -559,20 +515,25 @@ fn link_staticlib<'a>(
|
|||
return true;
|
||||
}
|
||||
|
||||
// Otherwise if this is *not* a rust object and we're skipping
|
||||
// objects then skip this file
|
||||
if skip_object_files
|
||||
&& (!fname.starts_with(&obj_start) || !fname.ends_with(".o"))
|
||||
{
|
||||
// Skip objects for bundled libs.
|
||||
if bundled_libs.contains(&Symbol::intern(fname)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ok, don't skip this
|
||||
false
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
archive_builder_builder
|
||||
.extract_bundled_libs(path, tempdir.as_ref(), &relevant_libs)
|
||||
.unwrap_or_else(|e| sess.emit_fatal(e));
|
||||
for filename in relevant_libs {
|
||||
let joined = tempdir.as_ref().join(filename.as_str());
|
||||
let path = joined.as_path();
|
||||
ab.add_archive(path, Box::new(|_| false)).unwrap();
|
||||
}
|
||||
|
||||
all_native_libs
|
||||
.extend(codegen_results.crate_info.native_libraries[&cnum].iter().cloned());
|
||||
},
|
||||
|
|
@ -2590,18 +2551,8 @@ fn add_static_crate<'a>(
|
|||
cmd.link_rlib(&fix_windows_verbatim_for_gcc(path));
|
||||
};
|
||||
|
||||
// See the comment above in `link_staticlib` and `link_rlib` for why if
|
||||
// there's a static library that's not relevant we skip all object
|
||||
// files.
|
||||
let native_libs = &codegen_results.crate_info.native_libraries[&cnum];
|
||||
let skip_native = native_libs.iter().any(|lib| {
|
||||
matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. })
|
||||
&& !relevant_lib(sess, lib)
|
||||
});
|
||||
|
||||
if (!are_upstream_rust_objects_already_included(sess)
|
||||
|| ignored_for_lto(sess, &codegen_results.crate_info, cnum))
|
||||
&& !skip_native
|
||||
if !are_upstream_rust_objects_already_included(sess)
|
||||
|| ignored_for_lto(sess, &codegen_results.crate_info, cnum)
|
||||
{
|
||||
link_upstream(cratepath);
|
||||
return;
|
||||
|
|
@ -2632,17 +2583,13 @@ fn add_static_crate<'a>(
|
|||
let is_rust_object =
|
||||
canonical.starts_with(&canonical_name) && looks_like_rust_object_file(&f);
|
||||
|
||||
// If we've been requested to skip all native object files
|
||||
// (those not generated by the rust compiler) then we can skip
|
||||
// this file. See above for why we may want to do this.
|
||||
let skip_because_cfg_say_so = skip_native && !is_rust_object;
|
||||
|
||||
// If we're performing LTO and this is a rust-generated object
|
||||
// file, then we don't need the object file as it's part of the
|
||||
// LTO module. Note that `#![no_builtins]` is excluded from LTO,
|
||||
// though, so we let that object file slide.
|
||||
let skip_because_lto =
|
||||
upstream_rust_objects_already_included && is_rust_object && is_builtins;
|
||||
if upstream_rust_objects_already_included && is_rust_object && is_builtins {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We skip native libraries because:
|
||||
// 1. This native libraries won't be used from the generated rlib,
|
||||
|
|
@ -2653,10 +2600,6 @@ fn add_static_crate<'a>(
|
|||
return true;
|
||||
}
|
||||
|
||||
if skip_because_cfg_say_so || skip_because_lto {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}),
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -858,6 +858,7 @@ impl CrateInfo {
|
|||
dependency_formats: tcx.dependency_formats(()).clone(),
|
||||
windows_subsystem,
|
||||
natvis_debugger_visualizers: Default::default(),
|
||||
feature_packed_bundled_libs: tcx.features().packed_bundled_libs,
|
||||
};
|
||||
let crates = tcx.crates(());
|
||||
|
||||
|
|
|
|||
|
|
@ -319,74 +319,62 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
|
|||
}
|
||||
}
|
||||
} else if attr.has_name(sym::instruction_set) {
|
||||
codegen_fn_attrs.instruction_set = match attr.meta_kind() {
|
||||
Some(MetaItemKind::List(ref items)) => match items.as_slice() {
|
||||
[NestedMetaItem::MetaItem(set)] => {
|
||||
let segments =
|
||||
set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
|
||||
match segments.as_slice() {
|
||||
[sym::arm, sym::a32] | [sym::arm, sym::t32] => {
|
||||
if !tcx.sess.target.has_thumb_interworking {
|
||||
struct_span_err!(
|
||||
tcx.sess.diagnostic(),
|
||||
attr.span,
|
||||
E0779,
|
||||
"target does not support `#[instruction_set]`"
|
||||
)
|
||||
.emit();
|
||||
None
|
||||
} else if segments[1] == sym::a32 {
|
||||
Some(InstructionSetAttr::ArmA32)
|
||||
} else if segments[1] == sym::t32 {
|
||||
Some(InstructionSetAttr::ArmT32)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
codegen_fn_attrs.instruction_set = attr.meta_item_list().and_then(|l| match &l[..] {
|
||||
[NestedMetaItem::MetaItem(set)] => {
|
||||
let segments =
|
||||
set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
|
||||
match segments.as_slice() {
|
||||
[sym::arm, sym::a32] | [sym::arm, sym::t32] => {
|
||||
if !tcx.sess.target.has_thumb_interworking {
|
||||
struct_span_err!(
|
||||
tcx.sess.diagnostic(),
|
||||
attr.span,
|
||||
E0779,
|
||||
"invalid instruction set specified",
|
||||
"target does not support `#[instruction_set]`"
|
||||
)
|
||||
.emit();
|
||||
None
|
||||
} else if segments[1] == sym::a32 {
|
||||
Some(InstructionSetAttr::ArmA32)
|
||||
} else if segments[1] == sym::t32 {
|
||||
Some(InstructionSetAttr::ArmT32)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
struct_span_err!(
|
||||
tcx.sess.diagnostic(),
|
||||
attr.span,
|
||||
E0779,
|
||||
"invalid instruction set specified",
|
||||
)
|
||||
.emit();
|
||||
None
|
||||
}
|
||||
}
|
||||
[] => {
|
||||
struct_span_err!(
|
||||
tcx.sess.diagnostic(),
|
||||
attr.span,
|
||||
E0778,
|
||||
"`#[instruction_set]` requires an argument"
|
||||
)
|
||||
.emit();
|
||||
None
|
||||
}
|
||||
_ => {
|
||||
struct_span_err!(
|
||||
tcx.sess.diagnostic(),
|
||||
attr.span,
|
||||
E0779,
|
||||
"cannot specify more than one instruction set"
|
||||
)
|
||||
.emit();
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
}
|
||||
[] => {
|
||||
struct_span_err!(
|
||||
tcx.sess.diagnostic(),
|
||||
attr.span,
|
||||
E0778,
|
||||
"must specify an instruction set"
|
||||
"`#[instruction_set]` requires an argument"
|
||||
)
|
||||
.emit();
|
||||
None
|
||||
}
|
||||
};
|
||||
_ => {
|
||||
struct_span_err!(
|
||||
tcx.sess.diagnostic(),
|
||||
attr.span,
|
||||
E0779,
|
||||
"cannot specify more than one instruction set"
|
||||
)
|
||||
.emit();
|
||||
None
|
||||
}
|
||||
})
|
||||
} else if attr.has_name(sym::repr) {
|
||||
codegen_fn_attrs.alignment = match attr.meta_item_list() {
|
||||
Some(items) => match items.as_slice() {
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@ pub struct CrateInfo {
|
|||
pub dependency_formats: Lrc<Dependencies>,
|
||||
pub windows_subsystem: Option<String>,
|
||||
pub natvis_debugger_visualizers: BTreeSet<DebuggerVisualizerFile>,
|
||||
pub feature_packed_bundled_libs: bool, // unstable feature flag.
|
||||
}
|
||||
|
||||
#[derive(Encodable, Decodable)]
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use rustc_middle::ty::cast::{CastTy, IntTy};
|
|||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
||||
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
|
||||
use rustc_span::source_map::{Span, DUMMY_SP};
|
||||
use rustc_target::abi::VariantIdx;
|
||||
|
||||
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
#[instrument(level = "trace", skip(self, bx))]
|
||||
|
|
@ -106,17 +107,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
}
|
||||
|
||||
mir::Rvalue::Aggregate(ref kind, ref operands) => {
|
||||
let (dest, active_field_index) = match **kind {
|
||||
mir::AggregateKind::Adt(adt_did, variant_index, _, _, active_field_index) => {
|
||||
dest.codegen_set_discr(bx, variant_index);
|
||||
if bx.tcx().adt_def(adt_did).is_enum() {
|
||||
(dest.project_downcast(bx, variant_index), active_field_index)
|
||||
} else {
|
||||
(dest, active_field_index)
|
||||
}
|
||||
let (variant_index, variant_dest, active_field_index) = match **kind {
|
||||
mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => {
|
||||
let variant_dest = dest.project_downcast(bx, variant_index);
|
||||
(variant_index, variant_dest, active_field_index)
|
||||
}
|
||||
_ => (dest, None),
|
||||
_ => (VariantIdx::from_u32(0), dest, None),
|
||||
};
|
||||
if active_field_index.is_some() {
|
||||
assert_eq!(operands.len(), 1);
|
||||
}
|
||||
for (i, operand) in operands.iter().enumerate() {
|
||||
let op = self.codegen_operand(bx, operand);
|
||||
// Do not generate stores and GEPis for zero-sized fields.
|
||||
|
|
@ -124,13 +124,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let field_index = active_field_index.unwrap_or(i);
|
||||
let field = if let mir::AggregateKind::Array(_) = **kind {
|
||||
let llindex = bx.cx().const_usize(field_index as u64);
|
||||
dest.project_index(bx, llindex)
|
||||
variant_dest.project_index(bx, llindex)
|
||||
} else {
|
||||
dest.project_field(bx, field_index)
|
||||
variant_dest.project_field(bx, field_index)
|
||||
};
|
||||
op.val.store(bx, field);
|
||||
}
|
||||
}
|
||||
dest.codegen_set_discr(bx, variant_index);
|
||||
}
|
||||
|
||||
_ => {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ rustc_infer = { path = "../rustc_infer" }
|
|||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_mir_dataflow = { path = "../rustc_mir_dataflow" }
|
||||
rustc_query_system = { path = "../rustc_query_system" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
rustc_trait_selection = { path = "../rustc_trait_selection" }
|
||||
|
|
|
|||
238
compiler/rustc_const_eval/src/interpret/discriminant.rs
Normal file
238
compiler/rustc_const_eval/src/interpret/discriminant.rs
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
//! Functions for reading and writing discriminants of multi-variant layouts (enums and generators).
|
||||
|
||||
use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt};
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_target::abi::{self, TagEncoding};
|
||||
use rustc_target::abi::{VariantIdx, Variants};
|
||||
|
||||
use super::{ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Scalar};
|
||||
|
||||
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
/// Writes the discriminant of the given variant.
|
||||
#[instrument(skip(self), level = "trace")]
|
||||
pub fn write_discriminant(
|
||||
&mut self,
|
||||
variant_index: VariantIdx,
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Layout computation excludes uninhabited variants from consideration
|
||||
// therefore there's no way to represent those variants in the given layout.
|
||||
// Essentially, uninhabited variants do not have a tag that corresponds to their
|
||||
// discriminant, so we cannot do anything here.
|
||||
// When evaluating we will always error before even getting here, but ConstProp 'executes'
|
||||
// dead code, so we cannot ICE here.
|
||||
if dest.layout.for_variant(self, variant_index).abi.is_uninhabited() {
|
||||
throw_ub!(UninhabitedEnumVariantWritten)
|
||||
}
|
||||
|
||||
match dest.layout.variants {
|
||||
abi::Variants::Single { index } => {
|
||||
assert_eq!(index, variant_index);
|
||||
}
|
||||
abi::Variants::Multiple {
|
||||
tag_encoding: TagEncoding::Direct,
|
||||
tag: tag_layout,
|
||||
tag_field,
|
||||
..
|
||||
} => {
|
||||
// No need to validate that the discriminant here because the
|
||||
// `TyAndLayout::for_variant()` call earlier already checks the variant is valid.
|
||||
|
||||
let discr_val =
|
||||
dest.layout.ty.discriminant_for_variant(*self.tcx, variant_index).unwrap().val;
|
||||
|
||||
// raw discriminants for enums are isize or bigger during
|
||||
// their computation, but the in-memory tag is the smallest possible
|
||||
// representation
|
||||
let size = tag_layout.size(self);
|
||||
let tag_val = size.truncate(discr_val);
|
||||
|
||||
let tag_dest = self.place_field(dest, tag_field)?;
|
||||
self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?;
|
||||
}
|
||||
abi::Variants::Multiple {
|
||||
tag_encoding:
|
||||
TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start },
|
||||
tag: tag_layout,
|
||||
tag_field,
|
||||
..
|
||||
} => {
|
||||
// No need to validate that the discriminant here because the
|
||||
// `TyAndLayout::for_variant()` call earlier already checks the variant is valid.
|
||||
|
||||
if variant_index != untagged_variant {
|
||||
let variants_start = niche_variants.start().as_u32();
|
||||
let variant_index_relative = variant_index
|
||||
.as_u32()
|
||||
.checked_sub(variants_start)
|
||||
.expect("overflow computing relative variant idx");
|
||||
// We need to use machine arithmetic when taking into account `niche_start`:
|
||||
// tag_val = variant_index_relative + niche_start_val
|
||||
let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?;
|
||||
let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
|
||||
let variant_index_relative_val =
|
||||
ImmTy::from_uint(variant_index_relative, tag_layout);
|
||||
let tag_val = self.binary_op(
|
||||
mir::BinOp::Add,
|
||||
&variant_index_relative_val,
|
||||
&niche_start_val,
|
||||
)?;
|
||||
// Write result.
|
||||
let niche_dest = self.place_field(dest, tag_field)?;
|
||||
self.write_immediate(*tag_val, &niche_dest)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read discriminant, return the runtime value as well as the variant index.
|
||||
/// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)!
|
||||
#[instrument(skip(self), level = "trace")]
|
||||
pub fn read_discriminant(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, (Scalar<M::Provenance>, VariantIdx)> {
|
||||
trace!("read_discriminant_value {:#?}", op.layout);
|
||||
// Get type and layout of the discriminant.
|
||||
let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?;
|
||||
trace!("discriminant type: {:?}", discr_layout.ty);
|
||||
|
||||
// We use "discriminant" to refer to the value associated with a particular enum variant.
|
||||
// This is not to be confused with its "variant index", which is just determining its position in the
|
||||
// declared list of variants -- they can differ with explicitly assigned discriminants.
|
||||
// We use "tag" to refer to how the discriminant is encoded in memory, which can be either
|
||||
// straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`).
|
||||
let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants {
|
||||
Variants::Single { index } => {
|
||||
let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) {
|
||||
Some(discr) => {
|
||||
// This type actually has discriminants.
|
||||
assert_eq!(discr.ty, discr_layout.ty);
|
||||
Scalar::from_uint(discr.val, discr_layout.size)
|
||||
}
|
||||
None => {
|
||||
// On a type without actual discriminants, variant is 0.
|
||||
assert_eq!(index.as_u32(), 0);
|
||||
Scalar::from_uint(index.as_u32(), discr_layout.size)
|
||||
}
|
||||
};
|
||||
return Ok((discr, index));
|
||||
}
|
||||
Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => {
|
||||
(tag, tag_encoding, tag_field)
|
||||
}
|
||||
};
|
||||
|
||||
// There are *three* layouts that come into play here:
|
||||
// - The discriminant has a type for typechecking. This is `discr_layout`, and is used for
|
||||
// the `Scalar` we return.
|
||||
// - The tag (encoded discriminant) has layout `tag_layout`. This is always an integer type,
|
||||
// and used to interpret the value we read from the tag field.
|
||||
// For the return value, a cast to `discr_layout` is performed.
|
||||
// - The field storing the tag has a layout, which is very similar to `tag_layout` but
|
||||
// may be a pointer. This is `tag_val.layout`; we just use it for sanity checks.
|
||||
|
||||
// Get layout for tag.
|
||||
let tag_layout = self.layout_of(tag_scalar_layout.primitive().to_int_ty(*self.tcx))?;
|
||||
|
||||
// Read tag and sanity-check `tag_layout`.
|
||||
let tag_val = self.read_immediate(&self.operand_field(op, tag_field)?)?;
|
||||
assert_eq!(tag_layout.size, tag_val.layout.size);
|
||||
assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed());
|
||||
trace!("tag value: {}", tag_val);
|
||||
|
||||
// Figure out which discriminant and variant this corresponds to.
|
||||
Ok(match *tag_encoding {
|
||||
TagEncoding::Direct => {
|
||||
let scalar = tag_val.to_scalar();
|
||||
// Generate a specific error if `tag_val` is not an integer.
|
||||
// (`tag_bits` itself is only used for error messages below.)
|
||||
let tag_bits = scalar
|
||||
.try_to_int()
|
||||
.map_err(|dbg_val| err_ub!(InvalidTag(dbg_val)))?
|
||||
.assert_bits(tag_layout.size);
|
||||
// Cast bits from tag layout to discriminant layout.
|
||||
// After the checks we did above, this cannot fail, as
|
||||
// discriminants are int-like.
|
||||
let discr_val =
|
||||
self.cast_from_int_like(scalar, tag_val.layout, discr_layout.ty).unwrap();
|
||||
let discr_bits = discr_val.assert_bits(discr_layout.size);
|
||||
// Convert discriminant to variant index, and catch invalid discriminants.
|
||||
let index = match *op.layout.ty.kind() {
|
||||
ty::Adt(adt, _) => {
|
||||
adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits)
|
||||
}
|
||||
ty::Generator(def_id, substs, _) => {
|
||||
let substs = substs.as_generator();
|
||||
substs
|
||||
.discriminants(def_id, *self.tcx)
|
||||
.find(|(_, var)| var.val == discr_bits)
|
||||
}
|
||||
_ => span_bug!(self.cur_span(), "tagged layout for non-adt non-generator"),
|
||||
}
|
||||
.ok_or_else(|| err_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))))?;
|
||||
// Return the cast value, and the index.
|
||||
(discr_val, index.0)
|
||||
}
|
||||
TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => {
|
||||
let tag_val = tag_val.to_scalar();
|
||||
// Compute the variant this niche value/"tag" corresponds to. With niche layout,
|
||||
// discriminant (encoded in niche/tag) and variant index are the same.
|
||||
let variants_start = niche_variants.start().as_u32();
|
||||
let variants_end = niche_variants.end().as_u32();
|
||||
let variant = match tag_val.try_to_int() {
|
||||
Err(dbg_val) => {
|
||||
// So this is a pointer then, and casting to an int failed.
|
||||
// Can only happen during CTFE.
|
||||
// The niche must be just 0, and the ptr not null, then we know this is
|
||||
// okay. Everything else, we conservatively reject.
|
||||
let ptr_valid = niche_start == 0
|
||||
&& variants_start == variants_end
|
||||
&& !self.scalar_may_be_null(tag_val)?;
|
||||
if !ptr_valid {
|
||||
throw_ub!(InvalidTag(dbg_val))
|
||||
}
|
||||
untagged_variant
|
||||
}
|
||||
Ok(tag_bits) => {
|
||||
let tag_bits = tag_bits.assert_bits(tag_layout.size);
|
||||
// We need to use machine arithmetic to get the relative variant idx:
|
||||
// variant_index_relative = tag_val - niche_start_val
|
||||
let tag_val = ImmTy::from_uint(tag_bits, tag_layout);
|
||||
let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
|
||||
let variant_index_relative_val =
|
||||
self.binary_op(mir::BinOp::Sub, &tag_val, &niche_start_val)?;
|
||||
let variant_index_relative =
|
||||
variant_index_relative_val.to_scalar().assert_bits(tag_val.layout.size);
|
||||
// Check if this is in the range that indicates an actual discriminant.
|
||||
if variant_index_relative <= u128::from(variants_end - variants_start) {
|
||||
let variant_index_relative = u32::try_from(variant_index_relative)
|
||||
.expect("we checked that this fits into a u32");
|
||||
// Then computing the absolute variant idx should not overflow any more.
|
||||
let variant_index = variants_start
|
||||
.checked_add(variant_index_relative)
|
||||
.expect("overflow computing absolute variant idx");
|
||||
let variants_len = op
|
||||
.layout
|
||||
.ty
|
||||
.ty_adt_def()
|
||||
.expect("tagged layout for non adt")
|
||||
.variants()
|
||||
.len();
|
||||
assert!(usize::try_from(variant_index).unwrap() < variants_len);
|
||||
VariantIdx::from_u32(variant_index)
|
||||
} else {
|
||||
untagged_variant
|
||||
}
|
||||
}
|
||||
};
|
||||
// Compute the size of the scalar we need to return.
|
||||
// No need to cast, because the variant index directly serves as discriminant and is
|
||||
// encoded in the tag.
|
||||
(Scalar::from_uint(variant.as_u32(), discr_layout.size), variant)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
//! An interpreter for MIR used in CTFE and by miri
|
||||
|
||||
mod cast;
|
||||
mod discriminant;
|
||||
mod eval_context;
|
||||
mod intern;
|
||||
mod intrinsics;
|
||||
|
|
|
|||
|
|
@ -4,13 +4,12 @@
|
|||
use either::{Either, Left, Right};
|
||||
|
||||
use rustc_hir::def::Namespace;
|
||||
use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
|
||||
use rustc_middle::ty::{ConstInt, Ty, ValTree};
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, TagEncoding};
|
||||
use rustc_target::abi::{VariantIdx, Variants};
|
||||
use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size};
|
||||
|
||||
use super::{
|
||||
alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId,
|
||||
|
|
@ -657,154 +656,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
};
|
||||
Ok(OpTy { op, layout, align: Some(layout.align.abi) })
|
||||
}
|
||||
|
||||
/// Read discriminant, return the runtime value as well as the variant index.
|
||||
/// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)!
|
||||
pub fn read_discriminant(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, (Scalar<M::Provenance>, VariantIdx)> {
|
||||
trace!("read_discriminant_value {:#?}", op.layout);
|
||||
// Get type and layout of the discriminant.
|
||||
let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?;
|
||||
trace!("discriminant type: {:?}", discr_layout.ty);
|
||||
|
||||
// We use "discriminant" to refer to the value associated with a particular enum variant.
|
||||
// This is not to be confused with its "variant index", which is just determining its position in the
|
||||
// declared list of variants -- they can differ with explicitly assigned discriminants.
|
||||
// We use "tag" to refer to how the discriminant is encoded in memory, which can be either
|
||||
// straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`).
|
||||
let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants {
|
||||
Variants::Single { index } => {
|
||||
let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) {
|
||||
Some(discr) => {
|
||||
// This type actually has discriminants.
|
||||
assert_eq!(discr.ty, discr_layout.ty);
|
||||
Scalar::from_uint(discr.val, discr_layout.size)
|
||||
}
|
||||
None => {
|
||||
// On a type without actual discriminants, variant is 0.
|
||||
assert_eq!(index.as_u32(), 0);
|
||||
Scalar::from_uint(index.as_u32(), discr_layout.size)
|
||||
}
|
||||
};
|
||||
return Ok((discr, index));
|
||||
}
|
||||
Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => {
|
||||
(tag, tag_encoding, tag_field)
|
||||
}
|
||||
};
|
||||
|
||||
// There are *three* layouts that come into play here:
|
||||
// - The discriminant has a type for typechecking. This is `discr_layout`, and is used for
|
||||
// the `Scalar` we return.
|
||||
// - The tag (encoded discriminant) has layout `tag_layout`. This is always an integer type,
|
||||
// and used to interpret the value we read from the tag field.
|
||||
// For the return value, a cast to `discr_layout` is performed.
|
||||
// - The field storing the tag has a layout, which is very similar to `tag_layout` but
|
||||
// may be a pointer. This is `tag_val.layout`; we just use it for sanity checks.
|
||||
|
||||
// Get layout for tag.
|
||||
let tag_layout = self.layout_of(tag_scalar_layout.primitive().to_int_ty(*self.tcx))?;
|
||||
|
||||
// Read tag and sanity-check `tag_layout`.
|
||||
let tag_val = self.read_immediate(&self.operand_field(op, tag_field)?)?;
|
||||
assert_eq!(tag_layout.size, tag_val.layout.size);
|
||||
assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed());
|
||||
trace!("tag value: {}", tag_val);
|
||||
|
||||
// Figure out which discriminant and variant this corresponds to.
|
||||
Ok(match *tag_encoding {
|
||||
TagEncoding::Direct => {
|
||||
let scalar = tag_val.to_scalar();
|
||||
// Generate a specific error if `tag_val` is not an integer.
|
||||
// (`tag_bits` itself is only used for error messages below.)
|
||||
let tag_bits = scalar
|
||||
.try_to_int()
|
||||
.map_err(|dbg_val| err_ub!(InvalidTag(dbg_val)))?
|
||||
.assert_bits(tag_layout.size);
|
||||
// Cast bits from tag layout to discriminant layout.
|
||||
// After the checks we did above, this cannot fail, as
|
||||
// discriminants are int-like.
|
||||
let discr_val =
|
||||
self.cast_from_int_like(scalar, tag_val.layout, discr_layout.ty).unwrap();
|
||||
let discr_bits = discr_val.assert_bits(discr_layout.size);
|
||||
// Convert discriminant to variant index, and catch invalid discriminants.
|
||||
let index = match *op.layout.ty.kind() {
|
||||
ty::Adt(adt, _) => {
|
||||
adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits)
|
||||
}
|
||||
ty::Generator(def_id, substs, _) => {
|
||||
let substs = substs.as_generator();
|
||||
substs
|
||||
.discriminants(def_id, *self.tcx)
|
||||
.find(|(_, var)| var.val == discr_bits)
|
||||
}
|
||||
_ => span_bug!(self.cur_span(), "tagged layout for non-adt non-generator"),
|
||||
}
|
||||
.ok_or_else(|| err_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))))?;
|
||||
// Return the cast value, and the index.
|
||||
(discr_val, index.0)
|
||||
}
|
||||
TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => {
|
||||
let tag_val = tag_val.to_scalar();
|
||||
// Compute the variant this niche value/"tag" corresponds to. With niche layout,
|
||||
// discriminant (encoded in niche/tag) and variant index are the same.
|
||||
let variants_start = niche_variants.start().as_u32();
|
||||
let variants_end = niche_variants.end().as_u32();
|
||||
let variant = match tag_val.try_to_int() {
|
||||
Err(dbg_val) => {
|
||||
// So this is a pointer then, and casting to an int failed.
|
||||
// Can only happen during CTFE.
|
||||
// The niche must be just 0, and the ptr not null, then we know this is
|
||||
// okay. Everything else, we conservatively reject.
|
||||
let ptr_valid = niche_start == 0
|
||||
&& variants_start == variants_end
|
||||
&& !self.scalar_may_be_null(tag_val)?;
|
||||
if !ptr_valid {
|
||||
throw_ub!(InvalidTag(dbg_val))
|
||||
}
|
||||
untagged_variant
|
||||
}
|
||||
Ok(tag_bits) => {
|
||||
let tag_bits = tag_bits.assert_bits(tag_layout.size);
|
||||
// We need to use machine arithmetic to get the relative variant idx:
|
||||
// variant_index_relative = tag_val - niche_start_val
|
||||
let tag_val = ImmTy::from_uint(tag_bits, tag_layout);
|
||||
let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
|
||||
let variant_index_relative_val =
|
||||
self.binary_op(mir::BinOp::Sub, &tag_val, &niche_start_val)?;
|
||||
let variant_index_relative =
|
||||
variant_index_relative_val.to_scalar().assert_bits(tag_val.layout.size);
|
||||
// Check if this is in the range that indicates an actual discriminant.
|
||||
if variant_index_relative <= u128::from(variants_end - variants_start) {
|
||||
let variant_index_relative = u32::try_from(variant_index_relative)
|
||||
.expect("we checked that this fits into a u32");
|
||||
// Then computing the absolute variant idx should not overflow any more.
|
||||
let variant_index = variants_start
|
||||
.checked_add(variant_index_relative)
|
||||
.expect("overflow computing absolute variant idx");
|
||||
let variants_len = op
|
||||
.layout
|
||||
.ty
|
||||
.ty_adt_def()
|
||||
.expect("tagged layout for non adt")
|
||||
.variants()
|
||||
.len();
|
||||
assert!(usize::try_from(variant_index).unwrap() < variants_len);
|
||||
VariantIdx::from_u32(variant_index)
|
||||
} else {
|
||||
untagged_variant
|
||||
}
|
||||
}
|
||||
};
|
||||
// Compute the size of the scalar we need to return.
|
||||
// No need to cast, because the variant index directly serves as discriminant and is
|
||||
// encoded in the tag.
|
||||
(Scalar::from_uint(variant.as_u32(), discr_layout.size), variant)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Some nodes are used a lot. Make sure they don't unintentionally get bigger.
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ use either::{Either, Left, Right};
|
|||
use rustc_ast::Mutability;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
|
||||
use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, TagEncoding, VariantIdx};
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, VariantIdx};
|
||||
|
||||
use super::{
|
||||
alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg,
|
||||
|
|
@ -767,92 +767,32 @@ where
|
|||
MPlaceTy { mplace, layout, align: layout.align.abi }
|
||||
}
|
||||
|
||||
/// Writes the discriminant of the given variant.
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub fn write_discriminant(
|
||||
/// Writes the aggregate to the destination.
|
||||
#[instrument(skip(self), level = "trace")]
|
||||
pub fn write_aggregate(
|
||||
&mut self,
|
||||
variant_index: VariantIdx,
|
||||
kind: &mir::AggregateKind<'tcx>,
|
||||
operands: &[mir::Operand<'tcx>],
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// This must be an enum or generator.
|
||||
match dest.layout.ty.kind() {
|
||||
ty::Adt(adt, _) => assert!(adt.is_enum()),
|
||||
ty::Generator(..) => {}
|
||||
_ => span_bug!(
|
||||
self.cur_span(),
|
||||
"write_discriminant called on non-variant-type (neither enum nor generator)"
|
||||
),
|
||||
}
|
||||
// Layout computation excludes uninhabited variants from consideration
|
||||
// therefore there's no way to represent those variants in the given layout.
|
||||
// Essentially, uninhabited variants do not have a tag that corresponds to their
|
||||
// discriminant, so we cannot do anything here.
|
||||
// When evaluating we will always error before even getting here, but ConstProp 'executes'
|
||||
// dead code, so we cannot ICE here.
|
||||
if dest.layout.for_variant(self, variant_index).abi.is_uninhabited() {
|
||||
throw_ub!(UninhabitedEnumVariantWritten)
|
||||
}
|
||||
|
||||
match dest.layout.variants {
|
||||
abi::Variants::Single { index } => {
|
||||
assert_eq!(index, variant_index);
|
||||
}
|
||||
abi::Variants::Multiple {
|
||||
tag_encoding: TagEncoding::Direct,
|
||||
tag: tag_layout,
|
||||
tag_field,
|
||||
..
|
||||
} => {
|
||||
// No need to validate that the discriminant here because the
|
||||
// `TyAndLayout::for_variant()` call earlier already checks the variant is valid.
|
||||
|
||||
let discr_val =
|
||||
dest.layout.ty.discriminant_for_variant(*self.tcx, variant_index).unwrap().val;
|
||||
|
||||
// raw discriminants for enums are isize or bigger during
|
||||
// their computation, but the in-memory tag is the smallest possible
|
||||
// representation
|
||||
let size = tag_layout.size(self);
|
||||
let tag_val = size.truncate(discr_val);
|
||||
|
||||
let tag_dest = self.place_field(dest, tag_field)?;
|
||||
self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?;
|
||||
}
|
||||
abi::Variants::Multiple {
|
||||
tag_encoding:
|
||||
TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start },
|
||||
tag: tag_layout,
|
||||
tag_field,
|
||||
..
|
||||
} => {
|
||||
// No need to validate that the discriminant here because the
|
||||
// `TyAndLayout::for_variant()` call earlier already checks the variant is valid.
|
||||
|
||||
if variant_index != untagged_variant {
|
||||
let variants_start = niche_variants.start().as_u32();
|
||||
let variant_index_relative = variant_index
|
||||
.as_u32()
|
||||
.checked_sub(variants_start)
|
||||
.expect("overflow computing relative variant idx");
|
||||
// We need to use machine arithmetic when taking into account `niche_start`:
|
||||
// tag_val = variant_index_relative + niche_start_val
|
||||
let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?;
|
||||
let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
|
||||
let variant_index_relative_val =
|
||||
ImmTy::from_uint(variant_index_relative, tag_layout);
|
||||
let tag_val = self.binary_op(
|
||||
mir::BinOp::Add,
|
||||
&variant_index_relative_val,
|
||||
&niche_start_val,
|
||||
)?;
|
||||
// Write result.
|
||||
let niche_dest = self.place_field(dest, tag_field)?;
|
||||
self.write_immediate(*tag_val, &niche_dest)?;
|
||||
}
|
||||
self.write_uninit(&dest)?;
|
||||
let (variant_index, variant_dest, active_field_index) = match *kind {
|
||||
mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => {
|
||||
let variant_dest = self.place_downcast(&dest, variant_index)?;
|
||||
(variant_index, variant_dest, active_field_index)
|
||||
}
|
||||
_ => (VariantIdx::from_u32(0), dest.clone(), None),
|
||||
};
|
||||
if active_field_index.is_some() {
|
||||
assert_eq!(operands.len(), 1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
for (field_index, operand) in operands.iter().enumerate() {
|
||||
let field_index = active_field_index.unwrap_or(field_index);
|
||||
let field_dest = self.place_field(&variant_dest, field_index)?;
|
||||
let op = self.eval_operand(operand, Some(field_dest.layout))?;
|
||||
self.copy_op(&op, &field_dest, /*allow_transmute*/ false)?;
|
||||
}
|
||||
self.write_discriminant(variant_index, &dest)
|
||||
}
|
||||
|
||||
pub fn raw_const_to_mplace(
|
||||
|
|
|
|||
|
|
@ -199,13 +199,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
}
|
||||
|
||||
Aggregate(box ref kind, ref operands) => {
|
||||
assert!(matches!(kind, mir::AggregateKind::Array(..)));
|
||||
|
||||
for (field_index, operand) in operands.iter().enumerate() {
|
||||
let op = self.eval_operand(operand, None)?;
|
||||
let field_dest = self.place_field(&dest, field_index)?;
|
||||
self.copy_op(&op, &field_dest, /*allow_transmute*/ false)?;
|
||||
}
|
||||
self.write_aggregate(kind, operands, &dest)?;
|
||||
}
|
||||
|
||||
Repeat(ref operand, _) => {
|
||||
|
|
|
|||
|
|
@ -453,7 +453,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
|
||||
Rvalue::Aggregate(kind, ..) => {
|
||||
if let AggregateKind::Generator(def_id, ..) = kind.as_ref()
|
||||
&& let Some(generator_kind @ hir::GeneratorKind::Async(..)) = self.tcx.generator_kind(def_id.to_def_id())
|
||||
&& let Some(generator_kind @ hir::GeneratorKind::Async(..)) = self.tcx.generator_kind(def_id)
|
||||
{
|
||||
self.check_op(ops::Generator(generator_kind));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,13 +22,7 @@ use rustc_span::{BytePos, Pos, Span, Symbol};
|
|||
use rustc_trait_selection::traits::SelectionContext;
|
||||
|
||||
use super::ConstCx;
|
||||
use crate::errors::{
|
||||
InteriorMutabilityBorrow, InteriorMutableDataRefer, MutDerefErr, NonConstFmtMacroCall,
|
||||
NonConstFnCall, NonConstOpErr, PanicNonStrErr, RawPtrToIntErr, StaticAccessErr,
|
||||
TransientMutBorrowErr, TransientMutBorrowErrRaw, UnallowedFnPointerCall,
|
||||
UnallowedHeapAllocations, UnallowedInlineAsm, UnallowedMutableRefs, UnallowedMutableRefsRaw,
|
||||
UnallowedOpInConstContext, UnstableConstFn,
|
||||
};
|
||||
use crate::errors;
|
||||
use crate::util::{call_kind, CallDesugaringKind, CallKind};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
|
|
@ -99,7 +93,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
|
|||
ccx: &ConstCx<'_, 'tcx>,
|
||||
span: Span,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
ccx.tcx.sess.create_err(UnallowedFnPointerCall { span, kind: ccx.const_kind() })
|
||||
ccx.tcx.sess.create_err(errors::UnallowedFnPointerCall { span, kind: ccx.const_kind() })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -303,10 +297,11 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
|
|||
diag_trait(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, Some(span)));
|
||||
err
|
||||
}
|
||||
_ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentV1Methods) => {
|
||||
ccx.tcx.sess.create_err(NonConstFmtMacroCall { span, kind: ccx.const_kind() })
|
||||
}
|
||||
_ => ccx.tcx.sess.create_err(NonConstFnCall {
|
||||
_ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentV1Methods) => ccx
|
||||
.tcx
|
||||
.sess
|
||||
.create_err(errors::NonConstFmtMacroCall { span, kind: ccx.const_kind() }),
|
||||
_ => ccx.tcx.sess.create_err(errors::NonConstFnCall {
|
||||
span,
|
||||
def_path_str: ccx.tcx.def_path_str_with_substs(callee, substs),
|
||||
kind: ccx.const_kind(),
|
||||
|
|
@ -351,7 +346,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
|
|||
let mut err = ccx
|
||||
.tcx
|
||||
.sess
|
||||
.create_err(UnstableConstFn { span, def_path: ccx.tcx.def_path_str(def_id) });
|
||||
.create_err(errors::UnstableConstFn { span, def_path: ccx.tcx.def_path_str(def_id) });
|
||||
|
||||
if ccx.is_const_stable_const_fn() {
|
||||
err.help("const-stable functions can only call other const-stable functions");
|
||||
|
|
@ -387,11 +382,11 @@ impl<'tcx> NonConstOp<'tcx> for Generator {
|
|||
let msg = format!("{}s are not allowed in {}s", self.0.descr(), ccx.const_kind());
|
||||
if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 {
|
||||
ccx.tcx.sess.create_feature_err(
|
||||
UnallowedOpInConstContext { span, msg },
|
||||
errors::UnallowedOpInConstContext { span, msg },
|
||||
sym::const_async_blocks,
|
||||
)
|
||||
} else {
|
||||
ccx.tcx.sess.create_err(UnallowedOpInConstContext { span, msg })
|
||||
ccx.tcx.sess.create_err(errors::UnallowedOpInConstContext { span, msg })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -404,7 +399,7 @@ impl<'tcx> NonConstOp<'tcx> for HeapAllocation {
|
|||
ccx: &ConstCx<'_, 'tcx>,
|
||||
span: Span,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
ccx.tcx.sess.create_err(UnallowedHeapAllocations {
|
||||
ccx.tcx.sess.create_err(errors::UnallowedHeapAllocations {
|
||||
span,
|
||||
kind: ccx.const_kind(),
|
||||
teach: ccx.tcx.sess.teach(&error_code!(E0010)).then_some(()),
|
||||
|
|
@ -420,7 +415,7 @@ impl<'tcx> NonConstOp<'tcx> for InlineAsm {
|
|||
ccx: &ConstCx<'_, 'tcx>,
|
||||
span: Span,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
ccx.tcx.sess.create_err(UnallowedInlineAsm { span, kind: ccx.const_kind() })
|
||||
ccx.tcx.sess.create_err(errors::UnallowedInlineAsm { span, kind: ccx.const_kind() })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -471,7 +466,9 @@ impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow {
|
|||
ccx: &ConstCx<'_, 'tcx>,
|
||||
span: Span,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
ccx.tcx.sess.create_feature_err(InteriorMutabilityBorrow { span }, sym::const_refs_to_cell)
|
||||
ccx.tcx
|
||||
.sess
|
||||
.create_feature_err(errors::InteriorMutabilityBorrow { span }, sym::const_refs_to_cell)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -488,14 +485,14 @@ impl<'tcx> NonConstOp<'tcx> for CellBorrow {
|
|||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
// FIXME: Maybe a more elegant solution to this if else case
|
||||
if let hir::ConstContext::Static(_) = ccx.const_kind() {
|
||||
ccx.tcx.sess.create_err(InteriorMutableDataRefer {
|
||||
ccx.tcx.sess.create_err(errors::InteriorMutableDataRefer {
|
||||
span,
|
||||
opt_help: Some(()),
|
||||
kind: ccx.const_kind(),
|
||||
teach: ccx.tcx.sess.teach(&error_code!(E0492)).then_some(()),
|
||||
})
|
||||
} else {
|
||||
ccx.tcx.sess.create_err(InteriorMutableDataRefer {
|
||||
ccx.tcx.sess.create_err(errors::InteriorMutableDataRefer {
|
||||
span,
|
||||
opt_help: None,
|
||||
kind: ccx.const_kind(),
|
||||
|
|
@ -528,12 +525,12 @@ impl<'tcx> NonConstOp<'tcx> for MutBorrow {
|
|||
span: Span,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
match self.0 {
|
||||
hir::BorrowKind::Raw => ccx.tcx.sess.create_err(UnallowedMutableRefsRaw {
|
||||
hir::BorrowKind::Raw => ccx.tcx.sess.create_err(errors::UnallowedMutableRefsRaw {
|
||||
span,
|
||||
kind: ccx.const_kind(),
|
||||
teach: ccx.tcx.sess.teach(&error_code!(E0764)).then_some(()),
|
||||
}),
|
||||
hir::BorrowKind::Ref => ccx.tcx.sess.create_err(UnallowedMutableRefs {
|
||||
hir::BorrowKind::Ref => ccx.tcx.sess.create_err(errors::UnallowedMutableRefs {
|
||||
span,
|
||||
kind: ccx.const_kind(),
|
||||
teach: ccx.tcx.sess.teach(&error_code!(E0764)).then_some(()),
|
||||
|
|
@ -557,14 +554,14 @@ impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow {
|
|||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
let kind = ccx.const_kind();
|
||||
match self.0 {
|
||||
hir::BorrowKind::Raw => ccx
|
||||
.tcx
|
||||
.sess
|
||||
.create_feature_err(TransientMutBorrowErrRaw { span, kind }, sym::const_mut_refs),
|
||||
hir::BorrowKind::Ref => ccx
|
||||
.tcx
|
||||
.sess
|
||||
.create_feature_err(TransientMutBorrowErr { span, kind }, sym::const_mut_refs),
|
||||
hir::BorrowKind::Raw => ccx.tcx.sess.create_feature_err(
|
||||
errors::TransientMutBorrowErrRaw { span, kind },
|
||||
sym::const_mut_refs,
|
||||
),
|
||||
hir::BorrowKind::Ref => ccx.tcx.sess.create_feature_err(
|
||||
errors::TransientMutBorrowErr { span, kind },
|
||||
sym::const_mut_refs,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -586,9 +583,10 @@ impl<'tcx> NonConstOp<'tcx> for MutDeref {
|
|||
ccx: &ConstCx<'_, 'tcx>,
|
||||
span: Span,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
ccx.tcx
|
||||
.sess
|
||||
.create_feature_err(MutDerefErr { span, kind: ccx.const_kind() }, sym::const_mut_refs)
|
||||
ccx.tcx.sess.create_feature_err(
|
||||
errors::MutDerefErr { span, kind: ccx.const_kind() },
|
||||
sym::const_mut_refs,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -601,7 +599,7 @@ impl<'tcx> NonConstOp<'tcx> for PanicNonStr {
|
|||
ccx: &ConstCx<'_, 'tcx>,
|
||||
span: Span,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
ccx.tcx.sess.create_err(PanicNonStrErr { span })
|
||||
ccx.tcx.sess.create_err(errors::PanicNonStrErr { span })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -652,7 +650,7 @@ impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast {
|
|||
ccx: &ConstCx<'_, 'tcx>,
|
||||
span: Span,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
ccx.tcx.sess.create_err(RawPtrToIntErr { span })
|
||||
ccx.tcx.sess.create_err(errors::RawPtrToIntErr { span })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -673,7 +671,7 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess {
|
|||
ccx: &ConstCx<'_, 'tcx>,
|
||||
span: Span,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
ccx.tcx.sess.create_err(StaticAccessErr {
|
||||
ccx.tcx.sess.create_err(errors::StaticAccessErr {
|
||||
span,
|
||||
kind: ccx.const_kind(),
|
||||
teach: ccx.tcx.sess.teach(&error_code!(E0013)).then_some(()),
|
||||
|
|
@ -690,7 +688,7 @@ impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
|
|||
ccx: &ConstCx<'_, 'tcx>,
|
||||
span: Span,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
ccx.tcx.sess.create_err(NonConstOpErr { span })
|
||||
ccx.tcx.sess.create_err(errors::NonConstOpErr { span })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ use rustc_middle::mir::interpret::Scalar;
|
|||
use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
|
||||
use rustc_middle::mir::visit::{PlaceContext, Visitor};
|
||||
use rustc_middle::mir::{
|
||||
traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, CastKind, CopyNonOverlapping,
|
||||
Local, Location, MirPass, MirPhase, NonDivergingIntrinsic, Operand, Place, PlaceElem, PlaceRef,
|
||||
ProjectionElem, RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind,
|
||||
Terminator, TerminatorKind, UnOp, START_BLOCK,
|
||||
traversal, BasicBlock, BinOp, Body, BorrowKind, CastKind, CopyNonOverlapping, Local, Location,
|
||||
MirPass, MirPhase, NonDivergingIntrinsic, Operand, Place, PlaceElem, PlaceRef, ProjectionElem,
|
||||
RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind, Terminator,
|
||||
TerminatorKind, UnOp, START_BLOCK,
|
||||
};
|
||||
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt};
|
||||
use rustc_mir_dataflow::impls::MaybeStorageLive;
|
||||
|
|
@ -423,19 +423,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
};
|
||||
}
|
||||
match rvalue {
|
||||
Rvalue::Use(_) | Rvalue::CopyForDeref(_) => {}
|
||||
Rvalue::Aggregate(agg_kind, _) => {
|
||||
let disallowed = match **agg_kind {
|
||||
AggregateKind::Array(..) => false,
|
||||
_ => self.mir_phase >= MirPhase::Runtime(RuntimePhase::PostCleanup),
|
||||
};
|
||||
if disallowed {
|
||||
self.fail(
|
||||
location,
|
||||
format!("{:?} have been lowered to field assignments", rvalue),
|
||||
)
|
||||
}
|
||||
}
|
||||
Rvalue::Use(_) | Rvalue::CopyForDeref(_) | Rvalue::Aggregate(..) => {}
|
||||
Rvalue::Ref(_, BorrowKind::Shallow, _) => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
use rustc_index::vec::Idx;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{Ty, TyCtxt};
|
||||
use rustc_target::abi::VariantIdx;
|
||||
|
||||
use std::iter::TrustedLen;
|
||||
|
||||
/// Expand `lhs = Rvalue::Aggregate(kind, operands)` into assignments to the fields.
|
||||
///
|
||||
/// Produces something like
|
||||
/// ```ignore (ilustrative)
|
||||
/// (lhs as Variant).field0 = arg0; // We only have a downcast if this is an enum
|
||||
/// (lhs as Variant).field1 = arg1;
|
||||
/// discriminant(lhs) = variant_index; // If lhs is an enum or generator.
|
||||
/// ```
|
||||
pub fn expand_aggregate<'tcx>(
|
||||
orig_lhs: Place<'tcx>,
|
||||
operands: impl Iterator<Item = (Operand<'tcx>, Ty<'tcx>)> + TrustedLen,
|
||||
kind: AggregateKind<'tcx>,
|
||||
source_info: SourceInfo,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> impl Iterator<Item = Statement<'tcx>> + TrustedLen {
|
||||
let mut lhs = orig_lhs;
|
||||
let mut set_discriminant = None;
|
||||
let active_field_index = match kind {
|
||||
AggregateKind::Adt(adt_did, variant_index, _, _, active_field_index) => {
|
||||
let adt_def = tcx.adt_def(adt_did);
|
||||
if adt_def.is_enum() {
|
||||
set_discriminant = Some(Statement {
|
||||
kind: StatementKind::SetDiscriminant {
|
||||
place: Box::new(orig_lhs),
|
||||
variant_index,
|
||||
},
|
||||
source_info,
|
||||
});
|
||||
lhs = tcx.mk_place_downcast(orig_lhs, adt_def, variant_index);
|
||||
}
|
||||
active_field_index
|
||||
}
|
||||
AggregateKind::Generator(..) => {
|
||||
// Right now we only support initializing generators to
|
||||
// variant 0 (Unresumed).
|
||||
let variant_index = VariantIdx::new(0);
|
||||
set_discriminant = Some(Statement {
|
||||
kind: StatementKind::SetDiscriminant { place: Box::new(orig_lhs), variant_index },
|
||||
source_info,
|
||||
});
|
||||
|
||||
// Operands are upvars stored on the base place, so no
|
||||
// downcast is necessary.
|
||||
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let operands = operands.enumerate().map(move |(i, (op, ty))| {
|
||||
let lhs_field = if let AggregateKind::Array(_) = kind {
|
||||
let offset = u64::try_from(i).unwrap();
|
||||
tcx.mk_place_elem(
|
||||
lhs,
|
||||
ProjectionElem::ConstantIndex { offset, min_length: offset + 1, from_end: false },
|
||||
)
|
||||
} else {
|
||||
let field = Field::new(active_field_index.unwrap_or(i));
|
||||
tcx.mk_place_field(lhs, field, ty)
|
||||
};
|
||||
Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(Box::new((lhs_field, Rvalue::Use(op)))),
|
||||
}
|
||||
});
|
||||
[Statement { source_info, kind: StatementKind::Deinit(Box::new(orig_lhs)) }]
|
||||
.into_iter()
|
||||
.chain(operands)
|
||||
.chain(set_discriminant)
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
pub mod aggregate;
|
||||
mod alignment;
|
||||
mod call_kind;
|
||||
pub mod collect_writes;
|
||||
|
|
@ -7,7 +6,6 @@ mod find_self_call;
|
|||
mod might_permit_raw_init;
|
||||
mod type_name;
|
||||
|
||||
pub use self::aggregate::expand_aggregate;
|
||||
pub use self::alignment::is_disaligned;
|
||||
pub use self::call_kind::{call_kind, CallDesugaringKind, CallKind};
|
||||
pub use self::compare_types::{is_equal_up_to_subtyping, is_subtype};
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ cfg_if! {
|
|||
pub auto trait Send {}
|
||||
pub auto trait Sync {}
|
||||
|
||||
impl<T: ?Sized> Send for T {}
|
||||
impl<T: ?Sized> Sync for T {}
|
||||
impl<T> Send for T {}
|
||||
impl<T> Sync for T {}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! rustc_erase_owner {
|
||||
|
|
|
|||
|
|
@ -7,39 +7,4 @@ edition = "2021"
|
|||
crate-type = ["dylib"]
|
||||
|
||||
[dependencies]
|
||||
tracing = { version = "0.1.35" }
|
||||
serde_json = "1.0.59"
|
||||
rustc_log = { path = "../rustc_log" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
rustc_lint = { path = "../rustc_lint" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_hir_pretty = { path = "../rustc_hir_pretty" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_metadata = { path = "../rustc_metadata" }
|
||||
rustc_parse = { path = "../rustc_parse" }
|
||||
rustc_plugin_impl = { path = "../rustc_plugin_impl" }
|
||||
rustc_save_analysis = { path = "../rustc_save_analysis" }
|
||||
rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_error_codes = { path = "../rustc_error_codes" }
|
||||
rustc_interface = { path = "../rustc_interface" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_hir_analysis = { path = "../rustc_hir_analysis" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["consoleapi", "debugapi", "processenv"] }
|
||||
|
||||
[features]
|
||||
llvm = ['rustc_interface/llvm']
|
||||
max_level_info = ['rustc_log/max_level_info']
|
||||
rustc_use_parallel_compiler = ['rustc_data_structures/rustc_use_parallel_compiler', 'rustc_interface/rustc_use_parallel_compiler',
|
||||
'rustc_middle/rustc_use_parallel_compiler']
|
||||
rustc_driver_impl = { path = "../rustc_driver_impl" }
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
44
compiler/rustc_driver_impl/Cargo.toml
Normal file
44
compiler/rustc_driver_impl/Cargo.toml
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[package]
|
||||
name = "rustc_driver_impl"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
tracing = { version = "0.1.35" }
|
||||
serde_json = "1.0.59"
|
||||
rustc_log = { path = "../rustc_log" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
rustc_lint = { path = "../rustc_lint" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_hir_pretty = { path = "../rustc_hir_pretty" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_metadata = { path = "../rustc_metadata" }
|
||||
rustc_parse = { path = "../rustc_parse" }
|
||||
rustc_plugin_impl = { path = "../rustc_plugin_impl" }
|
||||
rustc_save_analysis = { path = "../rustc_save_analysis" }
|
||||
rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_error_codes = { path = "../rustc_error_codes" }
|
||||
rustc_interface = { path = "../rustc_interface" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_hir_analysis = { path = "../rustc_hir_analysis" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["consoleapi", "debugapi", "processenv"] }
|
||||
|
||||
[features]
|
||||
llvm = ['rustc_interface/llvm']
|
||||
max_level_info = ['rustc_log/max_level_info']
|
||||
rustc_use_parallel_compiler = ['rustc_data_structures/rustc_use_parallel_compiler', 'rustc_interface/rustc_use_parallel_compiler',
|
||||
'rustc_middle/rustc_use_parallel_compiler']
|
||||
1353
compiler/rustc_driver_impl/src/lib.rs
Normal file
1353
compiler/rustc_driver_impl/src/lib.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,67 +1,67 @@
|
|||
use rustc_macros::Diagnostic;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(driver_rlink_unable_to_read)]
|
||||
#[diag(driver_impl_rlink_unable_to_read)]
|
||||
pub(crate) struct RlinkUnableToRead {
|
||||
pub err: std::io::Error,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(driver_rlink_wrong_file_type)]
|
||||
#[diag(driver_impl_rlink_wrong_file_type)]
|
||||
pub(crate) struct RLinkWrongFileType;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(driver_rlink_empty_version_number)]
|
||||
#[diag(driver_impl_rlink_empty_version_number)]
|
||||
pub(crate) struct RLinkEmptyVersionNumber;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(driver_rlink_encoding_version_mismatch)]
|
||||
#[diag(driver_impl_rlink_encoding_version_mismatch)]
|
||||
pub(crate) struct RLinkEncodingVersionMismatch {
|
||||
pub version_array: String,
|
||||
pub rlink_version: u32,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(driver_rlink_rustc_version_mismatch)]
|
||||
#[diag(driver_impl_rlink_rustc_version_mismatch)]
|
||||
pub(crate) struct RLinkRustcVersionMismatch<'a> {
|
||||
pub rustc_version: String,
|
||||
pub current_version: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(driver_rlink_no_a_file)]
|
||||
#[diag(driver_impl_rlink_no_a_file)]
|
||||
pub(crate) struct RlinkNotAFile;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(driver_unpretty_dump_fail)]
|
||||
#[diag(driver_impl_unpretty_dump_fail)]
|
||||
pub(crate) struct UnprettyDumpFail {
|
||||
pub path: String,
|
||||
pub err: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(driver_ice)]
|
||||
#[diag(driver_impl_ice)]
|
||||
pub(crate) struct Ice;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(driver_ice_bug_report)]
|
||||
#[diag(driver_impl_ice_bug_report)]
|
||||
pub(crate) struct IceBugReport<'a> {
|
||||
pub bug_report_url: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(driver_ice_version)]
|
||||
#[diag(driver_impl_ice_version)]
|
||||
pub(crate) struct IceVersion<'a> {
|
||||
pub version: &'a str,
|
||||
pub triple: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(driver_ice_flags)]
|
||||
#[diag(driver_impl_ice_flags)]
|
||||
pub(crate) struct IceFlags {
|
||||
pub flags: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(driver_ice_exclude_cargo_defaults)]
|
||||
#[diag(driver_impl_ice_exclude_cargo_defaults)]
|
||||
pub(crate) struct IceExcludeCargoDefaults;
|
||||
|
|
@ -33,7 +33,7 @@ borrowck_var_here_defined = variable defined here
|
|||
|
||||
borrowck_var_here_captured = variable captured here
|
||||
|
||||
borrowck_closure_inferred_mut = inferred to be a `FnMut` closure
|
||||
borrowck_closure_inferred_mut = inferred to be a `FnMut` closure
|
||||
|
||||
borrowck_returned_closure_escaped =
|
||||
returns a closure that contains a reference to a captured variable, which then escapes the closure body
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files w
|
|||
|
||||
codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error}
|
||||
|
||||
codegen_ssa_incompatible_linking_modifiers = the linking modifiers `+bundle` and `+whole-archive` are not compatible with each other when generating rlibs
|
||||
codegen_ssa_incompatible_linking_modifiers = link modifiers combination `+bundle,+whole-archive` is unstable when generating rlibs
|
||||
|
||||
codegen_ssa_add_native_library = failed to add native library {$library_path}: {$error}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
driver_rlink_unable_to_read = failed to read rlink file: `{$err}`
|
||||
driver_impl_rlink_unable_to_read = failed to read rlink file: `{$err}`
|
||||
|
||||
driver_rlink_wrong_file_type = The input does not look like a .rlink file
|
||||
driver_impl_rlink_wrong_file_type = The input does not look like a .rlink file
|
||||
|
||||
driver_rlink_empty_version_number = The input does not contain version number
|
||||
driver_impl_rlink_empty_version_number = The input does not contain version number
|
||||
|
||||
driver_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}`
|
||||
driver_impl_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}`
|
||||
|
||||
driver_rlink_rustc_version_mismatch = .rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}`
|
||||
driver_impl_rlink_rustc_version_mismatch = .rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}`
|
||||
|
||||
driver_rlink_no_a_file = rlink must be a file
|
||||
driver_impl_rlink_no_a_file = rlink must be a file
|
||||
|
||||
driver_unpretty_dump_fail = pretty-print failed to write `{$path}` due to error `{$err}`
|
||||
driver_impl_unpretty_dump_fail = pretty-print failed to write `{$path}` due to error `{$err}`
|
||||
|
||||
driver_ice = the compiler unexpectedly panicked. this is a bug.
|
||||
driver_ice_bug_report = we would appreciate a bug report: {$bug_report_url}
|
||||
driver_ice_version = rustc {$version} running on {$triple}
|
||||
driver_ice_flags = compiler flags: {$flags}
|
||||
driver_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden
|
||||
driver_impl_ice = the compiler unexpectedly panicked. this is a bug.
|
||||
driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url}
|
||||
driver_impl_ice_version = rustc {$version} running on {$triple}
|
||||
driver_impl_ice_flags = compiler flags: {$flags}
|
||||
driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden
|
||||
|
|
|
|||
|
|
@ -61,3 +61,5 @@ hir_typeck_lang_start_incorrect_ret_ty = the return type of the `start` lang ite
|
|||
hir_typeck_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml`
|
||||
hir_typeck_help_set_edition_standalone = pass `--edition {$edition}` to `rustc`
|
||||
hir_typeck_note_edition_guide = for more on editions, read https://doc.rust-lang.org/edition-guide
|
||||
|
||||
hir_typeck_convert_to_str = try converting the passed type into a `&str`
|
||||
|
|
|
|||
|
|
@ -309,6 +309,7 @@ lint_unused_generator =
|
|||
.note = generators are lazy and do nothing unless resumed
|
||||
|
||||
lint_unused_def = unused {$pre}`{$def}`{$post} that must be used
|
||||
.suggestion = use `let _ = ...` to ignore the resulting value
|
||||
|
||||
lint_path_statement_drop = path statement drops value
|
||||
.suggestion = use `drop` to clarify the intent
|
||||
|
|
|
|||
|
|
@ -128,6 +128,9 @@ parse_missing_in_in_for_loop = missing `in` in `for` loop
|
|||
.use_in_not_of = try using `in` here instead
|
||||
.add_in = try adding `in` here
|
||||
|
||||
parse_missing_expression_in_for_loop = missing expression to iterate on in `for` loop
|
||||
.suggestion = try adding an expression to the `for` loop
|
||||
|
||||
parse_missing_comma_after_match_arm = expected `,` following `match` arm
|
||||
.suggestion = missing a comma here to end this `match` arm
|
||||
|
||||
|
|
@ -203,8 +206,9 @@ parse_inclusive_range_extra_equals = unexpected `=` after inclusive range
|
|||
.suggestion_remove_eq = use `..=` instead
|
||||
.note = inclusive ranges end with a single equals sign (`..=`)
|
||||
|
||||
parse_inclusive_range_match_arrow = unexpected `=>` after open range
|
||||
.suggestion_add_space = add a space between the pattern and `=>`
|
||||
parse_inclusive_range_match_arrow = unexpected `>` after inclusive range
|
||||
.label = this is parsed as an inclusive range `..=`
|
||||
.suggestion = add a space between the pattern and `=>`
|
||||
|
||||
parse_inclusive_range_no_end = inclusive range with no end
|
||||
.suggestion_open_range = use `..` instead
|
||||
|
|
@ -471,6 +475,9 @@ parse_unexpected_token_after_struct_name_found_other = expected `where`, `{"{"}`
|
|||
parse_unexpected_self_in_generic_parameters = unexpected keyword `Self` in generic parameters
|
||||
.note = you cannot use `Self` as a generic parameter because it is reserved for associated items
|
||||
|
||||
parse_unexpected_default_value_for_lifetime_in_generic_parameters = unexpected default lifetime parameter
|
||||
.label = lifetime parameters cannot have default values
|
||||
|
||||
parse_multiple_where_clauses = cannot define duplicate `where` clauses on an item
|
||||
.label = previous `where` clause starts here
|
||||
.suggestion = consider joining the two `where` clauses into one
|
||||
|
|
@ -535,8 +542,8 @@ parse_dot_dot_dot_range_to_pattern_not_allowed = range-to patterns with `...` ar
|
|||
|
||||
parse_enum_pattern_instead_of_identifier = expected identifier, found enum pattern
|
||||
|
||||
parse_dot_dot_dot_for_remaining_fields = expected field pattern, found `...`
|
||||
.suggestion = to omit remaining fields, use one fewer `.`
|
||||
parse_dot_dot_dot_for_remaining_fields = expected field pattern, found `{$token_str}`
|
||||
.suggestion = to omit remaining fields, use `..`
|
||||
|
||||
parse_expected_comma_after_pattern_field = expected `,`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
|
||||
use crate::errors::{
|
||||
ArgumentNotAttributes, AttrNoArguments, AttributeMetaItem, AttributeSingleWord,
|
||||
AttributesWrongForm, CannotBeNameOfMacro, ExpectedCommaInList, HelperAttributeNameInvalid,
|
||||
MacroBodyStability, MacroConstStability, NotAMetaItem, OnlyOneArgument, OnlyOneWord,
|
||||
ResolveRelativePath, TakesNoArguments, TraceMacro,
|
||||
};
|
||||
use crate::errors;
|
||||
use crate::expand::{self, AstFragment, Invocation};
|
||||
use crate::module::DirOwnership;
|
||||
|
||||
|
|
@ -796,13 +791,13 @@ impl SyntaxExtension {
|
|||
.unwrap_or_else(|| (None, helper_attrs));
|
||||
let (stability, const_stability, body_stability) = attr::find_stability(&sess, attrs, span);
|
||||
if let Some((_, sp)) = const_stability {
|
||||
sess.emit_err(MacroConstStability {
|
||||
sess.emit_err(errors::MacroConstStability {
|
||||
span: sp,
|
||||
head_span: sess.source_map().guess_head_span(span),
|
||||
});
|
||||
}
|
||||
if let Some((_, sp)) = body_stability {
|
||||
sess.emit_err(MacroBodyStability {
|
||||
sess.emit_err(errors::MacroBodyStability {
|
||||
span: sp,
|
||||
head_span: sess.source_map().guess_head_span(span),
|
||||
});
|
||||
|
|
@ -1143,7 +1138,7 @@ impl<'a> ExtCtxt<'a> {
|
|||
}
|
||||
pub fn trace_macros_diag(&mut self) {
|
||||
for (span, notes) in self.expansions.iter() {
|
||||
let mut db = self.sess.parse_sess.create_note(TraceMacro { span: *span });
|
||||
let mut db = self.sess.parse_sess.create_note(errors::TraceMacro { span: *span });
|
||||
for note in notes {
|
||||
db.note(note);
|
||||
}
|
||||
|
|
@ -1197,7 +1192,7 @@ pub fn resolve_path(
|
|||
.expect("attempting to resolve a file path in an external file"),
|
||||
FileName::DocTest(path, _) => path,
|
||||
other => {
|
||||
return Err(ResolveRelativePath {
|
||||
return Err(errors::ResolveRelativePath {
|
||||
span,
|
||||
path: parse_sess.source_map().filename_for_diagnostics(&other).to_string(),
|
||||
}
|
||||
|
|
@ -1279,7 +1274,7 @@ pub fn expr_to_string(
|
|||
/// done as rarely as possible).
|
||||
pub fn check_zero_tts(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream, name: &str) {
|
||||
if !tts.is_empty() {
|
||||
cx.emit_err(TakesNoArguments { span, name });
|
||||
cx.emit_err(errors::TakesNoArguments { span, name });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1307,14 +1302,14 @@ pub fn get_single_str_from_tts(
|
|||
) -> Option<Symbol> {
|
||||
let mut p = cx.new_parser_from_tts(tts);
|
||||
if p.token == token::Eof {
|
||||
cx.emit_err(OnlyOneArgument { span, name });
|
||||
cx.emit_err(errors::OnlyOneArgument { span, name });
|
||||
return None;
|
||||
}
|
||||
let ret = parse_expr(&mut p)?;
|
||||
let _ = p.eat(&token::Comma);
|
||||
|
||||
if p.token != token::Eof {
|
||||
cx.emit_err(OnlyOneArgument { span, name });
|
||||
cx.emit_err(errors::OnlyOneArgument { span, name });
|
||||
}
|
||||
expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| s)
|
||||
}
|
||||
|
|
@ -1336,7 +1331,7 @@ pub fn get_exprs_from_tts(cx: &mut ExtCtxt<'_>, tts: TokenStream) -> Option<Vec<
|
|||
continue;
|
||||
}
|
||||
if p.token != token::Eof {
|
||||
cx.emit_err(ExpectedCommaInList { span: p.token.span });
|
||||
cx.emit_err(errors::ExpectedCommaInList { span: p.token.span });
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
|
@ -1353,51 +1348,58 @@ pub fn parse_macro_name_and_helper_attrs(
|
|||
// `#[proc_macro_derive(Foo, attributes(A, ..))]`
|
||||
let list = attr.meta_item_list()?;
|
||||
if list.len() != 1 && list.len() != 2 {
|
||||
diag.emit_err(AttrNoArguments { span: attr.span });
|
||||
diag.emit_err(errors::AttrNoArguments { span: attr.span });
|
||||
return None;
|
||||
}
|
||||
let Some(trait_attr) = list[0].meta_item() else {
|
||||
diag.emit_err(NotAMetaItem {span: list[0].span()});
|
||||
diag.emit_err(errors::NotAMetaItem {span: list[0].span()});
|
||||
return None;
|
||||
};
|
||||
let trait_ident = match trait_attr.ident() {
|
||||
Some(trait_ident) if trait_attr.is_word() => trait_ident,
|
||||
_ => {
|
||||
diag.emit_err(OnlyOneWord { span: trait_attr.span });
|
||||
diag.emit_err(errors::OnlyOneWord { span: trait_attr.span });
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
if !trait_ident.name.can_be_raw() {
|
||||
diag.emit_err(CannotBeNameOfMacro { span: trait_attr.span, trait_ident, macro_type });
|
||||
diag.emit_err(errors::CannotBeNameOfMacro {
|
||||
span: trait_attr.span,
|
||||
trait_ident,
|
||||
macro_type,
|
||||
});
|
||||
}
|
||||
|
||||
let attributes_attr = list.get(1);
|
||||
let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
|
||||
if !attr.has_name(sym::attributes) {
|
||||
diag.emit_err(ArgumentNotAttributes { span: attr.span() });
|
||||
diag.emit_err(errors::ArgumentNotAttributes { span: attr.span() });
|
||||
}
|
||||
attr.meta_item_list()
|
||||
.unwrap_or_else(|| {
|
||||
diag.emit_err(AttributesWrongForm { span: attr.span() });
|
||||
diag.emit_err(errors::AttributesWrongForm { span: attr.span() });
|
||||
&[]
|
||||
})
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
let Some(attr) = attr.meta_item() else {
|
||||
diag.emit_err(AttributeMetaItem { span: attr.span() });
|
||||
diag.emit_err(errors::AttributeMetaItem { span: attr.span() });
|
||||
return None;
|
||||
};
|
||||
|
||||
let ident = match attr.ident() {
|
||||
Some(ident) if attr.is_word() => ident,
|
||||
_ => {
|
||||
diag.emit_err(AttributeSingleWord { span: attr.span });
|
||||
diag.emit_err(errors::AttributeSingleWord { span: attr.span });
|
||||
return None;
|
||||
}
|
||||
};
|
||||
if !ident.name.can_be_raw() {
|
||||
diag.emit_err(HelperAttributeNameInvalid { span: attr.span, name: ident });
|
||||
diag.emit_err(errors::HelperAttributeNameInvalid {
|
||||
span: attr.span,
|
||||
name: ident,
|
||||
});
|
||||
}
|
||||
|
||||
Some(ident.name)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use rustc_ast::token::{self, Delimiter};
|
||||
use rustc_ast::tokenstream::{CursorRef, TokenStream, TokenTree};
|
||||
use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree};
|
||||
use rustc_ast::{LitIntType, LitKind};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::{Applicability, PResult};
|
||||
|
|
@ -72,7 +72,7 @@ impl MetaVarExpr {
|
|||
|
||||
// Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
|
||||
fn check_trailing_token<'sess>(
|
||||
iter: &mut CursorRef<'_>,
|
||||
iter: &mut RefTokenTreeCursor<'_>,
|
||||
sess: &'sess ParseSess,
|
||||
) -> PResult<'sess, ()> {
|
||||
if let Some(tt) = iter.next() {
|
||||
|
|
@ -88,7 +88,7 @@ fn check_trailing_token<'sess>(
|
|||
|
||||
/// Parse a meta-variable `count` expression: `count(ident[, depth])`
|
||||
fn parse_count<'sess>(
|
||||
iter: &mut CursorRef<'_>,
|
||||
iter: &mut RefTokenTreeCursor<'_>,
|
||||
sess: &'sess ParseSess,
|
||||
span: Span,
|
||||
) -> PResult<'sess, MetaVarExpr> {
|
||||
|
|
@ -99,7 +99,7 @@ fn parse_count<'sess>(
|
|||
|
||||
/// Parses the depth used by index(depth) and length(depth).
|
||||
fn parse_depth<'sess>(
|
||||
iter: &mut CursorRef<'_>,
|
||||
iter: &mut RefTokenTreeCursor<'_>,
|
||||
sess: &'sess ParseSess,
|
||||
span: Span,
|
||||
) -> PResult<'sess, usize> {
|
||||
|
|
@ -126,7 +126,7 @@ fn parse_depth<'sess>(
|
|||
|
||||
/// Parses an generic ident
|
||||
fn parse_ident<'sess>(
|
||||
iter: &mut CursorRef<'_>,
|
||||
iter: &mut RefTokenTreeCursor<'_>,
|
||||
sess: &'sess ParseSess,
|
||||
span: Span,
|
||||
) -> PResult<'sess, Ident> {
|
||||
|
|
@ -152,7 +152,7 @@ fn parse_ident<'sess>(
|
|||
|
||||
/// Tries to move the iterator forward returning `true` if there is a comma. If not, then the
|
||||
/// iterator is not modified and the result is `false`.
|
||||
fn try_eat_comma(iter: &mut CursorRef<'_>) -> bool {
|
||||
fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool {
|
||||
if let Some(TokenTree::Token(token::Token { kind: token::Comma, .. }, _)) = iter.look_ahead(0) {
|
||||
let _ = iter.next();
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -164,6 +164,8 @@ declare_features! (
|
|||
(active, multiple_supertrait_upcastable, "CURRENT_RUSTC_VERSION", None, None),
|
||||
/// Allows using `#[omit_gdb_pretty_printer_section]`.
|
||||
(active, omit_gdb_pretty_printer_section, "1.5.0", None, None),
|
||||
/// Allows using `+bundled,+whole-archive` native libs.
|
||||
(active, packed_bundled_libs, "1.67.0", None, None),
|
||||
/// Allows using `#[prelude_import]` on glob `use` items.
|
||||
(active, prelude_import, "1.2.0", None, None),
|
||||
/// Used to identify crates that contain the profiler runtime.
|
||||
|
|
|
|||
|
|
@ -15,9 +15,7 @@ rustc_middle = { path = "../rustc_middle" }
|
|||
rustc_attr = { path = "../rustc_attr" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_graphviz = { path = "../rustc_graphviz" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_hir_pretty = { path = "../rustc_hir_pretty" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
|
||||
|
|
@ -27,6 +25,5 @@ rustc_index = { path = "../rustc_index" }
|
|||
rustc_infer = { path = "../rustc_infer" }
|
||||
rustc_trait_selection = { path = "../rustc_trait_selection" }
|
||||
rustc_lint = { path = "../rustc_lint" }
|
||||
rustc_serialize = { path = "../rustc_serialize" }
|
||||
rustc_type_ir = { path = "../rustc_type_ir" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
|
|
|
|||
|
|
@ -2945,12 +2945,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
if r.is_erased() { tcx.lifetimes.re_static } else { r }
|
||||
});
|
||||
let span = ast_ty.span;
|
||||
tcx.sess.emit_err(TypeofReservedKeywordUsed {
|
||||
span,
|
||||
ty,
|
||||
opt_sugg: Some((span, Applicability::MachineApplicable))
|
||||
.filter(|_| ty.is_suggestable(tcx, false)),
|
||||
});
|
||||
let (ty, opt_sugg) = if let Some(ty) = ty.make_suggestable(tcx, false) {
|
||||
(ty, Some((span, Applicability::MachineApplicable)))
|
||||
} else {
|
||||
(ty, None)
|
||||
};
|
||||
tcx.sess.emit_err(TypeofReservedKeywordUsed { span, ty, opt_sugg });
|
||||
|
||||
ty
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use rustc_hir as hir;
|
|||
use rustc_middle::ty::subst::InternalSubsts;
|
||||
use rustc_middle::ty::util::IgnoreRegions;
|
||||
use rustc_middle::ty::{
|
||||
self, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
|
||||
self, AliasKind, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
|
||||
};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::def_id::{DefId, LocalDefId};
|
||||
|
|
@ -86,7 +86,7 @@ fn do_orphan_check_impl<'tcx>(
|
|||
// struct B { }
|
||||
// impl Foo for A { }
|
||||
// impl Foo for B { }
|
||||
// impl !Send for (A, B) { }
|
||||
// impl !Foo for (A, B) { }
|
||||
// ```
|
||||
//
|
||||
// This final impl is legal according to the orphan
|
||||
|
|
@ -99,50 +99,193 @@ fn do_orphan_check_impl<'tcx>(
|
|||
tcx.trait_is_auto(trait_def_id)
|
||||
);
|
||||
|
||||
if tcx.trait_is_auto(trait_def_id) && !trait_def_id.is_local() {
|
||||
if tcx.trait_is_auto(trait_def_id) {
|
||||
let self_ty = trait_ref.self_ty();
|
||||
let opt_self_def_id = match *self_ty.kind() {
|
||||
ty::Adt(self_def, _) => Some(self_def.did()),
|
||||
ty::Foreign(did) => Some(did),
|
||||
_ => None,
|
||||
|
||||
// If the impl is in the same crate as the auto-trait, almost anything
|
||||
// goes.
|
||||
//
|
||||
// impl MyAuto for Rc<Something> {} // okay
|
||||
// impl<T> !MyAuto for *const T {} // okay
|
||||
// impl<T> MyAuto for T {} // okay
|
||||
//
|
||||
// But there is one important exception: implementing for a trait object
|
||||
// is not allowed.
|
||||
//
|
||||
// impl MyAuto for dyn Trait {} // NOT OKAY
|
||||
// impl<T: ?Sized> MyAuto for T {} // NOT OKAY
|
||||
//
|
||||
// With this restriction, it's guaranteed that an auto-trait is
|
||||
// implemented for a trait object if and only if the auto-trait is one
|
||||
// of the trait object's trait bounds (or a supertrait of a bound). In
|
||||
// other words `dyn Trait + AutoTrait` always implements AutoTrait,
|
||||
// while `dyn Trait` never implements AutoTrait.
|
||||
//
|
||||
// This is necessary in order for autotrait bounds on methods of trait
|
||||
// objects to be sound.
|
||||
//
|
||||
// auto trait AutoTrait {}
|
||||
//
|
||||
// trait ObjectSafeTrait {
|
||||
// fn f(&self) where Self: AutoTrait;
|
||||
// }
|
||||
//
|
||||
// We can allow f to be called on `dyn ObjectSafeTrait + AutoTrait`.
|
||||
//
|
||||
// If we didn't deny `impl AutoTrait for dyn Trait`, it would be unsound
|
||||
// for the ObjectSafeTrait shown above to be object safe because someone
|
||||
// could take some type implementing ObjectSafeTrait but not AutoTrait,
|
||||
// unsize it to `dyn ObjectSafeTrait`, and call .f() which has no
|
||||
// concrete implementation (issue #50781).
|
||||
enum LocalImpl {
|
||||
Allow,
|
||||
Disallow { problematic_kind: &'static str },
|
||||
}
|
||||
|
||||
// If the auto-trait is from a dependency, it must only be getting
|
||||
// implemented for a nominal type, and specifically one local to the
|
||||
// current crate.
|
||||
//
|
||||
// impl<T> Sync for MyStruct<T> {} // okay
|
||||
//
|
||||
// impl Sync for Rc<MyStruct> {} // NOT OKAY
|
||||
enum NonlocalImpl {
|
||||
Allow,
|
||||
DisallowBecauseNonlocal,
|
||||
DisallowOther,
|
||||
}
|
||||
|
||||
// Exhaustive match considering that this logic is essential for
|
||||
// soundness.
|
||||
let (local_impl, nonlocal_impl) = match self_ty.kind() {
|
||||
// struct Struct<T>;
|
||||
// impl AutoTrait for Struct<Foo> {}
|
||||
ty::Adt(self_def, _) => (
|
||||
LocalImpl::Allow,
|
||||
if self_def.did().is_local() {
|
||||
NonlocalImpl::Allow
|
||||
} else {
|
||||
NonlocalImpl::DisallowBecauseNonlocal
|
||||
},
|
||||
),
|
||||
|
||||
// extern { type OpaqueType; }
|
||||
// impl AutoTrait for OpaqueType {}
|
||||
ty::Foreign(did) => (
|
||||
LocalImpl::Allow,
|
||||
if did.is_local() {
|
||||
NonlocalImpl::Allow
|
||||
} else {
|
||||
NonlocalImpl::DisallowBecauseNonlocal
|
||||
},
|
||||
),
|
||||
|
||||
// impl AutoTrait for dyn Trait {}
|
||||
ty::Dynamic(..) => (
|
||||
LocalImpl::Disallow { problematic_kind: "trait object" },
|
||||
NonlocalImpl::DisallowOther,
|
||||
),
|
||||
|
||||
// impl<T> AutoTrait for T {}
|
||||
// impl<T: ?Sized> AutoTrait for T {}
|
||||
ty::Param(..) => (
|
||||
if self_ty.is_sized(tcx, tcx.param_env(def_id)) {
|
||||
LocalImpl::Allow
|
||||
} else {
|
||||
LocalImpl::Disallow { problematic_kind: "generic type" }
|
||||
},
|
||||
NonlocalImpl::DisallowOther,
|
||||
),
|
||||
|
||||
// trait Id { type This: ?Sized; }
|
||||
// impl<T: ?Sized> Id for T {
|
||||
// type This = T;
|
||||
// }
|
||||
// impl<T: ?Sized> AutoTrait for <T as Id>::This {}
|
||||
ty::Alias(AliasKind::Projection, _) => (
|
||||
LocalImpl::Disallow { problematic_kind: "associated type" },
|
||||
NonlocalImpl::DisallowOther,
|
||||
),
|
||||
|
||||
// type Opaque = impl Trait;
|
||||
// impl AutoTrait for Opaque {}
|
||||
ty::Alias(AliasKind::Opaque, _) => (
|
||||
LocalImpl::Disallow { problematic_kind: "opaque type" },
|
||||
NonlocalImpl::DisallowOther,
|
||||
),
|
||||
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(..)
|
||||
| ty::Uint(..)
|
||||
| ty::Float(..)
|
||||
| ty::Str
|
||||
| ty::Array(..)
|
||||
| ty::Slice(..)
|
||||
| ty::RawPtr(..)
|
||||
| ty::Ref(..)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(..) => (LocalImpl::Allow, NonlocalImpl::DisallowOther),
|
||||
|
||||
ty::Closure(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::Bound(..)
|
||||
| ty::Placeholder(..)
|
||||
| ty::Infer(..) => span_bug!(sp, "weird self type for autotrait impl"),
|
||||
|
||||
ty::Error(..) => (LocalImpl::Allow, NonlocalImpl::Allow),
|
||||
};
|
||||
|
||||
let msg = match opt_self_def_id {
|
||||
// We only want to permit nominal types, but not *all* nominal types.
|
||||
// They must be local to the current crate, so that people
|
||||
// can't do `unsafe impl Send for Rc<SomethingLocal>` or
|
||||
// `impl !Send for Box<SomethingLocalAndSend>`.
|
||||
Some(self_def_id) => {
|
||||
if self_def_id.is_local() {
|
||||
None
|
||||
} else {
|
||||
Some((
|
||||
format!(
|
||||
"cross-crate traits with a default impl, like `{}`, \
|
||||
can only be implemented for a struct/enum type \
|
||||
defined in the current crate",
|
||||
tcx.def_path_str(trait_def_id)
|
||||
),
|
||||
"can't implement cross-crate trait for type in another crate",
|
||||
))
|
||||
if trait_def_id.is_local() {
|
||||
match local_impl {
|
||||
LocalImpl::Allow => {}
|
||||
LocalImpl::Disallow { problematic_kind } => {
|
||||
let msg = format!(
|
||||
"traits with a default impl, like `{trait}`, \
|
||||
cannot be implemented for {problematic_kind} `{self_ty}`",
|
||||
trait = tcx.def_path_str(trait_def_id),
|
||||
);
|
||||
let label = format!(
|
||||
"a trait object implements `{trait}` if and only if `{trait}` \
|
||||
is one of the trait object's trait bounds",
|
||||
trait = tcx.def_path_str(trait_def_id),
|
||||
);
|
||||
let reported =
|
||||
struct_span_err!(tcx.sess, sp, E0321, "{}", msg).note(label).emit();
|
||||
return Err(reported);
|
||||
}
|
||||
}
|
||||
_ => Some((
|
||||
format!(
|
||||
"cross-crate traits with a default impl, like `{}`, can \
|
||||
} else {
|
||||
if let Some((msg, label)) = match nonlocal_impl {
|
||||
NonlocalImpl::Allow => None,
|
||||
NonlocalImpl::DisallowBecauseNonlocal => Some((
|
||||
format!(
|
||||
"cross-crate traits with a default impl, like `{}`, \
|
||||
can only be implemented for a struct/enum type \
|
||||
defined in the current crate",
|
||||
tcx.def_path_str(trait_def_id)
|
||||
),
|
||||
"can't implement cross-crate trait for type in another crate",
|
||||
)),
|
||||
NonlocalImpl::DisallowOther => Some((
|
||||
format!(
|
||||
"cross-crate traits with a default impl, like `{}`, can \
|
||||
only be implemented for a struct/enum type, not `{}`",
|
||||
tcx.def_path_str(trait_def_id),
|
||||
self_ty
|
||||
),
|
||||
"can't implement cross-crate trait with a default impl for \
|
||||
non-struct/enum type",
|
||||
)),
|
||||
};
|
||||
|
||||
if let Some((msg, label)) = msg {
|
||||
let reported =
|
||||
struct_span_err!(tcx.sess, sp, E0321, "{}", msg).span_label(sp, label).emit();
|
||||
return Err(reported);
|
||||
tcx.def_path_str(trait_def_id),
|
||||
self_ty
|
||||
),
|
||||
"can't implement cross-crate trait with a default impl for \
|
||||
non-struct/enum type",
|
||||
)),
|
||||
} {
|
||||
let reported =
|
||||
struct_span_err!(tcx.sess, sp, E0321, "{}", msg).span_label(sp, label).emit();
|
||||
return Err(reported);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1199,28 +1199,22 @@ fn infer_return_ty_for_fn_sig<'tcx>(
|
|||
visitor.visit_ty(ty);
|
||||
let mut diag = bad_placeholder(tcx, visitor.0, "return type");
|
||||
let ret_ty = fn_sig.output();
|
||||
if ret_ty.is_suggestable(tcx, false) {
|
||||
if let Some(ret_ty) = ret_ty.make_suggestable(tcx, false) {
|
||||
diag.span_suggestion(
|
||||
ty.span,
|
||||
"replace with the correct return type",
|
||||
ret_ty,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if matches!(ret_ty.kind(), ty::FnDef(..)) {
|
||||
let fn_sig = ret_ty.fn_sig(tcx);
|
||||
if fn_sig
|
||||
.skip_binder()
|
||||
.inputs_and_output
|
||||
.iter()
|
||||
.all(|t| t.is_suggestable(tcx, false))
|
||||
{
|
||||
diag.span_suggestion(
|
||||
ty.span,
|
||||
"replace with the correct return type",
|
||||
fn_sig,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
} else if matches!(ret_ty.kind(), ty::FnDef(..))
|
||||
&& let Some(fn_sig) = ret_ty.fn_sig(tcx).make_suggestable(tcx, false)
|
||||
{
|
||||
diag.span_suggestion(
|
||||
ty.span,
|
||||
"replace with the correct return type",
|
||||
fn_sig,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if let Some(sugg) = suggest_impl_trait(tcx, ret_ty, ty.span, hir_id, def_id) {
|
||||
diag.span_suggestion(
|
||||
ty.span,
|
||||
|
|
@ -1280,9 +1274,7 @@ fn suggest_impl_trait<'tcx>(
|
|||
let trait_name = tcx.item_name(trait_def_id);
|
||||
let args_tuple = substs.type_at(1);
|
||||
let ty::Tuple(types) = *args_tuple.kind() else { return None; };
|
||||
if !types.is_suggestable(tcx, false) {
|
||||
return None;
|
||||
}
|
||||
let types = types.make_suggestable(tcx, false)?;
|
||||
let maybe_ret =
|
||||
if item_ty.is_unit() { String::new() } else { format!(" -> {item_ty}") };
|
||||
Some(format!(
|
||||
|
|
@ -1337,7 +1329,7 @@ fn suggest_impl_trait<'tcx>(
|
|||
// FIXME(compiler-errors): We may benefit from resolving regions here.
|
||||
if ocx.select_where_possible().is_empty()
|
||||
&& let item_ty = infcx.resolve_vars_if_possible(item_ty)
|
||||
&& item_ty.is_suggestable(tcx, false)
|
||||
&& let Some(item_ty) = item_ty.make_suggestable(tcx, false)
|
||||
&& let Some(sugg) = formatter(tcx, infcx.resolve_vars_if_possible(substs), trait_def_id, assoc_item_def_id, item_ty)
|
||||
{
|
||||
return Some(sugg);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ use rustc_middle::hir::nested_filter;
|
|||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
use rustc_middle::ty::subst::InternalSubsts;
|
||||
use rustc_middle::ty::util::IntTypeExt;
|
||||
use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable};
|
||||
use rustc_middle::ty::{
|
||||
self, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable,
|
||||
};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
|
||||
|
|
@ -845,37 +847,23 @@ fn infer_placeholder_type<'a>(
|
|||
) -> Ty<'a> {
|
||||
// Attempts to make the type nameable by turning FnDefs into FnPtrs.
|
||||
struct MakeNameable<'tcx> {
|
||||
success: bool,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> MakeNameable<'tcx> {
|
||||
fn new(tcx: TyCtxt<'tcx>) -> Self {
|
||||
MakeNameable { success: true, tcx }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFolder<'tcx> for MakeNameable<'tcx> {
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
if !self.success {
|
||||
return ty;
|
||||
}
|
||||
|
||||
match ty.kind() {
|
||||
let ty = match *ty.kind() {
|
||||
ty::FnDef(def_id, substs) => {
|
||||
self.tcx.mk_fn_ptr(self.tcx.fn_sig(*def_id).subst(self.tcx, substs))
|
||||
self.tcx.mk_fn_ptr(self.tcx.fn_sig(def_id).subst(self.tcx, substs))
|
||||
}
|
||||
// FIXME: non-capturing closures should also suggest a function pointer
|
||||
ty::Closure(..) | ty::Generator(..) => {
|
||||
self.success = false;
|
||||
ty
|
||||
}
|
||||
_ => ty.super_fold_with(self),
|
||||
}
|
||||
_ => ty,
|
||||
};
|
||||
|
||||
ty.super_fold_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -898,15 +886,11 @@ fn infer_placeholder_type<'a>(
|
|||
suggestions.clear();
|
||||
}
|
||||
|
||||
// Suggesting unnameable types won't help.
|
||||
let mut mk_nameable = MakeNameable::new(tcx);
|
||||
let ty = mk_nameable.fold_ty(ty);
|
||||
let sugg_ty = if mk_nameable.success { Some(ty) } else { None };
|
||||
if let Some(sugg_ty) = sugg_ty {
|
||||
if let Some(ty) = ty.make_suggestable(tcx, false) {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
&format!("provide a type for the {item}", item = kind),
|
||||
format!("{colon} {sugg_ty}"),
|
||||
format!("{colon} {ty}"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
|
|
@ -923,15 +907,12 @@ fn infer_placeholder_type<'a>(
|
|||
let mut diag = bad_placeholder(tcx, vec![span], kind);
|
||||
|
||||
if !ty.references_error() {
|
||||
let mut mk_nameable = MakeNameable::new(tcx);
|
||||
let ty = mk_nameable.fold_ty(ty);
|
||||
let sugg_ty = if mk_nameable.success { Some(ty) } else { None };
|
||||
if let Some(sugg_ty) = sugg_ty {
|
||||
if let Some(ty) = ty.make_suggestable(tcx, false) {
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"replace with the correct type",
|
||||
sugg_ty,
|
||||
Applicability::MaybeIncorrect,
|
||||
ty,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
with_forced_trimmed_paths!(diag.span_note(
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|| self.suggest_clone_for_ref(err, expr, expr_ty, expected)
|
||||
|| self.suggest_into(err, expr, expr_ty, expected)
|
||||
|| self.suggest_floating_point_literal(err, expr, expected)
|
||||
|| self.suggest_null_ptr_for_literal_zero_given_to_ptr_arg(err, expr, expected)
|
||||
|| self.note_result_coercion(err, expr, expected, expr_ty);
|
||||
if !suggested {
|
||||
self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected, expr.span);
|
||||
|
|
|
|||
|
|
@ -921,6 +921,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
kind: hir::ImplItemKind::Fn(ref sig, ..),
|
||||
..
|
||||
}) => Some((&sig.decl, ident, false)),
|
||||
Node::Expr(&hir::Expr {
|
||||
hir_id,
|
||||
kind: hir::ExprKind::Closure(..),
|
||||
..
|
||||
}) if let Some(Node::Expr(&hir::Expr {
|
||||
hir_id,
|
||||
kind: hir::ExprKind::Call(..),
|
||||
..
|
||||
})) = self.tcx.hir().find_parent(hir_id) &&
|
||||
let Some(Node::Item(&hir::Item {
|
||||
ident,
|
||||
kind: hir::ItemKind::Fn(ref sig, ..),
|
||||
..
|
||||
})) = self.tcx.hir().find_parent(hir_id) => {
|
||||
Some((&sig.decl, ident, ident.name != sym::main))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,457 @@
|
|||
use crate::FnCtxt;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_middle::ty::{self, DefIdTree, Ty};
|
||||
use rustc_trait_selection::traits;
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/**
|
||||
* Recursively searches for the most-specific blamable expression.
|
||||
* For example, if you have a chain of constraints like:
|
||||
* - want `Vec<i32>: Copy`
|
||||
* - because `Option<Vec<i32>>: Copy` needs `Vec<i32>: Copy` because `impl <T: Copy> Copy for Option<T>`
|
||||
* - because `(Option<Vec<i32>, bool)` needs `Option<Vec<i32>>: Copy` because `impl <A: Copy, B: Copy> Copy for (A, B)`
|
||||
* then if you pass in `(Some(vec![1, 2, 3]), false)`, this helper `point_at_specific_expr_if_possible`
|
||||
* will find the expression `vec![1, 2, 3]` as the "most blameable" reason for this missing constraint.
|
||||
*
|
||||
* This function only updates the error span.
|
||||
*/
|
||||
pub fn blame_specific_expr_if_possible(
|
||||
&self,
|
||||
error: &mut traits::FulfillmentError<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
) {
|
||||
// Whether it succeeded or failed, it likely made some amount of progress.
|
||||
// In the very worst case, it's just the same `expr` we originally passed in.
|
||||
let expr = match self.blame_specific_expr_if_possible_for_obligation_cause_code(
|
||||
&error.obligation.cause.code(),
|
||||
expr,
|
||||
) {
|
||||
Ok(expr) => expr,
|
||||
Err(expr) => expr,
|
||||
};
|
||||
|
||||
// Either way, use this expression to update the error span.
|
||||
// If it doesn't overlap the existing span at all, use the original span.
|
||||
// FIXME: It would possibly be better to do this more continuously, at each level...
|
||||
error.obligation.cause.span = expr
|
||||
.span
|
||||
.find_ancestor_in_same_ctxt(error.obligation.cause.span)
|
||||
.unwrap_or(error.obligation.cause.span);
|
||||
}
|
||||
|
||||
fn blame_specific_expr_if_possible_for_obligation_cause_code(
|
||||
&self,
|
||||
obligation_cause_code: &traits::ObligationCauseCode<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> {
|
||||
match obligation_cause_code {
|
||||
traits::ObligationCauseCode::ExprBindingObligation(_, _, _, _) => {
|
||||
// This is the "root"; we assume that the `expr` is already pointing here.
|
||||
// Therefore, we return `Ok` so that this `expr` can be refined further.
|
||||
Ok(expr)
|
||||
}
|
||||
traits::ObligationCauseCode::ImplDerivedObligation(impl_derived) => self
|
||||
.blame_specific_expr_if_possible_for_derived_predicate_obligation(
|
||||
impl_derived,
|
||||
expr,
|
||||
),
|
||||
_ => {
|
||||
// We don't recognize this kind of constraint, so we cannot refine the expression
|
||||
// any further.
|
||||
Err(expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// We want to achieve the error span in the following example:
|
||||
///
|
||||
/// ```ignore (just for demonstration)
|
||||
/// struct Burrito<Filling> {
|
||||
/// filling: Filling,
|
||||
/// }
|
||||
/// impl <Filling: Delicious> Delicious for Burrito<Filling> {}
|
||||
/// fn eat_delicious_food<Food: Delicious>(_food: Food) {}
|
||||
///
|
||||
/// fn will_type_error() {
|
||||
/// eat_delicious_food(Burrito { filling: Kale });
|
||||
/// } // ^--- The trait bound `Kale: Delicious`
|
||||
/// // is not satisfied
|
||||
/// ```
|
||||
///
|
||||
/// Without calling this function, the error span will cover the entire argument expression.
|
||||
///
|
||||
/// Before we do any of this logic, we recursively call `point_at_specific_expr_if_possible` on the parent
|
||||
/// obligation. Hence we refine the `expr` "outwards-in" and bail at the first kind of expression/impl we don't recognize.
|
||||
///
|
||||
/// This function returns a `Result<&Expr, &Expr>` - either way, it returns the `Expr` whose span should be
|
||||
/// reported as an error. If it is `Ok`, then it means it refined successfull. If it is `Err`, then it may be
|
||||
/// only a partial success - but it cannot be refined even further.
|
||||
fn blame_specific_expr_if_possible_for_derived_predicate_obligation(
|
||||
&self,
|
||||
obligation: &traits::ImplDerivedObligationCause<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> {
|
||||
// First, we attempt to refine the `expr` for our span using the parent obligation.
|
||||
// If this cannot be done, then we are already stuck, so we stop early (hence the use
|
||||
// of the `?` try operator here).
|
||||
let expr = self.blame_specific_expr_if_possible_for_obligation_cause_code(
|
||||
&*obligation.derived.parent_code,
|
||||
expr,
|
||||
)?;
|
||||
|
||||
// This is the "trait" (meaning, the predicate "proved" by this `impl`) which provides the `Self` type we care about.
|
||||
// For the purposes of this function, we hope that it is a `struct` type, and that our current `expr` is a literal of
|
||||
// that struct type.
|
||||
let impl_trait_self_ref: Option<ty::TraitRef<'tcx>> =
|
||||
self.tcx.impl_trait_ref(obligation.impl_def_id).map(|impl_def| impl_def.skip_binder());
|
||||
|
||||
let Some(impl_trait_self_ref) = impl_trait_self_ref else {
|
||||
// It is possible that this is absent. In this case, we make no progress.
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
// We only really care about the `Self` type itself, which we extract from the ref.
|
||||
let impl_self_ty: Ty<'tcx> = impl_trait_self_ref.self_ty();
|
||||
|
||||
let impl_predicates: ty::GenericPredicates<'tcx> =
|
||||
self.tcx.predicates_of(obligation.impl_def_id);
|
||||
let Some(impl_predicate_index) = obligation.impl_def_predicate_index else {
|
||||
// We don't have the index, so we can only guess.
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
if impl_predicate_index >= impl_predicates.predicates.len() {
|
||||
// This shouldn't happen, but since this is only a diagnostic improvement, avoid breaking things.
|
||||
return Err(expr);
|
||||
}
|
||||
let relevant_broken_predicate: ty::PredicateKind<'tcx> =
|
||||
impl_predicates.predicates[impl_predicate_index].0.kind().skip_binder();
|
||||
|
||||
match relevant_broken_predicate {
|
||||
ty::PredicateKind::Clause(ty::Clause::Trait(broken_trait)) => {
|
||||
// ...
|
||||
self.blame_specific_part_of_expr_corresponding_to_generic_param(
|
||||
broken_trait.trait_ref.self_ty().into(),
|
||||
expr,
|
||||
impl_self_ty.into(),
|
||||
)
|
||||
}
|
||||
_ => Err(expr),
|
||||
}
|
||||
}
|
||||
|
||||
/// Drills into `expr` to arrive at the equivalent location of `find_generic_param` in `in_ty`.
|
||||
/// For example, given
|
||||
/// - expr: `(Some(vec![1, 2, 3]), false)`
|
||||
/// - param: `T`
|
||||
/// - in_ty: `(Option<Vec<T>, bool)`
|
||||
/// we would drill until we arrive at `vec![1, 2, 3]`.
|
||||
///
|
||||
/// If successful, we return `Ok(refined_expr)`. If unsuccesful, we return `Err(partially_refined_expr`),
|
||||
/// which will go as far as possible. For example, given `(foo(), false)` instead, we would drill to
|
||||
/// `foo()` and then return `Err("foo()")`.
|
||||
///
|
||||
/// This means that you can (and should) use the `?` try operator to chain multiple calls to this
|
||||
/// function with different types, since you can only continue drilling the second time if you
|
||||
/// succeeded the first time.
|
||||
fn blame_specific_part_of_expr_corresponding_to_generic_param(
|
||||
&self,
|
||||
param: ty::GenericArg<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
in_ty: ty::GenericArg<'tcx>,
|
||||
) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> {
|
||||
if param == in_ty {
|
||||
// The types match exactly, so we have drilled as far as we can.
|
||||
return Ok(expr);
|
||||
}
|
||||
|
||||
let ty::GenericArgKind::Type(in_ty) = in_ty.unpack() else {
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
if let (hir::ExprKind::Tup(expr_elements), ty::Tuple(in_ty_elements)) =
|
||||
(&expr.kind, in_ty.kind())
|
||||
{
|
||||
if in_ty_elements.len() != expr_elements.len() {
|
||||
return Err(expr);
|
||||
}
|
||||
// Find out which of `in_ty_elements` refer to `param`.
|
||||
// FIXME: It may be better to take the first if there are multiple,
|
||||
// just so that the error points to a smaller expression.
|
||||
let Some((drill_expr, drill_ty)) = Self::is_iterator_singleton(expr_elements.iter().zip( in_ty_elements.iter()).filter(|(_expr_elem, in_ty_elem)| {
|
||||
Self::find_param_in_ty((*in_ty_elem).into(), param)
|
||||
})) else {
|
||||
// The param is not mentioned, or it is mentioned in multiple indexes.
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
return self.blame_specific_part_of_expr_corresponding_to_generic_param(
|
||||
param,
|
||||
drill_expr,
|
||||
drill_ty.into(),
|
||||
);
|
||||
}
|
||||
|
||||
if let (
|
||||
hir::ExprKind::Struct(expr_struct_path, expr_struct_fields, _expr_struct_rest),
|
||||
ty::Adt(in_ty_adt, in_ty_adt_generic_args),
|
||||
) = (&expr.kind, in_ty.kind())
|
||||
{
|
||||
// First, confirm that this struct is the same one as in the types, and if so,
|
||||
// find the right variant.
|
||||
let Res::Def(expr_struct_def_kind, expr_struct_def_id) = self.typeck_results.borrow().qpath_res(expr_struct_path, expr.hir_id) else {
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
let variant_def_id = match expr_struct_def_kind {
|
||||
hir::def::DefKind::Struct => {
|
||||
if in_ty_adt.did() != expr_struct_def_id {
|
||||
// FIXME: Deal with type aliases?
|
||||
return Err(expr);
|
||||
}
|
||||
expr_struct_def_id
|
||||
}
|
||||
hir::def::DefKind::Variant => {
|
||||
// If this is a variant, its parent is the type definition.
|
||||
if in_ty_adt.did() != self.tcx.parent(expr_struct_def_id) {
|
||||
// FIXME: Deal with type aliases?
|
||||
return Err(expr);
|
||||
}
|
||||
expr_struct_def_id
|
||||
}
|
||||
_ => {
|
||||
return Err(expr);
|
||||
}
|
||||
};
|
||||
|
||||
// We need to know which of the generic parameters mentions our target param.
|
||||
// We expect that at least one of them does, since it is expected to be mentioned.
|
||||
let Some((drill_generic_index, generic_argument_type)) =
|
||||
Self::is_iterator_singleton(
|
||||
in_ty_adt_generic_args.iter().enumerate().filter(
|
||||
|(_index, in_ty_generic)| {
|
||||
Self::find_param_in_ty(*in_ty_generic, param)
|
||||
},
|
||||
),
|
||||
) else {
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
let struct_generic_parameters: &ty::Generics = self.tcx.generics_of(in_ty_adt.did());
|
||||
if drill_generic_index >= struct_generic_parameters.params.len() {
|
||||
return Err(expr);
|
||||
}
|
||||
|
||||
let param_to_point_at_in_struct = self.tcx.mk_param_from_def(
|
||||
struct_generic_parameters.param_at(drill_generic_index, self.tcx),
|
||||
);
|
||||
|
||||
// We make 3 steps:
|
||||
// Suppose we have a type like
|
||||
// ```ignore (just for demonstration)
|
||||
// struct ExampleStruct<T> {
|
||||
// enabled: bool,
|
||||
// item: Option<(usize, T, bool)>,
|
||||
// }
|
||||
//
|
||||
// f(ExampleStruct {
|
||||
// enabled: false,
|
||||
// item: Some((0, Box::new(String::new()), 1) }, true)),
|
||||
// });
|
||||
// ```
|
||||
// Here, `f` is passed a `ExampleStruct<Box<String>>`, but it wants
|
||||
// for `String: Copy`, which isn't true here.
|
||||
//
|
||||
// (1) First, we drill into `.item` and highlight that expression
|
||||
// (2) Then we use the template type `Option<(usize, T, bool)>` to
|
||||
// drill into the `T`, arriving at a `Box<String>` expression.
|
||||
// (3) Then we keep going, drilling into this expression using our
|
||||
// outer contextual information.
|
||||
|
||||
// (1) Find the (unique) field which mentions the type in our constraint:
|
||||
let (field_expr, field_type) = self
|
||||
.point_at_field_if_possible(
|
||||
in_ty_adt.did(),
|
||||
param_to_point_at_in_struct,
|
||||
variant_def_id,
|
||||
expr_struct_fields,
|
||||
)
|
||||
.ok_or(expr)?;
|
||||
|
||||
// (2) Continue drilling into the struct, ignoring the struct's
|
||||
// generic argument types.
|
||||
let expr = self.blame_specific_part_of_expr_corresponding_to_generic_param(
|
||||
param_to_point_at_in_struct,
|
||||
field_expr,
|
||||
field_type.into(),
|
||||
)?;
|
||||
|
||||
// (3) Continue drilling into the expression, having "passed
|
||||
// through" the struct entirely.
|
||||
return self.blame_specific_part_of_expr_corresponding_to_generic_param(
|
||||
param,
|
||||
expr,
|
||||
generic_argument_type,
|
||||
);
|
||||
}
|
||||
|
||||
if let (
|
||||
hir::ExprKind::Call(expr_callee, expr_args),
|
||||
ty::Adt(in_ty_adt, in_ty_adt_generic_args),
|
||||
) = (&expr.kind, in_ty.kind())
|
||||
{
|
||||
let hir::ExprKind::Path(expr_callee_path) = &expr_callee.kind else {
|
||||
// FIXME: This case overlaps with another one worth handling,
|
||||
// which should happen above since it applies to non-ADTs:
|
||||
// we can drill down into regular generic functions.
|
||||
return Err(expr);
|
||||
};
|
||||
// This is (possibly) a constructor call, like `Some(...)` or `MyStruct(a, b, c)`.
|
||||
|
||||
let Res::Def(expr_struct_def_kind, expr_ctor_def_id) = self.typeck_results.borrow().qpath_res(expr_callee_path, expr_callee.hir_id) else {
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
let variant_def_id = match expr_struct_def_kind {
|
||||
hir::def::DefKind::Ctor(hir::def::CtorOf::Struct, hir::def::CtorKind::Fn) => {
|
||||
if in_ty_adt.did() != self.tcx.parent(expr_ctor_def_id) {
|
||||
// FIXME: Deal with type aliases?
|
||||
return Err(expr);
|
||||
}
|
||||
self.tcx.parent(expr_ctor_def_id)
|
||||
}
|
||||
hir::def::DefKind::Ctor(hir::def::CtorOf::Variant, hir::def::CtorKind::Fn) => {
|
||||
// If this is a variant, its parent is the type definition.
|
||||
if in_ty_adt.did() != self.tcx.parent(expr_ctor_def_id) {
|
||||
// FIXME: Deal with type aliases?
|
||||
return Err(expr);
|
||||
}
|
||||
expr_ctor_def_id
|
||||
}
|
||||
_ => {
|
||||
return Err(expr);
|
||||
}
|
||||
};
|
||||
|
||||
// We need to know which of the generic parameters mentions our target param.
|
||||
// We expect that at least one of them does, since it is expected to be mentioned.
|
||||
let Some((drill_generic_index, generic_argument_type)) =
|
||||
Self::is_iterator_singleton(
|
||||
in_ty_adt_generic_args.iter().enumerate().filter(
|
||||
|(_index, in_ty_generic)| {
|
||||
Self::find_param_in_ty(*in_ty_generic, param)
|
||||
},
|
||||
),
|
||||
) else {
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
let struct_generic_parameters: &ty::Generics = self.tcx.generics_of(in_ty_adt.did());
|
||||
if drill_generic_index >= struct_generic_parameters.params.len() {
|
||||
return Err(expr);
|
||||
}
|
||||
|
||||
let param_to_point_at_in_struct = self.tcx.mk_param_from_def(
|
||||
struct_generic_parameters.param_at(drill_generic_index, self.tcx),
|
||||
);
|
||||
|
||||
// We make 3 steps:
|
||||
// Suppose we have a type like
|
||||
// ```ignore (just for demonstration)
|
||||
// struct ExampleStruct<T> {
|
||||
// enabled: bool,
|
||||
// item: Option<(usize, T, bool)>,
|
||||
// }
|
||||
//
|
||||
// f(ExampleStruct {
|
||||
// enabled: false,
|
||||
// item: Some((0, Box::new(String::new()), 1) }, true)),
|
||||
// });
|
||||
// ```
|
||||
// Here, `f` is passed a `ExampleStruct<Box<String>>`, but it wants
|
||||
// for `String: Copy`, which isn't true here.
|
||||
//
|
||||
// (1) First, we drill into `.item` and highlight that expression
|
||||
// (2) Then we use the template type `Option<(usize, T, bool)>` to
|
||||
// drill into the `T`, arriving at a `Box<String>` expression.
|
||||
// (3) Then we keep going, drilling into this expression using our
|
||||
// outer contextual information.
|
||||
|
||||
// (1) Find the (unique) field index which mentions the type in our constraint:
|
||||
let Some((field_index, field_type)) = Self::is_iterator_singleton(
|
||||
in_ty_adt
|
||||
.variant_with_id(variant_def_id)
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| field.ty(self.tcx, *in_ty_adt_generic_args))
|
||||
.enumerate()
|
||||
.filter(|(_index, field_type)| Self::find_param_in_ty((*field_type).into(), param))
|
||||
) else {
|
||||
return Err(expr);
|
||||
};
|
||||
|
||||
if field_index >= expr_args.len() {
|
||||
return Err(expr);
|
||||
}
|
||||
|
||||
// (2) Continue drilling into the struct, ignoring the struct's
|
||||
// generic argument types.
|
||||
let expr = self.blame_specific_part_of_expr_corresponding_to_generic_param(
|
||||
param_to_point_at_in_struct,
|
||||
&expr_args[field_index],
|
||||
field_type.into(),
|
||||
)?;
|
||||
|
||||
// (3) Continue drilling into the expression, having "passed
|
||||
// through" the struct entirely.
|
||||
return self.blame_specific_part_of_expr_corresponding_to_generic_param(
|
||||
param,
|
||||
expr,
|
||||
generic_argument_type,
|
||||
);
|
||||
}
|
||||
|
||||
// At this point, none of the basic patterns matched.
|
||||
// One major possibility which remains is that we have a function call.
|
||||
// In this case, it's often possible to dive deeper into the call to find something to blame,
|
||||
// but this is not always possible.
|
||||
|
||||
Err(expr)
|
||||
}
|
||||
|
||||
// FIXME: This can be made into a private, non-impl function later.
|
||||
/// Traverses the given ty (either a `ty::Ty` or a `ty::GenericArg`) and searches for references
|
||||
/// to the given `param_to_point_at`. Returns `true` if it finds any use of the param.
|
||||
pub fn find_param_in_ty(
|
||||
ty: ty::GenericArg<'tcx>,
|
||||
param_to_point_at: ty::GenericArg<'tcx>,
|
||||
) -> bool {
|
||||
let mut walk = ty.walk();
|
||||
while let Some(arg) = walk.next() {
|
||||
if arg == param_to_point_at {
|
||||
return true;
|
||||
} else if let ty::GenericArgKind::Type(ty) = arg.unpack()
|
||||
&& let ty::Alias(ty::Projection, ..) = ty.kind()
|
||||
{
|
||||
// This logic may seem a bit strange, but typically when
|
||||
// we have a projection type in a function signature, the
|
||||
// argument that's being passed into that signature is
|
||||
// not actually constraining that projection's substs in
|
||||
// a meaningful way. So we skip it, and see improvements
|
||||
// in some UI tests.
|
||||
walk.skip_current_subtree();
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
// FIXME: This can be made into a private, non-impl function later.
|
||||
/// Returns `Some(iterator.next())` if it has exactly one item, and `None` otherwise.
|
||||
pub fn is_iterator_singleton<T>(mut iterator: impl Iterator<Item = T>) -> Option<T> {
|
||||
match (iterator.next(), iterator.next()) {
|
||||
(_, Some(_)) => None,
|
||||
(first, _) => first,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -34,9 +34,10 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}
|
|||
|
||||
use std::iter;
|
||||
use std::mem;
|
||||
use std::ops::ControlFlow;
|
||||
use std::slice;
|
||||
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub(in super::super) fn check_casts(&mut self) {
|
||||
// don't hold the borrow to deferred_cast_checks while checking to avoid borrow checker errors
|
||||
|
|
@ -1843,7 +1844,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.into_iter()
|
||||
.flatten()
|
||||
{
|
||||
if self.point_at_arg_if_possible(
|
||||
if self.blame_specific_arg_if_possible(
|
||||
error,
|
||||
def_id,
|
||||
param,
|
||||
|
|
@ -1873,7 +1874,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.into_iter()
|
||||
.flatten()
|
||||
{
|
||||
if self.point_at_arg_if_possible(
|
||||
if self.blame_specific_arg_if_possible(
|
||||
error,
|
||||
def_id,
|
||||
param,
|
||||
|
|
@ -1898,16 +1899,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
for param in
|
||||
[param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
|
||||
{
|
||||
if let Some(param) = param
|
||||
&& self.point_at_field_if_possible(
|
||||
error,
|
||||
if let Some(param) = param {
|
||||
let refined_expr = self.point_at_field_if_possible(
|
||||
def_id,
|
||||
param,
|
||||
variant_def_id,
|
||||
fields,
|
||||
)
|
||||
{
|
||||
return true;
|
||||
);
|
||||
|
||||
match refined_expr {
|
||||
None => {}
|
||||
Some((refined_expr, _)) => {
|
||||
error.obligation.cause.span = refined_expr
|
||||
.span
|
||||
.find_ancestor_in_same_ctxt(error.obligation.cause.span)
|
||||
.unwrap_or(refined_expr.span);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1940,7 +1949,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn point_at_arg_if_possible(
|
||||
/// - `blame_specific_*` means that the function will recursively traverse the expression,
|
||||
/// looking for the most-specific-possible span to blame.
|
||||
///
|
||||
/// - `point_at_*` means that the function will only go "one level", pointing at the specific
|
||||
/// expression mentioned.
|
||||
///
|
||||
/// `blame_specific_arg_if_possible` will find the most-specific expression anywhere inside
|
||||
/// the provided function call expression, and mark it as responsible for the fullfillment
|
||||
/// error.
|
||||
fn blame_specific_arg_if_possible(
|
||||
&self,
|
||||
error: &mut traits::FulfillmentError<'tcx>,
|
||||
def_id: DefId,
|
||||
|
|
@ -1959,13 +1977,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.inputs()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, ty)| find_param_in_ty(**ty, param_to_point_at))
|
||||
.filter(|(_, ty)| Self::find_param_in_ty((**ty).into(), param_to_point_at))
|
||||
.collect();
|
||||
// If there's one field that references the given generic, great!
|
||||
if let [(idx, _)] = args_referencing_param.as_slice()
|
||||
&& let Some(arg) = receiver
|
||||
.map_or(args.get(*idx), |rcvr| if *idx == 0 { Some(rcvr) } else { args.get(*idx - 1) }) {
|
||||
|
||||
error.obligation.cause.span = arg.span.find_ancestor_in_same_ctxt(error.obligation.cause.span).unwrap_or(arg.span);
|
||||
|
||||
if let hir::Node::Expr(arg_expr) = self.tcx.hir().get(arg.hir_id) {
|
||||
// This is more specific than pointing at the entire argument.
|
||||
self.blame_specific_expr_if_possible(error, arg_expr)
|
||||
}
|
||||
|
||||
error.obligation.cause.map_code(|parent_code| {
|
||||
ObligationCauseCode::FunctionArgumentObligation {
|
||||
arg_hir_id: arg.hir_id,
|
||||
|
|
@ -1983,14 +2008,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
false
|
||||
}
|
||||
|
||||
fn point_at_field_if_possible(
|
||||
// FIXME: Make this private and move to mod adjust_fulfillment_errors
|
||||
pub fn point_at_field_if_possible(
|
||||
&self,
|
||||
error: &mut traits::FulfillmentError<'tcx>,
|
||||
def_id: DefId,
|
||||
param_to_point_at: ty::GenericArg<'tcx>,
|
||||
variant_def_id: DefId,
|
||||
expr_fields: &[hir::ExprField<'tcx>],
|
||||
) -> bool {
|
||||
) -> Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)> {
|
||||
let def = self.tcx.adt_def(def_id);
|
||||
|
||||
let identity_substs = ty::InternalSubsts::identity_for_item(self.tcx, def_id);
|
||||
|
|
@ -2000,7 +2025,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.iter()
|
||||
.filter(|field| {
|
||||
let field_ty = field.ty(self.tcx, identity_substs);
|
||||
find_param_in_ty(field_ty, param_to_point_at)
|
||||
Self::find_param_in_ty(field_ty.into(), param_to_point_at)
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
@ -2010,17 +2035,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// same rules that check_expr_struct uses for macro hygiene.
|
||||
if self.tcx.adjust_ident(expr_field.ident, variant_def_id) == field.ident(self.tcx)
|
||||
{
|
||||
error.obligation.cause.span = expr_field
|
||||
.expr
|
||||
.span
|
||||
.find_ancestor_in_same_ctxt(error.obligation.cause.span)
|
||||
.unwrap_or(expr_field.span);
|
||||
return true;
|
||||
return Some((expr_field.expr, self.tcx.type_of(field.did)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
None
|
||||
}
|
||||
|
||||
fn point_at_path_if_possible(
|
||||
|
|
@ -2240,23 +2260,3 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_param_in_ty<'tcx>(ty: Ty<'tcx>, param_to_point_at: ty::GenericArg<'tcx>) -> bool {
|
||||
let mut walk = ty.walk();
|
||||
while let Some(arg) = walk.next() {
|
||||
if arg == param_to_point_at {
|
||||
return true;
|
||||
} else if let ty::GenericArgKind::Type(ty) = arg.unpack()
|
||||
&& let ty::Alias(ty::Projection, ..) = ty.kind()
|
||||
{
|
||||
// This logic may seem a bit strange, but typically when
|
||||
// we have a projection type in a function signature, the
|
||||
// argument that's being passed into that signature is
|
||||
// not actually constraining that projection's substs in
|
||||
// a meaningful way. So we skip it, and see improvements
|
||||
// in some UI tests.
|
||||
walk.skip_current_subtree();
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
mod _impl;
|
||||
mod adjust_fulfillment_errors;
|
||||
mod arg_matrix;
|
||||
mod checks;
|
||||
mod suggestions;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use super::FnCtxt;
|
|||
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
|
||||
use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
|
||||
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
|
||||
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
|
||||
use rustc_errors::{fluent, Applicability, Diagnostic, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
|
|
@ -13,6 +13,7 @@ use rustc_hir::{
|
|||
use rustc_hir_analysis::astconv::AstConv;
|
||||
use rustc_infer::traits::{self, StatementAsExpression};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{
|
||||
self, suggest_constraining_type_params, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty,
|
||||
TypeVisitable,
|
||||
|
|
@ -414,11 +415,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
if let ty::Adt(adt, _) = peeled.kind()
|
||||
&& Some(adt.did()) == self.tcx.lang_items().string()
|
||||
{
|
||||
let sugg = if ref_cnt == 0 {
|
||||
".as_deref()"
|
||||
} else {
|
||||
".map(|x| x.as_str())"
|
||||
};
|
||||
err.span_suggestion_verbose(
|
||||
expr.span.shrink_to_hi(),
|
||||
"try converting the passed type into a `&str`",
|
||||
format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)),
|
||||
Applicability::MaybeIncorrect,
|
||||
fluent::hir_typeck_convert_to_str,
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -682,7 +688,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
return true;
|
||||
}
|
||||
&hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
|
||||
if found.is_suggestable(self.tcx, false) {
|
||||
if let Some(found) = found.make_suggestable(self.tcx, false) {
|
||||
err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
|
||||
return true;
|
||||
} else if let ty::Closure(_, substs) = found.kind()
|
||||
|
|
@ -699,10 +705,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
hir::FnRetTy::Return(ty) => {
|
||||
let span = ty.span;
|
||||
|
||||
if let hir::TyKind::OpaqueDef(item_id, ..) = ty.kind
|
||||
&& let hir::Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::OpaqueTy(op_ty),
|
||||
..
|
||||
}) = self.tcx.hir().get(item_id.hir_id())
|
||||
&& let hir::OpaqueTy {
|
||||
bounds: [bound], ..
|
||||
} = op_ty
|
||||
&& let hir::GenericBound::LangItemTrait(
|
||||
hir::LangItem::Future, _, _, generic_args) = bound
|
||||
&& let hir::GenericArgs { bindings: [ty_binding], .. } = generic_args
|
||||
&& let hir::TypeBinding { kind, .. } = ty_binding
|
||||
&& let hir::TypeBindingKind::Equality { term } = kind
|
||||
&& let hir::Term::Ty(term_ty) = term {
|
||||
// Check if async function's return type was omitted.
|
||||
// Don't emit suggestions if the found type is `impl Future<...>`.
|
||||
debug!("suggest_missing_return_type: found = {:?}", found);
|
||||
if found.is_suggestable(self.tcx, false) {
|
||||
if term_ty.span.is_empty() {
|
||||
err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
|
||||
return true;
|
||||
} else {
|
||||
err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only point to return type if the expected type is the return type, as if they
|
||||
// are not, the expectation must have been caused by something else.
|
||||
debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
|
||||
let span = ty.span;
|
||||
let ty = self.astconv().ast_ty_to_ty(ty);
|
||||
debug!("suggest_missing_return_type: return type {:?}", ty);
|
||||
debug!("suggest_missing_return_type: expected type {:?}", ty);
|
||||
|
|
@ -1239,6 +1273,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Suggest providing `std::ptr::null()` or `std::ptr::null_mut()` if they
|
||||
/// pass in a literal 0 to an raw pointer.
|
||||
#[instrument(skip(self, err))]
|
||||
pub(crate) fn suggest_null_ptr_for_literal_zero_given_to_ptr_arg(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
expr: &hir::Expr<'_>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
// Expected type needs to be a raw pointer.
|
||||
let ty::RawPtr(ty::TypeAndMut { mutbl, .. }) = expected_ty.kind() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Provided expression needs to be a literal `0`.
|
||||
let ExprKind::Lit(Spanned {
|
||||
node: rustc_ast::LitKind::Int(0, _),
|
||||
span,
|
||||
}) = expr.kind else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// We need to find a null pointer symbol to suggest
|
||||
let null_sym = match mutbl {
|
||||
hir::Mutability::Not => sym::ptr_null,
|
||||
hir::Mutability::Mut => sym::ptr_null_mut,
|
||||
};
|
||||
let Some(null_did) = self.tcx.get_diagnostic_item(null_sym) else {
|
||||
return false;
|
||||
};
|
||||
let null_path_str = with_no_trimmed_paths!(self.tcx.def_path_str(null_did));
|
||||
|
||||
// We have satisfied all requirements to provide a suggestion. Emit it.
|
||||
err.span_suggestion(
|
||||
span,
|
||||
format!("if you meant to create a null pointer, use `{null_path_str}()`"),
|
||||
null_path_str + "()",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) fn suggest_associated_const(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
|
|
|
|||
|
|
@ -1563,6 +1563,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
|||
traits::ImplDerivedObligationCause {
|
||||
derived,
|
||||
impl_def_id,
|
||||
impl_def_predicate_index: None,
|
||||
span,
|
||||
},
|
||||
))
|
||||
|
|
|
|||
|
|
@ -490,9 +490,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
if let Some(output_def_id) = output_def_id
|
||||
&& let Some(trait_def_id) = trait_def_id
|
||||
&& self.tcx.parent(output_def_id) == trait_def_id
|
||||
&& output_ty.is_suggestable(self.tcx, false)
|
||||
&& let Some(output_ty) = output_ty.make_suggestable(self.tcx, false)
|
||||
{
|
||||
Some(("Output", *output_ty))
|
||||
Some(("Output", output_ty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ rustc_hir = { path = "../rustc_hir" }
|
|||
rustc_index = { path = "../rustc_index" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_serialize = { path = "../rustc_serialize" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
|
||||
|
|
|
|||
|
|
@ -140,79 +140,21 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
|
||||
if !t.needs_infer() && !t.has_erasable_regions() {
|
||||
return t;
|
||||
}
|
||||
t
|
||||
} else {
|
||||
match *t.kind() {
|
||||
ty::Infer(v) => self.fold_infer_ty(v).unwrap_or(t),
|
||||
|
||||
let tcx = self.infcx.tcx;
|
||||
// This code is hot enough that a non-debug assertion here makes a noticeable
|
||||
// difference on benchmarks like `wg-grammar`.
|
||||
#[cfg(debug_assertions)]
|
||||
ty::Placeholder(..) | ty::Bound(..) => bug!("unexpected type {:?}", t),
|
||||
|
||||
match *t.kind() {
|
||||
ty::Infer(ty::TyVar(v)) => {
|
||||
let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known();
|
||||
self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy)
|
||||
_ => t.super_fold_with(self),
|
||||
}
|
||||
|
||||
ty::Infer(ty::IntVar(v)) => self.freshen_ty(
|
||||
self.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.int_unification_table()
|
||||
.probe_value(v)
|
||||
.map(|v| v.to_type(tcx)),
|
||||
ty::IntVar(v),
|
||||
ty::FreshIntTy,
|
||||
),
|
||||
|
||||
ty::Infer(ty::FloatVar(v)) => self.freshen_ty(
|
||||
self.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.float_unification_table()
|
||||
.probe_value(v)
|
||||
.map(|v| v.to_type(tcx)),
|
||||
ty::FloatVar(v),
|
||||
ty::FreshFloatTy,
|
||||
),
|
||||
|
||||
ty::Infer(ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct)) => {
|
||||
if ct >= self.ty_freshen_count {
|
||||
bug!(
|
||||
"Encountered a freshend type with id {} \
|
||||
but our counter is only at {}",
|
||||
ct,
|
||||
self.ty_freshen_count
|
||||
);
|
||||
}
|
||||
t
|
||||
}
|
||||
|
||||
ty::Generator(..)
|
||||
| ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(..)
|
||||
| ty::Uint(..)
|
||||
| ty::Float(..)
|
||||
| ty::Adt(..)
|
||||
| ty::Str
|
||||
| ty::Error(_)
|
||||
| ty::Array(..)
|
||||
| ty::Slice(..)
|
||||
| ty::RawPtr(..)
|
||||
| ty::Ref(..)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(_)
|
||||
| ty::Dynamic(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(..)
|
||||
| ty::Alias(..)
|
||||
| ty::Foreign(..)
|
||||
| ty::Param(..)
|
||||
| ty::Closure(..)
|
||||
| ty::GeneratorWitnessMIR(..)
|
||||
| ty::GeneratorWitness(..) => t.super_fold_with(self),
|
||||
|
||||
ty::Placeholder(..) | ty::Bound(..) => bug!("unexpected type {:?}", t),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -253,3 +195,54 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
|
||||
// This is separate from `fold_ty` to keep that method small and inlinable.
|
||||
#[inline(never)]
|
||||
fn fold_infer_ty(&mut self, v: ty::InferTy) -> Option<Ty<'tcx>> {
|
||||
match v {
|
||||
ty::TyVar(v) => {
|
||||
let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known();
|
||||
Some(self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy))
|
||||
}
|
||||
|
||||
ty::IntVar(v) => Some(
|
||||
self.freshen_ty(
|
||||
self.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.int_unification_table()
|
||||
.probe_value(v)
|
||||
.map(|v| v.to_type(self.infcx.tcx)),
|
||||
ty::IntVar(v),
|
||||
ty::FreshIntTy,
|
||||
),
|
||||
),
|
||||
|
||||
ty::FloatVar(v) => Some(
|
||||
self.freshen_ty(
|
||||
self.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.float_unification_table()
|
||||
.probe_value(v)
|
||||
.map(|v| v.to_type(self.infcx.tcx)),
|
||||
ty::FloatVar(v),
|
||||
ty::FreshFloatTy,
|
||||
),
|
||||
),
|
||||
|
||||
ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct) => {
|
||||
if ct >= self.ty_freshen_count {
|
||||
bug!(
|
||||
"Encountered a freshend type with id {} \
|
||||
but our counter is only at {}",
|
||||
ct,
|
||||
self.ty_freshen_count
|
||||
);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ use rustc_middle::ty::relate::RelateResult;
|
|||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef};
|
||||
use rustc_middle::ty::visit::TypeVisitable;
|
||||
pub use rustc_middle::ty::IntVarValue;
|
||||
use rustc_middle::ty::{self, GenericParamDefKind, InferConst, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, GenericParamDefKind, InferConst, InferTy, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{ConstVid, FloatVid, IntVid, TyVid};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::Span;
|
||||
|
|
@ -1389,8 +1389,8 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
if !value.needs_infer() {
|
||||
return value; // Avoid duplicated subst-folding.
|
||||
if !value.has_non_region_infer() {
|
||||
return value;
|
||||
}
|
||||
let mut r = resolve::OpportunisticVarResolver::new(self);
|
||||
value.fold_with(&mut r)
|
||||
|
|
@ -1870,43 +1870,9 @@ impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> {
|
|||
/// If `ty` is a type variable of some kind, resolve it one level
|
||||
/// (but do not resolve types found in the result). If `typ` is
|
||||
/// not a type variable, just return it unmodified.
|
||||
#[inline]
|
||||
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
match *ty.kind() {
|
||||
ty::Infer(ty::TyVar(v)) => {
|
||||
// Not entirely obvious: if `typ` is a type variable,
|
||||
// it can be resolved to an int/float variable, which
|
||||
// can then be recursively resolved, hence the
|
||||
// recursion. Note though that we prevent type
|
||||
// variables from unifying to other type variables
|
||||
// directly (though they may be embedded
|
||||
// structurally), and we prevent cycles in any case,
|
||||
// so this recursion should always be of very limited
|
||||
// depth.
|
||||
//
|
||||
// Note: if these two lines are combined into one we get
|
||||
// dynamic borrow errors on `self.inner`.
|
||||
let known = self.infcx.inner.borrow_mut().type_variables().probe(v).known();
|
||||
known.map_or(ty, |t| self.fold_ty(t))
|
||||
}
|
||||
|
||||
ty::Infer(ty::IntVar(v)) => self
|
||||
.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.int_unification_table()
|
||||
.probe_value(v)
|
||||
.map_or(ty, |v| v.to_type(self.infcx.tcx)),
|
||||
|
||||
ty::Infer(ty::FloatVar(v)) => self
|
||||
.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.float_unification_table()
|
||||
.probe_value(v)
|
||||
.map_or(ty, |v| v.to_type(self.infcx.tcx)),
|
||||
|
||||
_ => ty,
|
||||
}
|
||||
if let ty::Infer(v) = ty.kind() { self.fold_infer_ty(*v).unwrap_or(ty) } else { ty }
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||
|
|
@ -1925,6 +1891,49 @@ impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ShallowResolver<'a, 'tcx> {
|
||||
// This is separate from `fold_ty` to keep that method small and inlinable.
|
||||
#[inline(never)]
|
||||
fn fold_infer_ty(&mut self, v: InferTy) -> Option<Ty<'tcx>> {
|
||||
match v {
|
||||
ty::TyVar(v) => {
|
||||
// Not entirely obvious: if `typ` is a type variable,
|
||||
// it can be resolved to an int/float variable, which
|
||||
// can then be recursively resolved, hence the
|
||||
// recursion. Note though that we prevent type
|
||||
// variables from unifying to other type variables
|
||||
// directly (though they may be embedded
|
||||
// structurally), and we prevent cycles in any case,
|
||||
// so this recursion should always be of very limited
|
||||
// depth.
|
||||
//
|
||||
// Note: if these two lines are combined into one we get
|
||||
// dynamic borrow errors on `self.inner`.
|
||||
let known = self.infcx.inner.borrow_mut().type_variables().probe(v).known();
|
||||
known.map(|t| self.fold_ty(t))
|
||||
}
|
||||
|
||||
ty::IntVar(v) => self
|
||||
.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.int_unification_table()
|
||||
.probe_value(v)
|
||||
.map(|v| v.to_type(self.infcx.tcx)),
|
||||
|
||||
ty::FloatVar(v) => self
|
||||
.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.float_unification_table()
|
||||
.probe_value(v)
|
||||
.map(|v| v.to_type(self.infcx.tcx)),
|
||||
|
||||
ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeTrace<'tcx> {
|
||||
pub fn span(&self) -> Span {
|
||||
self.cause.span
|
||||
|
|
|
|||
|
|
@ -817,12 +817,13 @@ impl<'tcx, D> ConstEquateRelation<'tcx> for TypeRelating<'_, 'tcx, D>
|
|||
where
|
||||
D: TypeRelatingDelegate<'tcx>,
|
||||
{
|
||||
fn const_equate_obligation(&mut self, _a: ty::Const<'tcx>, _b: ty::Const<'tcx>) {
|
||||
// We don't have to worry about the equality of consts during borrow checking
|
||||
// as consts always have a static lifetime.
|
||||
// FIXME(oli-obk): is this really true? We can at least have HKL and with
|
||||
// inline consts we may have further lifetimes that may be unsound to treat as
|
||||
// 'static.
|
||||
fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) {
|
||||
self.delegate.register_obligations(vec![Obligation::new(
|
||||
self.tcx(),
|
||||
ObligationCause::dummy(),
|
||||
self.param_env(),
|
||||
ty::Binder::dummy(ty::PredicateKind::ConstEquate(a, b)),
|
||||
)]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,26 +16,29 @@ use std::ops::ControlFlow;
|
|||
/// useful for printing messages etc but also required at various
|
||||
/// points for correctness.
|
||||
pub struct OpportunisticVarResolver<'a, 'tcx> {
|
||||
infcx: &'a InferCtxt<'tcx>,
|
||||
// The shallow resolver is used to resolve inference variables at every
|
||||
// level of the type.
|
||||
shallow_resolver: crate::infer::ShallowResolver<'a, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> {
|
||||
#[inline]
|
||||
pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self {
|
||||
OpportunisticVarResolver { infcx }
|
||||
OpportunisticVarResolver { shallow_resolver: crate::infer::ShallowResolver { infcx } }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> {
|
||||
fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
|
||||
self.infcx.tcx
|
||||
TypeFolder::tcx(&self.shallow_resolver)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
|
||||
if !t.has_non_region_infer() {
|
||||
t // micro-optimize -- if there is nothing in this type that this fold affects...
|
||||
} else {
|
||||
let t = self.infcx.shallow_resolve(t);
|
||||
let t = self.shallow_resolver.fold_ty(t);
|
||||
t.super_fold_with(self)
|
||||
}
|
||||
}
|
||||
|
|
@ -44,7 +47,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> {
|
|||
if !ct.has_non_region_infer() {
|
||||
ct // micro-optimize -- if there is nothing in this const that this fold affects...
|
||||
} else {
|
||||
let ct = self.infcx.shallow_resolve(ct);
|
||||
let ct = self.shallow_resolver.fold_const(ct);
|
||||
ct.super_fold_with(self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,30 +145,32 @@ impl<'tcx> Elaborator<'tcx> {
|
|||
// Get predicates declared on the trait.
|
||||
let predicates = tcx.super_predicates_of(data.def_id());
|
||||
|
||||
let obligations = predicates.predicates.iter().map(|&(mut pred, span)| {
|
||||
// when parent predicate is non-const, elaborate it to non-const predicates.
|
||||
if data.constness == ty::BoundConstness::NotConst {
|
||||
pred = pred.without_const(tcx);
|
||||
}
|
||||
let obligations =
|
||||
predicates.predicates.iter().enumerate().map(|(index, &(mut pred, span))| {
|
||||
// when parent predicate is non-const, elaborate it to non-const predicates.
|
||||
if data.constness == ty::BoundConstness::NotConst {
|
||||
pred = pred.without_const(tcx);
|
||||
}
|
||||
|
||||
let cause = obligation.cause.clone().derived_cause(
|
||||
bound_predicate.rebind(data),
|
||||
|derived| {
|
||||
traits::ImplDerivedObligation(Box::new(
|
||||
traits::ImplDerivedObligationCause {
|
||||
derived,
|
||||
impl_def_id: data.def_id(),
|
||||
span,
|
||||
},
|
||||
))
|
||||
},
|
||||
);
|
||||
predicate_obligation(
|
||||
pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
|
||||
obligation.param_env,
|
||||
cause,
|
||||
)
|
||||
});
|
||||
let cause = obligation.cause.clone().derived_cause(
|
||||
bound_predicate.rebind(data),
|
||||
|derived| {
|
||||
traits::ImplDerivedObligation(Box::new(
|
||||
traits::ImplDerivedObligationCause {
|
||||
derived,
|
||||
impl_def_id: data.def_id(),
|
||||
impl_def_predicate_index: Some(index),
|
||||
span,
|
||||
},
|
||||
))
|
||||
},
|
||||
);
|
||||
predicate_obligation(
|
||||
pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
|
||||
obligation.param_env,
|
||||
cause,
|
||||
)
|
||||
});
|
||||
debug!(?data, ?obligations, "super_predicates");
|
||||
|
||||
// Only keep those bounds that we haven't already seen.
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ rustc_macros = { path = "../rustc_macros" }
|
|||
rustc_parse = { path = "../rustc_parse" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_serialize = { path = "../rustc_serialize" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_ast_lowering = { path = "../rustc_ast_lowering" }
|
||||
rustc_ast_passes = { path = "../rustc_ast_passes" }
|
||||
|
|
|
|||
|
|
@ -1,9 +1,4 @@
|
|||
use crate::errors::{
|
||||
CantEmitMIR, EmojiIdentifier, ErrorWritingDependencies, FerrisIdentifier,
|
||||
GeneratedFileConflictsWithDirectory, InputFileWouldBeOverWritten, MixedBinCrate,
|
||||
MixedProcMacroCrate, OutDirError, ProcMacroCratePanicAbort, ProcMacroDocWithoutArg,
|
||||
TempsDirError,
|
||||
};
|
||||
use crate::errors;
|
||||
use crate::interface::{Compiler, Result};
|
||||
use crate::proc_macro_decls;
|
||||
use crate::util;
|
||||
|
|
@ -374,15 +369,15 @@ pub fn configure_and_expand(
|
|||
|
||||
if crate_types.len() > 1 {
|
||||
if is_executable_crate {
|
||||
sess.emit_err(MixedBinCrate);
|
||||
sess.emit_err(errors::MixedBinCrate);
|
||||
}
|
||||
if is_proc_macro_crate {
|
||||
sess.emit_err(MixedProcMacroCrate);
|
||||
sess.emit_err(errors::MixedProcMacroCrate);
|
||||
}
|
||||
}
|
||||
|
||||
if is_proc_macro_crate && sess.panic_strategy() == PanicStrategy::Abort {
|
||||
sess.emit_warning(ProcMacroCratePanicAbort);
|
||||
sess.emit_warning(errors::ProcMacroCratePanicAbort);
|
||||
}
|
||||
|
||||
// For backwards compatibility, we don't try to run proc macro injection
|
||||
|
|
@ -392,7 +387,7 @@ pub fn configure_and_expand(
|
|||
// However, we do emit a warning, to let such users know that they should
|
||||
// start passing '--crate-type proc-macro'
|
||||
if has_proc_macro_decls && sess.opts.actually_rustdoc && !is_proc_macro_crate {
|
||||
sess.emit_warning(ProcMacroDocWithoutArg);
|
||||
sess.emit_warning(errors::ProcMacroDocWithoutArg);
|
||||
} else {
|
||||
krate = sess.time("maybe_create_a_macro_crate", || {
|
||||
let is_test_crate = sess.opts.test;
|
||||
|
|
@ -441,9 +436,9 @@ pub fn configure_and_expand(
|
|||
spans.sort();
|
||||
if ident == sym::ferris {
|
||||
let first_span = spans[0];
|
||||
sess.emit_err(FerrisIdentifier { spans, first_span });
|
||||
sess.emit_err(errors::FerrisIdentifier { spans, first_span });
|
||||
} else {
|
||||
sess.emit_err(EmojiIdentifier { spans, ident });
|
||||
sess.emit_err(errors::EmojiIdentifier { spans, ident });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -655,7 +650,7 @@ fn write_out_deps(
|
|||
}
|
||||
}
|
||||
Err(error) => {
|
||||
sess.emit_fatal(ErrorWritingDependencies { path: &deps_filename, error });
|
||||
sess.emit_fatal(errors::ErrorWritingDependencies { path: &deps_filename, error });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -676,17 +671,20 @@ fn output_filenames(tcx: TyCtxt<'_>, (): ()) -> Arc<OutputFilenames> {
|
|||
if let Some(ref input_path) = sess.io.input.opt_path() {
|
||||
if sess.opts.will_create_output_file() {
|
||||
if output_contains_path(&output_paths, input_path) {
|
||||
sess.emit_fatal(InputFileWouldBeOverWritten { path: input_path });
|
||||
sess.emit_fatal(errors::InputFileWouldBeOverWritten { path: input_path });
|
||||
}
|
||||
if let Some(ref dir_path) = output_conflicts_with_dir(&output_paths) {
|
||||
sess.emit_fatal(GeneratedFileConflictsWithDirectory { input_path, dir_path });
|
||||
sess.emit_fatal(errors::GeneratedFileConflictsWithDirectory {
|
||||
input_path,
|
||||
dir_path,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref dir) = sess.io.temps_dir {
|
||||
if fs::create_dir_all(dir).is_err() {
|
||||
sess.emit_fatal(TempsDirError);
|
||||
sess.emit_fatal(errors::TempsDirError);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -698,7 +696,7 @@ fn output_filenames(tcx: TyCtxt<'_>, (): ()) -> Arc<OutputFilenames> {
|
|||
if !only_dep_info {
|
||||
if let Some(ref dir) = sess.io.output_dir {
|
||||
if fs::create_dir_all(dir).is_err() {
|
||||
sess.emit_fatal(OutDirError);
|
||||
sess.emit_fatal(errors::OutDirError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -977,7 +975,7 @@ pub fn start_codegen<'tcx>(
|
|||
|
||||
if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) {
|
||||
if let Err(error) = rustc_mir_transform::dump_mir::emit_mir(tcx) {
|
||||
tcx.sess.emit_err(CantEmitMIR { error });
|
||||
tcx.sess.emit_err(errors::CantEmitMIR { error });
|
||||
tcx.sess.abort_if_errors();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1402,6 +1402,21 @@ pub struct UnusedDef<'a, 'b> {
|
|||
pub cx: &'a LateContext<'b>,
|
||||
pub def_id: DefId,
|
||||
pub note: Option<Symbol>,
|
||||
pub suggestion: Option<UnusedDefSuggestion>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub enum UnusedDefSuggestion {
|
||||
#[suggestion(
|
||||
suggestion,
|
||||
style = "verbose",
|
||||
code = "let _ = ",
|
||||
applicability = "machine-applicable"
|
||||
)]
|
||||
Default {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
// Needed because of def_path_str
|
||||
|
|
@ -1417,6 +1432,9 @@ impl<'a> DecorateLint<'a, ()> for UnusedDef<'_, '_> {
|
|||
if let Some(note) = self.note {
|
||||
diag.note(note.as_str());
|
||||
}
|
||||
if let Some(sugg) = self.suggestion {
|
||||
diag.subdiagnostic(sugg);
|
||||
}
|
||||
diag
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::lints::{
|
||||
PathStatementDrop, PathStatementDropSub, PathStatementNoEffect, UnusedAllocationDiag,
|
||||
UnusedAllocationMutDiag, UnusedClosure, UnusedDef, UnusedDelim, UnusedDelimSuggestion,
|
||||
UnusedGenerator, UnusedImportBracesDiag, UnusedOp, UnusedResult,
|
||||
UnusedAllocationMutDiag, UnusedClosure, UnusedDef, UnusedDefSuggestion, UnusedDelim,
|
||||
UnusedDelimSuggestion, UnusedGenerator, UnusedImportBracesDiag, UnusedOp, UnusedResult,
|
||||
};
|
||||
use crate::Lint;
|
||||
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
|
||||
|
|
@ -418,6 +418,19 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
|
|||
);
|
||||
}
|
||||
MustUsePath::Def(span, def_id, reason) => {
|
||||
let suggestion = if matches!(
|
||||
cx.tcx.get_diagnostic_name(*def_id),
|
||||
Some(sym::add)
|
||||
| Some(sym::sub)
|
||||
| Some(sym::mul)
|
||||
| Some(sym::div)
|
||||
| Some(sym::rem)
|
||||
| Some(sym::neg),
|
||||
) {
|
||||
Some(UnusedDefSuggestion::Default { span: span.shrink_to_lo() })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
cx.emit_spanned_lint(
|
||||
UNUSED_MUST_USE,
|
||||
*span,
|
||||
|
|
@ -427,6 +440,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
|
|||
cx,
|
||||
def_id: *def_id,
|
||||
note: *reason,
|
||||
suggestion,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -495,6 +509,7 @@ enum UnusedDelimsCtx {
|
|||
ArrayLenExpr,
|
||||
AnonConst,
|
||||
MatchArmExpr,
|
||||
IndexExpr,
|
||||
}
|
||||
|
||||
impl From<UnusedDelimsCtx> for &'static str {
|
||||
|
|
@ -514,6 +529,7 @@ impl From<UnusedDelimsCtx> for &'static str {
|
|||
UnusedDelimsCtx::LetScrutineeExpr => "`let` scrutinee expression",
|
||||
UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression",
|
||||
UnusedDelimsCtx::MatchArmExpr => "match arm expression",
|
||||
UnusedDelimsCtx::IndexExpr => "index expression",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -661,6 +677,10 @@ trait UnusedDelimLint {
|
|||
keep_space: (bool, bool),
|
||||
) {
|
||||
let primary_span = if let Some((lo, hi)) = spans {
|
||||
if hi.is_empty() {
|
||||
// do not point at delims that do not exist
|
||||
return;
|
||||
}
|
||||
MultiSpan::from(vec![lo, hi])
|
||||
} else {
|
||||
MultiSpan::from(value_span)
|
||||
|
|
@ -733,6 +753,8 @@ trait UnusedDelimLint {
|
|||
(value, UnusedDelimsCtx::ReturnValue, false, Some(left), None)
|
||||
}
|
||||
|
||||
Index(_, ref value) => (value, UnusedDelimsCtx::IndexExpr, false, None, None),
|
||||
|
||||
Assign(_, ref value, _) | AssignOp(.., ref value) => {
|
||||
(value, UnusedDelimsCtx::AssignedValue, false, None, None)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@
|
|||
#include "llvm/Passes/StandardInstrumentations.h"
|
||||
#include "llvm/Support/CBindingWrapping.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#if LLVM_VERSION_GE(17, 0)
|
||||
#include "llvm/Support/VirtualFileSystem.h"
|
||||
#endif
|
||||
#include "llvm/Support/Host.h"
|
||||
#if LLVM_VERSION_LT(14, 0)
|
||||
#include "llvm/Support/TargetRegistry.h"
|
||||
|
|
@ -651,21 +654,40 @@ LLVMRustOptimize(
|
|||
Optional<PGOOptions> PGOOpt;
|
||||
#else
|
||||
std::optional<PGOOptions> PGOOpt;
|
||||
#endif
|
||||
#if LLVM_VERSION_GE(17, 0)
|
||||
auto FS = vfs::getRealFileSystem();
|
||||
#endif
|
||||
if (PGOGenPath) {
|
||||
assert(!PGOUsePath && !PGOSampleUsePath);
|
||||
PGOOpt = PGOOptions(PGOGenPath, "", "", PGOOptions::IRInstr,
|
||||
PGOOptions::NoCSAction, DebugInfoForProfiling);
|
||||
PGOOpt = PGOOptions(PGOGenPath, "", "",
|
||||
#if LLVM_VERSION_GE(17, 0)
|
||||
FS,
|
||||
#endif
|
||||
PGOOptions::IRInstr, PGOOptions::NoCSAction,
|
||||
DebugInfoForProfiling);
|
||||
} else if (PGOUsePath) {
|
||||
assert(!PGOSampleUsePath);
|
||||
PGOOpt = PGOOptions(PGOUsePath, "", "", PGOOptions::IRUse,
|
||||
PGOOptions::NoCSAction, DebugInfoForProfiling);
|
||||
PGOOpt = PGOOptions(PGOUsePath, "", "",
|
||||
#if LLVM_VERSION_GE(17, 0)
|
||||
FS,
|
||||
#endif
|
||||
PGOOptions::IRUse, PGOOptions::NoCSAction,
|
||||
DebugInfoForProfiling);
|
||||
} else if (PGOSampleUsePath) {
|
||||
PGOOpt = PGOOptions(PGOSampleUsePath, "", "", PGOOptions::SampleUse,
|
||||
PGOOptions::NoCSAction, DebugInfoForProfiling);
|
||||
PGOOpt = PGOOptions(PGOSampleUsePath, "", "",
|
||||
#if LLVM_VERSION_GE(17, 0)
|
||||
FS,
|
||||
#endif
|
||||
PGOOptions::SampleUse, PGOOptions::NoCSAction,
|
||||
DebugInfoForProfiling);
|
||||
} else if (DebugInfoForProfiling) {
|
||||
PGOOpt = PGOOptions("", "", "", PGOOptions::NoAction,
|
||||
PGOOptions::NoCSAction, DebugInfoForProfiling);
|
||||
PGOOpt = PGOOptions("", "", "",
|
||||
#if LLVM_VERSION_GE(17, 0)
|
||||
FS,
|
||||
#endif
|
||||
PGOOptions::NoAction, PGOOptions::NoCSAction,
|
||||
DebugInfoForProfiling);
|
||||
}
|
||||
|
||||
PassBuilder PB(TM, PTO, PGOOpt, &PIC);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
//! Validates all used crates and extern libraries and loads their metadata
|
||||
|
||||
use crate::errors::{
|
||||
ConflictingAllocErrorHandler, ConflictingGlobalAlloc, CrateNotPanicRuntime,
|
||||
GlobalAllocRequired, NoMultipleAllocErrorHandler, NoMultipleGlobalAlloc, NoPanicStrategy,
|
||||
NoTransitiveNeedsDep, NotProfilerRuntime, ProfilerBuiltinsNeedsCore,
|
||||
};
|
||||
use crate::errors;
|
||||
use crate::locator::{CrateError, CrateLocator, CratePaths};
|
||||
use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob};
|
||||
|
||||
|
|
@ -33,6 +29,7 @@ use rustc_target::spec::{PanicStrategy, TargetTriple};
|
|||
use proc_macro::bridge::client::ProcMacro;
|
||||
use std::ops::Fn;
|
||||
use std::path::Path;
|
||||
use std::time::Duration;
|
||||
use std::{cmp, env};
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
@ -689,8 +686,7 @@ impl<'a> CrateLoader<'a> {
|
|||
) -> Result<&'static [ProcMacro], CrateError> {
|
||||
// Make sure the path contains a / or the linker will search for it.
|
||||
let path = env::current_dir().unwrap().join(path);
|
||||
let lib = unsafe { libloading::Library::new(path) }
|
||||
.map_err(|err| CrateError::DlOpen(err.to_string()))?;
|
||||
let lib = load_dylib(&path, 5).map_err(|err| CrateError::DlOpen(err))?;
|
||||
|
||||
let sym_name = self.sess.generate_proc_macro_decls_symbol(stable_crate_id);
|
||||
let sym = unsafe { lib.get::<*const &[ProcMacro]>(sym_name.as_bytes()) }
|
||||
|
|
@ -768,10 +764,11 @@ impl<'a> CrateLoader<'a> {
|
|||
// Sanity check the loaded crate to ensure it is indeed a panic runtime
|
||||
// and the panic strategy is indeed what we thought it was.
|
||||
if !data.is_panic_runtime() {
|
||||
self.sess.emit_err(CrateNotPanicRuntime { crate_name: name });
|
||||
self.sess.emit_err(errors::CrateNotPanicRuntime { crate_name: name });
|
||||
}
|
||||
if data.required_panic_strategy() != Some(desired_strategy) {
|
||||
self.sess.emit_err(NoPanicStrategy { crate_name: name, strategy: desired_strategy });
|
||||
self.sess
|
||||
.emit_err(errors::NoPanicStrategy { crate_name: name, strategy: desired_strategy });
|
||||
}
|
||||
|
||||
self.cstore.injected_panic_runtime = Some(cnum);
|
||||
|
|
@ -791,7 +788,7 @@ impl<'a> CrateLoader<'a> {
|
|||
|
||||
let name = Symbol::intern(&self.sess.opts.unstable_opts.profiler_runtime);
|
||||
if name == sym::profiler_builtins && self.sess.contains_name(&krate.attrs, sym::no_core) {
|
||||
self.sess.emit_err(ProfilerBuiltinsNeedsCore);
|
||||
self.sess.emit_err(errors::ProfilerBuiltinsNeedsCore);
|
||||
}
|
||||
|
||||
let Some(cnum) = self.resolve_crate(name, DUMMY_SP, CrateDepKind::Implicit) else { return; };
|
||||
|
|
@ -799,21 +796,22 @@ impl<'a> CrateLoader<'a> {
|
|||
|
||||
// Sanity check the loaded crate to ensure it is indeed a profiler runtime
|
||||
if !data.is_profiler_runtime() {
|
||||
self.sess.emit_err(NotProfilerRuntime { crate_name: name });
|
||||
self.sess.emit_err(errors::NotProfilerRuntime { crate_name: name });
|
||||
}
|
||||
}
|
||||
|
||||
fn inject_allocator_crate(&mut self, krate: &ast::Crate) {
|
||||
self.cstore.has_global_allocator = match &*global_allocator_spans(&self.sess, krate) {
|
||||
[span1, span2, ..] => {
|
||||
self.sess.emit_err(NoMultipleGlobalAlloc { span2: *span2, span1: *span1 });
|
||||
self.sess.emit_err(errors::NoMultipleGlobalAlloc { span2: *span2, span1: *span1 });
|
||||
true
|
||||
}
|
||||
spans => !spans.is_empty(),
|
||||
};
|
||||
self.cstore.has_alloc_error_handler = match &*alloc_error_handler_spans(&self.sess, krate) {
|
||||
[span1, span2, ..] => {
|
||||
self.sess.emit_err(NoMultipleAllocErrorHandler { span2: *span2, span1: *span1 });
|
||||
self.sess
|
||||
.emit_err(errors::NoMultipleAllocErrorHandler { span2: *span2, span1: *span1 });
|
||||
true
|
||||
}
|
||||
spans => !spans.is_empty(),
|
||||
|
|
@ -849,7 +847,7 @@ impl<'a> CrateLoader<'a> {
|
|||
if data.has_global_allocator() {
|
||||
match global_allocator {
|
||||
Some(other_crate) => {
|
||||
self.sess.emit_err(ConflictingGlobalAlloc {
|
||||
self.sess.emit_err(errors::ConflictingGlobalAlloc {
|
||||
crate_name: data.name(),
|
||||
other_crate_name: other_crate,
|
||||
});
|
||||
|
|
@ -864,7 +862,7 @@ impl<'a> CrateLoader<'a> {
|
|||
if data.has_alloc_error_handler() {
|
||||
match alloc_error_handler {
|
||||
Some(other_crate) => {
|
||||
self.sess.emit_err(ConflictingAllocErrorHandler {
|
||||
self.sess.emit_err(errors::ConflictingAllocErrorHandler {
|
||||
crate_name: data.name(),
|
||||
other_crate_name: other_crate,
|
||||
});
|
||||
|
|
@ -884,7 +882,7 @@ impl<'a> CrateLoader<'a> {
|
|||
if !self.sess.contains_name(&krate.attrs, sym::default_lib_allocator)
|
||||
&& !self.cstore.iter_crate_data().any(|(_, data)| data.has_default_lib_allocator())
|
||||
{
|
||||
self.sess.emit_err(GlobalAllocRequired);
|
||||
self.sess.emit_err(errors::GlobalAllocRequired);
|
||||
}
|
||||
self.cstore.allocator_kind = Some(AllocatorKind::Default);
|
||||
}
|
||||
|
|
@ -917,7 +915,7 @@ impl<'a> CrateLoader<'a> {
|
|||
for dep in self.cstore.crate_dependencies_in_reverse_postorder(krate) {
|
||||
let data = self.cstore.get_crate_data(dep);
|
||||
if needs_dep(&data) {
|
||||
self.sess.emit_err(NoTransitiveNeedsDep {
|
||||
self.sess.emit_err(errors::NoTransitiveNeedsDep {
|
||||
crate_name: self.cstore.get_crate_data(krate).name(),
|
||||
needs_crate_name: what,
|
||||
deps_crate_name: data.name(),
|
||||
|
|
@ -1093,3 +1091,41 @@ fn alloc_error_handler_spans(sess: &Session, krate: &ast::Crate) -> Vec<Span> {
|
|||
visit::walk_crate(&mut f, krate);
|
||||
f.spans
|
||||
}
|
||||
|
||||
// On Windows the compiler would sometimes intermittently fail to open the
|
||||
// proc-macro DLL with `Error::LoadLibraryExW`. It is suspected that something in the
|
||||
// system still holds a lock on the file, so we retry a few times before calling it
|
||||
// an error.
|
||||
fn load_dylib(path: &Path, max_attempts: usize) -> Result<libloading::Library, String> {
|
||||
assert!(max_attempts > 0);
|
||||
|
||||
let mut last_error = None;
|
||||
|
||||
for attempt in 0..max_attempts {
|
||||
match unsafe { libloading::Library::new(&path) } {
|
||||
Ok(lib) => {
|
||||
if attempt > 0 {
|
||||
debug!(
|
||||
"Loaded proc-macro `{}` after {} attempts.",
|
||||
path.display(),
|
||||
attempt + 1
|
||||
);
|
||||
}
|
||||
return Ok(lib);
|
||||
}
|
||||
Err(err) => {
|
||||
// Only try to recover from this specific error.
|
||||
if !matches!(err, libloading::Error::LoadLibraryExW { .. }) {
|
||||
return Err(err.to_string());
|
||||
}
|
||||
|
||||
last_error = Some(err);
|
||||
std::thread::sleep(Duration::from_millis(100));
|
||||
debug!("Failed to load proc-macro `{}`. Retrying.", path.display());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Failed to load proc-macro `{}` even after {} attempts.", path.display(), max_attempts);
|
||||
Err(format!("{} (retried {} times)", last_error.unwrap(), max_attempts))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -213,12 +213,7 @@
|
|||
//! metadata::locator or metadata::creader for all the juicy details!
|
||||
|
||||
use crate::creader::Library;
|
||||
use crate::errors::{
|
||||
CannotFindCrate, CrateLocationUnknownType, DlError, ExternLocationNotExist,
|
||||
ExternLocationNotFile, FoundStaticlib, IncompatibleRustc, InvalidMetadataFiles,
|
||||
LibFilenameForm, MultipleCandidates, NewerCrateVersion, NoCrateWithTriple, NoDylibPlugin,
|
||||
NonAsciiName, StableCrateIdCollision, SymbolConflictsCurrent, SymbolConflictsOthers,
|
||||
};
|
||||
use crate::errors;
|
||||
use crate::rmeta::{rustc_version, MetadataBlob, METADATA_HEADER};
|
||||
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
|
|
@ -980,28 +975,28 @@ impl CrateError {
|
|||
pub(crate) fn report(self, sess: &Session, span: Span, missing_core: bool) {
|
||||
match self {
|
||||
CrateError::NonAsciiName(crate_name) => {
|
||||
sess.emit_err(NonAsciiName { span, crate_name });
|
||||
sess.emit_err(errors::NonAsciiName { span, crate_name });
|
||||
}
|
||||
CrateError::ExternLocationNotExist(crate_name, loc) => {
|
||||
sess.emit_err(ExternLocationNotExist { span, crate_name, location: &loc });
|
||||
sess.emit_err(errors::ExternLocationNotExist { span, crate_name, location: &loc });
|
||||
}
|
||||
CrateError::ExternLocationNotFile(crate_name, loc) => {
|
||||
sess.emit_err(ExternLocationNotFile { span, crate_name, location: &loc });
|
||||
sess.emit_err(errors::ExternLocationNotFile { span, crate_name, location: &loc });
|
||||
}
|
||||
CrateError::MultipleCandidates(crate_name, flavor, candidates) => {
|
||||
sess.emit_err(MultipleCandidates { span, crate_name, flavor, candidates });
|
||||
sess.emit_err(errors::MultipleCandidates { span, crate_name, flavor, candidates });
|
||||
}
|
||||
CrateError::SymbolConflictsCurrent(root_name) => {
|
||||
sess.emit_err(SymbolConflictsCurrent { span, crate_name: root_name });
|
||||
sess.emit_err(errors::SymbolConflictsCurrent { span, crate_name: root_name });
|
||||
}
|
||||
CrateError::SymbolConflictsOthers(root_name) => {
|
||||
sess.emit_err(SymbolConflictsOthers { span, crate_name: root_name });
|
||||
sess.emit_err(errors::SymbolConflictsOthers { span, crate_name: root_name });
|
||||
}
|
||||
CrateError::StableCrateIdCollision(crate_name0, crate_name1) => {
|
||||
sess.emit_err(StableCrateIdCollision { span, crate_name0, crate_name1 });
|
||||
sess.emit_err(errors::StableCrateIdCollision { span, crate_name0, crate_name1 });
|
||||
}
|
||||
CrateError::DlOpen(s) | CrateError::DlSym(s) => {
|
||||
sess.emit_err(DlError { span, err: s });
|
||||
sess.emit_err(errors::DlError { span, err: s });
|
||||
}
|
||||
CrateError::LocatorCombined(locator) => {
|
||||
let crate_name = locator.crate_name;
|
||||
|
|
@ -1012,8 +1007,12 @@ impl CrateError {
|
|||
if !locator.crate_rejections.via_filename.is_empty() {
|
||||
let mismatches = locator.crate_rejections.via_filename.iter();
|
||||
for CrateMismatch { path, .. } in mismatches {
|
||||
sess.emit_err(CrateLocationUnknownType { span, path: &path, crate_name });
|
||||
sess.emit_err(LibFilenameForm {
|
||||
sess.emit_err(errors::CrateLocationUnknownType {
|
||||
span,
|
||||
path: &path,
|
||||
crate_name,
|
||||
});
|
||||
sess.emit_err(errors::LibFilenameForm {
|
||||
span,
|
||||
dll_prefix: &locator.dll_prefix,
|
||||
dll_suffix: &locator.dll_suffix,
|
||||
|
|
@ -1039,7 +1038,7 @@ impl CrateError {
|
|||
));
|
||||
}
|
||||
}
|
||||
sess.emit_err(NewerCrateVersion {
|
||||
sess.emit_err(errors::NewerCrateVersion {
|
||||
span,
|
||||
crate_name: crate_name,
|
||||
add_info,
|
||||
|
|
@ -1055,7 +1054,7 @@ impl CrateError {
|
|||
path.display(),
|
||||
));
|
||||
}
|
||||
sess.emit_err(NoCrateWithTriple {
|
||||
sess.emit_err(errors::NoCrateWithTriple {
|
||||
span,
|
||||
crate_name,
|
||||
locator_triple: locator.triple.triple(),
|
||||
|
|
@ -1071,7 +1070,12 @@ impl CrateError {
|
|||
path.display()
|
||||
));
|
||||
}
|
||||
sess.emit_err(FoundStaticlib { span, crate_name, add_info, found_crates });
|
||||
sess.emit_err(errors::FoundStaticlib {
|
||||
span,
|
||||
crate_name,
|
||||
add_info,
|
||||
found_crates,
|
||||
});
|
||||
} else if !locator.crate_rejections.via_version.is_empty() {
|
||||
let mismatches = locator.crate_rejections.via_version.iter();
|
||||
for CrateMismatch { path, got } in mismatches {
|
||||
|
|
@ -1082,7 +1086,7 @@ impl CrateError {
|
|||
path.display(),
|
||||
));
|
||||
}
|
||||
sess.emit_err(IncompatibleRustc {
|
||||
sess.emit_err(errors::IncompatibleRustc {
|
||||
span,
|
||||
crate_name,
|
||||
add_info,
|
||||
|
|
@ -1094,14 +1098,14 @@ impl CrateError {
|
|||
for CrateMismatch { path: _, got } in locator.crate_rejections.via_invalid {
|
||||
crate_rejections.push(got);
|
||||
}
|
||||
sess.emit_err(InvalidMetadataFiles {
|
||||
sess.emit_err(errors::InvalidMetadataFiles {
|
||||
span,
|
||||
crate_name,
|
||||
add_info,
|
||||
crate_rejections,
|
||||
});
|
||||
} else {
|
||||
sess.emit_err(CannotFindCrate {
|
||||
sess.emit_err(errors::CannotFindCrate {
|
||||
span,
|
||||
crate_name,
|
||||
add_info,
|
||||
|
|
@ -1118,7 +1122,7 @@ impl CrateError {
|
|||
}
|
||||
}
|
||||
CrateError::NonDylibPlugin(crate_name) => {
|
||||
sess.emit_err(NoDylibPlugin { span, crate_name });
|
||||
sess.emit_err(errors::NoDylibPlugin { span, crate_name });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,17 +13,7 @@ use rustc_session::Session;
|
|||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use crate::errors::{
|
||||
AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, EmptyRenamingTarget,
|
||||
FrameworkOnlyWindows, ImportNameTypeForm, ImportNameTypeRaw, ImportNameTypeX86,
|
||||
IncompatibleWasmLink, InvalidLinkModifier, LibFrameworkApple, LinkCfgForm,
|
||||
LinkCfgSinglePredicate, LinkFrameworkApple, LinkKindForm, LinkModifiersForm, LinkNameForm,
|
||||
LinkOrdinalRawDylib, LinkRequiresName, MissingNativeLibrary, MultipleCfgs,
|
||||
MultipleImportNameType, MultipleKindsInLink, MultipleLinkModifiers, MultipleModifiers,
|
||||
MultipleNamesInLink, MultipleRenamings, MultipleWasmImport, NoLinkModOverride, RawDylibNoNul,
|
||||
RenamingNoLink, UnexpectedLinkArg, UnknownImportNameType, UnknownLinkKind, UnknownLinkModifier,
|
||||
UnsupportedAbi, UnsupportedAbiI686, WasmImportForm, WholeArchiveNeedsStatic,
|
||||
};
|
||||
use crate::errors;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
|
@ -52,27 +42,28 @@ pub fn find_native_static_library(
|
|||
}
|
||||
}
|
||||
|
||||
sess.emit_fatal(MissingNativeLibrary::new(name, verbatim));
|
||||
sess.emit_fatal(errors::MissingNativeLibrary::new(name, verbatim));
|
||||
}
|
||||
|
||||
fn find_bundled_library(
|
||||
name: Option<Symbol>,
|
||||
verbatim: Option<bool>,
|
||||
kind: NativeLibKind,
|
||||
has_cfg: bool,
|
||||
sess: &Session,
|
||||
) -> Option<Symbol> {
|
||||
if sess.opts.unstable_opts.packed_bundled_libs &&
|
||||
sess.crate_types().iter().any(|ct| ct == &CrateType::Rlib || ct == &CrateType::Staticlib) &&
|
||||
let NativeLibKind::Static { bundle: Some(true) | None, .. } = kind {
|
||||
find_native_static_library(
|
||||
name.unwrap().as_str(),
|
||||
verbatim.unwrap_or(false),
|
||||
&sess.target_filesearch(PathKind::Native).search_path_dirs(),
|
||||
sess,
|
||||
).file_name().and_then(|s| s.to_str()).map(Symbol::intern)
|
||||
} else {
|
||||
None
|
||||
if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive } = kind
|
||||
&& sess.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::Staticlib))
|
||||
&& (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true))
|
||||
{
|
||||
let verbatim = verbatim.unwrap_or(false);
|
||||
let search_paths = &sess.target_filesearch(PathKind::Native).search_path_dirs();
|
||||
return find_native_static_library(name.unwrap().as_str(), verbatim, search_paths, sess)
|
||||
.file_name()
|
||||
.and_then(|s| s.to_str())
|
||||
.map(Symbol::intern);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> {
|
||||
|
|
@ -129,26 +120,26 @@ impl<'tcx> Collector<'tcx> {
|
|||
match item.name_or_empty() {
|
||||
sym::name => {
|
||||
if name.is_some() {
|
||||
sess.emit_err(MultipleNamesInLink { span: item.span() });
|
||||
sess.emit_err(errors::MultipleNamesInLink { span: item.span() });
|
||||
continue;
|
||||
}
|
||||
let Some(link_name) = item.value_str() else {
|
||||
sess.emit_err(LinkNameForm { span: item.span() });
|
||||
sess.emit_err(errors::LinkNameForm { span: item.span() });
|
||||
continue;
|
||||
};
|
||||
let span = item.name_value_literal_span().unwrap();
|
||||
if link_name.is_empty() {
|
||||
sess.emit_err(EmptyLinkName { span });
|
||||
sess.emit_err(errors::EmptyLinkName { span });
|
||||
}
|
||||
name = Some((link_name, span));
|
||||
}
|
||||
sym::kind => {
|
||||
if kind.is_some() {
|
||||
sess.emit_err(MultipleKindsInLink { span: item.span() });
|
||||
sess.emit_err(errors::MultipleKindsInLink { span: item.span() });
|
||||
continue;
|
||||
}
|
||||
let Some(link_kind) = item.value_str() else {
|
||||
sess.emit_err(LinkKindForm { span: item.span() });
|
||||
sess.emit_err(errors::LinkKindForm { span: item.span() });
|
||||
continue;
|
||||
};
|
||||
|
||||
|
|
@ -158,13 +149,13 @@ impl<'tcx> Collector<'tcx> {
|
|||
"dylib" => NativeLibKind::Dylib { as_needed: None },
|
||||
"framework" => {
|
||||
if !sess.target.is_like_osx {
|
||||
sess.emit_err(LinkFrameworkApple { span });
|
||||
sess.emit_err(errors::LinkFrameworkApple { span });
|
||||
}
|
||||
NativeLibKind::Framework { as_needed: None }
|
||||
}
|
||||
"raw-dylib" => {
|
||||
if !sess.target.is_like_windows {
|
||||
sess.emit_err(FrameworkOnlyWindows { span });
|
||||
sess.emit_err(errors::FrameworkOnlyWindows { span });
|
||||
} else if !features.raw_dylib && sess.target.arch == "x86" {
|
||||
feature_err(
|
||||
&sess.parse_sess,
|
||||
|
|
@ -177,7 +168,7 @@ impl<'tcx> Collector<'tcx> {
|
|||
NativeLibKind::RawDylib
|
||||
}
|
||||
kind => {
|
||||
sess.emit_err(UnknownLinkKind { span, kind });
|
||||
sess.emit_err(errors::UnknownLinkKind { span, kind });
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
|
@ -185,26 +176,26 @@ impl<'tcx> Collector<'tcx> {
|
|||
}
|
||||
sym::modifiers => {
|
||||
if modifiers.is_some() {
|
||||
sess.emit_err(MultipleLinkModifiers { span: item.span() });
|
||||
sess.emit_err(errors::MultipleLinkModifiers { span: item.span() });
|
||||
continue;
|
||||
}
|
||||
let Some(link_modifiers) = item.value_str() else {
|
||||
sess.emit_err(LinkModifiersForm { span: item.span() });
|
||||
sess.emit_err(errors::LinkModifiersForm { span: item.span() });
|
||||
continue;
|
||||
};
|
||||
modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap()));
|
||||
}
|
||||
sym::cfg => {
|
||||
if cfg.is_some() {
|
||||
sess.emit_err(MultipleCfgs { span: item.span() });
|
||||
sess.emit_err(errors::MultipleCfgs { span: item.span() });
|
||||
continue;
|
||||
}
|
||||
let Some(link_cfg) = item.meta_item_list() else {
|
||||
sess.emit_err(LinkCfgForm { span: item.span() });
|
||||
sess.emit_err(errors::LinkCfgForm { span: item.span() });
|
||||
continue;
|
||||
};
|
||||
let [NestedMetaItem::MetaItem(link_cfg)] = link_cfg else {
|
||||
sess.emit_err(LinkCfgSinglePredicate { span: item.span() });
|
||||
sess.emit_err(errors::LinkCfgSinglePredicate { span: item.span() });
|
||||
continue;
|
||||
};
|
||||
if !features.link_cfg {
|
||||
|
|
@ -220,26 +211,26 @@ impl<'tcx> Collector<'tcx> {
|
|||
}
|
||||
sym::wasm_import_module => {
|
||||
if wasm_import_module.is_some() {
|
||||
sess.emit_err(MultipleWasmImport { span: item.span() });
|
||||
sess.emit_err(errors::MultipleWasmImport { span: item.span() });
|
||||
continue;
|
||||
}
|
||||
let Some(link_wasm_import_module) = item.value_str() else {
|
||||
sess.emit_err(WasmImportForm { span: item.span() });
|
||||
sess.emit_err(errors::WasmImportForm { span: item.span() });
|
||||
continue;
|
||||
};
|
||||
wasm_import_module = Some((link_wasm_import_module, item.span()));
|
||||
}
|
||||
sym::import_name_type => {
|
||||
if import_name_type.is_some() {
|
||||
sess.emit_err(MultipleImportNameType { span: item.span() });
|
||||
sess.emit_err(errors::MultipleImportNameType { span: item.span() });
|
||||
continue;
|
||||
}
|
||||
let Some(link_import_name_type) = item.value_str() else {
|
||||
sess.emit_err(ImportNameTypeForm { span: item.span() });
|
||||
sess.emit_err(errors::ImportNameTypeForm { span: item.span() });
|
||||
continue;
|
||||
};
|
||||
if self.tcx.sess.target.arch != "x86" {
|
||||
sess.emit_err(ImportNameTypeX86 { span: item.span() });
|
||||
sess.emit_err(errors::ImportNameTypeX86 { span: item.span() });
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -248,7 +239,7 @@ impl<'tcx> Collector<'tcx> {
|
|||
"noprefix" => PeImportNameType::NoPrefix,
|
||||
"undecorated" => PeImportNameType::Undecorated,
|
||||
import_name_type => {
|
||||
sess.emit_err(UnknownImportNameType {
|
||||
sess.emit_err(errors::UnknownImportNameType {
|
||||
span: item.span(),
|
||||
import_name_type,
|
||||
});
|
||||
|
|
@ -268,7 +259,7 @@ impl<'tcx> Collector<'tcx> {
|
|||
import_name_type = Some((link_import_name_type, item.span()));
|
||||
}
|
||||
_ => {
|
||||
sess.emit_err(UnexpectedLinkArg { span: item.span() });
|
||||
sess.emit_err(errors::UnexpectedLinkArg { span: item.span() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -280,7 +271,7 @@ impl<'tcx> Collector<'tcx> {
|
|||
let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
|
||||
Some(m) => (m, modifier.starts_with('+')),
|
||||
None => {
|
||||
sess.emit_err(InvalidLinkModifier { span });
|
||||
sess.emit_err(errors::InvalidLinkModifier { span });
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
|
@ -298,7 +289,7 @@ impl<'tcx> Collector<'tcx> {
|
|||
}
|
||||
let assign_modifier = |dst: &mut Option<bool>| {
|
||||
if dst.is_some() {
|
||||
sess.emit_err(MultipleModifiers { span, modifier });
|
||||
sess.emit_err(errors::MultipleModifiers { span, modifier });
|
||||
} else {
|
||||
*dst = Some(value);
|
||||
}
|
||||
|
|
@ -308,7 +299,7 @@ impl<'tcx> Collector<'tcx> {
|
|||
assign_modifier(bundle)
|
||||
}
|
||||
("bundle", _) => {
|
||||
sess.emit_err(BundleNeedsStatic { span });
|
||||
sess.emit_err(errors::BundleNeedsStatic { span });
|
||||
}
|
||||
|
||||
("verbatim", _) => assign_modifier(&mut verbatim),
|
||||
|
|
@ -317,7 +308,7 @@ impl<'tcx> Collector<'tcx> {
|
|||
assign_modifier(whole_archive)
|
||||
}
|
||||
("whole-archive", _) => {
|
||||
sess.emit_err(WholeArchiveNeedsStatic { span });
|
||||
sess.emit_err(errors::WholeArchiveNeedsStatic { span });
|
||||
}
|
||||
|
||||
("as-needed", Some(NativeLibKind::Dylib { as_needed }))
|
||||
|
|
@ -326,11 +317,11 @@ impl<'tcx> Collector<'tcx> {
|
|||
assign_modifier(as_needed)
|
||||
}
|
||||
("as-needed", _) => {
|
||||
sess.emit_err(AsNeededCompatibility { span });
|
||||
sess.emit_err(errors::AsNeededCompatibility { span });
|
||||
}
|
||||
|
||||
_ => {
|
||||
sess.emit_err(UnknownLinkModifier { span, modifier });
|
||||
sess.emit_err(errors::UnknownLinkModifier { span, modifier });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -338,23 +329,23 @@ impl<'tcx> Collector<'tcx> {
|
|||
|
||||
if let Some((_, span)) = wasm_import_module {
|
||||
if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
|
||||
sess.emit_err(IncompatibleWasmLink { span });
|
||||
sess.emit_err(errors::IncompatibleWasmLink { span });
|
||||
}
|
||||
} else if name.is_none() {
|
||||
sess.emit_err(LinkRequiresName { span: m.span });
|
||||
sess.emit_err(errors::LinkRequiresName { span: m.span });
|
||||
}
|
||||
|
||||
// Do this outside of the loop so that `import_name_type` can be specified before `kind`.
|
||||
if let Some((_, span)) = import_name_type {
|
||||
if kind != Some(NativeLibKind::RawDylib) {
|
||||
sess.emit_err(ImportNameTypeRaw { span });
|
||||
sess.emit_err(errors::ImportNameTypeRaw { span });
|
||||
}
|
||||
}
|
||||
|
||||
let dll_imports = match kind {
|
||||
Some(NativeLibKind::RawDylib) => {
|
||||
if let Some((name, span)) = name && name.as_str().contains('\0') {
|
||||
sess.emit_err(RawDylibNoNul { span });
|
||||
sess.emit_err(errors::RawDylibNoNul { span });
|
||||
}
|
||||
foreign_mod_items
|
||||
.iter()
|
||||
|
|
@ -383,7 +374,9 @@ impl<'tcx> Collector<'tcx> {
|
|||
.iter()
|
||||
.find(|a| a.has_name(sym::link_ordinal))
|
||||
.unwrap();
|
||||
sess.emit_err(LinkOrdinalRawDylib { span: link_ordinal_attr.span });
|
||||
sess.emit_err(errors::LinkOrdinalRawDylib {
|
||||
span: link_ordinal_attr.span,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -393,7 +386,7 @@ impl<'tcx> Collector<'tcx> {
|
|||
|
||||
let name = name.map(|(name, _)| name);
|
||||
let kind = kind.unwrap_or(NativeLibKind::Unspecified);
|
||||
let filename = find_bundled_library(name, verbatim, kind, sess);
|
||||
let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), sess);
|
||||
self.libs.push(NativeLib {
|
||||
name,
|
||||
filename,
|
||||
|
|
@ -414,7 +407,7 @@ impl<'tcx> Collector<'tcx> {
|
|||
for lib in &self.tcx.sess.opts.libs {
|
||||
if let NativeLibKind::Framework { .. } = lib.kind && !self.tcx.sess.target.is_like_osx {
|
||||
// Cannot check this when parsing options because the target is not yet available.
|
||||
self.tcx.sess.emit_err(LibFrameworkApple);
|
||||
self.tcx.sess.emit_err(errors::LibFrameworkApple);
|
||||
}
|
||||
if let Some(ref new_name) = lib.new_name {
|
||||
let any_duplicate = self
|
||||
|
|
@ -423,11 +416,11 @@ impl<'tcx> Collector<'tcx> {
|
|||
.filter_map(|lib| lib.name.as_ref())
|
||||
.any(|n| n.as_str() == lib.name);
|
||||
if new_name.is_empty() {
|
||||
self.tcx.sess.emit_err(EmptyRenamingTarget { lib_name: &lib.name });
|
||||
self.tcx.sess.emit_err(errors::EmptyRenamingTarget { lib_name: &lib.name });
|
||||
} else if !any_duplicate {
|
||||
self.tcx.sess.emit_err(RenamingNoLink { lib_name: &lib.name });
|
||||
self.tcx.sess.emit_err(errors::RenamingNoLink { lib_name: &lib.name });
|
||||
} else if !renames.insert(&lib.name) {
|
||||
self.tcx.sess.emit_err(MultipleRenamings { lib_name: &lib.name });
|
||||
self.tcx.sess.emit_err(errors::MultipleRenamings { lib_name: &lib.name });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -453,12 +446,15 @@ impl<'tcx> Collector<'tcx> {
|
|||
// explicit `:rename` in particular.
|
||||
if lib.has_modifiers() || passed_lib.has_modifiers() {
|
||||
match lib.foreign_module {
|
||||
Some(def_id) => self.tcx.sess.emit_err(NoLinkModOverride {
|
||||
span: Some(self.tcx.def_span(def_id)),
|
||||
}),
|
||||
None => {
|
||||
self.tcx.sess.emit_err(NoLinkModOverride { span: None })
|
||||
Some(def_id) => {
|
||||
self.tcx.sess.emit_err(errors::NoLinkModOverride {
|
||||
span: Some(self.tcx.def_span(def_id)),
|
||||
})
|
||||
}
|
||||
None => self
|
||||
.tcx
|
||||
.sess
|
||||
.emit_err(errors::NoLinkModOverride { span: None }),
|
||||
};
|
||||
}
|
||||
if passed_lib.kind != NativeLibKind::Unspecified {
|
||||
|
|
@ -480,7 +476,7 @@ impl<'tcx> Collector<'tcx> {
|
|||
let name = Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name)));
|
||||
let sess = self.tcx.sess;
|
||||
let filename =
|
||||
find_bundled_library(name, passed_lib.verbatim, passed_lib.kind, sess);
|
||||
find_bundled_library(name, passed_lib.verbatim, passed_lib.kind, false, sess);
|
||||
self.libs.push(NativeLib {
|
||||
name,
|
||||
filename,
|
||||
|
|
@ -542,14 +538,14 @@ impl<'tcx> Collector<'tcx> {
|
|||
DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
|
||||
}
|
||||
_ => {
|
||||
self.tcx.sess.emit_fatal(UnsupportedAbiI686 { span: item.span });
|
||||
self.tcx.sess.emit_fatal(errors::UnsupportedAbiI686 { span: item.span });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match abi {
|
||||
Abi::C { .. } | Abi::Win64 { .. } | Abi::System { .. } => DllCallingConvention::C,
|
||||
_ => {
|
||||
self.tcx.sess.emit_fatal(UnsupportedAbi { span: item.span });
|
||||
self.tcx.sess.emit_fatal(errors::UnsupportedAbi { span: item.span });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -654,7 +654,7 @@ impl<'a, 'tcx, T> Decodable<DecodeContext<'a, 'tcx>> for LazyValue<T> {
|
|||
impl<'a, 'tcx, T> Decodable<DecodeContext<'a, 'tcx>> for LazyArray<T> {
|
||||
fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Self {
|
||||
let len = decoder.read_usize();
|
||||
if len == 0 { LazyArray::empty() } else { decoder.read_lazy_array(len) }
|
||||
if len == 0 { LazyArray::default() } else { decoder.read_lazy_array(len) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -864,7 +864,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
.tables
|
||||
.children
|
||||
.get(self, index)
|
||||
.unwrap_or_else(LazyArray::empty)
|
||||
.expect("fields are not encoded for a variant")
|
||||
.decode(self)
|
||||
.map(|index| ty::FieldDef {
|
||||
did: self.local_def_id(index),
|
||||
|
|
@ -896,7 +896,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
.tables
|
||||
.children
|
||||
.get(self, item_id)
|
||||
.unwrap_or_else(LazyArray::empty)
|
||||
.expect("variants are not encoded for an enum")
|
||||
.decode(self)
|
||||
.filter_map(|index| {
|
||||
let kind = self.def_kind(index);
|
||||
|
|
@ -1045,7 +1045,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
.tables
|
||||
.fn_arg_names
|
||||
.get(self, id)
|
||||
.unwrap_or_else(LazyArray::empty)
|
||||
.expect("argument names not encoded for a function")
|
||||
.decode((self, sess))
|
||||
.nth(0)
|
||||
.map_or(false, |ident| ident.name == kw::SelfLower)
|
||||
|
|
@ -1060,7 +1060,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
.tables
|
||||
.children
|
||||
.get(self, id)
|
||||
.unwrap_or_else(LazyArray::empty)
|
||||
.expect("associated items not encoded for an item")
|
||||
.decode((self, sess))
|
||||
.map(move |child_index| self.local_def_id(child_index))
|
||||
}
|
||||
|
|
@ -1068,13 +1068,12 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
fn get_associated_item(self, id: DefIndex, sess: &'a Session) -> ty::AssocItem {
|
||||
let name = self.item_name(id);
|
||||
|
||||
let kind = match self.def_kind(id) {
|
||||
DefKind::AssocConst => ty::AssocKind::Const,
|
||||
DefKind::AssocFn => ty::AssocKind::Fn,
|
||||
DefKind::AssocTy => ty::AssocKind::Type,
|
||||
let (kind, has_self) = match self.def_kind(id) {
|
||||
DefKind::AssocConst => (ty::AssocKind::Const, false),
|
||||
DefKind::AssocFn => (ty::AssocKind::Fn, self.get_fn_has_self_parameter(id, sess)),
|
||||
DefKind::AssocTy => (ty::AssocKind::Type, false),
|
||||
_ => bug!("cannot get associated-item of `{:?}`", self.def_key(id)),
|
||||
};
|
||||
let has_self = self.get_fn_has_self_parameter(id, sess);
|
||||
let container = self.root.tables.assoc_container.get(self, id).unwrap();
|
||||
|
||||
ty::AssocItem {
|
||||
|
|
@ -1131,7 +1130,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
.tables
|
||||
.children
|
||||
.get(self, id)
|
||||
.unwrap_or_else(LazyArray::empty)
|
||||
.expect("fields not encoded for a struct")
|
||||
.decode(self)
|
||||
.map(move |index| respan(self.get_span(index, sess), self.item_name(index)))
|
||||
}
|
||||
|
|
@ -1144,7 +1143,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
.tables
|
||||
.children
|
||||
.get(self, id)
|
||||
.unwrap_or_else(LazyArray::empty)
|
||||
.expect("fields not encoded for a struct")
|
||||
.decode(self)
|
||||
.map(move |field_index| self.get_visibility(field_index))
|
||||
}
|
||||
|
|
@ -1159,7 +1158,6 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
.tables
|
||||
.inherent_impls
|
||||
.get(self, id)
|
||||
.unwrap_or_else(LazyArray::empty)
|
||||
.decode(self)
|
||||
.map(|index| self.local_def_id(index)),
|
||||
)
|
||||
|
|
@ -1174,7 +1172,6 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
.tables
|
||||
.inherent_impls
|
||||
.get(self, ty_index)
|
||||
.unwrap_or_else(LazyArray::empty)
|
||||
.decode(self)
|
||||
.map(move |impl_index| (ty_def_id, self.local_def_id(impl_index)))
|
||||
})
|
||||
|
|
@ -1322,7 +1319,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
) -> DefPathHash {
|
||||
*def_path_hashes
|
||||
.entry(index)
|
||||
.or_insert_with(|| self.root.tables.def_path_hashes.get(self, index).unwrap())
|
||||
.or_insert_with(|| self.root.tables.def_path_hashes.get(self, index))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::creader::{CStore, LoadedMacro};
|
||||
use crate::foreign_modules;
|
||||
use crate::native_libs;
|
||||
use crate::rmeta::table::IsDefault;
|
||||
use crate::rmeta::AttrFlags;
|
||||
|
||||
use rustc_ast as ast;
|
||||
|
|
@ -88,6 +89,14 @@ macro_rules! provide_one {
|
|||
}
|
||||
}
|
||||
};
|
||||
($tcx:ident, $def_id:ident, $other:ident, $cdata:ident, $name:ident => { table_defaulted_array }) => {
|
||||
provide_one! {
|
||||
$tcx, $def_id, $other, $cdata, $name => {
|
||||
let lazy = $cdata.root.tables.$name.get($cdata, $def_id.index);
|
||||
if lazy.is_default() { &[] } else { $tcx.arena.alloc_from_iter(lazy.decode(($cdata, $tcx))) }
|
||||
}
|
||||
}
|
||||
};
|
||||
($tcx:ident, $def_id:ident, $other:ident, $cdata:ident, $name:ident => { table_direct }) => {
|
||||
provide_one! {
|
||||
$tcx, $def_id, $other, $cdata, $name => {
|
||||
|
|
@ -187,10 +196,10 @@ impl IntoArgs for (CrateNum, SimplifiedType) {
|
|||
}
|
||||
|
||||
provide! { tcx, def_id, other, cdata,
|
||||
explicit_item_bounds => { table }
|
||||
explicit_item_bounds => { table_defaulted_array }
|
||||
explicit_predicates_of => { table }
|
||||
generics_of => { table }
|
||||
inferred_outlives_of => { table }
|
||||
inferred_outlives_of => { table_defaulted_array }
|
||||
super_predicates_of => { table }
|
||||
type_of => { table }
|
||||
variances_of => { table }
|
||||
|
|
|
|||
|
|
@ -76,13 +76,13 @@ pub(super) struct EncodeContext<'a, 'tcx> {
|
|||
symbol_table: FxHashMap<Symbol, usize>,
|
||||
}
|
||||
|
||||
/// If the current crate is a proc-macro, returns early with `LazyArray::empty()`.
|
||||
/// If the current crate is a proc-macro, returns early with `LazyArray::default()`.
|
||||
/// This is useful for skipping the encoding of things that aren't needed
|
||||
/// for proc-macro crates.
|
||||
macro_rules! empty_proc_macro {
|
||||
($self:ident) => {
|
||||
if $self.is_proc_macro {
|
||||
return LazyArray::empty();
|
||||
return LazyArray::default();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -365,21 +365,31 @@ impl<'a, 'tcx> TyEncoder for EncodeContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// Shorthand for `$self.$tables.$table.set($def_id.index, $self.lazy_value($value))`, which would
|
||||
// Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy_value($value))`, which would
|
||||
// normally need extra variables to avoid errors about multiple mutable borrows.
|
||||
macro_rules! record {
|
||||
($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{
|
||||
{
|
||||
let value = $value;
|
||||
let lazy = $self.lazy(value);
|
||||
$self.$tables.$table.set($def_id.index, lazy);
|
||||
$self.$tables.$table.set_some($def_id.index, lazy);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
// Shorthand for `$self.$tables.$table.set($def_id.index, $self.lazy_value($value))`, which would
|
||||
// Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy_value($value))`, which would
|
||||
// normally need extra variables to avoid errors about multiple mutable borrows.
|
||||
macro_rules! record_array {
|
||||
($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{
|
||||
{
|
||||
let value = $value;
|
||||
let lazy = $self.lazy_array(value);
|
||||
$self.$tables.$table.set_some($def_id.index, lazy);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! record_defaulted_array {
|
||||
($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{
|
||||
{
|
||||
let value = $value;
|
||||
|
|
@ -467,13 +477,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
{
|
||||
let def_key = self.lazy(table.def_key(def_index));
|
||||
let def_path_hash = table.def_path_hash(def_index);
|
||||
self.tables.def_keys.set(def_index, def_key);
|
||||
self.tables.def_keys.set_some(def_index, def_key);
|
||||
self.tables.def_path_hashes.set(def_index, def_path_hash);
|
||||
}
|
||||
} else {
|
||||
for (def_index, def_key, def_path_hash) in table.enumerated_keys_and_path_hashes() {
|
||||
let def_key = self.lazy(def_key);
|
||||
self.tables.def_keys.set(def_index, def_key);
|
||||
self.tables.def_keys.set_some(def_index, def_key);
|
||||
self.tables.def_path_hashes.set(def_index, *def_path_hash);
|
||||
}
|
||||
}
|
||||
|
|
@ -548,7 +558,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
|
||||
let on_disk_index: u32 =
|
||||
on_disk_index.try_into().expect("cannot export more than U32_MAX files");
|
||||
adapted.set(on_disk_index, self.lazy(source_file));
|
||||
adapted.set_some(on_disk_index, self.lazy(source_file));
|
||||
}
|
||||
|
||||
adapted.encode(&mut self.opaque)
|
||||
|
|
@ -1147,9 +1157,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
if state.is_doc_hidden {
|
||||
attr_flags |= AttrFlags::IS_DOC_HIDDEN;
|
||||
}
|
||||
if !attr_flags.is_empty() {
|
||||
self.tables.attr_flags.set_nullable(def_id.local_def_index, attr_flags);
|
||||
}
|
||||
self.tables.attr_flags.set(def_id.local_def_index, attr_flags);
|
||||
}
|
||||
|
||||
fn encode_def_ids(&mut self) {
|
||||
|
|
@ -1161,7 +1169,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
let def_id = local_id.to_def_id();
|
||||
let def_kind = tcx.opt_def_kind(local_id);
|
||||
let Some(def_kind) = def_kind else { continue };
|
||||
self.tables.opt_def_kind.set(def_id.index, def_kind);
|
||||
self.tables.opt_def_kind.set_some(def_id.index, def_kind);
|
||||
let def_span = tcx.def_span(local_id);
|
||||
record!(self.tables.def_span[def_id] <- def_span);
|
||||
self.encode_attrs(local_id);
|
||||
|
|
@ -1192,9 +1200,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
record!(self.tables.generics_of[def_id] <- g);
|
||||
record!(self.tables.explicit_predicates_of[def_id] <- self.tcx.explicit_predicates_of(def_id));
|
||||
let inferred_outlives = self.tcx.inferred_outlives_of(def_id);
|
||||
if !inferred_outlives.is_empty() {
|
||||
record_array!(self.tables.inferred_outlives_of[def_id] <- inferred_outlives);
|
||||
}
|
||||
record_defaulted_array!(self.tables.inferred_outlives_of[def_id] <- inferred_outlives);
|
||||
}
|
||||
if should_encode_type(tcx, local_id, def_kind) {
|
||||
record!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id));
|
||||
|
|
@ -1215,15 +1221,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
record!(self.tables.trait_impl_trait_tys[def_id] <- table);
|
||||
}
|
||||
}
|
||||
|
||||
let inherent_impls = tcx.with_stable_hashing_context(|hcx| {
|
||||
tcx.crate_inherent_impls(()).inherent_impls.to_sorted(&hcx, true)
|
||||
});
|
||||
|
||||
for (def_id, implementations) in inherent_impls {
|
||||
if implementations.is_empty() {
|
||||
continue;
|
||||
}
|
||||
record_array!(self.tables.inherent_impls[def_id.to_def_id()] <- implementations.iter().map(|&def_id| {
|
||||
for (def_id, impls) in inherent_impls {
|
||||
record_defaulted_array!(self.tables.inherent_impls[def_id.to_def_id()] <- impls.iter().map(|def_id| {
|
||||
assert!(def_id.is_local());
|
||||
def_id.index
|
||||
}));
|
||||
|
|
@ -1264,14 +1267,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
};
|
||||
record!(self.tables.variant_data[variant.def_id] <- data);
|
||||
|
||||
self.tables.constness.set(variant.def_id.index, hir::Constness::Const);
|
||||
self.tables.constness.set_some(variant.def_id.index, hir::Constness::Const);
|
||||
record_array!(self.tables.children[variant.def_id] <- variant.fields.iter().map(|f| {
|
||||
assert!(f.did.is_local());
|
||||
f.did.index
|
||||
}));
|
||||
|
||||
if let Some((CtorKind::Fn, ctor_def_id)) = variant.ctor {
|
||||
self.tables.constness.set(ctor_def_id.index, hir::Constness::Const);
|
||||
self.tables.constness.set_some(ctor_def_id.index, hir::Constness::Const);
|
||||
let fn_sig = tcx.fn_sig(ctor_def_id);
|
||||
record!(self.tables.fn_sig[ctor_def_id] <- fn_sig);
|
||||
// FIXME only encode signature for ctor_def_id
|
||||
|
|
@ -1332,9 +1335,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
fn encode_explicit_item_bounds(&mut self, def_id: DefId) {
|
||||
debug!("EncodeContext::encode_explicit_item_bounds({:?})", def_id);
|
||||
let bounds = self.tcx.explicit_item_bounds(def_id);
|
||||
if !bounds.is_empty() {
|
||||
record_array!(self.tables.explicit_item_bounds[def_id] <- bounds);
|
||||
}
|
||||
record_defaulted_array!(self.tables.explicit_item_bounds[def_id] <- bounds);
|
||||
}
|
||||
|
||||
fn encode_info_for_trait_item(&mut self, def_id: DefId) {
|
||||
|
|
@ -1342,16 +1343,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
let tcx = self.tcx;
|
||||
|
||||
let impl_defaultness = tcx.impl_defaultness(def_id.expect_local());
|
||||
self.tables.impl_defaultness.set(def_id.index, impl_defaultness);
|
||||
self.tables.impl_defaultness.set_some(def_id.index, impl_defaultness);
|
||||
let trait_item = tcx.associated_item(def_id);
|
||||
self.tables.assoc_container.set(def_id.index, trait_item.container);
|
||||
self.tables.assoc_container.set_some(def_id.index, trait_item.container);
|
||||
|
||||
match trait_item.kind {
|
||||
ty::AssocKind::Const => {}
|
||||
ty::AssocKind::Fn => {
|
||||
record_array!(self.tables.fn_arg_names[def_id] <- tcx.fn_arg_names(def_id));
|
||||
self.tables.asyncness.set(def_id.index, tcx.asyncness(def_id));
|
||||
self.tables.constness.set(def_id.index, hir::Constness::NotConst);
|
||||
self.tables.asyncness.set_some(def_id.index, tcx.asyncness(def_id));
|
||||
self.tables.constness.set_some(def_id.index, hir::Constness::NotConst);
|
||||
}
|
||||
ty::AssocKind::Type => {
|
||||
self.encode_explicit_item_bounds(def_id);
|
||||
|
|
@ -1367,14 +1368,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
let tcx = self.tcx;
|
||||
|
||||
let ast_item = self.tcx.hir().expect_impl_item(def_id.expect_local());
|
||||
self.tables.impl_defaultness.set(def_id.index, ast_item.defaultness);
|
||||
self.tables.impl_defaultness.set_some(def_id.index, ast_item.defaultness);
|
||||
let impl_item = self.tcx.associated_item(def_id);
|
||||
self.tables.assoc_container.set(def_id.index, impl_item.container);
|
||||
self.tables.assoc_container.set_some(def_id.index, impl_item.container);
|
||||
|
||||
match impl_item.kind {
|
||||
ty::AssocKind::Fn => {
|
||||
let hir::ImplItemKind::Fn(ref sig, body) = ast_item.kind else { bug!() };
|
||||
self.tables.asyncness.set(def_id.index, sig.header.asyncness);
|
||||
self.tables.asyncness.set_some(def_id.index, sig.header.asyncness);
|
||||
record_array!(self.tables.fn_arg_names[def_id] <- self.tcx.hir().body_param_names(body));
|
||||
// Can be inside `impl const Trait`, so using sig.header.constness is not reliable
|
||||
let constness = if self.tcx.is_const_fn_raw(def_id) {
|
||||
|
|
@ -1382,18 +1383,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
} else {
|
||||
hir::Constness::NotConst
|
||||
};
|
||||
self.tables.constness.set(def_id.index, constness);
|
||||
self.tables.constness.set_some(def_id.index, constness);
|
||||
}
|
||||
ty::AssocKind::Const | ty::AssocKind::Type => {}
|
||||
}
|
||||
if let Some(trait_item_def_id) = impl_item.trait_item_def_id {
|
||||
self.tables.trait_item_def_id.set(def_id.index, trait_item_def_id.into());
|
||||
self.tables.trait_item_def_id.set_some(def_id.index, trait_item_def_id.into());
|
||||
}
|
||||
if impl_item.kind == ty::AssocKind::Fn {
|
||||
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
|
||||
if tcx.is_intrinsic(def_id) {
|
||||
self.tables.is_intrinsic.set_nullable(def_id.index, true);
|
||||
}
|
||||
self.tables.is_intrinsic.set(def_id.index, tcx.is_intrinsic(def_id));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1522,14 +1521,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
|
||||
match item.kind {
|
||||
hir::ItemKind::Fn(ref sig, .., body) => {
|
||||
self.tables.asyncness.set(def_id.index, sig.header.asyncness);
|
||||
self.tables.asyncness.set_some(def_id.index, sig.header.asyncness);
|
||||
record_array!(self.tables.fn_arg_names[def_id] <- self.tcx.hir().body_param_names(body));
|
||||
self.tables.constness.set(def_id.index, sig.header.constness);
|
||||
self.tables.constness.set_some(def_id.index, sig.header.constness);
|
||||
}
|
||||
hir::ItemKind::Macro(ref macro_def, _) => {
|
||||
if macro_def.macro_rules {
|
||||
self.tables.is_macro_rules.set_nullable(def_id.index, true);
|
||||
}
|
||||
self.tables.is_macro_rules.set(def_id.index, macro_def.macro_rules);
|
||||
record!(self.tables.macro_definition[def_id] <- &*macro_def.body);
|
||||
}
|
||||
hir::ItemKind::Mod(ref m) => {
|
||||
|
|
@ -1537,20 +1534,20 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
}
|
||||
hir::ItemKind::OpaqueTy(ref opaque) => {
|
||||
self.encode_explicit_item_bounds(def_id);
|
||||
if matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias) {
|
||||
self.tables.is_type_alias_impl_trait.set_nullable(def_id.index, true);
|
||||
}
|
||||
self.tables
|
||||
.is_type_alias_impl_trait
|
||||
.set(def_id.index, matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias));
|
||||
}
|
||||
hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => {
|
||||
self.tables.impl_defaultness.set(def_id.index, *defaultness);
|
||||
self.tables.constness.set(def_id.index, *constness);
|
||||
self.tables.impl_defaultness.set_some(def_id.index, *defaultness);
|
||||
self.tables.constness.set_some(def_id.index, *constness);
|
||||
|
||||
let trait_ref = self.tcx.impl_trait_ref(def_id).map(ty::EarlyBinder::skip_binder);
|
||||
if let Some(trait_ref) = trait_ref {
|
||||
let trait_def = self.tcx.trait_def(trait_ref.def_id);
|
||||
if let Ok(mut an) = trait_def.ancestors(self.tcx, def_id) {
|
||||
if let Some(specialization_graph::Node::Impl(parent)) = an.nth(1) {
|
||||
self.tables.impl_parent.set(def_id.index, parent.into());
|
||||
self.tables.impl_parent.set_some(def_id.index, parent.into());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1564,7 +1561,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
let polarity = self.tcx.impl_polarity(def_id);
|
||||
self.tables.impl_polarity.set(def_id.index, polarity);
|
||||
self.tables.impl_polarity.set_some(def_id.index, polarity);
|
||||
}
|
||||
hir::ItemKind::Trait(..) => {
|
||||
let trait_def = self.tcx.trait_def(def_id);
|
||||
|
|
@ -1601,9 +1598,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
}
|
||||
if let hir::ItemKind::Fn(..) = item.kind {
|
||||
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
|
||||
if tcx.is_intrinsic(def_id) {
|
||||
self.tables.is_intrinsic.set_nullable(def_id.index, true);
|
||||
}
|
||||
self.tables.is_intrinsic.set(def_id.index, tcx.is_intrinsic(def_id));
|
||||
}
|
||||
if let hir::ItemKind::Impl { .. } = item.kind {
|
||||
if let Some(trait_ref) = self.tcx.impl_trait_ref(def_id) {
|
||||
|
|
@ -1650,7 +1645,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
|
||||
ty::Closure(_, substs) => {
|
||||
let constness = self.tcx.constness(def_id.to_def_id());
|
||||
self.tables.constness.set(def_id.to_def_id().index, constness);
|
||||
self.tables.constness.set_some(def_id.to_def_id().index, constness);
|
||||
record!(self.tables.fn_sig[def_id.to_def_id()] <- ty::EarlyBinder(substs.as_closure().sig()));
|
||||
}
|
||||
|
||||
|
|
@ -1678,12 +1673,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
self.hygiene_ctxt.encode(
|
||||
&mut (&mut *self, &mut syntax_contexts, &mut expn_data_table, &mut expn_hash_table),
|
||||
|(this, syntax_contexts, _, _), index, ctxt_data| {
|
||||
syntax_contexts.set(index, this.lazy(ctxt_data));
|
||||
syntax_contexts.set_some(index, this.lazy(ctxt_data));
|
||||
},
|
||||
|(this, _, expn_data_table, expn_hash_table), index, expn_data, hash| {
|
||||
if let Some(index) = index.as_local() {
|
||||
expn_data_table.set(index.as_raw(), this.lazy(expn_data));
|
||||
expn_hash_table.set(index.as_raw(), this.lazy(hash));
|
||||
expn_data_table.set_some(index.as_raw(), this.lazy(expn_data));
|
||||
expn_hash_table.set_some(index.as_raw(), this.lazy(hash));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
@ -1708,10 +1703,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
let spans = self.tcx.sess.parse_sess.proc_macro_quoted_spans();
|
||||
for (i, span) in spans.into_iter().enumerate() {
|
||||
let span = self.lazy(span);
|
||||
self.tables.proc_macro_quoted_spans.set(i, span);
|
||||
self.tables.proc_macro_quoted_spans.set_some(i, span);
|
||||
}
|
||||
|
||||
self.tables.opt_def_kind.set(LOCAL_CRATE.as_def_id().index, DefKind::Mod);
|
||||
self.tables.opt_def_kind.set_some(LOCAL_CRATE.as_def_id().index, DefKind::Mod);
|
||||
record!(self.tables.def_span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id()));
|
||||
self.encode_attrs(LOCAL_CRATE.as_def_id().expect_local());
|
||||
let vis = tcx.local_visibility(CRATE_DEF_ID).map_id(|def_id| def_id.local_def_index);
|
||||
|
|
@ -1753,8 +1748,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
def_key.disambiguated_data.data = DefPathData::MacroNs(name);
|
||||
|
||||
let def_id = id.to_def_id();
|
||||
self.tables.opt_def_kind.set(def_id.index, DefKind::Macro(macro_kind));
|
||||
self.tables.proc_macro.set(def_id.index, macro_kind);
|
||||
self.tables.opt_def_kind.set_some(def_id.index, DefKind::Macro(macro_kind));
|
||||
self.tables.proc_macro.set_some(def_id.index, macro_kind);
|
||||
self.encode_attrs(id);
|
||||
record!(self.tables.def_keys[def_id] <- def_key);
|
||||
record!(self.tables.def_ident_span[def_id] <- span);
|
||||
|
|
@ -1969,7 +1964,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
Linkage::Static => Some(LinkagePreference::RequireStatic),
|
||||
}));
|
||||
}
|
||||
LazyArray::empty()
|
||||
LazyArray::default()
|
||||
}
|
||||
|
||||
fn encode_info_for_foreign_item(&mut self, def_id: DefId, nitem: &hir::ForeignItem<'_>) {
|
||||
|
|
@ -1979,22 +1974,20 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
|
||||
match nitem.kind {
|
||||
hir::ForeignItemKind::Fn(_, ref names, _) => {
|
||||
self.tables.asyncness.set(def_id.index, hir::IsAsync::NotAsync);
|
||||
self.tables.asyncness.set_some(def_id.index, hir::IsAsync::NotAsync);
|
||||
record_array!(self.tables.fn_arg_names[def_id] <- *names);
|
||||
let constness = if self.tcx.is_const_fn_raw(def_id) {
|
||||
hir::Constness::Const
|
||||
} else {
|
||||
hir::Constness::NotConst
|
||||
};
|
||||
self.tables.constness.set(def_id.index, constness);
|
||||
self.tables.constness.set_some(def_id.index, constness);
|
||||
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
|
||||
}
|
||||
hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => {}
|
||||
}
|
||||
if let hir::ForeignItemKind::Fn(..) = nitem.kind {
|
||||
if tcx.is_intrinsic(def_id) {
|
||||
self.tables.is_intrinsic.set_nullable(def_id.index, true);
|
||||
}
|
||||
self.tables.is_intrinsic.set(def_id.index, tcx.is_intrinsic(def_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,14 +115,16 @@ impl<T: ParameterizedOverTcx> ParameterizedOverTcx for LazyArray<T> {
|
|||
type Value<'tcx> = LazyArray<T::Value<'tcx>>;
|
||||
}
|
||||
|
||||
impl<T> Default for LazyArray<T> {
|
||||
fn default() -> LazyArray<T> {
|
||||
LazyArray::from_position_and_num_elems(NonZeroUsize::new(1).unwrap(), 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> LazyArray<T> {
|
||||
fn from_position_and_num_elems(position: NonZeroUsize, num_elems: usize) -> LazyArray<T> {
|
||||
LazyArray { position, num_elems, _marker: PhantomData }
|
||||
}
|
||||
|
||||
fn empty() -> LazyArray<T> {
|
||||
LazyArray::from_position_and_num_elems(NonZeroUsize::new(1).unwrap(), 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of lazily-decoded values, with the added capability of random access.
|
||||
|
|
@ -316,7 +318,7 @@ pub(crate) struct IncoherentImpls {
|
|||
/// Define `LazyTables` and `TableBuilders` at the same time.
|
||||
macro_rules! define_tables {
|
||||
(
|
||||
- nullable: $($name1:ident: Table<$IDX1:ty, $T1:ty>,)+
|
||||
- defaulted: $($name1:ident: Table<$IDX1:ty, $T1:ty>,)+
|
||||
- optional: $($name2:ident: Table<$IDX2:ty, $T2:ty>,)+
|
||||
) => {
|
||||
#[derive(MetadataEncodable, MetadataDecodable)]
|
||||
|
|
@ -343,11 +345,15 @@ macro_rules! define_tables {
|
|||
}
|
||||
|
||||
define_tables! {
|
||||
- nullable:
|
||||
- defaulted:
|
||||
is_intrinsic: Table<DefIndex, bool>,
|
||||
is_macro_rules: Table<DefIndex, bool>,
|
||||
is_type_alias_impl_trait: Table<DefIndex, bool>,
|
||||
attr_flags: Table<DefIndex, AttrFlags>,
|
||||
def_path_hashes: Table<DefIndex, DefPathHash>,
|
||||
explicit_item_bounds: Table<DefIndex, LazyArray<(ty::Predicate<'static>, Span)>>,
|
||||
inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
|
||||
inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
|
||||
|
||||
- optional:
|
||||
attributes: Table<DefIndex, LazyArray<ast::Attribute>>,
|
||||
|
|
@ -360,12 +366,8 @@ define_tables! {
|
|||
lookup_const_stability: Table<DefIndex, LazyValue<attr::ConstStability>>,
|
||||
lookup_default_body_stability: Table<DefIndex, LazyValue<attr::DefaultBodyStability>>,
|
||||
lookup_deprecation_entry: Table<DefIndex, LazyValue<attr::Deprecation>>,
|
||||
// As an optimization, a missing entry indicates an empty `&[]`.
|
||||
explicit_item_bounds: Table<DefIndex, LazyArray<(ty::Predicate<'static>, Span)>>,
|
||||
explicit_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>,
|
||||
generics_of: Table<DefIndex, LazyValue<ty::Generics>>,
|
||||
// As an optimization, a missing entry indicates an empty `&[]`.
|
||||
inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
|
||||
super_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>,
|
||||
type_of: Table<DefIndex, LazyValue<Ty<'static>>>,
|
||||
variances_of: Table<DefIndex, LazyArray<ty::Variance>>,
|
||||
|
|
@ -393,7 +395,6 @@ define_tables! {
|
|||
generator_kind: Table<DefIndex, LazyValue<hir::GeneratorKind>>,
|
||||
trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>,
|
||||
trait_item_def_id: Table<DefIndex, RawDefId>,
|
||||
inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
|
||||
expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>,
|
||||
unused_generic_params: Table<DefIndex, LazyValue<UnusedGenericParams>>,
|
||||
params_in_repr: Table<DefIndex, LazyValue<BitSet<u32>>>,
|
||||
|
|
@ -403,7 +404,6 @@ define_tables! {
|
|||
// `DefPathTable` up front, since we may only ever use a few
|
||||
// definitions from any given crate.
|
||||
def_keys: Table<DefIndex, LazyValue<DefKey>>,
|
||||
def_path_hashes: Table<DefIndex, DefPathHash>,
|
||||
proc_macro_quoted_spans: Table<usize, LazyValue<Span>>,
|
||||
generator_diagnostic_data: Table<DefIndex, LazyValue<GeneratorDiagnosticData<'static>>>,
|
||||
variant_data: Table<DefIndex, LazyValue<VariantData>>,
|
||||
|
|
|
|||
|
|
@ -10,11 +10,51 @@ use rustc_span::hygiene::MacroKind;
|
|||
use std::marker::PhantomData;
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
pub(super) trait IsDefault: Default {
|
||||
fn is_default(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<T> IsDefault for Option<T> {
|
||||
fn is_default(&self) -> bool {
|
||||
self.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
impl IsDefault for AttrFlags {
|
||||
fn is_default(&self) -> bool {
|
||||
self.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl IsDefault for bool {
|
||||
fn is_default(&self) -> bool {
|
||||
!self
|
||||
}
|
||||
}
|
||||
|
||||
impl IsDefault for u32 {
|
||||
fn is_default(&self) -> bool {
|
||||
*self == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IsDefault for LazyArray<T> {
|
||||
fn is_default(&self) -> bool {
|
||||
self.num_elems == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl IsDefault for DefPathHash {
|
||||
fn is_default(&self) -> bool {
|
||||
self.0 == Fingerprint::ZERO
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper trait, for encoding to, and decoding from, a fixed number of bytes.
|
||||
/// Used mainly for Lazy positions and lengths.
|
||||
/// Unchecked invariant: `Self::default()` should encode as `[0; BYTE_LEN]`,
|
||||
/// but this has no impact on safety.
|
||||
pub(super) trait FixedSizeEncoding: Default {
|
||||
pub(super) trait FixedSizeEncoding: IsDefault {
|
||||
/// This should be `[u8; BYTE_LEN]`;
|
||||
/// Cannot use an associated `const BYTE_LEN: usize` instead due to const eval limitations.
|
||||
type ByteArray;
|
||||
|
|
@ -23,6 +63,8 @@ pub(super) trait FixedSizeEncoding: Default {
|
|||
fn write_to_bytes(self, b: &mut Self::ByteArray);
|
||||
}
|
||||
|
||||
/// This implementation is not used generically, but for reading/writing
|
||||
/// concrete `u32` fields in `Lazy*` structures, which may be zero.
|
||||
impl FixedSizeEncoding for u32 {
|
||||
type ByteArray = [u8; 4];
|
||||
|
||||
|
|
@ -58,7 +100,7 @@ macro_rules! fixed_size_enum {
|
|||
fn write_to_bytes(self, b: &mut [u8;1]) {
|
||||
use $ty::*;
|
||||
b[0] = match self {
|
||||
None => 0,
|
||||
None => unreachable!(),
|
||||
$(Some($($pat)*) => 1 + ${index()},)*
|
||||
}
|
||||
}
|
||||
|
|
@ -155,20 +197,18 @@ fixed_size_enum! {
|
|||
}
|
||||
|
||||
// We directly encode `DefPathHash` because a `LazyValue` would incur a 25% cost.
|
||||
impl FixedSizeEncoding for Option<DefPathHash> {
|
||||
impl FixedSizeEncoding for DefPathHash {
|
||||
type ByteArray = [u8; 16];
|
||||
|
||||
#[inline]
|
||||
fn from_bytes(b: &[u8; 16]) -> Self {
|
||||
Some(DefPathHash(Fingerprint::from_le_bytes(*b)))
|
||||
DefPathHash(Fingerprint::from_le_bytes(*b))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_to_bytes(self, b: &mut [u8; 16]) {
|
||||
let Some(DefPathHash(fingerprint)) = self else {
|
||||
panic!("Trying to encode absent DefPathHash.")
|
||||
};
|
||||
*b = fingerprint.to_le_bytes();
|
||||
debug_assert!(!self.is_default());
|
||||
*b = self.0.to_le_bytes();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -179,17 +219,17 @@ impl FixedSizeEncoding for Option<RawDefId> {
|
|||
#[inline]
|
||||
fn from_bytes(b: &[u8; 8]) -> Self {
|
||||
let krate = u32::from_le_bytes(b[0..4].try_into().unwrap());
|
||||
let index = u32::from_le_bytes(b[4..8].try_into().unwrap());
|
||||
if krate == 0 {
|
||||
return None;
|
||||
}
|
||||
let index = u32::from_le_bytes(b[4..8].try_into().unwrap());
|
||||
Some(RawDefId { krate: krate - 1, index })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_to_bytes(self, b: &mut [u8; 8]) {
|
||||
match self {
|
||||
None => *b = [0; 8],
|
||||
None => unreachable!(),
|
||||
Some(RawDefId { krate, index }) => {
|
||||
// CrateNum is less than `CrateNum::MAX_AS_U32`.
|
||||
debug_assert!(krate < u32::MAX);
|
||||
|
|
@ -210,6 +250,7 @@ impl FixedSizeEncoding for AttrFlags {
|
|||
|
||||
#[inline]
|
||||
fn write_to_bytes(self, b: &mut [u8; 1]) {
|
||||
debug_assert!(!self.is_default());
|
||||
b[0] = self.bits();
|
||||
}
|
||||
}
|
||||
|
|
@ -224,6 +265,7 @@ impl FixedSizeEncoding for bool {
|
|||
|
||||
#[inline]
|
||||
fn write_to_bytes(self, b: &mut [u8; 1]) {
|
||||
debug_assert!(!self.is_default());
|
||||
b[0] = self as u8
|
||||
}
|
||||
}
|
||||
|
|
@ -242,9 +284,54 @@ impl<T> FixedSizeEncoding for Option<LazyValue<T>> {
|
|||
|
||||
#[inline]
|
||||
fn write_to_bytes(self, b: &mut [u8; 4]) {
|
||||
let position = self.map_or(0, |lazy| lazy.position.get());
|
||||
match self {
|
||||
None => unreachable!(),
|
||||
Some(lazy) => {
|
||||
let position = lazy.position.get();
|
||||
let position: u32 = position.try_into().unwrap();
|
||||
position.write_to_bytes(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> LazyArray<T> {
|
||||
#[inline]
|
||||
fn write_to_bytes_impl(self, b: &mut [u8; 8]) {
|
||||
let ([position_bytes, meta_bytes],[])= b.as_chunks_mut::<4>() else { panic!() };
|
||||
|
||||
let position = self.position.get();
|
||||
let position: u32 = position.try_into().unwrap();
|
||||
position.write_to_bytes(b)
|
||||
position.write_to_bytes(position_bytes);
|
||||
|
||||
let len = self.num_elems;
|
||||
let len: u32 = len.try_into().unwrap();
|
||||
len.write_to_bytes(meta_bytes);
|
||||
}
|
||||
|
||||
fn from_bytes_impl(position_bytes: &[u8; 4], meta_bytes: &[u8; 4]) -> Option<LazyArray<T>> {
|
||||
let position = NonZeroUsize::new(u32::from_bytes(position_bytes) as usize)?;
|
||||
let len = u32::from_bytes(meta_bytes) as usize;
|
||||
Some(LazyArray::from_position_and_num_elems(position, len))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FixedSizeEncoding for LazyArray<T> {
|
||||
type ByteArray = [u8; 8];
|
||||
|
||||
#[inline]
|
||||
fn from_bytes(b: &[u8; 8]) -> Self {
|
||||
let ([position_bytes, meta_bytes],[])= b.as_chunks::<4>() else { panic!() };
|
||||
if *meta_bytes == [0; 4] {
|
||||
return Default::default();
|
||||
}
|
||||
LazyArray::from_bytes_impl(position_bytes, meta_bytes).unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_to_bytes(self, b: &mut [u8; 8]) {
|
||||
assert!(!self.is_default());
|
||||
self.write_to_bytes_impl(b)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -253,23 +340,16 @@ impl<T> FixedSizeEncoding for Option<LazyArray<T>> {
|
|||
|
||||
#[inline]
|
||||
fn from_bytes(b: &[u8; 8]) -> Self {
|
||||
let ([ref position_bytes, ref meta_bytes],[])= b.as_chunks::<4>() else { panic!() };
|
||||
let position = NonZeroUsize::new(u32::from_bytes(position_bytes) as usize)?;
|
||||
let len = u32::from_bytes(meta_bytes) as usize;
|
||||
Some(LazyArray::from_position_and_num_elems(position, len))
|
||||
let ([position_bytes, meta_bytes],[])= b.as_chunks::<4>() else { panic!() };
|
||||
LazyArray::from_bytes_impl(position_bytes, meta_bytes)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_to_bytes(self, b: &mut [u8; 8]) {
|
||||
let ([ref mut position_bytes, ref mut meta_bytes],[])= b.as_chunks_mut::<4>() else { panic!() };
|
||||
|
||||
let position = self.map_or(0, |lazy| lazy.position.get());
|
||||
let position: u32 = position.try_into().unwrap();
|
||||
position.write_to_bytes(position_bytes);
|
||||
|
||||
let len = self.map_or(0, |lazy| lazy.num_elems);
|
||||
let len: u32 = len.try_into().unwrap();
|
||||
len.write_to_bytes(meta_bytes);
|
||||
match self {
|
||||
None => unreachable!(),
|
||||
Some(lazy) => lazy.write_to_bytes_impl(b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -289,20 +369,27 @@ impl<I: Idx, const N: usize, T> TableBuilder<I, Option<T>>
|
|||
where
|
||||
Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
|
||||
{
|
||||
pub(crate) fn set(&mut self, i: I, value: T) {
|
||||
self.set_nullable(i, Some(value))
|
||||
pub(crate) fn set_some(&mut self, i: I, value: T) {
|
||||
self.set(i, Some(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]>> TableBuilder<I, T> {
|
||||
pub(crate) fn set_nullable(&mut self, i: I, value: T) {
|
||||
// FIXME(eddyb) investigate more compact encodings for sparse tables.
|
||||
// On the PR @michaelwoerister mentioned:
|
||||
// > Space requirements could perhaps be optimized by using the HAMT `popcnt`
|
||||
// > trick (i.e. divide things into buckets of 32 or 64 items and then
|
||||
// > store bit-masks of which item in each bucket is actually serialized).
|
||||
self.blocks.ensure_contains_elem(i, || [0; N]);
|
||||
value.write_to_bytes(&mut self.blocks[i]);
|
||||
/// Sets the table value if it is not default.
|
||||
/// ATTENTION: For optimization default values are simply ignored by this function, because
|
||||
/// right now metadata tables never need to reset non-default values to default. If such need
|
||||
/// arises in the future then a new method (e.g. `clear` or `reset`) will need to be introduced
|
||||
/// for doing that explicitly.
|
||||
pub(crate) fn set(&mut self, i: I, value: T) {
|
||||
if !value.is_default() {
|
||||
// FIXME(eddyb) investigate more compact encodings for sparse tables.
|
||||
// On the PR @michaelwoerister mentioned:
|
||||
// > Space requirements could perhaps be optimized by using the HAMT `popcnt`
|
||||
// > trick (i.e. divide things into buckets of 32 or 64 items and then
|
||||
// > store bit-masks of which item in each bucket is actually serialized).
|
||||
self.blocks.ensure_contains_elem(i, || [0; N]);
|
||||
value.write_to_bytes(&mut self.blocks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn encode(&self, buf: &mut FileEncoder) -> LazyTable<I, T> {
|
||||
|
|
@ -331,10 +418,7 @@ where
|
|||
let start = self.position.get();
|
||||
let bytes = &metadata.blob()[start..start + self.encoded_size];
|
||||
let (bytes, []) = bytes.as_chunks::<N>() else { panic!() };
|
||||
match bytes.get(i.index()) {
|
||||
Some(bytes) => FixedSizeEncoding::from_bytes(bytes),
|
||||
None => FixedSizeEncoding::from_bytes(&[0; N]),
|
||||
}
|
||||
bytes.get(i.index()).map_or_else(Default::default, FixedSizeEncoding::from_bytes)
|
||||
}
|
||||
|
||||
/// Size of the table in entries, including possible gaps.
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ macro_rules! arena_types {
|
|||
|
||||
[decode] trait_impl_trait_tys: rustc_data_structures::fx::FxHashMap<rustc_hir::def_id::DefId, rustc_middle::ty::Ty<'tcx>>,
|
||||
[] bit_set_u32: rustc_index::bit_set::BitSet<u32>,
|
||||
[] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData<'tcx>,
|
||||
]);
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2098,10 +2098,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
|||
AggregateKind::Closure(def_id, substs) => ty::tls::with(|tcx| {
|
||||
let name = if tcx.sess.opts.unstable_opts.span_free_formats {
|
||||
let substs = tcx.lift(substs).unwrap();
|
||||
format!(
|
||||
"[closure@{}]",
|
||||
tcx.def_path_str_with_substs(def_id.to_def_id(), substs),
|
||||
)
|
||||
format!("[closure@{}]", tcx.def_path_str_with_substs(def_id, substs),)
|
||||
} else {
|
||||
let span = tcx.def_span(def_id);
|
||||
format!(
|
||||
|
|
@ -2112,11 +2109,17 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
|||
let mut struct_fmt = fmt.debug_struct(&name);
|
||||
|
||||
// FIXME(project-rfc-2229#48): This should be a list of capture names/places
|
||||
if let Some(upvars) = tcx.upvars_mentioned(def_id) {
|
||||
if let Some(def_id) = def_id.as_local()
|
||||
&& let Some(upvars) = tcx.upvars_mentioned(def_id)
|
||||
{
|
||||
for (&var_id, place) in iter::zip(upvars.keys(), places) {
|
||||
let var_name = tcx.hir().name(var_id);
|
||||
struct_fmt.field(var_name.as_str(), place);
|
||||
}
|
||||
} else {
|
||||
for (index, place) in places.iter().enumerate() {
|
||||
struct_fmt.field(&format!("{index}"), place);
|
||||
}
|
||||
}
|
||||
|
||||
struct_fmt.finish()
|
||||
|
|
@ -2127,11 +2130,17 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
|||
let mut struct_fmt = fmt.debug_struct(&name);
|
||||
|
||||
// FIXME(project-rfc-2229#48): This should be a list of capture names/places
|
||||
if let Some(upvars) = tcx.upvars_mentioned(def_id) {
|
||||
if let Some(def_id) = def_id.as_local()
|
||||
&& let Some(upvars) = tcx.upvars_mentioned(def_id)
|
||||
{
|
||||
for (&var_id, place) in iter::zip(upvars.keys(), places) {
|
||||
let var_name = tcx.hir().name(var_id);
|
||||
struct_fmt.field(var_name.as_str(), place);
|
||||
}
|
||||
} else {
|
||||
for (index, place) in places.iter().enumerate() {
|
||||
struct_fmt.field(&format!("{index}"), place);
|
||||
}
|
||||
}
|
||||
|
||||
struct_fmt.finish()
|
||||
|
|
|
|||
|
|
@ -1203,10 +1203,8 @@ pub enum AggregateKind<'tcx> {
|
|||
/// active field index would identity the field `c`
|
||||
Adt(DefId, VariantIdx, SubstsRef<'tcx>, Option<UserTypeAnnotationIndex>, Option<usize>),
|
||||
|
||||
// Note: We can use LocalDefId since closures and generators a deaggregated
|
||||
// before codegen.
|
||||
Closure(LocalDefId, SubstsRef<'tcx>),
|
||||
Generator(LocalDefId, SubstsRef<'tcx>, hir::Movability),
|
||||
Closure(DefId, SubstsRef<'tcx>),
|
||||
Generator(DefId, SubstsRef<'tcx>, hir::Movability),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
|
||||
|
|
|
|||
|
|
@ -205,9 +205,9 @@ impl<'tcx> Rvalue<'tcx> {
|
|||
AggregateKind::Adt(did, _, substs, _, _) => {
|
||||
tcx.bound_type_of(did).subst(tcx, substs)
|
||||
}
|
||||
AggregateKind::Closure(did, substs) => tcx.mk_closure(did.to_def_id(), substs),
|
||||
AggregateKind::Closure(did, substs) => tcx.mk_closure(did, substs),
|
||||
AggregateKind::Generator(did, substs, movability) => {
|
||||
tcx.mk_generator(did.to_def_id(), substs, movability)
|
||||
tcx.mk_generator(did, substs, movability)
|
||||
}
|
||||
},
|
||||
Rvalue::ShallowInitBox(_, ty) => tcx.mk_box(ty),
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
mod chalk;
|
||||
pub mod query;
|
||||
pub mod select;
|
||||
pub mod solve;
|
||||
pub mod specialization_graph;
|
||||
mod structural_impls;
|
||||
pub mod util;
|
||||
|
|
@ -474,6 +475,8 @@ pub enum WellFormedLoc {
|
|||
pub struct ImplDerivedObligationCause<'tcx> {
|
||||
pub derived: DerivedObligationCause<'tcx>,
|
||||
pub impl_def_id: DefId,
|
||||
/// The index of the derived predicate in the parent impl's predicates.
|
||||
pub impl_def_predicate_index: Option<usize>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
|
|
|
|||
55
compiler/rustc_middle/src/traits/solve.rs
Normal file
55
compiler/rustc_middle/src/traits/solve.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
use rustc_data_structures::intern::Interned;
|
||||
|
||||
use crate::ty::{FallibleTypeFolder, Ty, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
|
||||
pub struct ExternalConstraints<'tcx>(pub(crate) Interned<'tcx, ExternalConstraintsData<'tcx>>);
|
||||
|
||||
impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> {
|
||||
type Target = ExternalConstraintsData<'tcx>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional constraints returned on success.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
|
||||
pub struct ExternalConstraintsData<'tcx> {
|
||||
// FIXME: implement this.
|
||||
pub regions: (),
|
||||
pub opaque_types: Vec<(Ty<'tcx>, Ty<'tcx>)>,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFoldable<'tcx> for ExternalConstraints<'tcx> {
|
||||
fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
Ok(FallibleTypeFolder::tcx(folder).intern_external_constraints(ExternalConstraintsData {
|
||||
regions: (),
|
||||
opaque_types: self
|
||||
.opaque_types
|
||||
.iter()
|
||||
.map(|opaque| opaque.try_fold_with(folder))
|
||||
.collect::<Result<_, F::Error>>()?,
|
||||
}))
|
||||
}
|
||||
|
||||
fn fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self {
|
||||
TypeFolder::tcx(folder).intern_external_constraints(ExternalConstraintsData {
|
||||
regions: (),
|
||||
opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeVisitable<'tcx> for ExternalConstraints<'tcx> {
|
||||
fn visit_with<V: TypeVisitor<'tcx>>(
|
||||
&self,
|
||||
visitor: &mut V,
|
||||
) -> std::ops::ControlFlow<V::BreakTy> {
|
||||
self.regions.visit_with(visitor)?;
|
||||
self.opaque_types.visit_with(visitor)?;
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ use crate::mir::{
|
|||
};
|
||||
use crate::thir::Thir;
|
||||
use crate::traits;
|
||||
use crate::traits::solve::{ExternalConstraints, ExternalConstraintsData};
|
||||
use crate::ty::query::{self, TyCtxtAt};
|
||||
use crate::ty::{
|
||||
self, AdtDef, AdtDefData, AdtKind, Binder, Const, ConstData, DefIdTree, FloatTy, FloatVar,
|
||||
|
|
@ -148,6 +149,7 @@ pub struct CtxtInterners<'tcx> {
|
|||
bound_variable_kinds: InternedSet<'tcx, List<ty::BoundVariableKind>>,
|
||||
layout: InternedSet<'tcx, LayoutS<VariantIdx>>,
|
||||
adt_def: InternedSet<'tcx, AdtDefData>,
|
||||
external_constraints: InternedSet<'tcx, ExternalConstraintsData<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> CtxtInterners<'tcx> {
|
||||
|
|
@ -169,6 +171,7 @@ impl<'tcx> CtxtInterners<'tcx> {
|
|||
bound_variable_kinds: Default::default(),
|
||||
layout: Default::default(),
|
||||
adt_def: Default::default(),
|
||||
external_constraints: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1449,6 +1452,7 @@ direct_interners! {
|
|||
const_allocation: intern_const_alloc(Allocation): ConstAllocation -> ConstAllocation<'tcx>,
|
||||
layout: intern_layout(LayoutS<VariantIdx>): Layout -> Layout<'tcx>,
|
||||
adt_def: intern_adt_def(AdtDefData): AdtDef -> AdtDef<'tcx>,
|
||||
external_constraints: intern_external_constraints(ExternalConstraintsData<'tcx>): ExternalConstraints -> ExternalConstraints<'tcx>,
|
||||
}
|
||||
|
||||
macro_rules! slice_interners {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
use crate::ty::{
|
||||
visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, InferConst, InferTy, Opaque,
|
||||
PolyTraitPredicate, Projection, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
|
||||
visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, FallibleTypeFolder, InferConst,
|
||||
InferTy, Opaque, PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
|
||||
TypeSuperVisitable, TypeVisitor,
|
||||
};
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
|
@ -76,7 +77,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait IsSuggestable<'tcx> {
|
||||
pub trait IsSuggestable<'tcx>: Sized {
|
||||
/// Whether this makes sense to suggest in a diagnostic.
|
||||
///
|
||||
/// We filter out certain types and constants since they don't provide
|
||||
|
|
@ -87,15 +88,21 @@ pub trait IsSuggestable<'tcx> {
|
|||
/// Only if `infer_suggestable` is true, we consider type and const
|
||||
/// inference variables to be suggestable.
|
||||
fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool;
|
||||
|
||||
fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option<Self>;
|
||||
}
|
||||
|
||||
impl<'tcx, T> IsSuggestable<'tcx> for T
|
||||
where
|
||||
T: TypeVisitable<'tcx>,
|
||||
T: TypeVisitable<'tcx> + TypeFoldable<'tcx>,
|
||||
{
|
||||
fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool {
|
||||
self.visit_with(&mut IsSuggestableVisitor { tcx, infer_suggestable }).is_continue()
|
||||
}
|
||||
|
||||
fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option<T> {
|
||||
self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable }).ok()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn suggest_arbitrary_trait_bound<'tcx>(
|
||||
|
|
@ -509,3 +516,83 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> {
|
|||
c.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MakeSuggestableFolder<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
infer_suggestable: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> FallibleTypeFolder<'tcx> for MakeSuggestableFolder<'tcx> {
|
||||
type Error = ();
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
|
||||
let t = match *t.kind() {
|
||||
Infer(InferTy::TyVar(_)) if self.infer_suggestable => t,
|
||||
|
||||
FnDef(def_id, substs) => {
|
||||
self.tcx.mk_fn_ptr(self.tcx.fn_sig(def_id).subst(self.tcx, substs))
|
||||
}
|
||||
|
||||
// FIXME(compiler-errors): We could replace these with infer, I guess.
|
||||
Closure(..)
|
||||
| Infer(..)
|
||||
| Generator(..)
|
||||
| GeneratorWitness(..)
|
||||
| Bound(_, _)
|
||||
| Placeholder(_)
|
||||
| Error(_) => {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Alias(Opaque, AliasTy { def_id, .. }) => {
|
||||
let parent = self.tcx.parent(def_id);
|
||||
if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = self.tcx.def_kind(parent)
|
||||
&& let Alias(Opaque, AliasTy { def_id: parent_opaque_def_id, .. }) = *self.tcx.type_of(parent).kind()
|
||||
&& parent_opaque_def_id == def_id
|
||||
{
|
||||
t
|
||||
} else {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
Param(param) => {
|
||||
// FIXME: It would be nice to make this not use string manipulation,
|
||||
// but it's pretty hard to do this, since `ty::ParamTy` is missing
|
||||
// sufficient info to determine if it is synthetic, and we don't
|
||||
// always have a convenient way of getting `ty::Generics` at the call
|
||||
// sites we invoke `IsSuggestable::is_suggestable`.
|
||||
if param.name.as_str().starts_with("impl ") {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
t
|
||||
}
|
||||
|
||||
_ => t,
|
||||
};
|
||||
|
||||
t.try_super_fold_with(self)
|
||||
}
|
||||
|
||||
fn try_fold_const(&mut self, c: Const<'tcx>) -> Result<Const<'tcx>, ()> {
|
||||
let c = match c.kind() {
|
||||
ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => c,
|
||||
|
||||
ConstKind::Infer(..)
|
||||
| ConstKind::Bound(..)
|
||||
| ConstKind::Placeholder(..)
|
||||
| ConstKind::Error(..) => {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
_ => c,
|
||||
};
|
||||
|
||||
c.try_super_fold_with(self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ pub trait TypeSuperFoldable<'tcx>: TypeFoldable<'tcx> {
|
|||
/// the infallible methods of this trait to ensure that the two APIs
|
||||
/// are coherent.
|
||||
pub trait TypeFolder<'tcx>: FallibleTypeFolder<'tcx, Error = !> {
|
||||
fn tcx<'a>(&'a self) -> TyCtxt<'tcx>;
|
||||
fn tcx(&self) -> TyCtxt<'tcx>;
|
||||
|
||||
fn fold_binder<T>(&mut self, t: Binder<'tcx, T>) -> Binder<'tcx, T>
|
||||
where
|
||||
|
|
|
|||
|
|
@ -818,125 +818,114 @@ where
|
|||
let tcx = cx.tcx();
|
||||
let param_env = cx.param_env();
|
||||
|
||||
let pointee_info =
|
||||
match *this.ty.kind() {
|
||||
ty::RawPtr(mt) if offset.bytes() == 0 => {
|
||||
tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo {
|
||||
size: layout.size,
|
||||
align: layout.align.abi,
|
||||
safe: None,
|
||||
})
|
||||
let pointee_info = match *this.ty.kind() {
|
||||
ty::RawPtr(mt) if offset.bytes() == 0 => {
|
||||
tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo {
|
||||
size: layout.size,
|
||||
align: layout.align.abi,
|
||||
safe: None,
|
||||
})
|
||||
}
|
||||
ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
|
||||
tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| PointeeInfo {
|
||||
size: layout.size,
|
||||
align: layout.align.abi,
|
||||
safe: None,
|
||||
})
|
||||
}
|
||||
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
|
||||
// Use conservative pointer kind if not optimizing. This saves us the
|
||||
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
|
||||
// attributes in LLVM have compile-time cost even in unoptimized builds).
|
||||
let optimize = tcx.sess.opts.optimize != OptLevel::No;
|
||||
let kind = match mt {
|
||||
hir::Mutability::Not => PointerKind::SharedRef {
|
||||
frozen: optimize && ty.is_freeze(tcx, cx.param_env()),
|
||||
},
|
||||
hir::Mutability::Mut => PointerKind::MutableRef {
|
||||
unpin: optimize && ty.is_unpin(tcx, cx.param_env()),
|
||||
},
|
||||
};
|
||||
|
||||
tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
|
||||
size: layout.size,
|
||||
align: layout.align.abi,
|
||||
safe: Some(kind),
|
||||
})
|
||||
}
|
||||
|
||||
_ => {
|
||||
let mut data_variant = match this.variants {
|
||||
// Within the discriminant field, only the niche itself is
|
||||
// always initialized, so we only check for a pointer at its
|
||||
// offset.
|
||||
//
|
||||
// If the niche is a pointer, it's either valid (according
|
||||
// to its type), or null (which the niche field's scalar
|
||||
// validity range encodes). This allows using
|
||||
// `dereferenceable_or_null` for e.g., `Option<&T>`, and
|
||||
// this will continue to work as long as we don't start
|
||||
// using more niches than just null (e.g., the first page of
|
||||
// the address space, or unaligned pointers).
|
||||
Variants::Multiple {
|
||||
tag_encoding: TagEncoding::Niche { untagged_variant, .. },
|
||||
tag_field,
|
||||
..
|
||||
} if this.fields.offset(tag_field) == offset => {
|
||||
Some(this.for_variant(cx, untagged_variant))
|
||||
}
|
||||
_ => Some(this),
|
||||
};
|
||||
|
||||
if let Some(variant) = data_variant {
|
||||
// We're not interested in any unions.
|
||||
if let FieldsShape::Union(_) = variant.fields {
|
||||
data_variant = None;
|
||||
}
|
||||
}
|
||||
ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
|
||||
tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| {
|
||||
PointeeInfo { size: layout.size, align: layout.align.abi, safe: None }
|
||||
})
|
||||
}
|
||||
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
|
||||
let kind = if tcx.sess.opts.optimize == OptLevel::No {
|
||||
// Use conservative pointer kind if not optimizing. This saves us the
|
||||
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
|
||||
// attributes in LLVM have compile-time cost even in unoptimized builds).
|
||||
PointerKind::SharedMutable
|
||||
} else {
|
||||
match mt {
|
||||
hir::Mutability::Not => {
|
||||
if ty.is_freeze(tcx, cx.param_env()) {
|
||||
PointerKind::Frozen
|
||||
|
||||
let mut result = None;
|
||||
|
||||
if let Some(variant) = data_variant {
|
||||
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
|
||||
// (requires passing in the expected address space from the caller)
|
||||
let ptr_end = offset + Pointer(AddressSpace::DATA).size(cx);
|
||||
for i in 0..variant.fields.count() {
|
||||
let field_start = variant.fields.offset(i);
|
||||
if field_start <= offset {
|
||||
let field = variant.field(cx, i);
|
||||
result = field.to_result().ok().and_then(|field| {
|
||||
if ptr_end <= field_start + field.size {
|
||||
// We found the right field, look inside it.
|
||||
let field_info =
|
||||
field.pointee_info_at(cx, offset - field_start);
|
||||
field_info
|
||||
} else {
|
||||
PointerKind::SharedMutable
|
||||
}
|
||||
}
|
||||
hir::Mutability::Mut => {
|
||||
// References to self-referential structures should not be considered
|
||||
// noalias, as another pointer to the structure can be obtained, that
|
||||
// is not based-on the original reference. We consider all !Unpin
|
||||
// types to be potentially self-referential here.
|
||||
if ty.is_unpin(tcx, cx.param_env()) {
|
||||
PointerKind::UniqueBorrowed
|
||||
} else {
|
||||
PointerKind::UniqueBorrowedPinned
|
||||
None
|
||||
}
|
||||
});
|
||||
if result.is_some() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
|
||||
size: layout.size,
|
||||
align: layout.align.abi,
|
||||
safe: Some(kind),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
let mut data_variant = match this.variants {
|
||||
// Within the discriminant field, only the niche itself is
|
||||
// always initialized, so we only check for a pointer at its
|
||||
// offset.
|
||||
//
|
||||
// If the niche is a pointer, it's either valid (according
|
||||
// to its type), or null (which the niche field's scalar
|
||||
// validity range encodes). This allows using
|
||||
// `dereferenceable_or_null` for e.g., `Option<&T>`, and
|
||||
// this will continue to work as long as we don't start
|
||||
// using more niches than just null (e.g., the first page of
|
||||
// the address space, or unaligned pointers).
|
||||
Variants::Multiple {
|
||||
tag_encoding: TagEncoding::Niche { untagged_variant, .. },
|
||||
tag_field,
|
||||
..
|
||||
} if this.fields.offset(tag_field) == offset => {
|
||||
Some(this.for_variant(cx, untagged_variant))
|
||||
}
|
||||
_ => Some(this),
|
||||
};
|
||||
|
||||
if let Some(variant) = data_variant {
|
||||
// We're not interested in any unions.
|
||||
if let FieldsShape::Union(_) = variant.fields {
|
||||
data_variant = None;
|
||||
// FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
|
||||
if let Some(ref mut pointee) = result {
|
||||
if let ty::Adt(def, _) = this.ty.kind() {
|
||||
if def.is_box() && offset.bytes() == 0 {
|
||||
let optimize = tcx.sess.opts.optimize != OptLevel::No;
|
||||
pointee.safe = Some(PointerKind::Box {
|
||||
unpin: optimize && this.ty.boxed_ty().is_unpin(tcx, cx.param_env()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = None;
|
||||
|
||||
if let Some(variant) = data_variant {
|
||||
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
|
||||
// (requires passing in the expected address space from the caller)
|
||||
let ptr_end = offset + Pointer(AddressSpace::DATA).size(cx);
|
||||
for i in 0..variant.fields.count() {
|
||||
let field_start = variant.fields.offset(i);
|
||||
if field_start <= offset {
|
||||
let field = variant.field(cx, i);
|
||||
result = field.to_result().ok().and_then(|field| {
|
||||
if ptr_end <= field_start + field.size {
|
||||
// We found the right field, look inside it.
|
||||
let field_info =
|
||||
field.pointee_info_at(cx, offset - field_start);
|
||||
field_info
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if result.is_some() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
|
||||
if let Some(ref mut pointee) = result {
|
||||
if let ty::Adt(def, _) = this.ty.kind() {
|
||||
if def.is_box() && offset.bytes() == 0 {
|
||||
pointee.safe = Some(PointerKind::UniqueOwned);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
};
|
||||
|
||||
result
|
||||
}
|
||||
};
|
||||
|
||||
debug!(
|
||||
"pointee_info_at (offset={:?}, type kind: {:?}) => {:?}",
|
||||
|
|
|
|||
|
|
@ -675,8 +675,12 @@ pub trait PrettyPrinter<'tcx>:
|
|||
p!(")")
|
||||
}
|
||||
ty::FnDef(def_id, substs) => {
|
||||
let sig = self.tcx().fn_sig(def_id).subst(self.tcx(), substs);
|
||||
p!(print(sig), " {{", print_value_path(def_id, substs), "}}");
|
||||
if NO_QUERIES.with(|q| q.get()) {
|
||||
p!(print_def_path(def_id, substs));
|
||||
} else {
|
||||
let sig = self.tcx().fn_sig(def_id).subst(self.tcx(), substs);
|
||||
p!(print(sig), " {{", print_value_path(def_id, substs), "}}");
|
||||
}
|
||||
}
|
||||
ty::FnPtr(ref bare_fn) => p!(print(bare_fn)),
|
||||
ty::Infer(infer_ty) => {
|
||||
|
|
@ -734,13 +738,13 @@ pub trait PrettyPrinter<'tcx>:
|
|||
}
|
||||
ty::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)),
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
|
||||
// FIXME(eddyb) print this with `print_def_path`.
|
||||
// We use verbose printing in 'NO_QUERIES' mode, to
|
||||
// avoid needing to call `predicates_of`. This should
|
||||
// only affect certain debug messages (e.g. messages printed
|
||||
// from `rustc_middle::ty` during the computation of `tcx.predicates_of`),
|
||||
// and should have no effect on any compiler output.
|
||||
if self.should_print_verbose() || NO_QUERIES.with(|q| q.get()) {
|
||||
if self.should_print_verbose() {
|
||||
// FIXME(eddyb) print this with `print_def_path`.
|
||||
p!(write("Opaque({:?}, {:?})", def_id, substs));
|
||||
return Ok(self);
|
||||
}
|
||||
|
|
@ -748,6 +752,8 @@ pub trait PrettyPrinter<'tcx>:
|
|||
let parent = self.tcx().parent(def_id);
|
||||
match self.tcx().def_kind(parent) {
|
||||
DefKind::TyAlias | DefKind::AssocTy => {
|
||||
// NOTE: I know we should check for NO_QUERIES here, but it's alright.
|
||||
// `type_of` on a type alias or assoc type should never cause a cycle.
|
||||
if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: d, .. }) =
|
||||
*self.tcx().type_of(parent).kind()
|
||||
{
|
||||
|
|
@ -762,7 +768,14 @@ pub trait PrettyPrinter<'tcx>:
|
|||
p!(print_def_path(def_id, substs));
|
||||
return Ok(self);
|
||||
}
|
||||
_ => return self.pretty_print_opaque_impl_type(def_id, substs),
|
||||
_ => {
|
||||
if NO_QUERIES.with(|q| q.get()) {
|
||||
p!(print_def_path(def_id, &[]));
|
||||
return Ok(self);
|
||||
} else {
|
||||
return self.pretty_print_opaque_impl_type(def_id, substs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ty::Str => p!("str"),
|
||||
|
|
|
|||
|
|
@ -106,16 +106,6 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Helper for `TyCtxtEnsure` to avoid a closure.
|
||||
#[inline(always)]
|
||||
fn noop<T>(_: &T) {}
|
||||
|
||||
/// Helper to ensure that queries only return `Copy` types.
|
||||
#[inline(always)]
|
||||
fn copy<T: Copy>(x: &T) -> T {
|
||||
*x
|
||||
}
|
||||
|
||||
macro_rules! query_helper_param_ty {
|
||||
(DefId) => { impl IntoQueryParam<DefId> };
|
||||
(LocalDefId) => { impl IntoQueryParam<LocalDefId> };
|
||||
|
|
@ -225,14 +215,10 @@ macro_rules! define_callbacks {
|
|||
let key = key.into_query_param();
|
||||
opt_remap_env_constness!([$($modifiers)*][key]);
|
||||
|
||||
let cached = try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key, noop);
|
||||
|
||||
match cached {
|
||||
Ok(()) => return,
|
||||
Err(()) => (),
|
||||
}
|
||||
|
||||
self.tcx.queries.$name(self.tcx, DUMMY_SP, key, QueryMode::Ensure);
|
||||
match try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key) {
|
||||
Some(_) => return,
|
||||
None => self.tcx.queries.$name(self.tcx, DUMMY_SP, key, QueryMode::Ensure),
|
||||
};
|
||||
})*
|
||||
}
|
||||
|
||||
|
|
@ -254,14 +240,10 @@ macro_rules! define_callbacks {
|
|||
let key = key.into_query_param();
|
||||
opt_remap_env_constness!([$($modifiers)*][key]);
|
||||
|
||||
let cached = try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key, copy);
|
||||
|
||||
match cached {
|
||||
Ok(value) => return value,
|
||||
Err(()) => (),
|
||||
match try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key) {
|
||||
Some(value) => value,
|
||||
None => self.tcx.queries.$name(self.tcx, self.span, key, QueryMode::Get).unwrap(),
|
||||
}
|
||||
|
||||
self.tcx.queries.$name(self.tcx, self.span, key, QueryMode::Get).unwrap()
|
||||
})*
|
||||
}
|
||||
|
||||
|
|
@ -353,27 +335,25 @@ macro_rules! define_feedable {
|
|||
let tcx = self.tcx;
|
||||
let cache = &tcx.query_caches.$name;
|
||||
|
||||
let cached = try_get_cached(tcx, cache, &key, copy);
|
||||
|
||||
match cached {
|
||||
Ok(old) => {
|
||||
match try_get_cached(tcx, cache, &key) {
|
||||
Some(old) => {
|
||||
bug!(
|
||||
"Trying to feed an already recorded value for query {} key={key:?}:\nold value: {old:?}\nnew value: {value:?}",
|
||||
stringify!($name),
|
||||
);
|
||||
)
|
||||
}
|
||||
None => {
|
||||
let dep_node = dep_graph::DepNode::construct(tcx, dep_graph::DepKind::$name, &key);
|
||||
let dep_node_index = tcx.dep_graph.with_feed_task(
|
||||
dep_node,
|
||||
tcx,
|
||||
key,
|
||||
&value,
|
||||
hash_result!([$($modifiers)*]),
|
||||
);
|
||||
cache.complete(key, value, dep_node_index)
|
||||
}
|
||||
Err(()) => (),
|
||||
}
|
||||
|
||||
let dep_node = dep_graph::DepNode::construct(tcx, dep_graph::DepKind::$name, &key);
|
||||
let dep_node_index = tcx.dep_graph.with_feed_task(
|
||||
dep_node,
|
||||
tcx,
|
||||
key,
|
||||
&value,
|
||||
hash_result!([$($modifiers)*]),
|
||||
);
|
||||
cache.complete(key, value, dep_node_index)
|
||||
}
|
||||
})*
|
||||
}
|
||||
|
|
|
|||
|
|
@ -629,6 +629,8 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
|
|||
b = tcx.expand_abstract_consts(b);
|
||||
}
|
||||
|
||||
debug!("{}.super_relate_consts(normed_a = {:?}, normed_b = {:?})", relation.tag(), a, b);
|
||||
|
||||
// Currently, the values that can be unified are primitive types,
|
||||
// and those that derive both `PartialEq` and `Eq`, corresponding
|
||||
// to structural-match types.
|
||||
|
|
@ -665,30 +667,28 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
|
|||
|
||||
// FIXME(generic_const_exprs): is it possible to relate two consts which are not identical
|
||||
// exprs? Should we care about that?
|
||||
// FIXME(generic_const_exprs): relating the `ty()`s is a little weird since it is supposed to
|
||||
// ICE If they mismatch. Unfortunately `ConstKind::Expr` is a little special and can be thought
|
||||
// of as being generic over the argument types, however this is implicit so these types don't get
|
||||
// related when we relate the substs of the item this const arg is for.
|
||||
let expr = match (ae, be) {
|
||||
(Expr::Binop(a_op, al, ar), Expr::Binop(b_op, bl, br))
|
||||
if a_op == b_op && al.ty() == bl.ty() && ar.ty() == br.ty() =>
|
||||
{
|
||||
(Expr::Binop(a_op, al, ar), Expr::Binop(b_op, bl, br)) if a_op == b_op => {
|
||||
r.relate(al.ty(), bl.ty())?;
|
||||
r.relate(ar.ty(), br.ty())?;
|
||||
Expr::Binop(a_op, r.consts(al, bl)?, r.consts(ar, br)?)
|
||||
}
|
||||
(Expr::UnOp(a_op, av), Expr::UnOp(b_op, bv))
|
||||
if a_op == b_op && av.ty() == bv.ty() =>
|
||||
{
|
||||
(Expr::UnOp(a_op, av), Expr::UnOp(b_op, bv)) if a_op == b_op => {
|
||||
r.relate(av.ty(), bv.ty())?;
|
||||
Expr::UnOp(a_op, r.consts(av, bv)?)
|
||||
}
|
||||
(Expr::Cast(ak, av, at), Expr::Cast(bk, bv, bt))
|
||||
if ak == bk && av.ty() == bv.ty() =>
|
||||
{
|
||||
(Expr::Cast(ak, av, at), Expr::Cast(bk, bv, bt)) if ak == bk => {
|
||||
r.relate(av.ty(), bv.ty())?;
|
||||
Expr::Cast(ak, r.consts(av, bv)?, r.tys(at, bt)?)
|
||||
}
|
||||
(Expr::FunctionCall(af, aa), Expr::FunctionCall(bf, ba))
|
||||
if aa.len() == ba.len()
|
||||
&& af.ty() == bf.ty()
|
||||
&& aa
|
||||
.iter()
|
||||
.zip(ba.iter())
|
||||
.all(|(a_arg, b_arg)| a_arg.ty() == b_arg.ty()) =>
|
||||
if aa.len() == ba.len() =>
|
||||
{
|
||||
r.relate(af.ty(), bf.ty())?;
|
||||
let func = r.consts(af, bf)?;
|
||||
let mut related_args = Vec::with_capacity(aa.len());
|
||||
for (a_arg, b_arg) in aa.iter().zip(ba.iter()) {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue