Auto merge of #2780 - RalfJung:rustup, r=RalfJung

Rustup
This commit is contained in:
bors 2023-02-07 09:34:17 +00:00
commit 0d225bcf1e
440 changed files with 8460 additions and 5513 deletions

View file

@ -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"

View file

@ -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>

View file

@ -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",

View file

@ -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']

View file

@ -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.

View file

@ -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(..))

View file

@ -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, .. }, _)) => {

View file

@ -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)]

View file

@ -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);

View file

@ -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" }

View file

@ -7,5 +7,4 @@ edition = "2021"
[dependencies]
rustc_ast = { path = "../rustc_ast" }
rustc_parse_format = { path = "../rustc_parse_format" }
rustc_span = { path = "../rustc_span" }

View file

@ -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);

View file

@ -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={:?} \

View file

@ -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={:?}",

View file

@ -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 => {}
}

View file

@ -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());
}
}
_ => {}

View file

@ -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);

View file

@ -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 => {

View file

@ -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);
}

View file

@ -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"] }

View file

@ -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"

View file

@ -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
}),
) {

View file

@ -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(());

View file

@ -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() {

View file

@ -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)]

View file

@ -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);
}
_ => {

View file

@ -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" }

View 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)
}
})
}
}

View file

@ -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;

View file

@ -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.

View file

@ -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(

View file

@ -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, _) => {

View file

@ -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));
}

View file

@ -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 })
}
}

View file

@ -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(

View file

@ -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)
}

View file

@ -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};

View file

@ -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 {

View file

@ -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

View 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']

File diff suppressed because it is too large Load diff

View file

@ -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;

View file

@ -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

View file

@ -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}

View file

@ -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

View file

@ -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`

View file

@ -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

View file

@ -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 `,`

View file

@ -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)

View file

@ -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;

View file

@ -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.

View file

@ -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" }

View file

@ -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
}

View file

@ -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);
}
}
}

View file

@ -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);

View file

@ -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(

View file

@ -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);

View file

@ -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,
}
}

View file

@ -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,
}
}
}

View file

@ -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
}

View file

@ -1,4 +1,5 @@
mod _impl;
mod adjust_fulfillment_errors;
mod arg_matrix;
mod checks;
mod suggestions;

View file

@ -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,

View file

@ -1563,6 +1563,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
traits::ImplDerivedObligationCause {
derived,
impl_def_id,
impl_def_predicate_index: None,
span,
},
))

View file

@ -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
}

View file

@ -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"] }

View file

@ -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
}
}
}
}

View file

@ -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

View file

@ -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)),
)]);
}
}

View file

@ -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)
}
}

View file

@ -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.

View file

@ -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" }

View file

@ -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();
}
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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);

View file

@ -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))
}

View file

@ -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 });
}
}
}

View file

@ -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 });
}
}
};

View file

@ -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]

View file

@ -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 }

View file

@ -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));
}
}
}

View file

@ -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>>,

View file

@ -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.

View file

@ -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>,
]);
)
}

View file

@ -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()

View file

@ -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)]

View file

@ -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),

View file

@ -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,
}

View 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(())
}
}

View file

@ -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 {

View file

@ -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)
}
}

View file

@ -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

View file

@ -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: {:?}) => {:?}",

View file

@ -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"),

View file

@ -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)
}
})*
}

View file

@ -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