Merge from rustc
This commit is contained in:
commit
0ca3921747
915 changed files with 20929 additions and 6839 deletions
97
Cargo.lock
97
Cargo.lock
|
|
@ -195,7 +195,7 @@ version = "0.4.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01667f6f40216b9a0b2945e05fed5f1ad0ab6470e69cb9378001e37b1c0668e4"
|
||||
dependencies = [
|
||||
"object 0.36.5",
|
||||
"object 0.36.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -405,9 +405,9 @@ version = "0.1.0"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.0"
|
||||
version = "1.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8"
|
||||
checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
|
@ -503,9 +503,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.5.39"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd4db298d517d5fa00b2b84bbe044efd3fde43874a41db0d46f91994646a2da4"
|
||||
checksum = "ac2e663e3e3bed2d32d065a8404024dad306e699a04263ec59919529f803aee9"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
|
@ -1107,9 +1107,9 @@ checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
|||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
|
||||
checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"regex",
|
||||
|
|
@ -1117,9 +1117,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.11.5"
|
||||
version = "0.11.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
|
||||
checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
|
@ -1153,9 +1153,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "expect-test"
|
||||
version = "1.5.0"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e0be0a561335815e06dab7c62e50353134c796e7a6155402a64bcff66b6a5e0"
|
||||
checksum = "63af43ff4431e848fb47472a920f14fa71c24de13255a5692e93d4e90302acb0"
|
||||
dependencies = [
|
||||
"dissimilar",
|
||||
"once_cell",
|
||||
|
|
@ -1212,7 +1212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide 0.8.1",
|
||||
"miniz_oxide 0.8.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1257,9 +1257,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
|||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
|
||||
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
|
|
@ -2305,9 +2305,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2ef2593ffb6958c941575cee70c8e257438749971869c4ae5acf6f91a168a61"
|
||||
checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
|
@ -2526,9 +2526,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.5"
|
||||
version = "0.36.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
|
||||
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"flate2",
|
||||
|
|
@ -2536,7 +2536,7 @@ dependencies = [
|
|||
"indexmap",
|
||||
"memchr",
|
||||
"ruzstd",
|
||||
"wasmparser 0.218.0",
|
||||
"wasmparser 0.222.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2706,7 +2706,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.9",
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
|
|
@ -3169,7 +3169,7 @@ dependencies = [
|
|||
"build_helper",
|
||||
"gimli 0.31.1",
|
||||
"libc",
|
||||
"object 0.36.5",
|
||||
"object 0.36.7",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"similar",
|
||||
|
|
@ -3488,7 +3488,7 @@ dependencies = [
|
|||
"itertools",
|
||||
"libc",
|
||||
"measureme",
|
||||
"object 0.36.5",
|
||||
"object 0.36.7",
|
||||
"rustc-demangle",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
|
|
@ -3527,7 +3527,7 @@ dependencies = [
|
|||
"either",
|
||||
"itertools",
|
||||
"libc",
|
||||
"object 0.36.5",
|
||||
"object 0.36.7",
|
||||
"pathdiff",
|
||||
"regex",
|
||||
"rustc_abi",
|
||||
|
|
@ -4529,7 +4529,7 @@ name = "rustc_target"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"object 0.36.5",
|
||||
"object 0.36.7",
|
||||
"rustc_abi",
|
||||
"rustc_data_structures",
|
||||
"rustc_fs_util",
|
||||
|
|
@ -4882,9 +4882,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.133"
|
||||
version = "1.0.134"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||
checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
|
|
@ -5279,11 +5279,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.7"
|
||||
version = "2.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767"
|
||||
checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.7",
|
||||
"thiserror-impl 2.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5299,9 +5299,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.7"
|
||||
version = "2.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36"
|
||||
checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -5316,7 +5316,7 @@ checksum = "813ba76597db32dc4f6992fd8bf8f394715b88d352fd97401da67dab6283b4c6"
|
|||
dependencies = [
|
||||
"gimli 0.30.0",
|
||||
"hashbrown 0.14.5",
|
||||
"object 0.36.5",
|
||||
"object 0.36.7",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
|
@ -5413,9 +5413,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.8.0"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
|
||||
checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
|
@ -5955,12 +5955,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.221.2"
|
||||
version = "0.222.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c17a3bd88f2155da63a1f2fcb8a56377a24f0b6dfed12733bb5f544e86f690c5"
|
||||
checksum = "3432682105d7e994565ef928ccf5856cf6af4ba3dddebedb737f61caed70f956"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
"wasmparser 0.221.2",
|
||||
"wasmparser 0.222.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5979,15 +5979,6 @@ dependencies = [
|
|||
"wasmparser 0.219.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.218.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b09e46c7fceceaa72b2dd1a8a137ea7fd8f93dfaa69806010a709918e496c5dc"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.219.1"
|
||||
|
|
@ -6004,9 +5995,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.221.2"
|
||||
version = "0.222.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9845c470a2e10b61dd42c385839cdd6496363ed63b5c9e420b5488b77bd22083"
|
||||
checksum = "4adf50fde1b1a49c1add6a80d47aea500c88db70551805853aa8b88f3ea27ab5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
|
|
@ -6015,22 +6006,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wast"
|
||||
version = "221.0.2"
|
||||
version = "222.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcc4470b9de917ba199157d1f0ae104f2ae362be728c43e68c571c7715bd629e"
|
||||
checksum = "5ce7191f4b7da0dd300cc32476abae6457154e4625d9b1bc26890828a9a26f6e"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"leb128",
|
||||
"memchr",
|
||||
"unicode-width 0.2.0",
|
||||
"wasm-encoder 0.221.2",
|
||||
"wasm-encoder 0.222.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wat"
|
||||
version = "1.221.2"
|
||||
version = "1.222.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b1f3c6d82af47286494c6caea1d332037f5cbeeac82bbf5ef59cb8c201c466e"
|
||||
checksum = "8fde61b4b52f9a84ae31b5e8902a2cd3162ea45d8bf564c729c3288fe52f4334"
|
||||
dependencies = [
|
||||
"wast",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ use rustc_data_structures::sync::Lrc;
|
|||
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
|
||||
pub use rustc_span::AttrId;
|
||||
use rustc_span::source_map::{Spanned, respan};
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
|
||||
use thin_vec::{ThinVec, thin_vec};
|
||||
|
||||
pub use crate::format::*;
|
||||
|
|
@ -387,22 +387,15 @@ impl GenericParam {
|
|||
|
||||
/// Represents lifetime, type and const parameters attached to a declaration of
|
||||
/// a function, enum, trait, etc.
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Default)]
|
||||
pub struct Generics {
|
||||
pub params: ThinVec<GenericParam>,
|
||||
pub where_clause: WhereClause,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl Default for Generics {
|
||||
/// Creates an instance of `Generics`.
|
||||
fn default() -> Generics {
|
||||
Generics { params: ThinVec::new(), where_clause: Default::default(), span: DUMMY_SP }
|
||||
}
|
||||
}
|
||||
|
||||
/// A where-clause in a definition.
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Default)]
|
||||
pub struct WhereClause {
|
||||
/// `true` if we ate a `where` token.
|
||||
///
|
||||
|
|
@ -419,12 +412,6 @@ impl WhereClause {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for WhereClause {
|
||||
fn default() -> WhereClause {
|
||||
WhereClause { has_where_token: false, predicates: ThinVec::new(), span: DUMMY_SP }
|
||||
}
|
||||
}
|
||||
|
||||
/// A single predicate in a where-clause.
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
pub struct WherePredicate {
|
||||
|
|
|
|||
|
|
@ -153,9 +153,10 @@ impl AssocOp {
|
|||
match *self {
|
||||
Assign | AssignOp(_) => Fixity::Right,
|
||||
As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd
|
||||
| BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual
|
||||
| LAnd | LOr => Fixity::Left,
|
||||
DotDot | DotDotEq => Fixity::None,
|
||||
| BitXor | BitOr | LAnd | LOr => Fixity::Left,
|
||||
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | DotDot | DotDotEq => {
|
||||
Fixity::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -263,7 +263,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
|||
&self,
|
||||
negative_impls,
|
||||
span.to(of_trait.as_ref().map_or(span, |t| t.path.span)),
|
||||
"negative trait bounds are not yet fully implemented; \
|
||||
"negative trait bounds are not fully implemented; \
|
||||
use marker types for now"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1204,8 +1204,10 @@ impl<'a> State<'a> {
|
|||
}
|
||||
ast::TyKind::Path(Some(qself), path) => self.print_qpath(path, qself, false),
|
||||
ast::TyKind::TraitObject(bounds, syntax) => {
|
||||
if *syntax == ast::TraitObjectSyntax::Dyn {
|
||||
self.word_nbsp("dyn");
|
||||
match syntax {
|
||||
ast::TraitObjectSyntax::Dyn => self.word_nbsp("dyn"),
|
||||
ast::TraitObjectSyntax::DynStar => self.word_nbsp("dyn*"),
|
||||
ast::TraitObjectSyntax::None => {}
|
||||
}
|
||||
self.print_type_bounds(bounds);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
use rustc_ast::Expr;
|
||||
use rustc_ast::util::{classify, parser};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
// The default amount of fixing is minimal fixing, so all fixups are set to `false` by `Default`.
|
||||
// Fixups should be turned on in a targeted fashion where needed.
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub(crate) struct FixupContext {
|
||||
/// Print expression such that it can be parsed back as a statement
|
||||
/// consisting of the original expression.
|
||||
|
|
@ -93,20 +95,6 @@ pub(crate) struct FixupContext {
|
|||
parenthesize_exterior_struct_lit: bool,
|
||||
}
|
||||
|
||||
/// The default amount of fixing is minimal fixing. Fixups should be turned on
|
||||
/// in a targeted fashion where needed.
|
||||
impl Default for FixupContext {
|
||||
fn default() -> Self {
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: false,
|
||||
match_arm: false,
|
||||
leftmost_subexpression_in_match_arm: false,
|
||||
parenthesize_exterior_struct_lit: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FixupContext {
|
||||
/// Create the initial fixup for printing an expression in statement
|
||||
/// position.
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ fn do_mir_borrowck<'tcx>(
|
|||
polonius_output,
|
||||
opt_closure_req,
|
||||
nll_errors,
|
||||
localized_outlives_constraints,
|
||||
} = nll::compute_regions(
|
||||
&infcx,
|
||||
free_regions,
|
||||
|
|
@ -315,6 +316,16 @@ fn do_mir_borrowck<'tcx>(
|
|||
|
||||
mbcx.report_move_errors();
|
||||
|
||||
// If requested, dump polonius MIR.
|
||||
polonius::dump_polonius_mir(
|
||||
&infcx,
|
||||
body,
|
||||
®ioncx,
|
||||
&borrow_set,
|
||||
localized_outlives_constraints,
|
||||
&opt_closure_req,
|
||||
);
|
||||
|
||||
// For each non-user used mutable variable, check if it's been assigned from
|
||||
// a user-declared local. If so, then put that local into the used_mut set.
|
||||
// Note that this set is expected to be small - only upvars from closures
|
||||
|
|
@ -809,7 +820,6 @@ use self::ReadOrWrite::{Activation, Read, Reservation, Write};
|
|||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
enum ArtificialField {
|
||||
ArrayLength,
|
||||
FakeBorrow,
|
||||
}
|
||||
|
||||
|
|
@ -1257,16 +1267,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
&(Rvalue::Len(place) | Rvalue::Discriminant(place)) => {
|
||||
let af = match *rvalue {
|
||||
Rvalue::Len(..) => Some(ArtificialField::ArrayLength),
|
||||
Rvalue::Discriminant(..) => None,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
&Rvalue::Discriminant(place) => {
|
||||
self.access_place(
|
||||
location,
|
||||
(place, span),
|
||||
(Shallow(af), Read(ReadKind::Copy)),
|
||||
(Shallow(None), Read(ReadKind::Copy)),
|
||||
LocalMutationIsAllowed::No,
|
||||
state,
|
||||
);
|
||||
|
|
@ -1602,6 +1607,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::UnsafeBinder(_)
|
||||
| ty::Alias(_, _)
|
||||
| ty::Param(_)
|
||||
| ty::Bound(_, _)
|
||||
|
|
@ -1643,6 +1649,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
| ty::Dynamic(_, _, _)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
| ty::UnsafeBinder(_)
|
||||
| ty::Alias(_, _)
|
||||
| ty::Param(_)
|
||||
| ty::Bound(_, _)
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@ use std::ops::Index;
|
|||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::infer::MemberConstraint;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::Span;
|
||||
use tracing::debug;
|
||||
use tracing::instrument;
|
||||
|
||||
/// Compactly stores a set of `R0 member of [R1...Rn]` constraints,
|
||||
/// indexed by the region `R0`.
|
||||
|
|
@ -23,7 +22,7 @@ where
|
|||
/// Stores the data about each `R0 member of [R1..Rn]` constraint.
|
||||
/// These are organized into a linked list, so each constraint
|
||||
/// contains the index of the next constraint with the same `R0`.
|
||||
constraints: IndexVec<NllMemberConstraintIndex, NllMemberConstraint<'tcx>>,
|
||||
constraints: IndexVec<NllMemberConstraintIndex, MemberConstraint<'tcx>>,
|
||||
|
||||
/// Stores the `R1..Rn` regions for *all* sets. For any given
|
||||
/// constraint, we keep two indices so that we can pull out a
|
||||
|
|
@ -33,7 +32,7 @@ where
|
|||
|
||||
/// Represents a `R0 member of [R1..Rn]` constraint
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct NllMemberConstraint<'tcx> {
|
||||
pub(crate) struct MemberConstraint<'tcx> {
|
||||
next_constraint: Option<NllMemberConstraintIndex>,
|
||||
|
||||
/// The span where the hidden type was instantiated.
|
||||
|
|
@ -70,37 +69,34 @@ impl Default for MemberConstraintSet<'_, ty::RegionVid> {
|
|||
}
|
||||
|
||||
impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> {
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.constraints.is_empty()
|
||||
}
|
||||
|
||||
/// Pushes a member constraint into the set.
|
||||
///
|
||||
/// The input member constraint `m_c` is in the form produced by
|
||||
/// the `rustc_middle::infer` code.
|
||||
///
|
||||
/// The `to_region_vid` callback fn is used to convert the regions
|
||||
/// within into `RegionVid` format -- it typically consults the
|
||||
/// `UniversalRegions` data structure that is known to the caller
|
||||
/// (but which this code is unaware of).
|
||||
pub(crate) fn push_constraint(
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn add_member_constraint(
|
||||
&mut self,
|
||||
m_c: &MemberConstraint<'tcx>,
|
||||
mut to_region_vid: impl FnMut(ty::Region<'tcx>) -> ty::RegionVid,
|
||||
key: ty::OpaqueTypeKey<'tcx>,
|
||||
hidden_ty: Ty<'tcx>,
|
||||
definition_span: Span,
|
||||
member_region_vid: ty::RegionVid,
|
||||
choice_regions: &[ty::RegionVid],
|
||||
) {
|
||||
debug!("push_constraint(m_c={:?})", m_c);
|
||||
let member_region_vid: ty::RegionVid = to_region_vid(m_c.member_region);
|
||||
let next_constraint = self.first_constraints.get(&member_region_vid).cloned();
|
||||
let start_index = self.choice_regions.len();
|
||||
let end_index = start_index + m_c.choice_regions.len();
|
||||
debug!("push_constraint: member_region_vid={:?}", member_region_vid);
|
||||
let constraint_index = self.constraints.push(NllMemberConstraint {
|
||||
self.choice_regions.extend(choice_regions);
|
||||
let end_index = self.choice_regions.len();
|
||||
let constraint_index = self.constraints.push(MemberConstraint {
|
||||
next_constraint,
|
||||
member_region_vid,
|
||||
definition_span: m_c.definition_span,
|
||||
hidden_ty: m_c.hidden_ty,
|
||||
key: m_c.key,
|
||||
definition_span,
|
||||
hidden_ty,
|
||||
key,
|
||||
start_index,
|
||||
end_index,
|
||||
});
|
||||
self.first_constraints.insert(member_region_vid, constraint_index);
|
||||
self.choice_regions.extend(m_c.choice_regions.iter().map(|&r| to_region_vid(r)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -182,7 +178,7 @@ where
|
|||
/// R0 member of [R1..Rn]
|
||||
/// ```
|
||||
pub(crate) fn choice_regions(&self, pci: NllMemberConstraintIndex) -> &[ty::RegionVid] {
|
||||
let NllMemberConstraint { start_index, end_index, .. } = &self.constraints[pci];
|
||||
let MemberConstraint { start_index, end_index, .. } = &self.constraints[pci];
|
||||
&self.choice_regions[*start_index..*end_index]
|
||||
}
|
||||
}
|
||||
|
|
@ -191,9 +187,9 @@ impl<'tcx, R> Index<NllMemberConstraintIndex> for MemberConstraintSet<'tcx, R>
|
|||
where
|
||||
R: Copy + Eq,
|
||||
{
|
||||
type Output = NllMemberConstraint<'tcx>;
|
||||
type Output = MemberConstraint<'tcx>;
|
||||
|
||||
fn index(&self, i: NllMemberConstraintIndex) -> &NllMemberConstraint<'tcx> {
|
||||
fn index(&self, i: NllMemberConstraintIndex) -> &MemberConstraint<'tcx> {
|
||||
&self.constraints[i]
|
||||
}
|
||||
}
|
||||
|
|
@ -215,7 +211,7 @@ where
|
|||
/// target_list: A -> B -> C -> D -> E -> F -> (None)
|
||||
/// ```
|
||||
fn append_list(
|
||||
constraints: &mut IndexSlice<NllMemberConstraintIndex, NllMemberConstraint<'_>>,
|
||||
constraints: &mut IndexSlice<NllMemberConstraintIndex, MemberConstraint<'_>>,
|
||||
target_list: NllMemberConstraintIndex,
|
||||
source_list: NllMemberConstraintIndex,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ use crate::consumers::ConsumerOptions;
|
|||
use crate::diagnostics::RegionErrors;
|
||||
use crate::facts::{AllFacts, AllFactsExt, RustcFacts};
|
||||
use crate::location::LocationTable;
|
||||
use crate::polonius::LocalizedOutlivesConstraintSet;
|
||||
use crate::region_infer::RegionInferenceContext;
|
||||
use crate::type_check::{self, MirTypeckResults};
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
|
@ -45,6 +46,9 @@ pub(crate) struct NllOutput<'tcx> {
|
|||
pub polonius_output: Option<Box<PoloniusOutput>>,
|
||||
pub opt_closure_req: Option<ClosureRegionRequirements<'tcx>>,
|
||||
pub nll_errors: RegionErrors<'tcx>,
|
||||
|
||||
/// When using `-Zpolonius=next`: the localized typeck and liveness constraints.
|
||||
pub localized_outlives_constraints: Option<LocalizedOutlivesConstraintSet>,
|
||||
}
|
||||
|
||||
/// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal
|
||||
|
|
@ -135,6 +139,15 @@ pub(crate) fn compute_regions<'a, 'tcx>(
|
|||
elements,
|
||||
);
|
||||
|
||||
// If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives
|
||||
// constraints.
|
||||
let localized_outlives_constraints =
|
||||
if infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
|
||||
Some(polonius::create_localized_constraints(&mut regioncx, body))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// If requested: dump NLL facts, and run legacy polonius analysis.
|
||||
let polonius_output = all_facts.as_ref().and_then(|all_facts| {
|
||||
if infcx.tcx.sess.opts.unstable_opts.nll_facts {
|
||||
|
|
@ -175,6 +188,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
|
|||
polonius_output,
|
||||
opt_closure_req: closure_region_requirements,
|
||||
nll_errors,
|
||||
localized_outlives_constraints,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -215,40 +229,7 @@ pub(super) fn dump_nll_mir<'tcx>(
|
|||
&0,
|
||||
body,
|
||||
|pass_where, out| {
|
||||
match pass_where {
|
||||
// Before the CFG, dump out the values for each region variable.
|
||||
PassWhere::BeforeCFG => {
|
||||
regioncx.dump_mir(tcx, out)?;
|
||||
writeln!(out, "|")?;
|
||||
|
||||
if let Some(closure_region_requirements) = closure_region_requirements {
|
||||
writeln!(out, "| Free Region Constraints")?;
|
||||
for_each_region_constraint(tcx, closure_region_requirements, &mut |msg| {
|
||||
writeln!(out, "| {msg}")
|
||||
})?;
|
||||
writeln!(out, "|")?;
|
||||
}
|
||||
|
||||
if borrow_set.len() > 0 {
|
||||
writeln!(out, "| Borrows")?;
|
||||
for (borrow_idx, borrow_data) in borrow_set.iter_enumerated() {
|
||||
writeln!(
|
||||
out,
|
||||
"| {:?}: issued at {:?} in {:?}",
|
||||
borrow_idx, borrow_data.reserve_location, borrow_data.region
|
||||
)?;
|
||||
}
|
||||
writeln!(out, "|")?;
|
||||
}
|
||||
}
|
||||
|
||||
PassWhere::BeforeLocation(_) => {}
|
||||
|
||||
PassWhere::AfterTerminator(_) => {}
|
||||
|
||||
PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
|
||||
}
|
||||
Ok(())
|
||||
emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out)
|
||||
},
|
||||
options,
|
||||
);
|
||||
|
|
@ -266,6 +247,51 @@ pub(super) fn dump_nll_mir<'tcx>(
|
|||
};
|
||||
}
|
||||
|
||||
/// Produces the actual NLL MIR sections to emit during the dumping process.
|
||||
pub(crate) fn emit_nll_mir<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
regioncx: &RegionInferenceContext<'tcx>,
|
||||
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
pass_where: PassWhere,
|
||||
out: &mut dyn io::Write,
|
||||
) -> io::Result<()> {
|
||||
match pass_where {
|
||||
// Before the CFG, dump out the values for each region variable.
|
||||
PassWhere::BeforeCFG => {
|
||||
regioncx.dump_mir(tcx, out)?;
|
||||
writeln!(out, "|")?;
|
||||
|
||||
if let Some(closure_region_requirements) = closure_region_requirements {
|
||||
writeln!(out, "| Free Region Constraints")?;
|
||||
for_each_region_constraint(tcx, closure_region_requirements, &mut |msg| {
|
||||
writeln!(out, "| {msg}")
|
||||
})?;
|
||||
writeln!(out, "|")?;
|
||||
}
|
||||
|
||||
if borrow_set.len() > 0 {
|
||||
writeln!(out, "| Borrows")?;
|
||||
for (borrow_idx, borrow_data) in borrow_set.iter_enumerated() {
|
||||
writeln!(
|
||||
out,
|
||||
"| {:?}: issued at {:?} in {:?}",
|
||||
borrow_idx, borrow_data.reserve_location, borrow_data.region
|
||||
)?;
|
||||
}
|
||||
writeln!(out, "|")?;
|
||||
}
|
||||
}
|
||||
|
||||
PassWhere::BeforeLocation(_) => {}
|
||||
|
||||
PassWhere::AfterTerminator(_) => {}
|
||||
|
||||
PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
pub(super) fn dump_annotation<'tcx, 'infcx>(
|
||||
|
|
|
|||
|
|
@ -203,8 +203,7 @@ fn place_components_conflict<'tcx>(
|
|||
let base_ty = base.ty(body, tcx).ty;
|
||||
|
||||
match (elem, base_ty.kind(), access) {
|
||||
(_, _, Shallow(Some(ArtificialField::ArrayLength)))
|
||||
| (_, _, Shallow(Some(ArtificialField::FakeBorrow))) => {
|
||||
(_, _, Shallow(Some(ArtificialField::FakeBorrow))) => {
|
||||
// The array length is like additional fields on the
|
||||
// type; it does not overlap any existing data there.
|
||||
// Furthermore, if cannot actually be a prefix of any
|
||||
|
|
|
|||
45
compiler/rustc_borrowck/src/polonius/constraints.rs
Normal file
45
compiler/rustc_borrowck/src/polonius/constraints.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
use rustc_middle::ty::RegionVid;
|
||||
use rustc_mir_dataflow::points::PointIndex;
|
||||
|
||||
/// A localized outlives constraint reifies the CFG location where the outlives constraint holds,
|
||||
/// within the origins themselves as if they were different from point to point: from `a: b`
|
||||
/// outlives constraints to `a@p: b@p`, where `p` is the point in the CFG.
|
||||
///
|
||||
/// This models two sources of constraints:
|
||||
/// - constraints that traverse the subsets between regions at a given point, `a@p: b@p`. These
|
||||
/// depend on typeck constraints generated via assignments, calls, etc. (In practice there are
|
||||
/// subtleties where a statement's effect only starts being visible at the successor point, via
|
||||
/// the "result" of that statement).
|
||||
/// - constraints that traverse the CFG via the same region, `a@p: a@q`, where `p` is a predecessor
|
||||
/// of `q`. These depend on the liveness of the regions at these points, as well as their
|
||||
/// variance.
|
||||
///
|
||||
/// The `source` origin at `from` flows into the `target` origin at `to`.
|
||||
///
|
||||
/// This dual of NLL's [crate::constraints::OutlivesConstraint] therefore encodes the
|
||||
/// position-dependent outlives constraints used by Polonius, to model the flow-sensitive loan
|
||||
/// propagation via reachability within a graph of localized constraints.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub(crate) struct LocalizedOutlivesConstraint {
|
||||
pub source: RegionVid,
|
||||
pub from: PointIndex,
|
||||
pub target: RegionVid,
|
||||
pub to: PointIndex,
|
||||
}
|
||||
|
||||
/// A container of [LocalizedOutlivesConstraint]s that can be turned into a traversable
|
||||
/// `rustc_data_structures` graph.
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub(crate) struct LocalizedOutlivesConstraintSet {
|
||||
pub outlives: Vec<LocalizedOutlivesConstraint>,
|
||||
}
|
||||
|
||||
impl LocalizedOutlivesConstraintSet {
|
||||
pub(crate) fn push(&mut self, constraint: LocalizedOutlivesConstraint) {
|
||||
if constraint.source == constraint.target && constraint.from == constraint.to {
|
||||
// 'a@p: 'a@p is pretty uninteresting
|
||||
return;
|
||||
}
|
||||
self.outlives.push(constraint);
|
||||
}
|
||||
}
|
||||
104
compiler/rustc_borrowck/src/polonius/dump.rs
Normal file
104
compiler/rustc_borrowck/src/polonius/dump.rs
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
use std::io;
|
||||
|
||||
use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options};
|
||||
use rustc_middle::mir::{Body, ClosureRegionRequirements, PassWhere};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::config::MirIncludeSpans;
|
||||
|
||||
use crate::borrow_set::BorrowSet;
|
||||
use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet};
|
||||
use crate::{BorrowckInferCtxt, RegionInferenceContext};
|
||||
|
||||
/// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information.
|
||||
// Note: this currently duplicates most of NLL MIR, with some additions for the localized outlives
|
||||
// constraints. This is ok for now as this dump will change in the near future to an HTML file to
|
||||
// become more useful.
|
||||
pub(crate) fn dump_polonius_mir<'tcx>(
|
||||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
regioncx: &RegionInferenceContext<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
localized_outlives_constraints: Option<LocalizedOutlivesConstraintSet>,
|
||||
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
|
||||
) {
|
||||
let tcx = infcx.tcx;
|
||||
if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
let localized_outlives_constraints = localized_outlives_constraints
|
||||
.expect("missing localized constraints with `-Zpolonius=next`");
|
||||
|
||||
// We want the NLL extra comments printed by default in NLL MIR dumps (they were removed in
|
||||
// #112346). Specifying `-Z mir-include-spans` on the CLI still has priority: for example,
|
||||
// they're always disabled in mir-opt tests to make working with blessed dumps easier.
|
||||
let options = PrettyPrintMirOptions {
|
||||
include_extra_comments: matches!(
|
||||
tcx.sess.opts.unstable_opts.mir_include_spans,
|
||||
MirIncludeSpans::On | MirIncludeSpans::Nll
|
||||
),
|
||||
};
|
||||
|
||||
dump_mir_with_options(
|
||||
tcx,
|
||||
false,
|
||||
"polonius",
|
||||
&0,
|
||||
body,
|
||||
|pass_where, out| {
|
||||
emit_polonius_mir(
|
||||
tcx,
|
||||
regioncx,
|
||||
closure_region_requirements,
|
||||
borrow_set,
|
||||
&localized_outlives_constraints,
|
||||
pass_where,
|
||||
out,
|
||||
)
|
||||
},
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
/// Produces the actual NLL + Polonius MIR sections to emit during the dumping process.
|
||||
fn emit_polonius_mir<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
regioncx: &RegionInferenceContext<'tcx>,
|
||||
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
|
||||
pass_where: PassWhere,
|
||||
out: &mut dyn io::Write,
|
||||
) -> io::Result<()> {
|
||||
// Emit the regular NLL front-matter
|
||||
crate::nll::emit_nll_mir(
|
||||
tcx,
|
||||
regioncx,
|
||||
closure_region_requirements,
|
||||
borrow_set,
|
||||
pass_where.clone(),
|
||||
out,
|
||||
)?;
|
||||
|
||||
let liveness = regioncx.liveness_constraints();
|
||||
|
||||
// Add localized outlives constraints
|
||||
match pass_where {
|
||||
PassWhere::BeforeCFG => {
|
||||
if localized_outlives_constraints.outlives.len() > 0 {
|
||||
writeln!(out, "| Localized constraints")?;
|
||||
|
||||
for constraint in &localized_outlives_constraints.outlives {
|
||||
let LocalizedOutlivesConstraint { source, from, target, to } = constraint;
|
||||
let from = liveness.location_from_point(*from);
|
||||
let to = liveness.location_from_point(*to);
|
||||
writeln!(out, "| {source:?} at {from:?} -> {target:?} at {to:?}")?;
|
||||
}
|
||||
writeln!(out, "|")?;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -299,16 +299,11 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
|
|||
self.consume_operand(location, op);
|
||||
}
|
||||
|
||||
&(Rvalue::Len(place) | Rvalue::Discriminant(place)) => {
|
||||
let af = match rvalue {
|
||||
Rvalue::Len(..) => Some(ArtificialField::ArrayLength),
|
||||
Rvalue::Discriminant(..) => None,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
&Rvalue::Discriminant(place) => {
|
||||
self.access_place(
|
||||
location,
|
||||
place,
|
||||
(Shallow(af), Read(ReadKind::Copy)),
|
||||
(Shallow(None), Read(ReadKind::Copy)),
|
||||
LocalMutationIsAllowed::No,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1,180 @@
|
|||
//! Polonius analysis and support code:
|
||||
//! - dedicated constraints
|
||||
//! - conversion from NLL constraints
|
||||
//! - debugging utilities
|
||||
//! - etc.
|
||||
//!
|
||||
//! The current implementation models the flow-sensitive borrow-checking concerns as a graph
|
||||
//! containing both information about regions and information about the control flow.
|
||||
//!
|
||||
//! Loan propagation is seen as a reachability problem (with some subtleties) between where the loan
|
||||
//! is introduced and a given point.
|
||||
//!
|
||||
//! Constraints arising from type-checking allow loans to flow from region to region at the same CFG
|
||||
//! point. Constraints arising from liveness allow loans to flow within from point to point, between
|
||||
//! live regions at these points.
|
||||
//!
|
||||
//! Edges can be bidirectional to encode invariant relationships, and loans can flow "back in time"
|
||||
//! to traverse these constraints arising earlier in the CFG.
|
||||
//!
|
||||
//! When incorporating kills in the traversal, the loans reaching a given point are considered live.
|
||||
//!
|
||||
//! After this, the usual NLL process happens. These live loans are fed into a dataflow analysis
|
||||
//! combining them with the points where loans go out of NLL scope (the frontier where they stop
|
||||
//! propagating to a live region), to yield the "loans in scope" or "active loans", at a given
|
||||
//! point.
|
||||
//!
|
||||
//! Illegal accesses are still computed by checking whether one of these resulting loans is
|
||||
//! invalidated.
|
||||
//!
|
||||
//! More information on this simple approach can be found in the following links, and in the future
|
||||
//! in the rustc dev guide:
|
||||
//! - <https://smallcultfollowing.com/babysteps/blog/2023/09/22/polonius-part-1/>
|
||||
//! - <https://smallcultfollowing.com/babysteps/blog/2023/09/29/polonius-part-2/>
|
||||
//!
|
||||
|
||||
mod constraints;
|
||||
pub(crate) use constraints::*;
|
||||
mod dump;
|
||||
pub(crate) use dump::dump_polonius_mir;
|
||||
pub(crate) mod legacy;
|
||||
|
||||
use rustc_middle::mir::{Body, Location};
|
||||
use rustc_mir_dataflow::points::PointIndex;
|
||||
|
||||
use crate::RegionInferenceContext;
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use crate::region_infer::values::LivenessValues;
|
||||
use crate::type_check::Locations;
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
/// Creates a constraint set for `-Zpolonius=next` by:
|
||||
/// - converting NLL typeck constraints to be localized
|
||||
/// - encoding liveness constraints
|
||||
pub(crate) fn create_localized_constraints<'tcx>(
|
||||
regioncx: &mut RegionInferenceContext<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
) -> LocalizedOutlivesConstraintSet {
|
||||
let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default();
|
||||
convert_typeck_constraints(
|
||||
body,
|
||||
regioncx.liveness_constraints(),
|
||||
regioncx.outlives_constraints(),
|
||||
&mut localized_outlives_constraints,
|
||||
);
|
||||
create_liveness_constraints(
|
||||
body,
|
||||
regioncx.liveness_constraints(),
|
||||
regioncx.universal_regions(),
|
||||
&mut localized_outlives_constraints,
|
||||
);
|
||||
|
||||
// FIXME: here, we can trace loan reachability in the constraint graph and record this as loan
|
||||
// liveness for the next step in the chain, the NLL loan scope and active loans computations.
|
||||
|
||||
localized_outlives_constraints
|
||||
}
|
||||
|
||||
/// Propagate loans throughout the subset graph at a given point (with some subtleties around the
|
||||
/// location where effects start to be visible).
|
||||
fn convert_typeck_constraints<'tcx>(
|
||||
body: &Body<'tcx>,
|
||||
liveness: &LivenessValues,
|
||||
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
|
||||
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
|
||||
) {
|
||||
for outlives_constraint in outlives_constraints {
|
||||
match outlives_constraint.locations {
|
||||
Locations::All(_) => {
|
||||
// For now, turn logical constraints holding at all points into physical edges at
|
||||
// every point in the graph.
|
||||
// FIXME: encode this into *traversal* instead.
|
||||
for (block, bb) in body.basic_blocks.iter_enumerated() {
|
||||
let statement_count = bb.statements.len();
|
||||
for statement_index in 0..=statement_count {
|
||||
let current_location = Location { block, statement_index };
|
||||
let current_point = liveness.point_from_location(current_location);
|
||||
|
||||
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
|
||||
source: outlives_constraint.sup,
|
||||
from: current_point,
|
||||
target: outlives_constraint.sub,
|
||||
to: current_point,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Propagate loans throughout the CFG: for each statement in the MIR, create localized outlives
|
||||
/// constraints for loans that are propagated to the next statements.
|
||||
pub(crate) fn create_liveness_constraints<'tcx>(
|
||||
body: &Body<'tcx>,
|
||||
liveness: &LivenessValues,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
|
||||
) {
|
||||
for (block, bb) in body.basic_blocks.iter_enumerated() {
|
||||
let statement_count = bb.statements.len();
|
||||
for statement_index in 0..=statement_count {
|
||||
let current_location = Location { block, statement_index };
|
||||
let current_point = liveness.point_from_location(current_location);
|
||||
|
||||
if statement_index < statement_count {
|
||||
// Intra-block edges, straight line constraints from each point to its successor
|
||||
// within the same block.
|
||||
let next_location = Location { block, statement_index: statement_index + 1 };
|
||||
let next_point = liveness.point_from_location(next_location);
|
||||
propagate_loans_between_points(
|
||||
current_point,
|
||||
next_point,
|
||||
liveness,
|
||||
universal_regions,
|
||||
localized_outlives_constraints,
|
||||
);
|
||||
} else {
|
||||
// Inter-block edges, from the block's terminator to each successor block's entry
|
||||
// point.
|
||||
for successor_block in bb.terminator().successors() {
|
||||
let next_location = Location { block: successor_block, statement_index: 0 };
|
||||
let next_point = liveness.point_from_location(next_location);
|
||||
propagate_loans_between_points(
|
||||
current_point,
|
||||
next_point,
|
||||
liveness,
|
||||
universal_regions,
|
||||
localized_outlives_constraints,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Propagate loans within a region between two points in the CFG, if that region is live at both
|
||||
/// the source and target points.
|
||||
fn propagate_loans_between_points(
|
||||
current_point: PointIndex,
|
||||
next_point: PointIndex,
|
||||
_liveness: &LivenessValues,
|
||||
universal_regions: &UniversalRegions<'_>,
|
||||
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
|
||||
) {
|
||||
// Universal regions are semantically live at all points.
|
||||
// Note: we always have universal regions but they're not always (or often) involved in the
|
||||
// subset graph. For now, we emit all their edges unconditionally, but some of these subgraphs
|
||||
// will be disconnected from the rest of the graph and thus, unnecessary.
|
||||
// FIXME: only emit the edges of universal regions that existential regions can reach.
|
||||
for region in universal_regions.universal_regions_iter() {
|
||||
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
|
||||
source: region,
|
||||
from: current_point,
|
||||
target: region,
|
||||
to: next_point,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -571,7 +571,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
/// Given a universal region in scope on the MIR, returns the
|
||||
/// corresponding index.
|
||||
///
|
||||
/// (Panics if `r` is not a registered universal region.)
|
||||
/// Panics if `r` is not a registered universal region, most notably
|
||||
/// if it is a placeholder. Handling placeholders requires access to the
|
||||
/// `MirTypeckRegionConstraints`.
|
||||
pub(crate) fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
|
||||
self.universal_regions().to_region_vid(r)
|
||||
}
|
||||
|
|
@ -2227,6 +2229,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid {
|
||||
self.constraint_sccs.annotation(scc).representative
|
||||
}
|
||||
|
||||
pub(crate) fn liveness_constraints(&self) -> &LivenessValues {
|
||||
&self.liveness_constraints
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> RegionDefinition<'tcx> {
|
||||
|
|
|
|||
|
|
@ -199,6 +199,11 @@ impl LivenessValues {
|
|||
self.elements.point_from_location(location)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn location_from_point(&self, point: PointIndex) -> Location {
|
||||
self.elements.to_location(point)
|
||||
}
|
||||
|
||||
/// When using `-Zpolonius=next`, returns whether the `loan_idx` is live at the given `point`.
|
||||
pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, point: PointIndex) -> bool {
|
||||
self.loans
|
||||
|
|
|
|||
|
|
@ -77,17 +77,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) {
|
||||
let QueryRegionConstraints { outlives, member_constraints } = query_constraints;
|
||||
|
||||
// Annoying: to invoke `self.to_region_vid`, we need access to
|
||||
// `self.constraints`, but we also want to be mutating
|
||||
// `self.member_constraints`. For now, just swap out the value
|
||||
// we want and replace at the end.
|
||||
let mut tmp = std::mem::take(&mut self.constraints.member_constraints);
|
||||
for member_constraint in member_constraints {
|
||||
tmp.push_constraint(member_constraint, |r| self.to_region_vid(r));
|
||||
}
|
||||
self.constraints.member_constraints = tmp;
|
||||
let QueryRegionConstraints { outlives } = query_constraints;
|
||||
|
||||
for &(predicate, constraint_category) in outlives {
|
||||
self.convert(predicate, constraint_category);
|
||||
|
|
@ -295,13 +285,8 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
|
||||
match result {
|
||||
Ok(TypeOpOutput { output: ty, constraints, .. }) => {
|
||||
if let Some(constraints) = constraints {
|
||||
assert!(
|
||||
constraints.member_constraints.is_empty(),
|
||||
"no member constraints expected from normalizing: {:#?}",
|
||||
constraints.member_constraints
|
||||
);
|
||||
next_outlives_predicates.extend(constraints.outlives.iter().copied());
|
||||
if let Some(QueryRegionConstraints { outlives }) = constraints {
|
||||
next_outlives_predicates.extend(outlives.iter().copied());
|
||||
}
|
||||
ty
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
use std::rc::Rc;
|
||||
use std::{fmt, iter, mem};
|
||||
|
||||
use rustc_abi::{FIRST_VARIANT, FieldIdx};
|
||||
use rustc_abi::FieldIdx;
|
||||
use rustc_data_structures::frozen::Frozen;
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
|
|
@ -40,9 +40,7 @@ use rustc_mir_dataflow::points::DenseLocationMap;
|
|||
use rustc_span::def_id::CRATE_DEF_ID;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{DUMMY_SP, Span, sym};
|
||||
use rustc_trait_selection::traits::query::type_op::custom::{
|
||||
CustomTypeOp, scrape_region_constraints,
|
||||
};
|
||||
use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
|
||||
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
||||
|
|
@ -75,20 +73,12 @@ macro_rules! span_mirbug {
|
|||
})
|
||||
}
|
||||
|
||||
macro_rules! span_mirbug_and_err {
|
||||
($context:expr, $elem:expr, $($message:tt)*) => ({
|
||||
{
|
||||
span_mirbug!($context, $elem, $($message)*);
|
||||
$context.error()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
mod canonical;
|
||||
mod constraint_conversion;
|
||||
pub(crate) mod free_region_relations;
|
||||
mod input_output;
|
||||
pub(crate) mod liveness;
|
||||
mod opaque_types;
|
||||
mod relate_tys;
|
||||
|
||||
/// Type checks the given `mir` in the context of the inference
|
||||
|
|
@ -179,52 +169,8 @@ pub(crate) fn type_check<'a, 'tcx>(
|
|||
|
||||
liveness::generate(&mut typeck, body, &elements, flow_inits, move_data);
|
||||
|
||||
let opaque_type_values = infcx
|
||||
.take_opaque_types()
|
||||
.into_iter()
|
||||
.map(|(opaque_type_key, decl)| {
|
||||
let _: Result<_, ErrorGuaranteed> = typeck.fully_perform_op(
|
||||
Locations::All(body.span),
|
||||
ConstraintCategory::OpaqueType,
|
||||
CustomTypeOp::new(
|
||||
|ocx| {
|
||||
ocx.infcx.register_member_constraints(
|
||||
opaque_type_key,
|
||||
decl.hidden_type.ty,
|
||||
decl.hidden_type.span,
|
||||
);
|
||||
Ok(())
|
||||
},
|
||||
"opaque_type_map",
|
||||
),
|
||||
);
|
||||
let hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type);
|
||||
trace!("finalized opaque type {:?} to {:#?}", opaque_type_key, hidden_type.ty.kind());
|
||||
if hidden_type.has_non_region_infer() {
|
||||
infcx.dcx().span_bug(
|
||||
decl.hidden_type.span,
|
||||
format!("could not resolve {:#?}", hidden_type.ty.kind()),
|
||||
);
|
||||
}
|
||||
|
||||
// Convert all regions to nll vars.
|
||||
let (opaque_type_key, hidden_type) =
|
||||
fold_regions(infcx.tcx, (opaque_type_key, hidden_type), |region, _| {
|
||||
match region.kind() {
|
||||
ty::ReVar(_) => region,
|
||||
ty::RePlaceholder(placeholder) => {
|
||||
typeck.constraints.placeholder_region(infcx, placeholder)
|
||||
}
|
||||
_ => ty::Region::new_var(
|
||||
infcx.tcx,
|
||||
typeck.universal_regions.to_region_vid(region),
|
||||
),
|
||||
}
|
||||
});
|
||||
|
||||
(opaque_type_key, hidden_type)
|
||||
})
|
||||
.collect();
|
||||
let opaque_type_values =
|
||||
opaque_types::take_opaques_and_register_member_constraints(&mut typeck);
|
||||
|
||||
MirTypeckResults { constraints, universal_region_relations, opaque_type_values }
|
||||
}
|
||||
|
|
@ -241,11 +187,9 @@ enum FieldAccessError {
|
|||
OutOfRange { field_count: usize },
|
||||
}
|
||||
|
||||
/// Verifies that MIR types are sane to not crash further checks.
|
||||
/// Verifies that MIR types are sane.
|
||||
///
|
||||
/// The sanitize_XYZ methods here take an MIR object and compute its
|
||||
/// type, calling `span_mirbug` and returning an error type if there
|
||||
/// is a problem.
|
||||
/// FIXME: This should be merged with the actual `TypeChecker`.
|
||||
struct TypeVerifier<'a, 'b, 'tcx> {
|
||||
typeck: &'a mut TypeChecker<'b, 'tcx>,
|
||||
promoted: &'b IndexSlice<Promoted, Body<'tcx>>,
|
||||
|
|
@ -260,14 +204,91 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
|||
}
|
||||
|
||||
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
|
||||
self.sanitize_place(place, location, context);
|
||||
self.super_place(place, context, location);
|
||||
let tcx = self.tcx();
|
||||
let place_ty = place.ty(self.body(), tcx);
|
||||
if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context {
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(LangItem::Copy, Some(self.last_span)),
|
||||
[place_ty.ty],
|
||||
);
|
||||
|
||||
// To have a `Copy` operand, the type `T` of the
|
||||
// value must be `Copy`. Note that we prove that `T: Copy`,
|
||||
// rather than using the `is_copy_modulo_regions`
|
||||
// test. This is important because
|
||||
// `is_copy_modulo_regions` ignores the resulting region
|
||||
// obligations and assumes they pass. This can result in
|
||||
// bounds from `Copy` impls being unsoundly ignored (e.g.,
|
||||
// #29149). Note that we decide to use `Copy` before knowing
|
||||
// whether the bounds fully apply: in effect, the rule is
|
||||
// that if a value of some type could implement `Copy`, then
|
||||
// it must.
|
||||
self.typeck.prove_trait_ref(
|
||||
trait_ref,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::CopyBound,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_projection_elem(
|
||||
&mut self,
|
||||
place: PlaceRef<'tcx>,
|
||||
elem: PlaceElem<'tcx>,
|
||||
context: PlaceContext,
|
||||
location: Location,
|
||||
) {
|
||||
let tcx = self.tcx();
|
||||
let base_ty = place.ty(self.body(), tcx);
|
||||
match elem {
|
||||
// All these projections don't add any constraints, so there's nothing to
|
||||
// do here. We check their invariants in the MIR validator after all.
|
||||
ProjectionElem::Deref
|
||||
| ProjectionElem::Index(_)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::Downcast(..) => {}
|
||||
ProjectionElem::Field(field, fty) => {
|
||||
let fty = self.typeck.normalize(fty, location);
|
||||
let ty = base_ty.field_ty(tcx, field);
|
||||
let ty = self.typeck.normalize(ty, location);
|
||||
debug!(?fty, ?ty);
|
||||
|
||||
if let Err(terr) = self.typeck.relate_types(
|
||||
ty,
|
||||
context.ambient_variance(),
|
||||
fty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Boring,
|
||||
) {
|
||||
span_mirbug!(self, place, "bad field access ({:?}: {:?}): {:?}", ty, fty, terr);
|
||||
}
|
||||
}
|
||||
ProjectionElem::OpaqueCast(ty) => {
|
||||
let ty = self.typeck.normalize(ty, location);
|
||||
self.typeck
|
||||
.relate_types(
|
||||
ty,
|
||||
context.ambient_variance(),
|
||||
base_ty.ty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::TypeAnnotation,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
ProjectionElem::Subtype(_) => {
|
||||
bug!("ProjectionElem::Subtype shouldn't exist in borrowck")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, location: Location) {
|
||||
debug!(?constant, ?location, "visit_const_operand");
|
||||
|
||||
self.super_const_operand(constant, location);
|
||||
let ty = self.sanitize_type(constant, constant.const_.ty());
|
||||
let ty = constant.const_.ty();
|
||||
|
||||
self.typeck.infcx.tcx.for_each_free_region(&ty, |live_region| {
|
||||
let live_region_vid = self.typeck.universal_regions.to_region_vid(live_region);
|
||||
|
|
@ -337,7 +358,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
|||
};
|
||||
|
||||
let promoted_body = &self.promoted[promoted];
|
||||
self.sanitize_promoted(promoted_body, location);
|
||||
self.verify_promoted(promoted_body, location);
|
||||
|
||||
let promoted_ty = promoted_body.return_ty();
|
||||
check_err(self, promoted_body, ty, promoted_ty);
|
||||
|
|
@ -387,15 +408,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
|
||||
self.super_rvalue(rvalue, location);
|
||||
let rval_ty = rvalue.ty(self.body(), self.tcx());
|
||||
self.sanitize_type(rvalue, rval_ty);
|
||||
}
|
||||
|
||||
fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) {
|
||||
self.super_local_decl(local, local_decl);
|
||||
self.sanitize_type(local_decl, local_decl.ty);
|
||||
|
||||
if let Some(user_ty) = &local_decl.user_ty {
|
||||
for (user_ty, span) in user_ty.projections_and_spans() {
|
||||
|
|
@ -434,7 +448,6 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
|||
}
|
||||
|
||||
fn visit_body(&mut self, body: &Body<'tcx>) {
|
||||
self.sanitize_type(&"return type", body.return_ty());
|
||||
// The types of local_decls are checked above which is called in super_body.
|
||||
self.super_body(body);
|
||||
}
|
||||
|
|
@ -449,64 +462,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
|||
self.typeck.infcx.tcx
|
||||
}
|
||||
|
||||
fn sanitize_type(&mut self, parent: &dyn fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
if ty.has_escaping_bound_vars() || ty.references_error() {
|
||||
span_mirbug_and_err!(self, parent, "bad type {:?}", ty)
|
||||
} else {
|
||||
ty
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that the types internal to the `place` match up with
|
||||
/// what would be expected.
|
||||
#[instrument(level = "debug", skip(self, location), ret)]
|
||||
fn sanitize_place(
|
||||
&mut self,
|
||||
place: &Place<'tcx>,
|
||||
location: Location,
|
||||
context: PlaceContext,
|
||||
) -> PlaceTy<'tcx> {
|
||||
let mut place_ty = PlaceTy::from_ty(self.body().local_decls[place.local].ty);
|
||||
|
||||
for elem in place.projection.iter() {
|
||||
if place_ty.variant_index.is_none() {
|
||||
if let Err(guar) = place_ty.ty.error_reported() {
|
||||
return PlaceTy::from_ty(Ty::new_error(self.tcx(), guar));
|
||||
}
|
||||
}
|
||||
place_ty = self.sanitize_projection(place_ty, elem, place, location, context);
|
||||
}
|
||||
|
||||
if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context {
|
||||
let tcx = self.tcx();
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(LangItem::Copy, Some(self.last_span)),
|
||||
[place_ty.ty],
|
||||
);
|
||||
|
||||
// To have a `Copy` operand, the type `T` of the
|
||||
// value must be `Copy`. Note that we prove that `T: Copy`,
|
||||
// rather than using the `is_copy_modulo_regions`
|
||||
// test. This is important because
|
||||
// `is_copy_modulo_regions` ignores the resulting region
|
||||
// obligations and assumes they pass. This can result in
|
||||
// bounds from `Copy` impls being unsoundly ignored (e.g.,
|
||||
// #29149). Note that we decide to use `Copy` before knowing
|
||||
// whether the bounds fully apply: in effect, the rule is
|
||||
// that if a value of some type could implement `Copy`, then
|
||||
// it must.
|
||||
self.typeck.prove_trait_ref(
|
||||
trait_ref,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::CopyBound,
|
||||
);
|
||||
}
|
||||
|
||||
place_ty
|
||||
}
|
||||
|
||||
fn sanitize_promoted(&mut self, promoted_body: &'b Body<'tcx>, location: Location) {
|
||||
fn verify_promoted(&mut self, promoted_body: &'b Body<'tcx>, location: Location) {
|
||||
// Determine the constraints from the promoted MIR by running the type
|
||||
// checker on the promoted MIR, then transfer the constraints back to
|
||||
// the main MIR, changing the locations to the provided location.
|
||||
|
|
@ -562,240 +518,6 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
|||
self.typeck.constraints.liveness_constraints.add_location(region, location);
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self, location), ret, level = "debug")]
|
||||
fn sanitize_projection(
|
||||
&mut self,
|
||||
base: PlaceTy<'tcx>,
|
||||
pi: PlaceElem<'tcx>,
|
||||
place: &Place<'tcx>,
|
||||
location: Location,
|
||||
context: PlaceContext,
|
||||
) -> PlaceTy<'tcx> {
|
||||
let tcx = self.tcx();
|
||||
let base_ty = base.ty;
|
||||
match pi {
|
||||
ProjectionElem::Deref => {
|
||||
let deref_ty = base_ty.builtin_deref(true);
|
||||
PlaceTy::from_ty(deref_ty.unwrap_or_else(|| {
|
||||
span_mirbug_and_err!(self, place, "deref of non-pointer {:?}", base_ty)
|
||||
}))
|
||||
}
|
||||
ProjectionElem::Index(i) => {
|
||||
let index_ty = Place::from(i).ty(self.body(), tcx).ty;
|
||||
if index_ty != tcx.types.usize {
|
||||
PlaceTy::from_ty(span_mirbug_and_err!(self, i, "index by non-usize {:?}", i))
|
||||
} else {
|
||||
PlaceTy::from_ty(base_ty.builtin_index().unwrap_or_else(|| {
|
||||
span_mirbug_and_err!(self, place, "index of non-array {:?}", base_ty)
|
||||
}))
|
||||
}
|
||||
}
|
||||
ProjectionElem::ConstantIndex { .. } => {
|
||||
// consider verifying in-bounds
|
||||
PlaceTy::from_ty(base_ty.builtin_index().unwrap_or_else(|| {
|
||||
span_mirbug_and_err!(self, place, "index of non-array {:?}", base_ty)
|
||||
}))
|
||||
}
|
||||
ProjectionElem::Subslice { from, to, from_end } => {
|
||||
PlaceTy::from_ty(match base_ty.kind() {
|
||||
ty::Array(inner, _) => {
|
||||
assert!(!from_end, "array subslices should not use from_end");
|
||||
Ty::new_array(tcx, *inner, to - from)
|
||||
}
|
||||
ty::Slice(..) => {
|
||||
assert!(from_end, "slice subslices should use from_end");
|
||||
base_ty
|
||||
}
|
||||
_ => span_mirbug_and_err!(self, place, "slice of non-array {:?}", base_ty),
|
||||
})
|
||||
}
|
||||
ProjectionElem::Downcast(maybe_name, index) => match base_ty.kind() {
|
||||
ty::Adt(adt_def, _args) if adt_def.is_enum() => {
|
||||
if index.as_usize() >= adt_def.variants().len() {
|
||||
PlaceTy::from_ty(span_mirbug_and_err!(
|
||||
self,
|
||||
place,
|
||||
"cast to variant #{:?} but enum only has {:?}",
|
||||
index,
|
||||
adt_def.variants().len()
|
||||
))
|
||||
} else {
|
||||
PlaceTy { ty: base_ty, variant_index: Some(index) }
|
||||
}
|
||||
}
|
||||
// We do not need to handle coroutines here, because this runs
|
||||
// before the coroutine transform stage.
|
||||
_ => {
|
||||
let ty = if let Some(name) = maybe_name {
|
||||
span_mirbug_and_err!(
|
||||
self,
|
||||
place,
|
||||
"can't downcast {:?} as {:?}",
|
||||
base_ty,
|
||||
name
|
||||
)
|
||||
} else {
|
||||
span_mirbug_and_err!(self, place, "can't downcast {:?}", base_ty)
|
||||
};
|
||||
PlaceTy::from_ty(ty)
|
||||
}
|
||||
},
|
||||
ProjectionElem::Field(field, fty) => {
|
||||
let fty = self.sanitize_type(place, fty);
|
||||
let fty = self.typeck.normalize(fty, location);
|
||||
match self.field_ty(place, base, field, location) {
|
||||
Ok(ty) => {
|
||||
let ty = self.typeck.normalize(ty, location);
|
||||
debug!(?fty, ?ty);
|
||||
|
||||
if let Err(terr) = self.typeck.relate_types(
|
||||
ty,
|
||||
self.get_ambient_variance(context),
|
||||
fty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Boring,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
place,
|
||||
"bad field access ({:?}: {:?}): {:?}",
|
||||
ty,
|
||||
fty,
|
||||
terr
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(FieldAccessError::OutOfRange { field_count }) => span_mirbug!(
|
||||
self,
|
||||
place,
|
||||
"accessed field #{} but variant only has {}",
|
||||
field.index(),
|
||||
field_count
|
||||
),
|
||||
}
|
||||
PlaceTy::from_ty(fty)
|
||||
}
|
||||
ProjectionElem::Subtype(_) => {
|
||||
bug!("ProjectionElem::Subtype shouldn't exist in borrowck")
|
||||
}
|
||||
ProjectionElem::OpaqueCast(ty) => {
|
||||
let ty = self.sanitize_type(place, ty);
|
||||
let ty = self.typeck.normalize(ty, location);
|
||||
self.typeck
|
||||
.relate_types(
|
||||
ty,
|
||||
self.get_ambient_variance(context),
|
||||
base.ty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::TypeAnnotation,
|
||||
)
|
||||
.unwrap();
|
||||
PlaceTy::from_ty(ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn error(&mut self) -> Ty<'tcx> {
|
||||
Ty::new_misc_error(self.tcx())
|
||||
}
|
||||
|
||||
fn get_ambient_variance(&self, context: PlaceContext) -> ty::Variance {
|
||||
use rustc_middle::mir::visit::NonMutatingUseContext::*;
|
||||
use rustc_middle::mir::visit::NonUseContext::*;
|
||||
|
||||
match context {
|
||||
PlaceContext::MutatingUse(_) => ty::Invariant,
|
||||
PlaceContext::NonUse(StorageDead | StorageLive | VarDebugInfo) => ty::Invariant,
|
||||
PlaceContext::NonMutatingUse(
|
||||
Inspect | Copy | Move | PlaceMention | SharedBorrow | FakeBorrow | RawBorrow
|
||||
| Projection,
|
||||
) => ty::Covariant,
|
||||
PlaceContext::NonUse(AscribeUserTy(variance)) => variance,
|
||||
}
|
||||
}
|
||||
|
||||
fn field_ty(
|
||||
&mut self,
|
||||
parent: &dyn fmt::Debug,
|
||||
base_ty: PlaceTy<'tcx>,
|
||||
field: FieldIdx,
|
||||
location: Location,
|
||||
) -> Result<Ty<'tcx>, FieldAccessError> {
|
||||
let tcx = self.tcx();
|
||||
|
||||
let (variant, args) = match base_ty {
|
||||
PlaceTy { ty, variant_index: Some(variant_index) } => match *ty.kind() {
|
||||
ty::Adt(adt_def, args) => (adt_def.variant(variant_index), args),
|
||||
ty::Coroutine(def_id, args) => {
|
||||
let mut variants = args.as_coroutine().state_tys(def_id, tcx);
|
||||
let Some(mut variant) = variants.nth(variant_index.into()) else {
|
||||
bug!(
|
||||
"variant_index of coroutine out of range: {:?}/{:?}",
|
||||
variant_index,
|
||||
args.as_coroutine().state_tys(def_id, tcx).count()
|
||||
);
|
||||
};
|
||||
return match variant.nth(field.index()) {
|
||||
Some(ty) => Ok(ty),
|
||||
None => Err(FieldAccessError::OutOfRange { field_count: variant.count() }),
|
||||
};
|
||||
}
|
||||
_ => bug!("can't have downcast of non-adt non-coroutine type"),
|
||||
},
|
||||
PlaceTy { ty, variant_index: None } => match *ty.kind() {
|
||||
ty::Adt(adt_def, args) if !adt_def.is_enum() => {
|
||||
(adt_def.variant(FIRST_VARIANT), args)
|
||||
}
|
||||
ty::Closure(_, args) => {
|
||||
return match args.as_closure().upvar_tys().get(field.index()) {
|
||||
Some(&ty) => Ok(ty),
|
||||
None => Err(FieldAccessError::OutOfRange {
|
||||
field_count: args.as_closure().upvar_tys().len(),
|
||||
}),
|
||||
};
|
||||
}
|
||||
ty::CoroutineClosure(_, args) => {
|
||||
return match args.as_coroutine_closure().upvar_tys().get(field.index()) {
|
||||
Some(&ty) => Ok(ty),
|
||||
None => Err(FieldAccessError::OutOfRange {
|
||||
field_count: args.as_coroutine_closure().upvar_tys().len(),
|
||||
}),
|
||||
};
|
||||
}
|
||||
ty::Coroutine(_, args) => {
|
||||
// Only prefix fields (upvars and current state) are
|
||||
// accessible without a variant index.
|
||||
return match args.as_coroutine().prefix_tys().get(field.index()) {
|
||||
Some(ty) => Ok(*ty),
|
||||
None => Err(FieldAccessError::OutOfRange {
|
||||
field_count: args.as_coroutine().prefix_tys().len(),
|
||||
}),
|
||||
};
|
||||
}
|
||||
ty::Tuple(tys) => {
|
||||
return match tys.get(field.index()) {
|
||||
Some(&ty) => Ok(ty),
|
||||
None => Err(FieldAccessError::OutOfRange { field_count: tys.len() }),
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
return Ok(span_mirbug_and_err!(
|
||||
self,
|
||||
parent,
|
||||
"can't project out of {:?}",
|
||||
base_ty
|
||||
));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(field) = variant.fields.get(field) {
|
||||
Ok(self.typeck.normalize(field.ty(tcx, args), location))
|
||||
} else {
|
||||
Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The MIR type checker. Visits the MIR and enforces all the
|
||||
|
|
@ -955,6 +677,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
self.body
|
||||
}
|
||||
|
||||
fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> RegionVid {
|
||||
if let ty::RePlaceholder(placeholder) = r.kind() {
|
||||
self.constraints.placeholder_region(self.infcx, placeholder).as_var()
|
||||
} else {
|
||||
self.universal_regions.to_region_vid(r)
|
||||
}
|
||||
}
|
||||
|
||||
fn unsized_feature_enabled(&self) -> bool {
|
||||
let features = self.tcx().features();
|
||||
features.unsized_locals() || features.unsized_fn_params()
|
||||
|
|
@ -2464,7 +2194,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
|
||||
Rvalue::RawPtr(..)
|
||||
| Rvalue::ThreadLocalRef(..)
|
||||
| Rvalue::Len(..)
|
||||
| Rvalue::Discriminant(..)
|
||||
| Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => {}
|
||||
}
|
||||
|
|
@ -2480,7 +2209,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
| Rvalue::Repeat(..)
|
||||
| Rvalue::Ref(..)
|
||||
| Rvalue::RawPtr(..)
|
||||
| Rvalue::Len(..)
|
||||
| Rvalue::Cast(..)
|
||||
| Rvalue::ShallowInitBox(..)
|
||||
| Rvalue::BinaryOp(..)
|
||||
|
|
|
|||
335
compiler/rustc_borrowck/src/type_check/opaque_types.rs
Normal file
335
compiler/rustc_borrowck/src/type_check/opaque_types.rs
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
use std::iter;
|
||||
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::fold::fold_regions;
|
||||
use rustc_middle::ty::{
|
||||
self, GenericArgKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeSuperVisitable,
|
||||
TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||
};
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use super::{MemberConstraintSet, TypeChecker};
|
||||
|
||||
/// Once we're done with typechecking the body, we take all the opaque types
|
||||
/// defined by this function and add their 'member constraints'.
|
||||
pub(super) fn take_opaques_and_register_member_constraints<'tcx>(
|
||||
typeck: &mut TypeChecker<'_, 'tcx>,
|
||||
) -> FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>> {
|
||||
let infcx = typeck.infcx;
|
||||
// Annoying: to invoke `typeck.to_region_vid`, we need access to
|
||||
// `typeck.constraints`, but we also want to be mutating
|
||||
// `typeck.member_constraints`. For now, just swap out the value
|
||||
// we want and replace at the end.
|
||||
let mut member_constraints = std::mem::take(&mut typeck.constraints.member_constraints);
|
||||
let opaque_types = infcx
|
||||
.take_opaque_types()
|
||||
.into_iter()
|
||||
.map(|(opaque_type_key, decl)| {
|
||||
let hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type);
|
||||
register_member_constraints(
|
||||
typeck,
|
||||
&mut member_constraints,
|
||||
opaque_type_key,
|
||||
hidden_type,
|
||||
);
|
||||
trace!("finalized opaque type {:?} to {:#?}", opaque_type_key, hidden_type.ty.kind());
|
||||
if hidden_type.has_non_region_infer() {
|
||||
span_bug!(hidden_type.span, "could not resolve {:?}", hidden_type.ty);
|
||||
}
|
||||
|
||||
// Convert all regions to nll vars.
|
||||
let (opaque_type_key, hidden_type) =
|
||||
fold_regions(infcx.tcx, (opaque_type_key, hidden_type), |r, _| {
|
||||
ty::Region::new_var(infcx.tcx, typeck.to_region_vid(r))
|
||||
});
|
||||
|
||||
(opaque_type_key, hidden_type)
|
||||
})
|
||||
.collect();
|
||||
assert!(typeck.constraints.member_constraints.is_empty());
|
||||
typeck.constraints.member_constraints = member_constraints;
|
||||
opaque_types
|
||||
}
|
||||
|
||||
/// Given the map `opaque_types` containing the opaque
|
||||
/// `impl Trait` types whose underlying, hidden types are being
|
||||
/// inferred, this method adds constraints to the regions
|
||||
/// appearing in those underlying hidden types to ensure that they
|
||||
/// at least do not refer to random scopes within the current
|
||||
/// function. These constraints are not (quite) sufficient to
|
||||
/// guarantee that the regions are actually legal values; that
|
||||
/// final condition is imposed after region inference is done.
|
||||
///
|
||||
/// # The Problem
|
||||
///
|
||||
/// Let's work through an example to explain how it works. Assume
|
||||
/// the current function is as follows:
|
||||
///
|
||||
/// ```text
|
||||
/// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>)
|
||||
/// ```
|
||||
///
|
||||
/// Here, we have two `impl Trait` types whose values are being
|
||||
/// inferred (the `impl Bar<'a>` and the `impl
|
||||
/// Bar<'b>`). Conceptually, this is sugar for a setup where we
|
||||
/// define underlying opaque types (`Foo1`, `Foo2`) and then, in
|
||||
/// the return type of `foo`, we *reference* those definitions:
|
||||
///
|
||||
/// ```text
|
||||
/// type Foo1<'x> = impl Bar<'x>;
|
||||
/// type Foo2<'x> = impl Bar<'x>;
|
||||
/// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. }
|
||||
/// // ^^^^ ^^
|
||||
/// // | |
|
||||
/// // | args
|
||||
/// // def_id
|
||||
/// ```
|
||||
///
|
||||
/// As indicating in the comments above, each of those references
|
||||
/// is (in the compiler) basically generic parameters (`args`)
|
||||
/// applied to the type of a suitable `def_id` (which identifies
|
||||
/// `Foo1` or `Foo2`).
|
||||
///
|
||||
/// Now, at this point in compilation, what we have done is to
|
||||
/// replace each of the references (`Foo1<'a>`, `Foo2<'b>`) with
|
||||
/// fresh inference variables C1 and C2. We wish to use the values
|
||||
/// of these variables to infer the underlying types of `Foo1` and
|
||||
/// `Foo2`. That is, this gives rise to higher-order (pattern) unification
|
||||
/// constraints like:
|
||||
///
|
||||
/// ```text
|
||||
/// for<'a> (Foo1<'a> = C1)
|
||||
/// for<'b> (Foo1<'b> = C2)
|
||||
/// ```
|
||||
///
|
||||
/// For these equation to be satisfiable, the types `C1` and `C2`
|
||||
/// can only refer to a limited set of regions. For example, `C1`
|
||||
/// can only refer to `'static` and `'a`, and `C2` can only refer
|
||||
/// to `'static` and `'b`. The job of this function is to impose that
|
||||
/// constraint.
|
||||
///
|
||||
/// Up to this point, C1 and C2 are basically just random type
|
||||
/// inference variables, and hence they may contain arbitrary
|
||||
/// regions. In fact, it is fairly likely that they do! Consider
|
||||
/// this possible definition of `foo`:
|
||||
///
|
||||
/// ```text
|
||||
/// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) {
|
||||
/// (&*x, &*y)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Here, the values for the concrete types of the two impl
|
||||
/// traits will include inference variables:
|
||||
///
|
||||
/// ```text
|
||||
/// &'0 i32
|
||||
/// &'1 i32
|
||||
/// ```
|
||||
///
|
||||
/// Ordinarily, the subtyping rules would ensure that these are
|
||||
/// sufficiently large. But since `impl Bar<'a>` isn't a specific
|
||||
/// type per se, we don't get such constraints by default. This
|
||||
/// is where this function comes into play. It adds extra
|
||||
/// constraints to ensure that all the regions which appear in the
|
||||
/// inferred type are regions that could validly appear.
|
||||
///
|
||||
/// This is actually a bit of a tricky constraint in general. We
|
||||
/// want to say that each variable (e.g., `'0`) can only take on
|
||||
/// values that were supplied as arguments to the opaque type
|
||||
/// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in
|
||||
/// scope. We don't have a constraint quite of this kind in the current
|
||||
/// region checker.
|
||||
///
|
||||
/// # The Solution
|
||||
///
|
||||
/// We generally prefer to make `<=` constraints, since they
|
||||
/// integrate best into the region solver. To do that, we find the
|
||||
/// "minimum" of all the arguments that appear in the args: that
|
||||
/// is, some region which is less than all the others. In the case
|
||||
/// of `Foo1<'a>`, that would be `'a` (it's the only choice, after
|
||||
/// all). Then we apply that as a least bound to the variables
|
||||
/// (e.g., `'a <= '0`).
|
||||
///
|
||||
/// In some cases, there is no minimum. Consider this example:
|
||||
///
|
||||
/// ```text
|
||||
/// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... }
|
||||
/// ```
|
||||
///
|
||||
/// Here we would report a more complex "in constraint", like `'r
|
||||
/// in ['a, 'b, 'static]` (where `'r` is some region appearing in
|
||||
/// the hidden type).
|
||||
///
|
||||
/// # Constrain regions, not the hidden concrete type
|
||||
///
|
||||
/// Note that generating constraints on each region `Rc` is *not*
|
||||
/// the same as generating an outlives constraint on `Tc` itself.
|
||||
/// For example, if we had a function like this:
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(type_alias_impl_trait)]
|
||||
/// # fn main() {}
|
||||
/// # trait Foo<'a> {}
|
||||
/// # impl<'a, T> Foo<'a> for (&'a u32, T) {}
|
||||
/// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> {
|
||||
/// (x, y)
|
||||
/// }
|
||||
///
|
||||
/// // Equivalent to:
|
||||
/// # mod dummy { use super::*;
|
||||
/// type FooReturn<'a, T> = impl Foo<'a>;
|
||||
/// fn foo<'a, T>(x: &'a u32, y: T) -> FooReturn<'a, T> {
|
||||
/// (x, y)
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0`
|
||||
/// is an inference variable). If we generated a constraint that
|
||||
/// `Tc: 'a`, then this would incorrectly require that `T: 'a` --
|
||||
/// but this is not necessary, because the opaque type we
|
||||
/// create will be allowed to reference `T`. So we only generate a
|
||||
/// constraint that `'0: 'a`.
|
||||
fn register_member_constraints<'tcx>(
|
||||
typeck: &mut TypeChecker<'_, 'tcx>,
|
||||
member_constraints: &mut MemberConstraintSet<'tcx, ty::RegionVid>,
|
||||
opaque_type_key: OpaqueTypeKey<'tcx>,
|
||||
OpaqueHiddenType { span, ty: hidden_ty }: OpaqueHiddenType<'tcx>,
|
||||
) {
|
||||
let tcx = typeck.tcx();
|
||||
let hidden_ty = typeck.infcx.resolve_vars_if_possible(hidden_ty);
|
||||
debug!(?hidden_ty);
|
||||
|
||||
let variances = tcx.variances_of(opaque_type_key.def_id);
|
||||
debug!(?variances);
|
||||
|
||||
// For a case like `impl Foo<'a, 'b>`, we would generate a constraint
|
||||
// `'r in ['a, 'b, 'static]` for each region `'r` that appears in the
|
||||
// hidden type (i.e., it must be equal to `'a`, `'b`, or `'static`).
|
||||
//
|
||||
// `conflict1` and `conflict2` are the two region bounds that we
|
||||
// detected which were unrelated. They are used for diagnostics.
|
||||
|
||||
// Create the set of choice regions: each region in the hidden
|
||||
// type can be equal to any of the region parameters of the
|
||||
// opaque type definition.
|
||||
let fr_static = typeck.universal_regions.fr_static;
|
||||
let choice_regions: Vec<_> = opaque_type_key
|
||||
.args
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(i, _)| variances[*i] == ty::Invariant)
|
||||
.filter_map(|(_, arg)| match arg.unpack() {
|
||||
GenericArgKind::Lifetime(r) => Some(typeck.to_region_vid(r)),
|
||||
GenericArgKind::Type(_) | GenericArgKind::Const(_) => None,
|
||||
})
|
||||
.chain(iter::once(fr_static))
|
||||
.collect();
|
||||
|
||||
// FIXME(#42940): This should use the `FreeRegionsVisitor`, but that's
|
||||
// not currently sound until we have existential regions.
|
||||
hidden_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
|
||||
tcx,
|
||||
op: |r| {
|
||||
member_constraints.add_member_constraint(
|
||||
opaque_type_key,
|
||||
hidden_ty,
|
||||
span,
|
||||
typeck.to_region_vid(r),
|
||||
&choice_regions,
|
||||
)
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/// Visitor that requires that (almost) all regions in the type visited outlive
|
||||
/// `least_region`. We cannot use `push_outlives_components` because regions in
|
||||
/// closure signatures are not included in their outlives components. We need to
|
||||
/// ensure all regions outlive the given bound so that we don't end up with,
|
||||
/// say, `ReVar` appearing in a return type and causing ICEs when other
|
||||
/// functions end up with region constraints involving regions from other
|
||||
/// functions.
|
||||
///
|
||||
/// We also cannot use `for_each_free_region` because for closures it includes
|
||||
/// the regions parameters from the enclosing item.
|
||||
///
|
||||
/// We ignore any type parameters because impl trait values are assumed to
|
||||
/// capture all the in-scope type parameters.
|
||||
struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
op: OP,
|
||||
}
|
||||
|
||||
impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP>
|
||||
where
|
||||
OP: FnMut(ty::Region<'tcx>),
|
||||
{
|
||||
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
|
||||
t.super_visit_with(self);
|
||||
}
|
||||
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) {
|
||||
match *r {
|
||||
// ignore bound regions, keep visiting
|
||||
ty::ReBound(_, _) => {}
|
||||
_ => (self.op)(r),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>) {
|
||||
// We're only interested in types involving regions
|
||||
if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
|
||||
return;
|
||||
}
|
||||
|
||||
match ty.kind() {
|
||||
ty::Closure(_, args) => {
|
||||
// Skip lifetime parameters of the enclosing item(s)
|
||||
|
||||
for upvar in args.as_closure().upvar_tys() {
|
||||
upvar.visit_with(self);
|
||||
}
|
||||
args.as_closure().sig_as_fn_ptr_ty().visit_with(self);
|
||||
}
|
||||
|
||||
ty::CoroutineClosure(_, args) => {
|
||||
// Skip lifetime parameters of the enclosing item(s)
|
||||
|
||||
for upvar in args.as_coroutine_closure().upvar_tys() {
|
||||
upvar.visit_with(self);
|
||||
}
|
||||
|
||||
args.as_coroutine_closure().signature_parts_ty().visit_with(self);
|
||||
}
|
||||
|
||||
ty::Coroutine(_, args) => {
|
||||
// Skip lifetime parameters of the enclosing item(s)
|
||||
// Also skip the witness type, because that has no free regions.
|
||||
|
||||
for upvar in args.as_coroutine().upvar_tys() {
|
||||
upvar.visit_with(self);
|
||||
}
|
||||
args.as_coroutine().return_ty().visit_with(self);
|
||||
args.as_coroutine().yield_ty().visit_with(self);
|
||||
args.as_coroutine().resume_ty().visit_with(self);
|
||||
}
|
||||
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
|
||||
// Skip lifetime parameters that are not captures.
|
||||
let variances = self.tcx.variances_of(*def_id);
|
||||
|
||||
for (v, s) in std::iter::zip(variances, args.iter()) {
|
||||
if *v != ty::Bivariant {
|
||||
s.visit_with(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
ty.super_visit_with(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -881,6 +881,10 @@ impl<'tcx> UniversalRegionIndices<'tcx> {
|
|||
/// reference those regions from the `ParamEnv`. It is also used
|
||||
/// during initialization. Relies on the `indices` map having been
|
||||
/// fully initialized.
|
||||
///
|
||||
/// Panics if `r` is not a registered universal region, most notably
|
||||
/// if it is a placeholder. Handling placeholders requires access to the
|
||||
/// `MirTypeckRegionConstraints`.
|
||||
fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
|
||||
if let ty::ReVar(..) = *r {
|
||||
r.as_var()
|
||||
|
|
|
|||
|
|
@ -249,9 +249,9 @@ builtin_macros_naked_functions_testing_attribute =
|
|||
.label = function marked with testing attribute here
|
||||
.naked_attribute = `#[naked]` is incompatible with testing attributes
|
||||
|
||||
builtin_macros_no_default_variant = no default declared
|
||||
.help = make a unit variant default by placing `#[default]` above it
|
||||
.suggestion = make `{$ident}` default
|
||||
builtin_macros_no_default_variant = `#[derive(Default)]` on enum with no `#[default]`
|
||||
.label = this enum needs a unit variant marked with `#[default]`
|
||||
.suggestion = make this unit variant default by placing `#[default]` on it
|
||||
|
||||
builtin_macros_non_abi = at least one abi must be provided as an argument to `clobber_abi`
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,9 @@ pub(crate) fn expand_deriving_default(
|
|||
StaticStruct(_, fields) => {
|
||||
default_struct_substructure(cx, trait_span, substr, fields)
|
||||
}
|
||||
StaticEnum(enum_def, _) => default_enum_substructure(cx, trait_span, enum_def),
|
||||
StaticEnum(enum_def, _) => {
|
||||
default_enum_substructure(cx, trait_span, enum_def, item.span())
|
||||
}
|
||||
_ => cx.dcx().span_bug(trait_span, "method in `derive(Default)`"),
|
||||
}
|
||||
})),
|
||||
|
|
@ -96,9 +98,10 @@ fn default_enum_substructure(
|
|||
cx: &ExtCtxt<'_>,
|
||||
trait_span: Span,
|
||||
enum_def: &EnumDef,
|
||||
item_span: Span,
|
||||
) -> BlockOrExpr {
|
||||
let expr = match try {
|
||||
let default_variant = extract_default_variant(cx, enum_def, trait_span)?;
|
||||
let default_variant = extract_default_variant(cx, enum_def, trait_span, item_span)?;
|
||||
validate_default_attribute(cx, default_variant)?;
|
||||
default_variant
|
||||
} {
|
||||
|
|
@ -146,6 +149,7 @@ fn extract_default_variant<'a>(
|
|||
cx: &ExtCtxt<'_>,
|
||||
enum_def: &'a EnumDef,
|
||||
trait_span: Span,
|
||||
item_span: Span,
|
||||
) -> Result<&'a rustc_ast::Variant, ErrorGuaranteed> {
|
||||
let default_variants: SmallVec<[_; 1]> = enum_def
|
||||
.variants
|
||||
|
|
@ -163,9 +167,10 @@ fn extract_default_variant<'a>(
|
|||
.filter(|variant| !attr::contains_name(&variant.attrs, sym::non_exhaustive));
|
||||
|
||||
let suggs = possible_defaults
|
||||
.map(|v| errors::NoDefaultVariantSugg { span: v.span, ident: v.ident })
|
||||
.map(|v| errors::NoDefaultVariantSugg { span: v.span.shrink_to_lo() })
|
||||
.collect();
|
||||
let guar = cx.dcx().emit_err(errors::NoDefaultVariant { span: trait_span, suggs });
|
||||
let guar =
|
||||
cx.dcx().emit_err(errors::NoDefaultVariant { span: trait_span, item_span, suggs });
|
||||
|
||||
return Err(guar);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -369,26 +369,21 @@ pub(crate) struct DerivePathArgsValue {
|
|||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_no_default_variant)]
|
||||
#[help]
|
||||
#[diag(builtin_macros_no_default_variant, code = E0665)]
|
||||
pub(crate) struct NoDefaultVariant {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
#[label]
|
||||
pub(crate) item_span: Span,
|
||||
#[subdiagnostic]
|
||||
pub(crate) suggs: Vec<NoDefaultVariantSugg>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[suggestion(
|
||||
builtin_macros_suggestion,
|
||||
code = "#[default] {ident}",
|
||||
applicability = "maybe-incorrect",
|
||||
style = "tool-only"
|
||||
)]
|
||||
#[suggestion(builtin_macros_suggestion, code = "#[default] ", applicability = "maybe-incorrect")]
|
||||
pub(crate) struct NoDefaultVariantSugg {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) ident: Ident,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
|
|||
|
|
@ -828,12 +828,6 @@ fn codegen_stmt<'tcx>(
|
|||
fx.bcx.ins().nop();
|
||||
}
|
||||
}
|
||||
Rvalue::Len(place) => {
|
||||
let place = codegen_place(fx, place);
|
||||
let usize_layout = fx.layout_of(fx.tcx.types.usize);
|
||||
let len = codegen_array_len(fx, place);
|
||||
lval.write_cvalue(fx, CValue::by_val(len, usize_layout));
|
||||
}
|
||||
Rvalue::ShallowInitBox(ref operand, content_ty) => {
|
||||
let content_ty = fx.monomorphize(content_ty);
|
||||
let box_layout = fx.layout_of(Ty::new_box(fx.tcx, content_ty));
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ edition = "2021"
|
|||
ar_archive_writer = "0.4.2"
|
||||
arrayvec = { version = "0.7", default-features = false }
|
||||
bitflags = "2.4.1"
|
||||
# Pinned so `cargo update` bumps don't cause breakage
|
||||
cc = "=1.2.0"
|
||||
# Pinned so `cargo update` bumps don't cause breakage. Please also update the
|
||||
# `cc` in `rustc_llvm` if you update the `cc` here.
|
||||
cc = "=1.2.5"
|
||||
either = "1.5.0"
|
||||
itertools = "0.12"
|
||||
pathdiff = "0.2.0"
|
||||
|
|
|
|||
|
|
@ -432,6 +432,7 @@ fn push_debuginfo_type_name<'tcx>(
|
|||
push_closure_or_coroutine_name(tcx, def_id, args, qualified, output, visited);
|
||||
}
|
||||
}
|
||||
ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binders)"),
|
||||
ty::Param(_)
|
||||
| ty::Error(_)
|
||||
| ty::Infer(_)
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ use rustc_session::config::OptLevel;
|
|||
use rustc_span::{DUMMY_SP, Span};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use super::FunctionCx;
|
||||
use super::operand::{OperandRef, OperandValue};
|
||||
use super::place::PlaceRef;
|
||||
use super::{FunctionCx, LocalRef};
|
||||
use crate::common::IntPredicate;
|
||||
use crate::traits::*;
|
||||
use crate::{MemFlags, base};
|
||||
|
|
@ -593,14 +593,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
self.codegen_place_to_pointer(bx, place, mk_ptr)
|
||||
}
|
||||
|
||||
mir::Rvalue::Len(place) => {
|
||||
let size = self.evaluate_array_len(bx, place);
|
||||
OperandRef {
|
||||
val: OperandValue::Immediate(size),
|
||||
layout: bx.cx().layout_of(bx.tcx().types.usize),
|
||||
}
|
||||
}
|
||||
|
||||
mir::Rvalue::BinaryOp(op_with_overflow, box (ref lhs, ref rhs))
|
||||
if let Some(op) = op_with_overflow.overflowing_to_wrapping() =>
|
||||
{
|
||||
|
|
@ -800,24 +792,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn evaluate_array_len(&mut self, bx: &mut Bx, place: mir::Place<'tcx>) -> Bx::Value {
|
||||
// ZST are passed as operands and require special handling
|
||||
// because codegen_place() panics if Local is operand.
|
||||
if let Some(index) = place.as_local() {
|
||||
if let LocalRef::Operand(op) = self.locals[index] {
|
||||
if let ty::Array(_, n) = op.layout.ty.kind() {
|
||||
let n = n
|
||||
.try_to_target_usize(bx.tcx())
|
||||
.expect("expected monomorphic const in codegen");
|
||||
return bx.cx().const_usize(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
// use common size calculation for non zero-sized types
|
||||
let cg_value = self.codegen_place(bx, place.as_ref());
|
||||
cg_value.len(bx.cx())
|
||||
}
|
||||
|
||||
/// Codegen an `Rvalue::RawPtr` or `Rvalue::Ref`
|
||||
fn codegen_place_to_pointer(
|
||||
&mut self,
|
||||
|
|
@ -1089,7 +1063,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
mir::Rvalue::Ref(..) |
|
||||
mir::Rvalue::CopyForDeref(..) |
|
||||
mir::Rvalue::RawPtr(..) |
|
||||
mir::Rvalue::Len(..) |
|
||||
mir::Rvalue::Cast(..) | // (*)
|
||||
mir::Rvalue::ShallowInitBox(..) | // (*)
|
||||
mir::Rvalue::BinaryOp(..) |
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ const_eval_non_const_fmt_macro_call =
|
|||
cannot call non-const formatting macro in {const_eval_const_context}s
|
||||
|
||||
const_eval_non_const_fn_call =
|
||||
cannot call non-const fn `{$def_path_str}` in {const_eval_const_context}s
|
||||
cannot call non-const {$def_descr} `{$def_path_str}` in {const_eval_const_context}s
|
||||
|
||||
const_eval_non_const_impl =
|
||||
impl defined here, but it is not `const`
|
||||
|
|
|
|||
|
|
@ -488,8 +488,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
Rvalue::Use(_)
|
||||
| Rvalue::CopyForDeref(..)
|
||||
| Rvalue::Repeat(..)
|
||||
| Rvalue::Discriminant(..)
|
||||
| Rvalue::Len(_) => {}
|
||||
| Rvalue::Discriminant(..) => {}
|
||||
|
||||
Rvalue::Aggregate(kind, ..) => {
|
||||
if let AggregateKind::Coroutine(def_id, ..) = kind.as_ref()
|
||||
|
|
|
|||
|
|
@ -304,6 +304,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
|
|||
}
|
||||
_ => ccx.dcx().create_err(errors::NonConstFnCall {
|
||||
span,
|
||||
def_descr: ccx.tcx.def_descr(callee),
|
||||
def_path_str: ccx.tcx.def_path_str_with_args(callee, args),
|
||||
kind: ccx.const_kind(),
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -230,9 +230,7 @@ where
|
|||
Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx))
|
||||
}
|
||||
|
||||
Rvalue::Discriminant(place) | Rvalue::Len(place) => {
|
||||
in_place::<Q, _>(cx, in_local, place.as_ref())
|
||||
}
|
||||
Rvalue::Discriminant(place) => in_place::<Q, _>(cx, in_local, place.as_ref()),
|
||||
|
||||
Rvalue::CopyForDeref(place) => in_place::<Q, _>(cx, in_local, place.as_ref()),
|
||||
|
||||
|
|
|
|||
|
|
@ -197,7 +197,6 @@ where
|
|||
| mir::Rvalue::CopyForDeref(..)
|
||||
| mir::Rvalue::ThreadLocalRef(..)
|
||||
| mir::Rvalue::Repeat(..)
|
||||
| mir::Rvalue::Len(..)
|
||||
| mir::Rvalue::BinaryOp(..)
|
||||
| mir::Rvalue::NullaryOp(..)
|
||||
| mir::Rvalue::UnaryOp(..)
|
||||
|
|
|
|||
|
|
@ -178,7 +178,8 @@ fn const_to_valtree_inner<'tcx>(
|
|||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..) => Err(ValTreeCreationError::NonSupportedType(ty)),
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::UnsafeBinder(_) => Err(ValTreeCreationError::NonSupportedType(ty)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -358,7 +359,10 @@ pub fn valtree_to_const_value<'tcx>(
|
|||
| ty::FnPtr(..)
|
||||
| ty::Str
|
||||
| ty::Slice(_)
|
||||
| ty::Dynamic(..) => bug!("no ValTree should have been created for type {:?}", ty.kind()),
|
||||
| ty::Dynamic(..)
|
||||
| ty::UnsafeBinder(_) => {
|
||||
bug!("no ValTree should have been created for type {:?}", ty.kind())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -192,6 +192,7 @@ pub(crate) struct NonConstFnCall {
|
|||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub def_path_str: String,
|
||||
pub def_descr: &'static str,
|
||||
pub kind: ConstContext,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
|
|||
| ty::CoroutineClosure(_, _)
|
||||
| ty::Coroutine(_, _)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::UnsafeBinder(_)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::Error(_) => ConstValue::from_target_usize(0u64, &tcx),
|
||||
|
|
|
|||
|
|
@ -505,6 +505,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// We don't want to do any queries, so there is not much we can do with ADTs.
|
||||
ty::Adt(..) => false,
|
||||
|
||||
ty::UnsafeBinder(ty) => is_very_trivially_sized(ty.skip_binder()),
|
||||
|
||||
ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => false,
|
||||
|
||||
ty::Infer(ty::TyVar(_)) => false,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use tracing::{info, instrument, trace};
|
|||
|
||||
use super::{
|
||||
FnArg, FnVal, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy,
|
||||
Projectable, Scalar, interp_ok, throw_ub,
|
||||
Projectable, interp_ok, throw_ub,
|
||||
};
|
||||
use crate::util;
|
||||
|
||||
|
|
@ -218,12 +218,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
self.write_repeat(operand, &dest)?;
|
||||
}
|
||||
|
||||
Len(place) => {
|
||||
let src = self.eval_place(place)?;
|
||||
let len = src.len(self)?;
|
||||
self.write_scalar(Scalar::from_target_usize(len, self), &dest)?;
|
||||
}
|
||||
|
||||
Ref(_, borrow_kind, place) => {
|
||||
let src = self.eval_place(place)?;
|
||||
let place = self.force_allocation(&src)?;
|
||||
|
|
|
|||
|
|
@ -768,6 +768,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
// Nothing to check.
|
||||
interp_ok(true)
|
||||
}
|
||||
ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"),
|
||||
// The above should be all the primitive types. The rest is compound, we
|
||||
// check them by visiting their fields/variants.
|
||||
ty::Adt(..)
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
|
|||
| ty::FnPtr(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::Dynamic(_, _, _) => self.pretty_print_type(ty),
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::UnsafeBinder(_) => self.pretty_print_type(ty),
|
||||
|
||||
// Placeholders (all printed as `_` to uniformize them).
|
||||
ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,13 @@ impl Pu128 {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Pu128> for u128 {
|
||||
#[inline]
|
||||
fn from(value: Pu128) -> Self {
|
||||
value.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u128> for Pu128 {
|
||||
#[inline]
|
||||
fn from(value: u128) -> Self {
|
||||
|
|
|
|||
|
|
@ -1388,7 +1388,13 @@ pub fn install_ice_hook(
|
|||
// opt in to less-verbose backtraces by manually setting "RUST_BACKTRACE"
|
||||
// (e.g. `RUST_BACKTRACE=1`)
|
||||
if env::var_os("RUST_BACKTRACE").is_none() {
|
||||
panic::set_backtrace_style(panic::BacktraceStyle::Full);
|
||||
// HACK: this check is extremely dumb, but we don't really need it to be smarter since this should only happen in the test suite anyway.
|
||||
let ui_testing = std::env::args().any(|arg| arg == "-Zui-testing");
|
||||
if env!("CFG_RELEASE_CHANNEL") == "dev" && !ui_testing {
|
||||
panic::set_backtrace_style(panic::BacktraceStyle::Short);
|
||||
} else {
|
||||
panic::set_backtrace_style(panic::BacktraceStyle::Full);
|
||||
}
|
||||
}
|
||||
|
||||
let using_internal_features = Arc::new(std::sync::atomic::AtomicBool::default());
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ fn create_some() -> Option<u8> {
|
|||
Some(1)
|
||||
}
|
||||
|
||||
// error: cannot call non-const fn `create_some` in constants
|
||||
// error: cannot call non-const function `create_some` in constants
|
||||
const FOO: Option<u8> = create_some();
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
#### Note: this error code is no longer emitted by the compiler.
|
||||
|
||||
The `Default` trait was derived on an enum.
|
||||
The `Default` trait was derived on an enum without specifying the default
|
||||
variant.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail
|
||||
```compile_fail,E0665
|
||||
#[derive(Default)]
|
||||
enum Food {
|
||||
Sweet,
|
||||
|
|
@ -16,18 +15,30 @@ The `Default` cannot be derived on an enum for the simple reason that the
|
|||
compiler doesn't know which value to pick by default whereas it can for a
|
||||
struct as long as all its fields implement the `Default` trait as well.
|
||||
|
||||
If you still want to implement `Default` on your enum, you'll have to do it "by
|
||||
hand":
|
||||
For the case where the desired default variant has no payload, you can
|
||||
annotate it with `#[default]` to derive it:
|
||||
|
||||
```
|
||||
#[derive(Default)]
|
||||
enum Food {
|
||||
#[default]
|
||||
Sweet,
|
||||
Salty,
|
||||
}
|
||||
```
|
||||
|
||||
In the case where the default variant does have a payload, you will have to
|
||||
implement `Default` on your enum manually:
|
||||
|
||||
```
|
||||
enum Food {
|
||||
Sweet,
|
||||
Sweet(i32),
|
||||
Salty,
|
||||
}
|
||||
|
||||
impl Default for Food {
|
||||
fn default() -> Food {
|
||||
Food::Sweet
|
||||
Food::Sweet(1)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,26 +1,24 @@
|
|||
A `#[coverage]` attribute was applied to something which does not show up
|
||||
in code coverage, or is too granular to be excluded from the coverage report.
|
||||
A `#[coverage(off|on)]` attribute was found in a position where it is not
|
||||
allowed.
|
||||
|
||||
For now, this attribute can only be applied to function, method, and closure
|
||||
definitions. In the future, it may be added to statements, blocks, and
|
||||
expressions, and for the time being, using this attribute in those places
|
||||
will just emit an `unused_attributes` lint instead of this error.
|
||||
Coverage attributes can be applied to:
|
||||
- Function and method declarations that have a body, including trait methods
|
||||
that have a default implementation.
|
||||
- Closure expressions, in situations where attributes can be applied to
|
||||
expressions.
|
||||
- `impl` blocks (inherent or trait), and modules.
|
||||
|
||||
Example of erroneous code:
|
||||
|
||||
```compile_fail,E0788
|
||||
#[coverage(off)]
|
||||
struct Foo;
|
||||
|
||||
#[coverage(on)]
|
||||
const FOO: Foo = Foo;
|
||||
unsafe extern "C" {
|
||||
#[coverage(off)]
|
||||
fn foreign_fn();
|
||||
}
|
||||
```
|
||||
|
||||
`#[coverage(off)]` tells the compiler to not generate coverage instrumentation
|
||||
for a piece of code when the `-C instrument-coverage` flag is passed. Things
|
||||
like structs and consts are not coverable code, and thus cannot do anything
|
||||
with this attribute.
|
||||
|
||||
If you wish to apply this attribute to all methods in an impl or module,
|
||||
manually annotate each method; it is not possible to annotate the entire impl
|
||||
with a `#[coverage]` attribute.
|
||||
When using the `-C instrument-coverage` flag, coverage attributes act as a
|
||||
hint to the compiler that it should instrument or not instrument the
|
||||
corresponding function or enclosed functions. The precise effect of applying
|
||||
a coverage attribute is not guaranteed and may change in future compiler
|
||||
versions.
|
||||
|
|
|
|||
|
|
@ -157,9 +157,6 @@ declare_features! (
|
|||
(accepted, const_refs_to_static, "1.83.0", Some(119618)),
|
||||
/// Allows implementing `Copy` for closures where possible (RFC 2132).
|
||||
(accepted, copy_closures, "1.26.0", Some(44490)),
|
||||
/// Allows function attribute `#[coverage(on/off)]`, to control coverage
|
||||
/// instrumentation of that function.
|
||||
(accepted, coverage_attribute, "CURRENT_RUSTC_VERSION", Some(84605)),
|
||||
/// Allows `crate` in paths.
|
||||
(accepted, crate_in_paths, "1.30.0", Some(45477)),
|
||||
/// Allows users to provide classes for fenced code block using `class:classname`.
|
||||
|
|
|
|||
|
|
@ -480,9 +480,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
|
||||
EncodeCrossCrate::No, experimental!(no_sanitize)
|
||||
),
|
||||
ungated!(
|
||||
gated!(
|
||||
coverage, Normal, template!(OneOf: &[sym::off, sym::on]),
|
||||
ErrorPreceding, EncodeCrossCrate::No,
|
||||
coverage_attribute, experimental!(coverage)
|
||||
),
|
||||
|
||||
ungated!(
|
||||
|
|
|
|||
|
|
@ -447,6 +447,9 @@ declare_features! (
|
|||
(unstable, coroutine_clone, "1.65.0", Some(95360)),
|
||||
/// Allows defining coroutines.
|
||||
(unstable, coroutines, "1.21.0", Some(43122)),
|
||||
/// Allows function attribute `#[coverage(on/off)]`, to control coverage
|
||||
/// instrumentation of that function.
|
||||
(unstable, coverage_attribute, "1.74.0", Some(84605)),
|
||||
/// Allows non-builtin attributes in inner attribute position.
|
||||
(unstable, custom_inner_attributes, "1.30.0", Some(54726)),
|
||||
/// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`.
|
||||
|
|
|
|||
|
|
@ -673,37 +673,6 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err
|
|||
let impl_span = tcx.def_span(checker.impl_def_id);
|
||||
let self_ty = tcx.impl_trait_ref(checker.impl_def_id).unwrap().instantiate_identity().self_ty();
|
||||
|
||||
// If an ADT is repr(transparent)...
|
||||
if let ty::Adt(def, args) = *self_ty.kind()
|
||||
&& def.repr().transparent()
|
||||
{
|
||||
// FIXME(compiler-errors): This should and could be deduplicated into a query.
|
||||
// Find the nontrivial field.
|
||||
let adt_typing_env = ty::TypingEnv::non_body_analysis(tcx, def.did());
|
||||
let nontrivial_field = def.all_fields().find(|field_def| {
|
||||
let field_ty = tcx.type_of(field_def.did).instantiate_identity();
|
||||
!tcx.layout_of(adt_typing_env.as_query_input(field_ty))
|
||||
.is_ok_and(|layout| layout.layout.is_1zst())
|
||||
});
|
||||
|
||||
if let Some(nontrivial_field) = nontrivial_field {
|
||||
// Check that the nontrivial field implements `PointerLike`.
|
||||
let nontrivial_field = nontrivial_field.ty(tcx, args);
|
||||
let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
ocx.register_bound(
|
||||
ObligationCause::misc(impl_span, checker.impl_def_id),
|
||||
param_env,
|
||||
nontrivial_field,
|
||||
tcx.lang_items().pointer_like().unwrap(),
|
||||
);
|
||||
// FIXME(dyn-star): We should regionck this implementation.
|
||||
if ocx.select_all_or_error().is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let is_permitted_primitive = match *self_ty.kind() {
|
||||
ty::Adt(def, _) => def.is_box(),
|
||||
ty::Uint(..) | ty::Int(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => true,
|
||||
|
|
@ -717,6 +686,74 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let why_disqualified = match *self_ty.kind() {
|
||||
// If an ADT is repr(transparent)
|
||||
ty::Adt(self_ty_def, args) => {
|
||||
if self_ty_def.repr().transparent() {
|
||||
// FIXME(compiler-errors): This should and could be deduplicated into a query.
|
||||
// Find the nontrivial field.
|
||||
let adt_typing_env = ty::TypingEnv::non_body_analysis(tcx, self_ty_def.did());
|
||||
let nontrivial_field = self_ty_def.all_fields().find(|field_def| {
|
||||
let field_ty = tcx.type_of(field_def.did).instantiate_identity();
|
||||
!tcx.layout_of(adt_typing_env.as_query_input(field_ty))
|
||||
.is_ok_and(|layout| layout.layout.is_1zst())
|
||||
});
|
||||
|
||||
if let Some(nontrivial_field) = nontrivial_field {
|
||||
// Check that the nontrivial field implements `PointerLike`.
|
||||
let nontrivial_field_ty = nontrivial_field.ty(tcx, args);
|
||||
let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
ocx.register_bound(
|
||||
ObligationCause::misc(impl_span, checker.impl_def_id),
|
||||
param_env,
|
||||
nontrivial_field_ty,
|
||||
tcx.lang_items().pointer_like().unwrap(),
|
||||
);
|
||||
// FIXME(dyn-star): We should regionck this implementation.
|
||||
if ocx.select_all_or_error().is_empty() {
|
||||
return Ok(());
|
||||
} else {
|
||||
format!(
|
||||
"the field `{field_name}` of {descr} `{self_ty}` \
|
||||
does not implement `PointerLike`",
|
||||
field_name = nontrivial_field.name,
|
||||
descr = self_ty_def.descr()
|
||||
)
|
||||
}
|
||||
} else {
|
||||
format!(
|
||||
"the {descr} `{self_ty}` is `repr(transparent)`, \
|
||||
but does not have a non-trivial field (it is zero-sized)",
|
||||
descr = self_ty_def.descr()
|
||||
)
|
||||
}
|
||||
} else if self_ty_def.is_box() {
|
||||
// If we got here, then the `layout.is_pointer_like()` check failed
|
||||
// and this box is not a thin pointer.
|
||||
|
||||
String::from("boxes of dynamically-sized types are too large to be `PointerLike`")
|
||||
} else {
|
||||
format!(
|
||||
"the {descr} `{self_ty}` is not `repr(transparent)`",
|
||||
descr = self_ty_def.descr()
|
||||
)
|
||||
}
|
||||
}
|
||||
ty::Ref(..) => {
|
||||
// If we got here, then the `layout.is_pointer_like()` check failed
|
||||
// and this reference is not a thin pointer.
|
||||
String::from("references to dynamically-sized types are too large to be `PointerLike`")
|
||||
}
|
||||
ty::Dynamic(..) | ty::Foreign(..) => {
|
||||
String::from("types of dynamic or unknown size may not implement `PointerLike`")
|
||||
}
|
||||
_ => {
|
||||
// This is a white lie; it is true everywhere outside the standard library.
|
||||
format!("only user-defined sized types are eligible for `impl PointerLike`")
|
||||
}
|
||||
};
|
||||
|
||||
Err(tcx
|
||||
.dcx()
|
||||
.struct_span_err(
|
||||
|
|
@ -724,5 +761,6 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err
|
|||
"implementation must be applied to type that has the same ABI as a pointer, \
|
||||
or is `repr(transparent)` and whose field is `PointerLike`",
|
||||
)
|
||||
.with_note(why_disqualified)
|
||||
.emit())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,7 +178,8 @@ impl<'tcx> InherentCollect<'tcx> {
|
|||
| ty::Ref(..)
|
||||
| ty::Never
|
||||
| ty::FnPtr(..)
|
||||
| ty::Tuple(..) => self.check_primitive_impl(id, self_ty),
|
||||
| ty::Tuple(..)
|
||||
| ty::UnsafeBinder(_) => self.check_primitive_impl(id, self_ty),
|
||||
ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _) | ty::Param(_) => {
|
||||
Err(self.tcx.dcx().emit_err(errors::InherentNominal { span: item_span }))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -225,7 +225,8 @@ pub(crate) fn orphan_check_impl(
|
|||
| ty::FnDef(..)
|
||||
| ty::FnPtr(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(..) => (LocalImpl::Allow, NonlocalImpl::DisallowOther),
|
||||
| ty::Tuple(..)
|
||||
| ty::UnsafeBinder(_) => (LocalImpl::Allow, NonlocalImpl::DisallowOther),
|
||||
|
||||
ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
|
|
|
|||
|
|
@ -2318,13 +2318,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
self.lower_fn_ty(hir_ty.hir_id, bf.safety, bf.abi, bf.decl, None, Some(hir_ty)),
|
||||
)
|
||||
}
|
||||
hir::TyKind::UnsafeBinder(_binder) => {
|
||||
let guar = self
|
||||
.dcx()
|
||||
.struct_span_err(hir_ty.span, "unsafe binders are not yet implemented")
|
||||
.emit();
|
||||
Ty::new_error(tcx, guar)
|
||||
}
|
||||
hir::TyKind::UnsafeBinder(binder) => Ty::new_unsafe_binder(
|
||||
tcx,
|
||||
ty::Binder::bind_with_vars(
|
||||
self.lower_ty(binder.inner_ty),
|
||||
tcx.late_bound_vars(hir_ty.hir_id),
|
||||
),
|
||||
),
|
||||
hir::TyKind::TraitObject(bounds, lifetime, repr) => {
|
||||
if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) {
|
||||
// Don't continue with type analysis if the `dyn` keyword is missing
|
||||
|
|
|
|||
|
|
@ -322,6 +322,11 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
|
|||
self.add_constraints_from_sig(current, sig_tys.with(hdr), variance);
|
||||
}
|
||||
|
||||
ty::UnsafeBinder(ty) => {
|
||||
// FIXME(unsafe_binders): This is covariant, right?
|
||||
self.add_constraints_from_ty(current, ty.skip_binder(), variance);
|
||||
}
|
||||
|
||||
ty::Error(_) => {
|
||||
// we encounter this when walking the trait references for object
|
||||
// types, where we use Error as the Self type
|
||||
|
|
|
|||
|
|
@ -402,8 +402,10 @@ impl<'a> State<'a> {
|
|||
}
|
||||
hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false),
|
||||
hir::TyKind::TraitObject(bounds, lifetime, syntax) => {
|
||||
if syntax == ast::TraitObjectSyntax::Dyn {
|
||||
self.word_space("dyn");
|
||||
match syntax {
|
||||
ast::TraitObjectSyntax::Dyn => self.word_nbsp("dyn"),
|
||||
ast::TraitObjectSyntax::DynStar => self.word_nbsp("dyn*"),
|
||||
ast::TraitObjectSyntax::None => {}
|
||||
}
|
||||
let mut first = true;
|
||||
for bound in bounds {
|
||||
|
|
|
|||
|
|
@ -77,12 +77,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let mut prior_non_diverging_arms = vec![]; // Used only for diagnostics.
|
||||
let mut prior_arm = None;
|
||||
for arm in arms {
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
|
||||
if let Some(e) = &arm.guard {
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {});
|
||||
|
||||
// FIXME: If this is the first arm and the pattern is irrefutable,
|
||||
// e.g. `_` or `x`, and the guard diverges, then the whole match
|
||||
// may also be considered to diverge. We should warn on all subsequent
|
||||
// arms, too, just like we do for diverging scrutinees above.
|
||||
}
|
||||
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
// N.B. We don't reset diverges here b/c we want to warn in the arm
|
||||
// if the guard diverges, like: `x if { loop {} } => f()`, and we
|
||||
// also want to consider the arm to diverge itself.
|
||||
|
||||
let arm_ty = self.check_expr_with_expectation(arm.body, expected);
|
||||
all_arms_diverge &= self.diverges.get();
|
||||
|
|
|
|||
|
|
@ -116,6 +116,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Some(&f) => self.pointer_kind(f, span)?,
|
||||
},
|
||||
|
||||
ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"),
|
||||
|
||||
// Pointers to foreign types are thin, despite being unsized
|
||||
ty::Foreign(..) => Some(PointerKind::Thin),
|
||||
// We should really try to normalize here.
|
||||
|
|
@ -721,13 +723,11 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
use rustc_middle::ty::cast::IntTy::*;
|
||||
|
||||
if self.cast_ty.is_dyn_star() {
|
||||
if fcx.tcx.features().dyn_star() {
|
||||
span_bug!(self.span, "should be handled by `coerce`");
|
||||
} else {
|
||||
// Report "casting is invalid" rather than "non-primitive cast"
|
||||
// if the feature is not enabled.
|
||||
return Err(CastError::IllegalCast);
|
||||
}
|
||||
// This coercion will fail if the feature is not enabled, OR
|
||||
// if the coercion is (currently) illegal (e.g. `dyn* Foo + Send`
|
||||
// to `dyn* Foo`). Report "casting is invalid" rather than
|
||||
// "non-primitive cast".
|
||||
return Err(CastError::IllegalCast);
|
||||
}
|
||||
|
||||
let (t_from, t_cast) = match (CastTy::from_ty(self.expr_ty), CastTy::from_ty(self.cast_ty))
|
||||
|
|
|
|||
|
|
@ -737,8 +737,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||
return Err(TypeError::Mismatch);
|
||||
}
|
||||
|
||||
if let ty::Dynamic(a_data, _, _) = a.kind()
|
||||
&& let ty::Dynamic(b_data, _, _) = b.kind()
|
||||
// FIXME(dyn_star): We should probably allow things like casting from
|
||||
// `dyn* Foo + Send` to `dyn* Foo`.
|
||||
if let ty::Dynamic(a_data, _, ty::DynStar) = a.kind()
|
||||
&& let ty::Dynamic(b_data, _, ty::DynStar) = b.kind()
|
||||
&& a_data.principal_def_id() == b_data.principal_def_id()
|
||||
{
|
||||
return self.unify_and(a, b, |_| vec![]);
|
||||
|
|
|
|||
|
|
@ -574,8 +574,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
self.check_expr_index(base, idx, expr, brackets_span)
|
||||
}
|
||||
ExprKind::Yield(value, _) => self.check_expr_yield(value, expr),
|
||||
ExprKind::UnsafeBinderCast(kind, expr, ty) => {
|
||||
self.check_expr_unsafe_binder_cast(kind, expr, ty, expected)
|
||||
ExprKind::UnsafeBinderCast(kind, inner_expr, ty) => {
|
||||
self.check_expr_unsafe_binder_cast(expr.span, kind, inner_expr, ty, expected)
|
||||
}
|
||||
ExprKind::Err(guar) => Ty::new_error(tcx, guar),
|
||||
}
|
||||
|
|
@ -1649,14 +1649,94 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
fn check_expr_unsafe_binder_cast(
|
||||
&self,
|
||||
_kind: hir::UnsafeBinderCastKind,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
_hir_ty: Option<&'tcx hir::Ty<'tcx>>,
|
||||
_expected: Expectation<'tcx>,
|
||||
span: Span,
|
||||
kind: hir::UnsafeBinderCastKind,
|
||||
inner_expr: &'tcx hir::Expr<'tcx>,
|
||||
hir_ty: Option<&'tcx hir::Ty<'tcx>>,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let guar =
|
||||
self.dcx().struct_span_err(expr.span, "unsafe binders are not yet implemented").emit();
|
||||
Ty::new_error(self.tcx, guar)
|
||||
self.dcx().span_err(inner_expr.span, "unsafe binder casts are not fully implemented");
|
||||
|
||||
match kind {
|
||||
hir::UnsafeBinderCastKind::Wrap => {
|
||||
let ascribed_ty =
|
||||
hir_ty.map(|hir_ty| self.lower_ty_saving_user_provided_ty(hir_ty));
|
||||
let expected_ty = expected.only_has_type(self);
|
||||
let binder_ty = match (ascribed_ty, expected_ty) {
|
||||
(Some(ascribed_ty), Some(expected_ty)) => {
|
||||
self.demand_eqtype(inner_expr.span, expected_ty, ascribed_ty);
|
||||
expected_ty
|
||||
}
|
||||
(Some(ty), None) | (None, Some(ty)) => ty,
|
||||
// This will always cause a structural resolve error, but we do it
|
||||
// so we don't need to manually report an E0282 both on this codepath
|
||||
// and in the others; it all happens in `structurally_resolve_type`.
|
||||
(None, None) => self.next_ty_var(inner_expr.span),
|
||||
};
|
||||
|
||||
let binder_ty = self.structurally_resolve_type(inner_expr.span, binder_ty);
|
||||
let hint_ty = match *binder_ty.kind() {
|
||||
ty::UnsafeBinder(binder) => self.instantiate_binder_with_fresh_vars(
|
||||
inner_expr.span,
|
||||
infer::BoundRegionConversionTime::HigherRankedType,
|
||||
binder.into(),
|
||||
),
|
||||
ty::Error(e) => Ty::new_error(self.tcx, e),
|
||||
_ => {
|
||||
let guar = self
|
||||
.dcx()
|
||||
.struct_span_err(
|
||||
hir_ty.map_or(span, |hir_ty| hir_ty.span),
|
||||
format!(
|
||||
"`wrap_binder!()` can only wrap into unsafe binder, not {}",
|
||||
binder_ty.sort_string(self.tcx)
|
||||
),
|
||||
)
|
||||
.with_note("unsafe binders are the only valid output of wrap")
|
||||
.emit();
|
||||
Ty::new_error(self.tcx, guar)
|
||||
}
|
||||
};
|
||||
|
||||
self.check_expr_has_type_or_error(inner_expr, hint_ty, |_| {});
|
||||
|
||||
binder_ty
|
||||
}
|
||||
hir::UnsafeBinderCastKind::Unwrap => {
|
||||
let ascribed_ty =
|
||||
hir_ty.map(|hir_ty| self.lower_ty_saving_user_provided_ty(hir_ty));
|
||||
let hint_ty = ascribed_ty.unwrap_or_else(|| self.next_ty_var(inner_expr.span));
|
||||
// FIXME(unsafe_binders): coerce here if needed?
|
||||
let binder_ty = self.check_expr_has_type_or_error(inner_expr, hint_ty, |_| {});
|
||||
|
||||
// Unwrap the binder. This will be ambiguous if it's an infer var, and will error
|
||||
// if it's not an unsafe binder.
|
||||
let binder_ty = self.structurally_resolve_type(inner_expr.span, binder_ty);
|
||||
match *binder_ty.kind() {
|
||||
ty::UnsafeBinder(binder) => self.instantiate_binder_with_fresh_vars(
|
||||
inner_expr.span,
|
||||
infer::BoundRegionConversionTime::HigherRankedType,
|
||||
binder.into(),
|
||||
),
|
||||
ty::Error(e) => Ty::new_error(self.tcx, e),
|
||||
_ => {
|
||||
let guar = self
|
||||
.dcx()
|
||||
.struct_span_err(
|
||||
hir_ty.map_or(inner_expr.span, |hir_ty| hir_ty.span),
|
||||
format!(
|
||||
"expected unsafe binder, found {} as input of \
|
||||
`unwrap_binder!()`",
|
||||
binder_ty.sort_string(self.tcx)
|
||||
),
|
||||
)
|
||||
.with_note("only an unsafe binder type can be unwrapped")
|
||||
.emit();
|
||||
Ty::new_error(self.tcx, guar)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr_array(
|
||||
|
|
@ -2720,12 +2800,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
/// Check field access expressions, this works for both structs and tuples.
|
||||
/// Returns the Ty of the field.
|
||||
///
|
||||
/// ```not_rust
|
||||
/// base.field
|
||||
/// ^^^^^^^^^^ expr
|
||||
/// ^^^^ base
|
||||
/// ^^^^^ field
|
||||
/// ```
|
||||
/// ```ignore (illustrative)
|
||||
/// base.field
|
||||
/// ^^^^^^^^^^ expr
|
||||
/// ^^^^ base
|
||||
/// ^^^^^ field
|
||||
/// ```
|
||||
fn check_expr_field(
|
||||
&self,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
rustc_index_macros = { path = "../rustc_index_macros", default-features = false }
|
||||
rustc_index_macros = { path = "../rustc_index_macros" }
|
||||
rustc_macros = { path = "../rustc_macros", optional = true }
|
||||
rustc_serialize = { path = "../rustc_serialize", optional = true }
|
||||
smallvec = "1.8.1"
|
||||
|
|
|
|||
|
|
@ -12,5 +12,4 @@ proc-macro2 = "1"
|
|||
quote = "1"
|
||||
|
||||
[features]
|
||||
default = ["nightly"]
|
||||
nightly = []
|
||||
|
|
|
|||
|
|
@ -441,6 +441,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
|
|||
| ty::FnDef(..)
|
||||
| ty::FnPtr(..)
|
||||
| ty::Dynamic(..)
|
||||
| ty::UnsafeBinder(_)
|
||||
| ty::Never
|
||||
| ty::Tuple(..)
|
||||
| ty::Alias(..)
|
||||
|
|
|
|||
|
|
@ -316,16 +316,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
}),
|
||||
);
|
||||
|
||||
// ...also include the query member constraints.
|
||||
output_query_region_constraints.member_constraints.extend(
|
||||
query_response
|
||||
.value
|
||||
.region_constraints
|
||||
.member_constraints
|
||||
.iter()
|
||||
.map(|p_c| instantiate_value(self.tcx, &result_args, p_c.clone())),
|
||||
);
|
||||
|
||||
let user_result: R =
|
||||
query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone());
|
||||
|
||||
|
|
@ -643,7 +633,7 @@ pub fn make_query_region_constraints<'tcx>(
|
|||
outlives_obligations: impl Iterator<Item = (Ty<'tcx>, ty::Region<'tcx>, ConstraintCategory<'tcx>)>,
|
||||
region_constraints: &RegionConstraintData<'tcx>,
|
||||
) -> QueryRegionConstraints<'tcx> {
|
||||
let RegionConstraintData { constraints, verifys, member_constraints } = region_constraints;
|
||||
let RegionConstraintData { constraints, verifys } = region_constraints;
|
||||
|
||||
assert!(verifys.is_empty());
|
||||
|
||||
|
|
@ -674,5 +664,5 @@ pub fn make_query_region_constraints<'tcx>(
|
|||
}))
|
||||
.collect();
|
||||
|
||||
QueryRegionConstraints { outlives, member_constraints: member_constraints.clone() }
|
||||
QueryRegionConstraints { outlives }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ pub use relate::StructurallyRelateAliases;
|
|||
pub use relate::combine::PredicateEmittingRelation;
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_data_structures::undo_log::{Rollback, UndoLogs};
|
||||
use rustc_data_structures::unify as ut;
|
||||
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed};
|
||||
|
|
@ -685,26 +684,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
self.inner.borrow_mut().unwrap_region_constraints().make_subregion(origin, a, b);
|
||||
}
|
||||
|
||||
/// Require that the region `r` be equal to one of the regions in
|
||||
/// the set `regions`.
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub fn add_member_constraint(
|
||||
&self,
|
||||
key: ty::OpaqueTypeKey<'tcx>,
|
||||
definition_span: Span,
|
||||
hidden_ty: Ty<'tcx>,
|
||||
region: ty::Region<'tcx>,
|
||||
in_regions: Lrc<Vec<ty::Region<'tcx>>>,
|
||||
) {
|
||||
self.inner.borrow_mut().unwrap_region_constraints().add_member_constraint(
|
||||
key,
|
||||
definition_span,
|
||||
hidden_ty,
|
||||
region,
|
||||
in_regions,
|
||||
);
|
||||
}
|
||||
|
||||
/// Processes a `Coerce` predicate from the fulfillment context.
|
||||
/// This is NOT the preferred way to handle coercion, which is to
|
||||
/// invoke `FnCtxt::coerce` or a similar method (see `coercion.rs`).
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
|
|
@ -8,8 +7,7 @@ use rustc_middle::traits::solve::Goal;
|
|||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::fold::BottomUpFolder;
|
||||
use rustc_middle::ty::{
|
||||
self, GenericArgKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
|
||||
TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||
self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeVisitableExt,
|
||||
};
|
||||
use rustc_span::Span;
|
||||
use tracing::{debug, instrument};
|
||||
|
|
@ -181,289 +179,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
Err(TypeError::Sorts(ExpectedFound::new(a, b)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Given the map `opaque_types` containing the opaque
|
||||
/// `impl Trait` types whose underlying, hidden types are being
|
||||
/// inferred, this method adds constraints to the regions
|
||||
/// appearing in those underlying hidden types to ensure that they
|
||||
/// at least do not refer to random scopes within the current
|
||||
/// function. These constraints are not (quite) sufficient to
|
||||
/// guarantee that the regions are actually legal values; that
|
||||
/// final condition is imposed after region inference is done.
|
||||
///
|
||||
/// # The Problem
|
||||
///
|
||||
/// Let's work through an example to explain how it works. Assume
|
||||
/// the current function is as follows:
|
||||
///
|
||||
/// ```text
|
||||
/// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>)
|
||||
/// ```
|
||||
///
|
||||
/// Here, we have two `impl Trait` types whose values are being
|
||||
/// inferred (the `impl Bar<'a>` and the `impl
|
||||
/// Bar<'b>`). Conceptually, this is sugar for a setup where we
|
||||
/// define underlying opaque types (`Foo1`, `Foo2`) and then, in
|
||||
/// the return type of `foo`, we *reference* those definitions:
|
||||
///
|
||||
/// ```text
|
||||
/// type Foo1<'x> = impl Bar<'x>;
|
||||
/// type Foo2<'x> = impl Bar<'x>;
|
||||
/// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. }
|
||||
/// // ^^^^ ^^
|
||||
/// // | |
|
||||
/// // | args
|
||||
/// // def_id
|
||||
/// ```
|
||||
///
|
||||
/// As indicating in the comments above, each of those references
|
||||
/// is (in the compiler) basically generic parameters (`args`)
|
||||
/// applied to the type of a suitable `def_id` (which identifies
|
||||
/// `Foo1` or `Foo2`).
|
||||
///
|
||||
/// Now, at this point in compilation, what we have done is to
|
||||
/// replace each of the references (`Foo1<'a>`, `Foo2<'b>`) with
|
||||
/// fresh inference variables C1 and C2. We wish to use the values
|
||||
/// of these variables to infer the underlying types of `Foo1` and
|
||||
/// `Foo2`. That is, this gives rise to higher-order (pattern) unification
|
||||
/// constraints like:
|
||||
///
|
||||
/// ```text
|
||||
/// for<'a> (Foo1<'a> = C1)
|
||||
/// for<'b> (Foo1<'b> = C2)
|
||||
/// ```
|
||||
///
|
||||
/// For these equation to be satisfiable, the types `C1` and `C2`
|
||||
/// can only refer to a limited set of regions. For example, `C1`
|
||||
/// can only refer to `'static` and `'a`, and `C2` can only refer
|
||||
/// to `'static` and `'b`. The job of this function is to impose that
|
||||
/// constraint.
|
||||
///
|
||||
/// Up to this point, C1 and C2 are basically just random type
|
||||
/// inference variables, and hence they may contain arbitrary
|
||||
/// regions. In fact, it is fairly likely that they do! Consider
|
||||
/// this possible definition of `foo`:
|
||||
///
|
||||
/// ```text
|
||||
/// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) {
|
||||
/// (&*x, &*y)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Here, the values for the concrete types of the two impl
|
||||
/// traits will include inference variables:
|
||||
///
|
||||
/// ```text
|
||||
/// &'0 i32
|
||||
/// &'1 i32
|
||||
/// ```
|
||||
///
|
||||
/// Ordinarily, the subtyping rules would ensure that these are
|
||||
/// sufficiently large. But since `impl Bar<'a>` isn't a specific
|
||||
/// type per se, we don't get such constraints by default. This
|
||||
/// is where this function comes into play. It adds extra
|
||||
/// constraints to ensure that all the regions which appear in the
|
||||
/// inferred type are regions that could validly appear.
|
||||
///
|
||||
/// This is actually a bit of a tricky constraint in general. We
|
||||
/// want to say that each variable (e.g., `'0`) can only take on
|
||||
/// values that were supplied as arguments to the opaque type
|
||||
/// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in
|
||||
/// scope. We don't have a constraint quite of this kind in the current
|
||||
/// region checker.
|
||||
///
|
||||
/// # The Solution
|
||||
///
|
||||
/// We generally prefer to make `<=` constraints, since they
|
||||
/// integrate best into the region solver. To do that, we find the
|
||||
/// "minimum" of all the arguments that appear in the args: that
|
||||
/// is, some region which is less than all the others. In the case
|
||||
/// of `Foo1<'a>`, that would be `'a` (it's the only choice, after
|
||||
/// all). Then we apply that as a least bound to the variables
|
||||
/// (e.g., `'a <= '0`).
|
||||
///
|
||||
/// In some cases, there is no minimum. Consider this example:
|
||||
///
|
||||
/// ```text
|
||||
/// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... }
|
||||
/// ```
|
||||
///
|
||||
/// Here we would report a more complex "in constraint", like `'r
|
||||
/// in ['a, 'b, 'static]` (where `'r` is some region appearing in
|
||||
/// the hidden type).
|
||||
///
|
||||
/// # Constrain regions, not the hidden concrete type
|
||||
///
|
||||
/// Note that generating constraints on each region `Rc` is *not*
|
||||
/// the same as generating an outlives constraint on `Tc` itself.
|
||||
/// For example, if we had a function like this:
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(type_alias_impl_trait)]
|
||||
/// # fn main() {}
|
||||
/// # trait Foo<'a> {}
|
||||
/// # impl<'a, T> Foo<'a> for (&'a u32, T) {}
|
||||
/// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> {
|
||||
/// (x, y)
|
||||
/// }
|
||||
///
|
||||
/// // Equivalent to:
|
||||
/// # mod dummy { use super::*;
|
||||
/// type FooReturn<'a, T> = impl Foo<'a>;
|
||||
/// fn foo<'a, T>(x: &'a u32, y: T) -> FooReturn<'a, T> {
|
||||
/// (x, y)
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0`
|
||||
/// is an inference variable). If we generated a constraint that
|
||||
/// `Tc: 'a`, then this would incorrectly require that `T: 'a` --
|
||||
/// but this is not necessary, because the opaque type we
|
||||
/// create will be allowed to reference `T`. So we only generate a
|
||||
/// constraint that `'0: 'a`.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn register_member_constraints(
|
||||
&self,
|
||||
opaque_type_key: OpaqueTypeKey<'tcx>,
|
||||
concrete_ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
) {
|
||||
let concrete_ty = self.resolve_vars_if_possible(concrete_ty);
|
||||
debug!(?concrete_ty);
|
||||
|
||||
let variances = self.tcx.variances_of(opaque_type_key.def_id);
|
||||
debug!(?variances);
|
||||
|
||||
// For a case like `impl Foo<'a, 'b>`, we would generate a constraint
|
||||
// `'r in ['a, 'b, 'static]` for each region `'r` that appears in the
|
||||
// hidden type (i.e., it must be equal to `'a`, `'b`, or `'static`).
|
||||
//
|
||||
// `conflict1` and `conflict2` are the two region bounds that we
|
||||
// detected which were unrelated. They are used for diagnostics.
|
||||
|
||||
// Create the set of choice regions: each region in the hidden
|
||||
// type can be equal to any of the region parameters of the
|
||||
// opaque type definition.
|
||||
let choice_regions: Lrc<Vec<ty::Region<'tcx>>> = Lrc::new(
|
||||
opaque_type_key
|
||||
.args
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(i, _)| variances[*i] == ty::Invariant)
|
||||
.filter_map(|(_, arg)| match arg.unpack() {
|
||||
GenericArgKind::Lifetime(r) => Some(r),
|
||||
GenericArgKind::Type(_) | GenericArgKind::Const(_) => None,
|
||||
})
|
||||
.chain(std::iter::once(self.tcx.lifetimes.re_static))
|
||||
.collect(),
|
||||
);
|
||||
|
||||
// FIXME(#42940): This should use the `FreeRegionsVisitor`, but that's
|
||||
// not currently sound until we have existential regions.
|
||||
concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
|
||||
tcx: self.tcx,
|
||||
op: |r| {
|
||||
self.add_member_constraint(
|
||||
opaque_type_key,
|
||||
span,
|
||||
concrete_ty,
|
||||
r,
|
||||
Lrc::clone(&choice_regions),
|
||||
)
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Visitor that requires that (almost) all regions in the type visited outlive
|
||||
/// `least_region`. We cannot use `push_outlives_components` because regions in
|
||||
/// closure signatures are not included in their outlives components. We need to
|
||||
/// ensure all regions outlive the given bound so that we don't end up with,
|
||||
/// say, `ReVar` appearing in a return type and causing ICEs when other
|
||||
/// functions end up with region constraints involving regions from other
|
||||
/// functions.
|
||||
///
|
||||
/// We also cannot use `for_each_free_region` because for closures it includes
|
||||
/// the regions parameters from the enclosing item.
|
||||
///
|
||||
/// We ignore any type parameters because impl trait values are assumed to
|
||||
/// capture all the in-scope type parameters.
|
||||
struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
op: OP,
|
||||
}
|
||||
|
||||
impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP>
|
||||
where
|
||||
OP: FnMut(ty::Region<'tcx>),
|
||||
{
|
||||
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
|
||||
t.super_visit_with(self);
|
||||
}
|
||||
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) {
|
||||
match *r {
|
||||
// ignore bound regions, keep visiting
|
||||
ty::ReBound(_, _) => {}
|
||||
_ => (self.op)(r),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>) {
|
||||
// We're only interested in types involving regions
|
||||
if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
|
||||
return;
|
||||
}
|
||||
|
||||
match ty.kind() {
|
||||
ty::Closure(_, args) => {
|
||||
// Skip lifetime parameters of the enclosing item(s)
|
||||
|
||||
for upvar in args.as_closure().upvar_tys() {
|
||||
upvar.visit_with(self);
|
||||
}
|
||||
args.as_closure().sig_as_fn_ptr_ty().visit_with(self);
|
||||
}
|
||||
|
||||
ty::CoroutineClosure(_, args) => {
|
||||
// Skip lifetime parameters of the enclosing item(s)
|
||||
|
||||
for upvar in args.as_coroutine_closure().upvar_tys() {
|
||||
upvar.visit_with(self);
|
||||
}
|
||||
|
||||
args.as_coroutine_closure().signature_parts_ty().visit_with(self);
|
||||
}
|
||||
|
||||
ty::Coroutine(_, args) => {
|
||||
// Skip lifetime parameters of the enclosing item(s)
|
||||
// Also skip the witness type, because that has no free regions.
|
||||
|
||||
for upvar in args.as_coroutine().upvar_tys() {
|
||||
upvar.visit_with(self);
|
||||
}
|
||||
args.as_coroutine().return_ty().visit_with(self);
|
||||
args.as_coroutine().yield_ty().visit_with(self);
|
||||
args.as_coroutine().resume_ty().visit_with(self);
|
||||
}
|
||||
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
|
||||
// Skip lifetime parameters that are not captures.
|
||||
let variances = self.tcx.variances_of(*def_id);
|
||||
|
||||
for (v, s) in std::iter::zip(variances, args.iter()) {
|
||||
if *v != ty::Bivariant {
|
||||
s.visit_with(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
ty.super_visit_with(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> InferCtxt<'tcx> {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ use std::ops::Range;
|
|||
use std::{cmp, fmt, mem};
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_data_structures::undo_log::UndoLogs;
|
||||
use rustc_data_structures::unify as ut;
|
||||
use rustc_index::IndexVec;
|
||||
|
|
@ -12,7 +11,6 @@ use rustc_macros::{TypeFoldable, TypeVisitable};
|
|||
use rustc_middle::infer::unify_key::{RegionVariableValue, RegionVidKey};
|
||||
use rustc_middle::ty::{self, ReBound, ReStatic, ReVar, Region, RegionVid, Ty, TyCtxt};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::Span;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use self::CombineMapType::*;
|
||||
|
|
@ -22,8 +20,6 @@ use crate::infer::snapshot::undo_log::{InferCtxtUndoLogs, Snapshot};
|
|||
|
||||
mod leak_check;
|
||||
|
||||
pub use rustc_middle::infer::MemberConstraint;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct RegionConstraintStorage<'tcx> {
|
||||
/// For each `RegionVid`, the corresponding `RegionVariableOrigin`.
|
||||
|
|
@ -73,11 +69,6 @@ pub struct RegionConstraintData<'tcx> {
|
|||
/// be a region variable (or neither, as it happens).
|
||||
pub constraints: Vec<(Constraint<'tcx>, SubregionOrigin<'tcx>)>,
|
||||
|
||||
/// Constraints of the form `R0 member of [R1, ..., Rn]`, meaning that
|
||||
/// `R0` must be equal to one of the regions `R1..Rn`. These occur
|
||||
/// with `impl Trait` quite frequently.
|
||||
pub member_constraints: Vec<MemberConstraint<'tcx>>,
|
||||
|
||||
/// A "verify" is something that we need to verify after inference
|
||||
/// is done, but which does not directly affect inference in any
|
||||
/// way.
|
||||
|
|
@ -466,29 +457,6 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn add_member_constraint(
|
||||
&mut self,
|
||||
key: ty::OpaqueTypeKey<'tcx>,
|
||||
definition_span: Span,
|
||||
hidden_ty: Ty<'tcx>,
|
||||
member_region: ty::Region<'tcx>,
|
||||
choice_regions: Lrc<Vec<ty::Region<'tcx>>>,
|
||||
) {
|
||||
debug!("member_constraint({:?} in {:#?})", member_region, choice_regions);
|
||||
|
||||
if choice_regions.iter().any(|&r| r == member_region) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.storage.data.member_constraints.push(MemberConstraint {
|
||||
key,
|
||||
definition_span,
|
||||
hidden_ty,
|
||||
member_region,
|
||||
choice_regions,
|
||||
});
|
||||
}
|
||||
|
||||
#[instrument(skip(self, origin), level = "debug")]
|
||||
pub(super) fn make_subregion(
|
||||
&mut self,
|
||||
|
|
@ -745,8 +713,8 @@ impl<'tcx> RegionConstraintData<'tcx> {
|
|||
/// Returns `true` if this region constraint data contains no constraints, and `false`
|
||||
/// otherwise.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
let RegionConstraintData { constraints, member_constraints, verifys } = self;
|
||||
constraints.is_empty() && member_constraints.is_empty() && verifys.is_empty()
|
||||
let RegionConstraintData { constraints, verifys } = self;
|
||||
constraints.is_empty() && verifys.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1284,6 +1284,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
FfiSafe
|
||||
}
|
||||
|
||||
ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"),
|
||||
|
||||
ty::Param(..)
|
||||
| ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
|
||||
| ty::Infer(..)
|
||||
|
|
|
|||
|
|
@ -1023,6 +1023,7 @@ declare_lint! {
|
|||
"`if`, `match`, `while` and `return` do not need parentheses"
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct UnusedParens {
|
||||
with_self_ty_parens: bool,
|
||||
/// `1 as (i32) < 2` parses to ExprKind::Lt
|
||||
|
|
@ -1030,12 +1031,6 @@ pub(crate) struct UnusedParens {
|
|||
parens_in_cast_in_lt: Vec<ast::NodeId>,
|
||||
}
|
||||
|
||||
impl Default for UnusedParens {
|
||||
fn default() -> Self {
|
||||
Self { with_self_ty_parens: false, parens_in_cast_in_lt: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(UnusedParens => [UNUSED_PARENS]);
|
||||
|
||||
impl UnusedDelimLint for UnusedParens {
|
||||
|
|
|
|||
|
|
@ -10,5 +10,7 @@ libc = "0.2.73"
|
|||
|
||||
[build-dependencies]
|
||||
# tidy-alphabetical-start
|
||||
cc = "1.1.23"
|
||||
# Pinned so `cargo update` bumps don't cause breakage. Please also update the
|
||||
# pinned `cc` in `rustc_codegen_ssa` if you update `cc` here.
|
||||
cc = "=1.2.5"
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ pub use rustc_type_ir as ir;
|
|||
pub use rustc_type_ir::{CanonicalTyVarKind, CanonicalVarKind};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::infer::MemberConstraint;
|
||||
use crate::mir::ConstraintCategory;
|
||||
use crate::ty::{self, GenericArg, List, Ty, TyCtxt, TypeFlags, TypeVisitableExt};
|
||||
|
||||
|
|
@ -91,14 +90,13 @@ pub struct QueryResponse<'tcx, R> {
|
|||
#[derive(HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct QueryRegionConstraints<'tcx> {
|
||||
pub outlives: Vec<QueryOutlivesConstraint<'tcx>>,
|
||||
pub member_constraints: Vec<MemberConstraint<'tcx>>,
|
||||
}
|
||||
|
||||
impl QueryRegionConstraints<'_> {
|
||||
/// Represents an empty (trivially true) set of region
|
||||
/// constraints.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.outlives.is_empty() && self.member_constraints.is_empty()
|
||||
self.outlives.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,34 +1,2 @@
|
|||
pub mod canonical;
|
||||
pub mod unify_key;
|
||||
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_macros::{HashStable, TypeFoldable, TypeVisitable};
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::ty::{OpaqueTypeKey, Region, Ty};
|
||||
|
||||
/// Requires that `region` must be equal to one of the regions in `choice_regions`.
|
||||
/// We often denote this using the syntax:
|
||||
///
|
||||
/// ```text
|
||||
/// R0 member of [O1..On]
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct MemberConstraint<'tcx> {
|
||||
/// The `DefId` and args of the opaque type causing this constraint.
|
||||
/// Used for error reporting.
|
||||
pub key: OpaqueTypeKey<'tcx>,
|
||||
|
||||
/// The span where the hidden type was instantiated.
|
||||
pub definition_span: Span,
|
||||
|
||||
/// The hidden type in which `member_region` appears: used for error reporting.
|
||||
pub hidden_ty: Ty<'tcx>,
|
||||
|
||||
/// The region `R0`.
|
||||
pub member_region: Region<'tcx>,
|
||||
|
||||
/// The options `O1..On`.
|
||||
pub choice_regions: Lrc<Vec<Region<'tcx>>>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ pub(crate) const ALIGN: usize = 40;
|
|||
|
||||
/// An indication of where we are in the control flow graph. Used for printing
|
||||
/// extra information in `dump_mir`
|
||||
#[derive(Clone)]
|
||||
pub enum PassWhere {
|
||||
/// We have not started dumping the control flow graph, but we are about to.
|
||||
BeforeCFG,
|
||||
|
|
@ -1067,7 +1068,6 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
|||
pretty_print_const(b, fmt, false)?;
|
||||
write!(fmt, "]")
|
||||
}
|
||||
Len(ref a) => write!(fmt, "Len({a:?})"),
|
||||
Cast(ref kind, ref place, ref ty) => {
|
||||
with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -424,7 +424,6 @@ impl<'tcx> Rvalue<'tcx> {
|
|||
| Rvalue::Ref(_, _, _)
|
||||
| Rvalue::ThreadLocalRef(_)
|
||||
| Rvalue::RawPtr(_, _)
|
||||
| Rvalue::Len(_)
|
||||
| Rvalue::Cast(
|
||||
CastKind::IntToInt
|
||||
| CastKind::FloatToInt
|
||||
|
|
|
|||
|
|
@ -821,6 +821,11 @@ pub enum TerminatorKind<'tcx> {
|
|||
/// continues at the `resume` basic block, with the second argument written to the `resume_arg`
|
||||
/// place. If the coroutine is dropped before then, the `drop` basic block is invoked.
|
||||
///
|
||||
/// Note that coroutines can be (unstably) cloned under certain conditions, which means that
|
||||
/// this terminator can **return multiple times**! MIR optimizations that reorder code into
|
||||
/// different basic blocks needs to be aware of that.
|
||||
/// See <https://github.com/rust-lang/rust/issues/95360>.
|
||||
///
|
||||
/// Not permitted in bodies that are not coroutine bodies, or after coroutine lowering.
|
||||
///
|
||||
/// **Needs clarification**: What about the evaluation order of the `resume_arg` and `value`?
|
||||
|
|
@ -1346,16 +1351,6 @@ pub enum Rvalue<'tcx> {
|
|||
/// model.
|
||||
RawPtr(Mutability, Place<'tcx>),
|
||||
|
||||
/// Yields the length of the place, as a `usize`.
|
||||
///
|
||||
/// If the type of the place is an array, this is the array length. For slices (`[T]`, not
|
||||
/// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is
|
||||
/// ill-formed for places of other types.
|
||||
///
|
||||
/// This cannot be a `UnOp(PtrMetadata, _)` because that expects a value, and we only
|
||||
/// have a place, and `UnOp(PtrMetadata, RawPtr(place))` is not a thing.
|
||||
Len(Place<'tcx>),
|
||||
|
||||
/// Performs essentially all of the casts that can be performed via `as`.
|
||||
///
|
||||
/// This allows for casts from/to a variety of types.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
use rustc_hir as hir;
|
||||
use tracing::{debug, instrument};
|
||||
use ty::CoroutineArgsExt;
|
||||
|
||||
use crate::mir::*;
|
||||
|
||||
|
|
@ -25,29 +26,63 @@ impl<'tcx> PlaceTy<'tcx> {
|
|||
PlaceTy { ty, variant_index: None }
|
||||
}
|
||||
|
||||
/// `place_ty.field_ty(tcx, f)` computes the type at a given field
|
||||
/// of a record or enum-variant. (Most clients of `PlaceTy` can
|
||||
/// instead just extract the relevant type directly from their
|
||||
/// `PlaceElem`, but some instances of `ProjectionElem<V, T>` do
|
||||
/// not carry a `Ty` for `T`.)
|
||||
/// `place_ty.field_ty(tcx, f)` computes the type of a given field.
|
||||
///
|
||||
/// Most clients of `PlaceTy` can instead just extract the relevant type
|
||||
/// directly from their `PlaceElem`, but some instances of `ProjectionElem<V, T>`
|
||||
/// do not carry a `Ty` for `T`.
|
||||
///
|
||||
/// Note that the resulting type has not been normalized.
|
||||
#[instrument(level = "debug", skip(tcx), ret)]
|
||||
pub fn field_ty(self, tcx: TyCtxt<'tcx>, f: FieldIdx) -> Ty<'tcx> {
|
||||
match self.ty.kind() {
|
||||
ty::Adt(adt_def, args) => {
|
||||
let variant_def = match self.variant_index {
|
||||
None => adt_def.non_enum_variant(),
|
||||
Some(variant_index) => {
|
||||
assert!(adt_def.is_enum());
|
||||
adt_def.variant(variant_index)
|
||||
}
|
||||
};
|
||||
let field_def = &variant_def.fields[f];
|
||||
field_def.ty(tcx, args)
|
||||
if let Some(variant_index) = self.variant_index {
|
||||
match *self.ty.kind() {
|
||||
ty::Adt(adt_def, args) if adt_def.is_enum() => {
|
||||
adt_def.variant(variant_index).fields[f].ty(tcx, args)
|
||||
}
|
||||
ty::Coroutine(def_id, args) => {
|
||||
let mut variants = args.as_coroutine().state_tys(def_id, tcx);
|
||||
let Some(mut variant) = variants.nth(variant_index.into()) else {
|
||||
bug!("variant {variant_index:?} of coroutine out of range: {self:?}");
|
||||
};
|
||||
|
||||
variant
|
||||
.nth(f.index())
|
||||
.unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}"))
|
||||
}
|
||||
_ => bug!("can't downcast non-adt non-coroutine type: {self:?}"),
|
||||
}
|
||||
} else {
|
||||
match self.ty.kind() {
|
||||
ty::Adt(adt_def, args) if !adt_def.is_enum() => {
|
||||
adt_def.non_enum_variant().fields[f].ty(tcx, args)
|
||||
}
|
||||
ty::Closure(_, args) => args
|
||||
.as_closure()
|
||||
.upvar_tys()
|
||||
.get(f.index())
|
||||
.copied()
|
||||
.unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
|
||||
ty::CoroutineClosure(_, args) => args
|
||||
.as_coroutine_closure()
|
||||
.upvar_tys()
|
||||
.get(f.index())
|
||||
.copied()
|
||||
.unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
|
||||
// Only prefix fields (upvars and current state) are
|
||||
// accessible without a variant index.
|
||||
ty::Coroutine(_, args) => args
|
||||
.as_coroutine()
|
||||
.prefix_tys()
|
||||
.get(f.index())
|
||||
.copied()
|
||||
.unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
|
||||
ty::Tuple(tys) => tys
|
||||
.get(f.index())
|
||||
.copied()
|
||||
.unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")),
|
||||
_ => bug!("can't project out of {self:?}"),
|
||||
}
|
||||
ty::Tuple(tys) => tys[f.index()],
|
||||
_ => bug!("extracting field of non-tuple non-adt: {:?}", self),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -175,7 +210,6 @@ impl<'tcx> Rvalue<'tcx> {
|
|||
let place_ty = place.ty(local_decls, tcx).ty;
|
||||
Ty::new_ptr(tcx, place_ty, mutability)
|
||||
}
|
||||
Rvalue::Len(..) => tcx.types.usize,
|
||||
Rvalue::Cast(.., ty) => ty,
|
||||
Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => {
|
||||
let lhs_ty = lhs.ty(local_decls, tcx);
|
||||
|
|
|
|||
|
|
@ -67,6 +67,17 @@ impl SwitchTargets {
|
|||
&mut self.targets
|
||||
}
|
||||
|
||||
/// Returns a slice with all considered values (not including the fallback).
|
||||
#[inline]
|
||||
pub fn all_values(&self) -> &[Pu128] {
|
||||
&self.values
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn all_values_mut(&mut self) -> &mut [Pu128] {
|
||||
&mut self.values
|
||||
}
|
||||
|
||||
/// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
|
||||
/// specific value. This cannot fail, as it'll return the `otherwise`
|
||||
/// branch if there's not a specific match for the value.
|
||||
|
|
|
|||
|
|
@ -695,14 +695,6 @@ macro_rules! make_mir_visitor {
|
|||
self.visit_place(path, ctx, location);
|
||||
}
|
||||
|
||||
Rvalue::Len(path) => {
|
||||
self.visit_place(
|
||||
path,
|
||||
PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect),
|
||||
location
|
||||
);
|
||||
}
|
||||
|
||||
Rvalue::Cast(_cast_kind, operand, ty) => {
|
||||
self.visit_operand(operand, location);
|
||||
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
|
||||
|
|
@ -1369,12 +1361,12 @@ pub enum PlaceContext {
|
|||
impl PlaceContext {
|
||||
/// Returns `true` if this place context represents a drop.
|
||||
#[inline]
|
||||
pub fn is_drop(&self) -> bool {
|
||||
pub fn is_drop(self) -> bool {
|
||||
matches!(self, PlaceContext::MutatingUse(MutatingUseContext::Drop))
|
||||
}
|
||||
|
||||
/// Returns `true` if this place context represents a borrow.
|
||||
pub fn is_borrow(&self) -> bool {
|
||||
pub fn is_borrow(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
PlaceContext::NonMutatingUse(
|
||||
|
|
@ -1384,7 +1376,7 @@ impl PlaceContext {
|
|||
}
|
||||
|
||||
/// Returns `true` if this place context represents an address-of.
|
||||
pub fn is_address_of(&self) -> bool {
|
||||
pub fn is_address_of(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
PlaceContext::NonMutatingUse(NonMutatingUseContext::RawBorrow)
|
||||
|
|
@ -1394,7 +1386,7 @@ impl PlaceContext {
|
|||
|
||||
/// Returns `true` if this place context represents a storage live or storage dead marker.
|
||||
#[inline]
|
||||
pub fn is_storage_marker(&self) -> bool {
|
||||
pub fn is_storage_marker(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
PlaceContext::NonUse(NonUseContext::StorageLive | NonUseContext::StorageDead)
|
||||
|
|
@ -1403,18 +1395,18 @@ impl PlaceContext {
|
|||
|
||||
/// Returns `true` if this place context represents a use that potentially changes the value.
|
||||
#[inline]
|
||||
pub fn is_mutating_use(&self) -> bool {
|
||||
pub fn is_mutating_use(self) -> bool {
|
||||
matches!(self, PlaceContext::MutatingUse(..))
|
||||
}
|
||||
|
||||
/// Returns `true` if this place context represents a use.
|
||||
#[inline]
|
||||
pub fn is_use(&self) -> bool {
|
||||
pub fn is_use(self) -> bool {
|
||||
!matches!(self, PlaceContext::NonUse(..))
|
||||
}
|
||||
|
||||
/// Returns `true` if this place context represents an assignment statement.
|
||||
pub fn is_place_assignment(&self) -> bool {
|
||||
pub fn is_place_assignment(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
PlaceContext::MutatingUse(
|
||||
|
|
@ -1424,4 +1416,19 @@ impl PlaceContext {
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
/// The variance of a place in the given context.
|
||||
pub fn ambient_variance(self) -> ty::Variance {
|
||||
use NonMutatingUseContext::*;
|
||||
use NonUseContext::*;
|
||||
match self {
|
||||
PlaceContext::MutatingUse(_) => ty::Invariant,
|
||||
PlaceContext::NonUse(StorageDead | StorageLive | VarDebugInfo) => ty::Invariant,
|
||||
PlaceContext::NonMutatingUse(
|
||||
Inspect | Copy | Move | PlaceMention | SharedBorrow | FakeBorrow | RawBorrow
|
||||
| Projection,
|
||||
) => ty::Covariant,
|
||||
PlaceContext::NonUse(AscribeUserTy(variance)) => variance,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -471,7 +471,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
|||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(_, _)
|
||||
| ty::Never
|
||||
| ty::Tuple(_) => {
|
||||
| ty::Tuple(_)
|
||||
| ty::UnsafeBinder(_) => {
|
||||
let simp = ty::fast_reject::simplify_type(
|
||||
tcx,
|
||||
self_ty,
|
||||
|
|
@ -2295,6 +2296,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
Ref,
|
||||
FnDef,
|
||||
FnPtr,
|
||||
UnsafeBinder,
|
||||
Placeholder,
|
||||
Coroutine,
|
||||
CoroutineWitness,
|
||||
|
|
|
|||
|
|
@ -191,6 +191,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
_ => "fn item".into(),
|
||||
},
|
||||
ty::FnPtr(..) => "fn pointer".into(),
|
||||
ty::UnsafeBinder(_) => "unsafe binder".into(),
|
||||
ty::Dynamic(..) => "trait object".into(),
|
||||
ty::Closure(..) | ty::CoroutineClosure(..) => "closure".into(),
|
||||
ty::Coroutine(def_id, ..) => {
|
||||
|
|
|
|||
|
|
@ -253,6 +253,12 @@ impl FlagComputation {
|
|||
&ty::FnPtr(sig_tys, _) => self.bound_computation(sig_tys, |computation, sig_tys| {
|
||||
computation.add_tys(sig_tys.inputs_and_output);
|
||||
}),
|
||||
|
||||
&ty::UnsafeBinder(bound_ty) => {
|
||||
self.bound_computation(bound_ty.into(), |computation, ty| {
|
||||
computation.add_ty(ty);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -816,6 +816,11 @@ where
|
|||
bug!("TyAndLayout::field({:?}): not applicable", this)
|
||||
}
|
||||
|
||||
ty::UnsafeBinder(bound_ty) => {
|
||||
let ty = tcx.instantiate_bound_regions_with_erased(bound_ty.into());
|
||||
field_ty_or_layout(TyAndLayout { ty, ..this }, cx, i)
|
||||
}
|
||||
|
||||
// Potentially-wide pointers.
|
||||
ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => {
|
||||
assert!(i < this.fields.count());
|
||||
|
|
|
|||
|
|
@ -291,6 +291,7 @@ fn characteristic_def_id_of_type_cached<'a>(
|
|||
| ty::Uint(_)
|
||||
| ty::Str
|
||||
| ty::FnPtr(..)
|
||||
| ty::UnsafeBinder(_)
|
||||
| ty::Alias(..)
|
||||
| ty::Placeholder(..)
|
||||
| ty::Param(_)
|
||||
|
|
|
|||
|
|
@ -695,6 +695,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
|
|||
}
|
||||
}
|
||||
ty::FnPtr(ref sig_tys, hdr) => p!(print(sig_tys.with(hdr))),
|
||||
ty::UnsafeBinder(ref bound_ty) => {
|
||||
// FIXME(unsafe_binders): Make this print `unsafe<>` rather than `for<>`.
|
||||
self.wrap_binder(bound_ty, |ty, cx| cx.pretty_print_type(*ty))?;
|
||||
}
|
||||
ty::Infer(infer_ty) => {
|
||||
if self.should_print_verbose() {
|
||||
p!(write("{:?}", ty.kind()));
|
||||
|
|
@ -837,6 +841,12 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
|
|||
p!(
|
||||
" upvar_tys=",
|
||||
print(args.as_coroutine().tupled_upvars_ty()),
|
||||
" resume_ty=",
|
||||
print(args.as_coroutine().resume_ty()),
|
||||
" yield_ty=",
|
||||
print(args.as_coroutine().yield_ty()),
|
||||
" return_ty=",
|
||||
print(args.as_coroutine().return_ty()),
|
||||
" witness=",
|
||||
print(args.as_coroutine().witness())
|
||||
);
|
||||
|
|
|
|||
|
|
@ -393,6 +393,7 @@ impl<'tcx> TypeSuperFoldable<TyCtxt<'tcx>> for Ty<'tcx> {
|
|||
ty::Tuple(ts) => ty::Tuple(ts.try_fold_with(folder)?),
|
||||
ty::FnDef(def_id, args) => ty::FnDef(def_id, args.try_fold_with(folder)?),
|
||||
ty::FnPtr(sig_tys, hdr) => ty::FnPtr(sig_tys.try_fold_with(folder)?, hdr),
|
||||
ty::UnsafeBinder(f) => ty::UnsafeBinder(f.try_fold_with(folder)?),
|
||||
ty::Ref(r, ty, mutbl) => {
|
||||
ty::Ref(r.try_fold_with(folder)?, ty.try_fold_with(folder)?, mutbl)
|
||||
}
|
||||
|
|
@ -443,6 +444,7 @@ impl<'tcx> TypeSuperVisitable<TyCtxt<'tcx>> for Ty<'tcx> {
|
|||
ty::Tuple(ts) => ts.visit_with(visitor),
|
||||
ty::FnDef(_, args) => args.visit_with(visitor),
|
||||
ty::FnPtr(ref sig_tys, _) => sig_tys.visit_with(visitor),
|
||||
ty::UnsafeBinder(ref f) => f.visit_with(visitor),
|
||||
ty::Ref(r, ty, _) => {
|
||||
try_visit!(r.visit_with(visitor));
|
||||
ty.visit_with(visitor)
|
||||
|
|
|
|||
|
|
@ -673,6 +673,11 @@ impl<'tcx> Ty<'tcx> {
|
|||
Ty::new(tcx, FnPtr(sig_tys, hdr))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_unsafe_binder(tcx: TyCtxt<'tcx>, b: Binder<'tcx, Ty<'tcx>>) -> Ty<'tcx> {
|
||||
Ty::new(tcx, UnsafeBinder(b.into()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_dynamic(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
|
@ -962,6 +967,10 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
|
|||
Ty::new_pat(interner, ty, pat)
|
||||
}
|
||||
|
||||
fn new_unsafe_binder(interner: TyCtxt<'tcx>, ty: ty::Binder<'tcx, Ty<'tcx>>) -> Self {
|
||||
Ty::new_unsafe_binder(interner, ty)
|
||||
}
|
||||
|
||||
fn new_unit(interner: TyCtxt<'tcx>) -> Self {
|
||||
interner.types.unit
|
||||
}
|
||||
|
|
@ -1480,6 +1489,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::UnsafeBinder(_)
|
||||
| ty::Error(_)
|
||||
| ty::Infer(IntVar(_) | FloatVar(_)) => tcx.types.u8,
|
||||
|
||||
|
|
@ -1659,6 +1669,8 @@ impl<'tcx> Ty<'tcx> {
|
|||
// metadata of `tail`.
|
||||
ty::Param(_) | ty::Alias(..) => Err(tail),
|
||||
|
||||
| ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"),
|
||||
|
||||
ty::Infer(ty::TyVar(_))
|
||||
| ty::Pat(..)
|
||||
| ty::Bound(..)
|
||||
|
|
@ -1819,6 +1831,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
| ty::Float(_)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(..)
|
||||
| ty::UnsafeBinder(_)
|
||||
| ty::RawPtr(..)
|
||||
| ty::Char
|
||||
| ty::Ref(..)
|
||||
|
|
@ -1898,6 +1911,8 @@ impl<'tcx> Ty<'tcx> {
|
|||
// Might be, but not "trivial" so just giving the safe answer.
|
||||
ty::Adt(..) | ty::Closure(..) | ty::CoroutineClosure(..) => false,
|
||||
|
||||
ty::UnsafeBinder(_) => false,
|
||||
|
||||
// Needs normalization or revealing to determine, so no is the safe answer.
|
||||
ty::Alias(..) => false,
|
||||
|
||||
|
|
@ -1976,7 +1991,8 @@ impl<'tcx> Ty<'tcx> {
|
|||
| Coroutine(_, _)
|
||||
| CoroutineWitness(..)
|
||||
| Never
|
||||
| Tuple(_) => true,
|
||||
| Tuple(_)
|
||||
| UnsafeBinder(_) => true,
|
||||
Error(_) | Infer(_) | Alias(_, _) | Param(_) | Bound(_, _) | Placeholder(_) => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1241,6 +1241,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
| ty::Foreign(_)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::UnsafeBinder(_)
|
||||
| ty::Infer(_)
|
||||
| ty::Alias(..)
|
||||
| ty::Param(_)
|
||||
|
|
@ -1281,6 +1282,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
| ty::Foreign(_)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::UnsafeBinder(_)
|
||||
| ty::Infer(_)
|
||||
| ty::Alias(..)
|
||||
| ty::Param(_)
|
||||
|
|
@ -1322,6 +1324,9 @@ impl<'tcx> Ty<'tcx> {
|
|||
| ty::Infer(ty::FreshIntTy(_))
|
||||
| ty::Infer(ty::FreshFloatTy(_)) => AsyncDropGlueMorphology::Noop,
|
||||
|
||||
// FIXME(unsafe_binders):
|
||||
ty::UnsafeBinder(_) => todo!(),
|
||||
|
||||
ty::Tuple(tys) if tys.is_empty() => AsyncDropGlueMorphology::Noop,
|
||||
ty::Adt(adt_def, _) if adt_def.is_manually_drop() => AsyncDropGlueMorphology::Noop,
|
||||
|
||||
|
|
@ -1522,7 +1527,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
false
|
||||
}
|
||||
|
||||
ty::Foreign(_) | ty::CoroutineWitness(..) | ty::Error(_) => false,
|
||||
ty::Foreign(_) | ty::CoroutineWitness(..) | ty::Error(_) | ty::UnsafeBinder(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1681,7 +1686,8 @@ pub fn needs_drop_components_with_async<'tcx>(
|
|||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..) => Ok(smallvec![ty]),
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::UnsafeBinder(_) => Ok(smallvec![ty]),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -194,6 +194,9 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>)
|
|||
sig_tys.skip_binder().inputs_and_output.iter().rev().map(|ty| ty.into()),
|
||||
);
|
||||
}
|
||||
ty::UnsafeBinder(bound_ty) => {
|
||||
stack.push(bound_ty.skip_binder().into());
|
||||
}
|
||||
},
|
||||
GenericArgKind::Lifetime(_) => {}
|
||||
GenericArgKind::Const(parent_ct) => match parent_ct.kind() {
|
||||
|
|
|
|||
|
|
@ -246,7 +246,6 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
|
|||
let offset = self.parse_operand(args[1])?;
|
||||
Ok(Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr, offset))))
|
||||
},
|
||||
@call(mir_len, args) => Ok(Rvalue::Len(self.parse_place(args[0])?)),
|
||||
@call(mir_ptr_metadata, args) => Ok(Rvalue::UnaryOp(UnOp::PtrMetadata, self.parse_operand(args[0])?)),
|
||||
@call(mir_copy_for_deref, args) => Ok(Rvalue::CopyForDeref(self.parse_place(args[0])?)),
|
||||
ExprKind::Borrow { borrow_kind, arg } => Ok(
|
||||
|
|
|
|||
|
|
@ -635,7 +635,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
///
|
||||
/// For arrays it'll be `Operand::Constant` with the actual length;
|
||||
/// For slices it'll be `Operand::Move` of a local using `PtrMetadata`.
|
||||
fn len_of_slice_or_array(
|
||||
pub(in crate::builder) fn len_of_slice_or_array(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
place: Place<'tcx>,
|
||||
|
|
|
|||
|
|
@ -243,11 +243,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
|
||||
TestKind::Len { len, op } => {
|
||||
let usize_ty = self.tcx.types.usize;
|
||||
let actual = self.temp(usize_ty, test.span);
|
||||
|
||||
// actual = len(place)
|
||||
self.cfg.push_assign(block, source_info, actual, Rvalue::Len(place));
|
||||
let actual = self.len_of_slice_or_array(block, place, test.span, source_info);
|
||||
|
||||
// expected = <N>
|
||||
let expected = self.push_usize(block, source_info, len);
|
||||
|
|
@ -262,7 +259,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
fail_block,
|
||||
source_info,
|
||||
op,
|
||||
Operand::Move(actual),
|
||||
actual,
|
||||
Operand::Move(expected),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1481,14 +1481,6 @@ fn build_scope_drops<'tcx>(
|
|||
block = next;
|
||||
}
|
||||
DropKind::ForLint => {
|
||||
// If the operand has been moved, and we are not on an unwind
|
||||
// path, then don't generate the drop. (We only take this into
|
||||
// account for non-unwind paths so as not to disturb the
|
||||
// caching mechanism.)
|
||||
if scope.moved_locals.iter().any(|&o| o == local) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// As in the `DropKind::Storage` case below:
|
||||
// normally lint-related drops are not emitted for unwind,
|
||||
// so we can just leave `unwind_to` unmodified, but in some
|
||||
|
|
@ -1500,6 +1492,14 @@ fn build_scope_drops<'tcx>(
|
|||
unwind_to = unwind_drops.drops[unwind_to].next;
|
||||
}
|
||||
|
||||
// If the operand has been moved, and we are not on an unwind
|
||||
// path, then don't generate the drop. (We only take this into
|
||||
// account for non-unwind paths so as not to disturb the
|
||||
// caching mechanism.)
|
||||
if scope.moved_locals.iter().any(|&o| o == local) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cfg.push(block, Statement {
|
||||
source_info,
|
||||
kind: StatementKind::BackwardIncompatibleDropHint {
|
||||
|
|
@ -1552,7 +1552,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
|
|||
let mut unwind_indices = IndexVec::from_elem_n(unwind_target, 1);
|
||||
for (drop_idx, drop_node) in drops.drops.iter_enumerated().skip(1) {
|
||||
match drop_node.data.kind {
|
||||
DropKind::Storage => {
|
||||
DropKind::Storage | DropKind::ForLint => {
|
||||
if is_coroutine {
|
||||
let unwind_drop = self
|
||||
.scopes
|
||||
|
|
@ -1563,7 +1563,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
|
|||
unwind_indices.push(unwind_indices[drop_node.next]);
|
||||
}
|
||||
}
|
||||
DropKind::Value | DropKind::ForLint => {
|
||||
DropKind::Value => {
|
||||
let unwind_drop = self
|
||||
.scopes
|
||||
.unwind_drops
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{fmt, iter};
|
||||
use std::{fmt, iter, mem};
|
||||
|
||||
use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
|
|
@ -6,6 +6,7 @@ use rustc_index::Idx;
|
|||
use rustc_middle::mir::patch::MirPatch;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::adjustment::PointerCoercion;
|
||||
use rustc_middle::ty::util::IntTypeExt;
|
||||
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt};
|
||||
use rustc_span::DUMMY_SP;
|
||||
|
|
@ -738,8 +739,13 @@ where
|
|||
loop_block
|
||||
}
|
||||
|
||||
fn open_drop_for_array(&mut self, ety: Ty<'tcx>, opt_size: Option<u64>) -> BasicBlock {
|
||||
debug!("open_drop_for_array({:?}, {:?})", ety, opt_size);
|
||||
fn open_drop_for_array(
|
||||
&mut self,
|
||||
array_ty: Ty<'tcx>,
|
||||
ety: Ty<'tcx>,
|
||||
opt_size: Option<u64>,
|
||||
) -> BasicBlock {
|
||||
debug!("open_drop_for_array({:?}, {:?}, {:?})", array_ty, ety, opt_size);
|
||||
let tcx = self.tcx();
|
||||
|
||||
if let Some(size) = opt_size {
|
||||
|
|
@ -801,13 +807,50 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
self.drop_loop_pair(ety)
|
||||
let array_ptr_ty = Ty::new_mut_ptr(tcx, array_ty);
|
||||
let array_ptr = self.new_temp(array_ptr_ty);
|
||||
|
||||
let slice_ty = Ty::new_slice(tcx, ety);
|
||||
let slice_ptr_ty = Ty::new_mut_ptr(tcx, slice_ty);
|
||||
let slice_ptr = self.new_temp(slice_ptr_ty);
|
||||
|
||||
let mut delegate_block = BasicBlockData {
|
||||
statements: vec![
|
||||
self.assign(Place::from(array_ptr), Rvalue::RawPtr(Mutability::Mut, self.place)),
|
||||
self.assign(
|
||||
Place::from(slice_ptr),
|
||||
Rvalue::Cast(
|
||||
CastKind::PointerCoercion(
|
||||
PointerCoercion::Unsize,
|
||||
CoercionSource::Implicit,
|
||||
),
|
||||
Operand::Move(Place::from(array_ptr)),
|
||||
slice_ptr_ty,
|
||||
),
|
||||
),
|
||||
],
|
||||
is_cleanup: self.unwind.is_cleanup(),
|
||||
terminator: None,
|
||||
};
|
||||
|
||||
let array_place = mem::replace(
|
||||
&mut self.place,
|
||||
Place::from(slice_ptr).project_deeper(&[PlaceElem::Deref], tcx),
|
||||
);
|
||||
let slice_block = self.drop_loop_pair_for_slice(ety);
|
||||
self.place = array_place;
|
||||
|
||||
delegate_block.terminator = Some(Terminator {
|
||||
source_info: self.source_info,
|
||||
kind: TerminatorKind::Goto { target: slice_block },
|
||||
});
|
||||
self.elaborator.patch().new_block(delegate_block)
|
||||
}
|
||||
|
||||
/// Creates a pair of drop-loops of `place`, which drops its contents, even
|
||||
/// in the case of 1 panic.
|
||||
fn drop_loop_pair(&mut self, ety: Ty<'tcx>) -> BasicBlock {
|
||||
debug!("drop_loop_pair({:?})", ety);
|
||||
fn drop_loop_pair_for_slice(&mut self, ety: Ty<'tcx>) -> BasicBlock {
|
||||
debug!("drop_loop_pair_for_slice({:?})", ety);
|
||||
let tcx = self.tcx();
|
||||
let len = self.new_temp(tcx.types.usize);
|
||||
let cur = self.new_temp(tcx.types.usize);
|
||||
|
|
@ -817,10 +860,24 @@ where
|
|||
|
||||
let loop_block = self.drop_loop(self.succ, cur, len, ety, unwind);
|
||||
|
||||
let [PlaceElem::Deref] = self.place.projection.as_slice() else {
|
||||
span_bug!(
|
||||
self.source_info.span,
|
||||
"Expected place for slice drop shim to be *_n, but it's {:?}",
|
||||
self.place,
|
||||
);
|
||||
};
|
||||
|
||||
let zero = self.constant_usize(0);
|
||||
let block = BasicBlockData {
|
||||
statements: vec![
|
||||
self.assign(len.into(), Rvalue::Len(self.place)),
|
||||
self.assign(
|
||||
len.into(),
|
||||
Rvalue::UnaryOp(
|
||||
UnOp::PtrMetadata,
|
||||
Operand::Copy(Place::from(self.place.local)),
|
||||
),
|
||||
),
|
||||
self.assign(cur.into(), Rvalue::Use(zero)),
|
||||
],
|
||||
is_cleanup: unwind.is_cleanup(),
|
||||
|
|
@ -863,9 +920,9 @@ where
|
|||
ty::Dynamic(..) => self.complete_drop(self.succ, self.unwind),
|
||||
ty::Array(ety, size) => {
|
||||
let size = size.try_to_target_usize(self.tcx());
|
||||
self.open_drop_for_array(*ety, size)
|
||||
self.open_drop_for_array(ty, *ety, size)
|
||||
}
|
||||
ty::Slice(ety) => self.drop_loop_pair(*ety),
|
||||
ty::Slice(ety) => self.drop_loop_pair_for_slice(*ety),
|
||||
|
||||
_ => span_bug!(self.source_info.span, "open drop from non-ADT `{:?}`", ty),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,7 +91,6 @@ where
|
|||
| Rvalue::Use(..)
|
||||
| Rvalue::ThreadLocalRef(..)
|
||||
| Rvalue::Repeat(..)
|
||||
| Rvalue::Len(..)
|
||||
| Rvalue::BinaryOp(..)
|
||||
| Rvalue::NullaryOp(..)
|
||||
| Rvalue::UnaryOp(..)
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
|
|||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::UnsafeBinder(_)
|
||||
| ty::Alias(_, _)
|
||||
| ty::Param(_)
|
||||
| ty::Bound(_, _)
|
||||
|
|
@ -200,6 +201,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
|
|||
| ty::Dynamic(_, _, _)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
| ty::UnsafeBinder(_)
|
||||
| ty::Alias(_, _)
|
||||
| ty::Param(_)
|
||||
| ty::Bound(_, _)
|
||||
|
|
@ -411,7 +413,6 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
|
|||
Rvalue::Ref(..)
|
||||
| Rvalue::RawPtr(..)
|
||||
| Rvalue::Discriminant(..)
|
||||
| Rvalue::Len(..)
|
||||
| Rvalue::NullaryOp(
|
||||
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbChecks,
|
||||
_,
|
||||
|
|
|
|||
|
|
@ -408,18 +408,6 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
|
|||
state: &mut State<FlatSet<Scalar>>,
|
||||
) -> ValueOrPlace<FlatSet<Scalar>> {
|
||||
let val = match rvalue {
|
||||
Rvalue::Len(place) => {
|
||||
let place_ty = place.ty(self.local_decls, self.tcx);
|
||||
if let ty::Array(_, len) = place_ty.ty.kind() {
|
||||
Const::Ty(self.tcx.types.usize, *len)
|
||||
.try_eval_scalar(self.tcx, self.typing_env)
|
||||
.map_or(FlatSet::Top, FlatSet::Elem)
|
||||
} else if let [ProjectionElem::Deref] = place.projection[..] {
|
||||
state.get_len(place.local.into(), &self.map)
|
||||
} else {
|
||||
FlatSet::Top
|
||||
}
|
||||
}
|
||||
Rvalue::Cast(CastKind::IntToInt | CastKind::IntToFloat, operand, ty) => {
|
||||
let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) else {
|
||||
return ValueOrPlace::Value(FlatSet::Top);
|
||||
|
|
@ -944,7 +932,8 @@ fn try_write_constant<'tcx>(
|
|||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::Dynamic(..) => throw_machine_stop_str!("unsupported type"),
|
||||
| ty::Dynamic(..)
|
||||
| ty::UnsafeBinder(_) => throw_machine_stop_str!("unsupported type"),
|
||||
|
||||
ty::Error(_) | ty::Infer(..) | ty::CoroutineWitness(..) => bug!(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -574,7 +574,6 @@ impl WriteInfo {
|
|||
| Rvalue::NullaryOp(_, _)
|
||||
| Rvalue::Ref(_, _, _)
|
||||
| Rvalue::RawPtr(_, _)
|
||||
| Rvalue::Len(_)
|
||||
| Rvalue::Discriminant(_)
|
||||
| Rvalue::CopyForDeref(_) => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -223,8 +223,6 @@ enum Value<'tcx> {
|
|||
Projection(VnIndex, ProjectionElem<VnIndex, Ty<'tcx>>),
|
||||
/// Discriminant of the given value.
|
||||
Discriminant(VnIndex),
|
||||
/// Length of an array or slice.
|
||||
Len(VnIndex),
|
||||
|
||||
// Operations.
|
||||
NullaryOp(NullOp<'tcx>, Ty<'tcx>),
|
||||
|
|
@ -513,13 +511,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
self.ecx.discriminant_for_variant(base.layout.ty, variant).discard_err()?;
|
||||
discr_value.into()
|
||||
}
|
||||
Len(slice) => {
|
||||
let slice = self.evaluated[slice].as_ref()?;
|
||||
let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
|
||||
let len = slice.len(&self.ecx).discard_err()?;
|
||||
let imm = ImmTy::from_uint(len, usize_layout);
|
||||
imm.into()
|
||||
}
|
||||
NullaryOp(null_op, ty) => {
|
||||
let layout = self.ecx.layout_of(ty).ok()?;
|
||||
if let NullOp::SizeOf | NullOp::AlignOf = null_op
|
||||
|
|
@ -863,7 +854,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
}
|
||||
|
||||
// Operations.
|
||||
Rvalue::Len(ref mut place) => return self.simplify_len(place, location),
|
||||
Rvalue::Cast(ref mut kind, ref mut value, to) => {
|
||||
return self.simplify_cast(kind, value, to, location);
|
||||
}
|
||||
|
|
@ -1433,47 +1423,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
Some(self.insert(Value::Cast { kind: *kind, value, from, to }))
|
||||
}
|
||||
|
||||
fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option<VnIndex> {
|
||||
// Trivial case: we are fetching a statically known length.
|
||||
let place_ty = place.ty(self.local_decls, self.tcx).ty;
|
||||
if let ty::Array(_, len) = place_ty.kind() {
|
||||
return self.insert_constant(Const::from_ty_const(
|
||||
*len,
|
||||
self.tcx.types.usize,
|
||||
self.tcx,
|
||||
));
|
||||
}
|
||||
|
||||
let mut inner = self.simplify_place_value(place, location)?;
|
||||
|
||||
// The length information is stored in the wide pointer.
|
||||
// Reborrowing copies length information from one pointer to the other.
|
||||
while let Value::Address { place: borrowed, .. } = self.get(inner)
|
||||
&& let [PlaceElem::Deref] = borrowed.projection[..]
|
||||
&& let Some(borrowed) = self.locals[borrowed.local]
|
||||
{
|
||||
inner = borrowed;
|
||||
}
|
||||
|
||||
// We have an unsizing cast, which assigns the length to wide pointer metadata.
|
||||
if let Value::Cast { kind, from, to, .. } = self.get(inner)
|
||||
&& let CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) = kind
|
||||
&& let Some(from) = from.builtin_deref(true)
|
||||
&& let ty::Array(_, len) = from.kind()
|
||||
&& let Some(to) = to.builtin_deref(true)
|
||||
&& let ty::Slice(..) = to.kind()
|
||||
{
|
||||
return self.insert_constant(Const::from_ty_const(
|
||||
*len,
|
||||
self.tcx.types.usize,
|
||||
self.tcx,
|
||||
));
|
||||
}
|
||||
|
||||
// Fallback: a symbolic `Len`.
|
||||
Some(self.insert(Value::Len(inner)))
|
||||
}
|
||||
|
||||
fn pointers_have_same_metadata(&self, left_ptr_ty: Ty<'tcx>, right_ptr_ty: Ty<'tcx>) -> bool {
|
||||
let left_meta_ty = left_ptr_ty.pointee_metadata_ty_or_projection(self.tcx);
|
||||
let right_meta_ty = right_ptr_ty.pointee_metadata_ty_or_projection(self.tcx);
|
||||
|
|
|
|||
|
|
@ -440,7 +440,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
|||
| Rvalue::Use(..)
|
||||
| Rvalue::CopyForDeref(..)
|
||||
| Rvalue::Repeat(..)
|
||||
| Rvalue::Len(..)
|
||||
| Rvalue::Cast(..)
|
||||
| Rvalue::ShallowInitBox(..)
|
||||
| Rvalue::Discriminant(..)
|
||||
|
|
@ -600,20 +599,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
|||
return None;
|
||||
}
|
||||
|
||||
Len(place) => {
|
||||
let len = if let ty::Array(_, n) = place.ty(self.local_decls(), self.tcx).ty.kind()
|
||||
{
|
||||
n.try_to_target_usize(self.tcx)?
|
||||
} else {
|
||||
match self.get_const(place)? {
|
||||
Value::Immediate(src) => src.len(&self.ecx).discard_err()?,
|
||||
Value::Aggregate { fields, .. } => fields.len() as u64,
|
||||
Value::Uninit => return None,
|
||||
}
|
||||
};
|
||||
ImmTy::from_scalar(Scalar::from_target_usize(len, self), layout).into()
|
||||
}
|
||||
|
||||
Ref(..) | RawPtr(..) => return None,
|
||||
|
||||
NullaryOp(ref null_op, ty) => {
|
||||
|
|
|
|||
|
|
@ -437,6 +437,8 @@ fn mir_promoted(
|
|||
Some(MirPhase::Analysis(AnalysisPhase::Initial)),
|
||||
);
|
||||
|
||||
lint_tail_expr_drop_order::run_lint(tcx, def, &body);
|
||||
|
||||
let promoted = promote_pass.promoted_fragments.into_inner();
|
||||
(tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
|
||||
}
|
||||
|
|
@ -492,7 +494,6 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &
|
|||
}
|
||||
|
||||
let (body, _) = tcx.mir_promoted(def);
|
||||
lint_tail_expr_drop_order::run_lint(tcx, def, &body.borrow());
|
||||
let mut body = body.steal();
|
||||
|
||||
if let Some(error_reported) = tainted_by_errors {
|
||||
|
|
|
|||
|
|
@ -285,7 +285,9 @@ fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Span> {
|
|||
| ty::Placeholder(_)
|
||||
| ty::Infer(_)
|
||||
| ty::Slice(_)
|
||||
| ty::Array(_, _) => None,
|
||||
| ty::Array(_, _)
|
||||
| ty::UnsafeBinder(_) => None,
|
||||
|
||||
ty::Adt(adt_def, _) => {
|
||||
let did = adt_def.did();
|
||||
let try_local_did_span = |did: DefId| {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use rustc_middle::mir::*;
|
|||
use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
|
||||
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
|
||||
use rustc_type_ir::TyKind::*;
|
||||
use tracing::instrument;
|
||||
|
||||
use super::simplify::simplify_cfg;
|
||||
|
||||
|
|
@ -51,7 +52,7 @@ impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification {
|
|||
}
|
||||
|
||||
trait SimplifyMatch<'tcx> {
|
||||
/// Simplifies a match statement, returning true if the simplification succeeds, false
|
||||
/// Simplifies a match statement, returning `Some` if the simplification succeeds, `None`
|
||||
/// otherwise. Generic code is written here, and we generally don't need a custom
|
||||
/// implementation.
|
||||
fn simplify(
|
||||
|
|
@ -159,6 +160,7 @@ struct SimplifyToIf;
|
|||
/// }
|
||||
/// ```
|
||||
impl<'tcx> SimplifyMatch<'tcx> for SimplifyToIf {
|
||||
#[instrument(level = "debug", skip(self, tcx), ret)]
|
||||
fn can_simplify(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
|
@ -167,12 +169,15 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToIf {
|
|||
bbs: &IndexSlice<BasicBlock, BasicBlockData<'tcx>>,
|
||||
_discr_ty: Ty<'tcx>,
|
||||
) -> Option<()> {
|
||||
if targets.iter().len() != 1 {
|
||||
return None;
|
||||
}
|
||||
let (first, second) = match targets.all_targets() {
|
||||
&[first, otherwise] => (first, otherwise),
|
||||
&[first, second, otherwise] if bbs[otherwise].is_empty_unreachable() => (first, second),
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// We require that the possible target blocks all be distinct.
|
||||
let (_, first) = targets.iter().next().unwrap();
|
||||
let second = targets.otherwise();
|
||||
if first == second {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -221,8 +226,14 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToIf {
|
|||
discr_local: Local,
|
||||
discr_ty: Ty<'tcx>,
|
||||
) {
|
||||
let (val, first) = targets.iter().next().unwrap();
|
||||
let second = targets.otherwise();
|
||||
let ((val, first), second) = match (targets.all_targets(), targets.all_values()) {
|
||||
(&[first, otherwise], &[val]) => ((val, first), otherwise),
|
||||
(&[first, second, otherwise], &[val, _]) if bbs[otherwise].is_empty_unreachable() => {
|
||||
((val, first), second)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// We already checked that first and second are different blocks,
|
||||
// and bb_idx has a different terminator from both of them.
|
||||
let first = &bbs[first];
|
||||
|
|
@ -297,7 +308,7 @@ struct SimplifyToExp {
|
|||
transform_kinds: Vec<TransformKind>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum ExpectedTransformKind<'a, 'tcx> {
|
||||
/// Identical statements.
|
||||
Same(&'a StatementKind<'tcx>),
|
||||
|
|
@ -362,6 +373,7 @@ impl From<ExpectedTransformKind<'_, '_>> for TransformKind {
|
|||
/// }
|
||||
/// ```
|
||||
impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp {
|
||||
#[instrument(level = "debug", skip(self, tcx), ret)]
|
||||
fn can_simplify(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
|
|
|||
|
|
@ -430,9 +430,7 @@ impl<'tcx> Validator<'_, 'tcx> {
|
|||
self.validate_operand(op)?
|
||||
}
|
||||
|
||||
Rvalue::Discriminant(place) | Rvalue::Len(place) => {
|
||||
self.validate_place(place.as_ref())?
|
||||
}
|
||||
Rvalue::Discriminant(place) => self.validate_place(place.as_ref())?,
|
||||
|
||||
Rvalue::ThreadLocalRef(_) => return Err(Unpromotable),
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue