Auto merge of #3138 - rust-lang:rustup-2023-10-24, r=RalfJung
Automatic Rustup
This commit is contained in:
commit
cc13d045f4
311 changed files with 8272 additions and 3049 deletions
2
.mailmap
2
.mailmap
|
|
@ -31,6 +31,8 @@ Alexis Beingessner <a.beingessner@gmail.com>
|
|||
Alfie John <alfie@alfie.wtf> Alfie John <alfiej@fastmail.fm>
|
||||
Alona Enraght-Moony <code@alona.page> <nixon.emoony@gmail.com>
|
||||
Alona Enraght-Moony <code@alona.page> <nixon@caminus.local>
|
||||
Amanda Stjerna <mail@amandastjerna.se> <albin.stjerna@gmail.com>
|
||||
Amanda Stjerna <mail@amandastjerna.se> <amanda.stjerna@it.uu.se>
|
||||
Amos Onn <amosonn@gmail.com>
|
||||
Ana-Maria Mihalache <mihalacheana.maria@yahoo.com>
|
||||
Anatoly Ikorsky <aikorsky@gmail.com>
|
||||
|
|
|
|||
57
Cargo.lock
57
Cargo.lock
|
|
@ -143,9 +143,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.1"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
|
|
@ -370,11 +370,11 @@ checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
|
|||
|
||||
[[package]]
|
||||
name = "bytecount"
|
||||
version = "0.6.3"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c"
|
||||
checksum = "ad152d03a2c813c80bb94fedbf3a3f02b28f793e39e7c214c8a0bcc196343de7"
|
||||
dependencies = [
|
||||
"packed_simd_2",
|
||||
"packed_simd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -501,6 +501,16 @@ dependencies = [
|
|||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap-cargo"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "383f21342a464d4af96e9a4cad22a0b4f2880d4a5b3bbf5c9654dd1d9a224ee4"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"clap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.4"
|
||||
|
|
@ -1777,7 +1787,7 @@ version = "2.1.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
|
||||
dependencies = [
|
||||
"libm 0.2.7",
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2228,12 +2238,6 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.7"
|
||||
|
|
@ -2594,6 +2598,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2743,13 +2748,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||
|
||||
[[package]]
|
||||
name = "packed_simd_2"
|
||||
version = "0.3.8"
|
||||
name = "packed_simd"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282"
|
||||
checksum = "1f9f08af0c877571712e2e3e686ad79efad9657dbf0f7c3c8ba943ff6c38932d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libm 0.1.4",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3596,6 +3601,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cstr",
|
||||
"itertools",
|
||||
"libc",
|
||||
"measureme",
|
||||
"object",
|
||||
|
|
@ -4273,6 +4279,7 @@ dependencies = [
|
|||
"coverage_test_macros",
|
||||
"either",
|
||||
"itertools",
|
||||
"rustc_arena",
|
||||
"rustc_ast",
|
||||
"rustc_attr",
|
||||
"rustc_const_eval",
|
||||
|
|
@ -4514,9 +4521,7 @@ name = "rustc_smir"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rustc_data_structures",
|
||||
"rustc_driver",
|
||||
"rustc_hir",
|
||||
"rustc_interface",
|
||||
"rustc_middle",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
|
|
@ -4758,21 +4763,20 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustfmt-nightly"
|
||||
version = "1.6.0"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"annotate-snippets",
|
||||
"anyhow",
|
||||
"bytecount",
|
||||
"cargo_metadata 0.15.4",
|
||||
"clap",
|
||||
"clap-cargo",
|
||||
"diff",
|
||||
"dirs",
|
||||
"env_logger 0.10.0",
|
||||
"getopts",
|
||||
"ignore",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"regex",
|
||||
"rustfmt-config_proc_macro",
|
||||
"serde",
|
||||
|
|
@ -4780,9 +4784,11 @@ dependencies = [
|
|||
"term",
|
||||
"thiserror",
|
||||
"toml 0.7.5",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"unicode-properties",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
"unicode_categories",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5676,6 +5682,7 @@ dependencies = [
|
|||
"thread_local",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5891,12 +5898,6 @@ version = "0.2.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_categories"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||
|
||||
[[package]]
|
||||
name = "unified-diff"
|
||||
version = "0.2.1"
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ pub mod visit;
|
|||
|
||||
pub use self::ast::*;
|
||||
pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasSpan, HasTokens};
|
||||
pub use self::format::*;
|
||||
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
|
||||
|
|
|
|||
|
|
@ -31,16 +31,6 @@ pub fn is_builtin_attr(attr: &Attribute) -> bool {
|
|||
attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
|
||||
}
|
||||
|
||||
enum AttrError {
|
||||
MultipleItem(String),
|
||||
UnknownMetaItem(String, &'static [&'static str]),
|
||||
MissingSince,
|
||||
NonIdentFeature,
|
||||
MissingFeature,
|
||||
MultipleStabilityLevels,
|
||||
UnsupportedLiteral(UnsupportedLiteralReason, /* is_bytestr */ bool),
|
||||
}
|
||||
|
||||
pub(crate) enum UnsupportedLiteralReason {
|
||||
Generic,
|
||||
CfgString,
|
||||
|
|
@ -48,37 +38,6 @@ pub(crate) enum UnsupportedLiteralReason {
|
|||
DeprecatedKvPair,
|
||||
}
|
||||
|
||||
fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) {
|
||||
match error {
|
||||
AttrError::MultipleItem(item) => {
|
||||
sess.emit_err(session_diagnostics::MultipleItem { span, item });
|
||||
}
|
||||
AttrError::UnknownMetaItem(item, expected) => {
|
||||
sess.emit_err(session_diagnostics::UnknownMetaItem { span, item, expected });
|
||||
}
|
||||
AttrError::MissingSince => {
|
||||
sess.emit_err(session_diagnostics::MissingSince { span });
|
||||
}
|
||||
AttrError::NonIdentFeature => {
|
||||
sess.emit_err(session_diagnostics::NonIdentFeature { span });
|
||||
}
|
||||
AttrError::MissingFeature => {
|
||||
sess.emit_err(session_diagnostics::MissingFeature { span });
|
||||
}
|
||||
AttrError::MultipleStabilityLevels => {
|
||||
sess.emit_err(session_diagnostics::MultipleStabilityLevels { span });
|
||||
}
|
||||
AttrError::UnsupportedLiteral(reason, is_bytestr) => {
|
||||
sess.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span,
|
||||
reason,
|
||||
is_bytestr,
|
||||
start_point_span: sess.source_map().start_point(span),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub enum InlineAttr {
|
||||
None,
|
||||
|
|
@ -241,7 +200,7 @@ pub fn find_stability(
|
|||
sym::rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true,
|
||||
sym::unstable => {
|
||||
if stab.is_some() {
|
||||
handle_errors(&sess.parse_sess, attr.span, AttrError::MultipleStabilityLevels);
|
||||
sess.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span });
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -251,7 +210,7 @@ pub fn find_stability(
|
|||
}
|
||||
sym::stable => {
|
||||
if stab.is_some() {
|
||||
handle_errors(&sess.parse_sess, attr.span, AttrError::MultipleStabilityLevels);
|
||||
sess.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span });
|
||||
break;
|
||||
}
|
||||
if let Some((feature, level)) = parse_stability(sess, attr) {
|
||||
|
|
@ -295,7 +254,7 @@ pub fn find_const_stability(
|
|||
sym::rustc_promotable => promotable = true,
|
||||
sym::rustc_const_unstable => {
|
||||
if const_stab.is_some() {
|
||||
handle_errors(&sess.parse_sess, attr.span, AttrError::MultipleStabilityLevels);
|
||||
sess.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span });
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -306,7 +265,7 @@ pub fn find_const_stability(
|
|||
}
|
||||
sym::rustc_const_stable => {
|
||||
if const_stab.is_some() {
|
||||
handle_errors(&sess.parse_sess, attr.span, AttrError::MultipleStabilityLevels);
|
||||
sess.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span });
|
||||
break;
|
||||
}
|
||||
if let Some((feature, level)) = parse_stability(sess, attr) {
|
||||
|
|
@ -340,7 +299,7 @@ pub fn find_body_stability(
|
|||
for attr in attrs {
|
||||
if attr.has_name(sym::rustc_default_body_unstable) {
|
||||
if body_stab.is_some() {
|
||||
handle_errors(&sess.parse_sess, attr.span, AttrError::MultipleStabilityLevels);
|
||||
sess.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span });
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -355,11 +314,10 @@ pub fn find_body_stability(
|
|||
|
||||
fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option<Symbol>) -> Option<()> {
|
||||
if item.is_some() {
|
||||
handle_errors(
|
||||
&sess.parse_sess,
|
||||
meta.span,
|
||||
AttrError::MultipleItem(pprust::path_to_string(&meta.path)),
|
||||
);
|
||||
sess.emit_err(session_diagnostics::MultipleItem {
|
||||
span: meta.span,
|
||||
item: pprust::path_to_string(&meta.path),
|
||||
});
|
||||
None
|
||||
} else if let Some(v) = meta.value_str() {
|
||||
*item = Some(v);
|
||||
|
|
@ -380,11 +338,12 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit
|
|||
let mut since = None;
|
||||
for meta in metas {
|
||||
let Some(mi) = meta.meta_item() else {
|
||||
handle_errors(
|
||||
&sess.parse_sess,
|
||||
meta.span(),
|
||||
AttrError::UnsupportedLiteral(UnsupportedLiteralReason::Generic, false),
|
||||
);
|
||||
sess.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: meta.span(),
|
||||
reason: UnsupportedLiteralReason::Generic,
|
||||
is_bytestr: false,
|
||||
start_point_span: sess.source_map().start_point(meta.span()),
|
||||
});
|
||||
return None;
|
||||
};
|
||||
|
||||
|
|
@ -392,14 +351,11 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit
|
|||
sym::feature => insert_or_error(sess, mi, &mut feature)?,
|
||||
sym::since => insert_or_error(sess, mi, &mut since)?,
|
||||
_ => {
|
||||
handle_errors(
|
||||
&sess.parse_sess,
|
||||
meta.span(),
|
||||
AttrError::UnknownMetaItem(
|
||||
pprust::path_to_string(&mi.path),
|
||||
&["feature", "since"],
|
||||
),
|
||||
);
|
||||
sess.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: meta.span(),
|
||||
item: pprust::path_to_string(&mi.path),
|
||||
expected: &["feature", "since"],
|
||||
});
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
|
@ -417,11 +373,11 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit
|
|||
Some((feature, level))
|
||||
}
|
||||
(None, _) => {
|
||||
handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature);
|
||||
sess.emit_err(session_diagnostics::MissingFeature { span: attr.span });
|
||||
None
|
||||
}
|
||||
_ => {
|
||||
handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince);
|
||||
sess.emit_err(session_diagnostics::MissingSince { span: attr.span });
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
@ -441,11 +397,12 @@ fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabil
|
|||
let mut implied_by = None;
|
||||
for meta in metas {
|
||||
let Some(mi) = meta.meta_item() else {
|
||||
handle_errors(
|
||||
&sess.parse_sess,
|
||||
meta.span(),
|
||||
AttrError::UnsupportedLiteral(UnsupportedLiteralReason::Generic, false),
|
||||
);
|
||||
sess.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: meta.span(),
|
||||
reason: UnsupportedLiteralReason::Generic,
|
||||
is_bytestr: false,
|
||||
start_point_span: sess.source_map().start_point(meta.span()),
|
||||
});
|
||||
return None;
|
||||
};
|
||||
|
||||
|
|
@ -484,14 +441,11 @@ fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabil
|
|||
}
|
||||
sym::implied_by => insert_or_error(sess, mi, &mut implied_by)?,
|
||||
_ => {
|
||||
handle_errors(
|
||||
&sess.parse_sess,
|
||||
meta.span(),
|
||||
AttrError::UnknownMetaItem(
|
||||
pprust::path_to_string(&mi.path),
|
||||
&["feature", "reason", "issue", "soft", "implied_by"],
|
||||
),
|
||||
);
|
||||
sess.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: meta.span(),
|
||||
item: pprust::path_to_string(&mi.path),
|
||||
expected: &["feature", "reason", "issue", "soft", "implied_by"],
|
||||
});
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
|
@ -500,7 +454,7 @@ fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabil
|
|||
match (feature, reason, issue) {
|
||||
(Some(feature), reason, Some(_)) => {
|
||||
if !rustc_lexer::is_ident(feature.as_str()) {
|
||||
handle_errors(&sess.parse_sess, attr.span, AttrError::NonIdentFeature);
|
||||
sess.emit_err(session_diagnostics::NonIdentFeature { span: attr.span });
|
||||
return None;
|
||||
}
|
||||
let level = StabilityLevel::Unstable {
|
||||
|
|
@ -512,7 +466,7 @@ fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabil
|
|||
Some((feature, level))
|
||||
}
|
||||
(None, _, _) => {
|
||||
handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature);
|
||||
sess.emit_err(session_diagnostics::MissingFeature { span: attr.span });
|
||||
return None;
|
||||
}
|
||||
_ => {
|
||||
|
|
@ -659,11 +613,12 @@ pub fn eval_condition(
|
|||
ast::MetaItemKind::List(mis) => {
|
||||
for mi in mis.iter() {
|
||||
if !mi.is_meta_item() {
|
||||
handle_errors(
|
||||
sess,
|
||||
mi.span(),
|
||||
AttrError::UnsupportedLiteral(UnsupportedLiteralReason::Generic, false),
|
||||
);
|
||||
sess.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: mi.span(),
|
||||
reason: UnsupportedLiteralReason::Generic,
|
||||
is_bytestr: false,
|
||||
start_point_span: sess.source_map().start_point(mi.span()),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -731,14 +686,12 @@ pub fn eval_condition(
|
|||
true
|
||||
}
|
||||
MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
|
||||
handle_errors(
|
||||
sess,
|
||||
lit.span,
|
||||
AttrError::UnsupportedLiteral(
|
||||
UnsupportedLiteralReason::CfgString,
|
||||
lit.kind.is_bytestr(),
|
||||
),
|
||||
);
|
||||
sess.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: lit.span,
|
||||
reason: UnsupportedLiteralReason::CfgString,
|
||||
is_bytestr: lit.kind.is_bytestr(),
|
||||
start_point_span: sess.source_map().start_point(lit.span),
|
||||
});
|
||||
true
|
||||
}
|
||||
ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
|
||||
|
|
@ -795,11 +748,10 @@ pub fn find_deprecation(
|
|||
MetaItemKind::List(list) => {
|
||||
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
|
||||
if item.is_some() {
|
||||
handle_errors(
|
||||
&sess.parse_sess,
|
||||
meta.span,
|
||||
AttrError::MultipleItem(pprust::path_to_string(&meta.path)),
|
||||
);
|
||||
sess.emit_err(session_diagnostics::MultipleItem {
|
||||
span: meta.span,
|
||||
item: pprust::path_to_string(&meta.path),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if let Some(v) = meta.value_str() {
|
||||
|
|
@ -807,14 +759,12 @@ pub fn find_deprecation(
|
|||
true
|
||||
} else {
|
||||
if let Some(lit) = meta.name_value_literal() {
|
||||
handle_errors(
|
||||
&sess.parse_sess,
|
||||
lit.span,
|
||||
AttrError::UnsupportedLiteral(
|
||||
UnsupportedLiteralReason::DeprecatedString,
|
||||
lit.kind.is_bytestr(),
|
||||
),
|
||||
);
|
||||
sess.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: lit.span,
|
||||
reason: UnsupportedLiteralReason::DeprecatedString,
|
||||
is_bytestr: lit.kind.is_bytestr(),
|
||||
start_point_span: sess.source_map().start_point(lit.span),
|
||||
});
|
||||
} else {
|
||||
sess.emit_err(session_diagnostics::IncorrectMetaItem {
|
||||
span: meta.span,
|
||||
|
|
@ -852,30 +802,25 @@ pub fn find_deprecation(
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
handle_errors(
|
||||
&sess.parse_sess,
|
||||
meta.span(),
|
||||
AttrError::UnknownMetaItem(
|
||||
pprust::path_to_string(&mi.path),
|
||||
if features.deprecated_suggestion {
|
||||
&["since", "note", "suggestion"]
|
||||
} else {
|
||||
&["since", "note"]
|
||||
},
|
||||
),
|
||||
);
|
||||
sess.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: meta.span(),
|
||||
item: pprust::path_to_string(&mi.path),
|
||||
expected: if features.deprecated_suggestion {
|
||||
&["since", "note", "suggestion"]
|
||||
} else {
|
||||
&["since", "note"]
|
||||
},
|
||||
});
|
||||
continue 'outer;
|
||||
}
|
||||
},
|
||||
NestedMetaItem::Lit(lit) => {
|
||||
handle_errors(
|
||||
&sess.parse_sess,
|
||||
lit.span,
|
||||
AttrError::UnsupportedLiteral(
|
||||
UnsupportedLiteralReason::DeprecatedKvPair,
|
||||
false,
|
||||
),
|
||||
);
|
||||
sess.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: lit.span,
|
||||
reason: UnsupportedLiteralReason::DeprecatedKvPair,
|
||||
is_bytestr: false,
|
||||
start_point_span: sess.source_map().start_point(lit.span),
|
||||
});
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
|
|
@ -885,7 +830,7 @@ pub fn find_deprecation(
|
|||
|
||||
if is_rustc {
|
||||
if since.is_none() {
|
||||
handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince);
|
||||
sess.emit_err(session_diagnostics::MissingSince { span: attr.span });
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -272,21 +272,28 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
|
|||
loan_issued_at: Location,
|
||||
) {
|
||||
let sccs = self.regioncx.constraint_sccs();
|
||||
let universal_regions = self.regioncx.universal_regions();
|
||||
let issuing_region_scc = sccs.scc(issuing_region);
|
||||
|
||||
// We first handle the cases where the loan doesn't go out of scope, depending on the issuing
|
||||
// region's successors.
|
||||
for scc in sccs.depth_first_search(issuing_region_scc) {
|
||||
// 1. Via member constraints
|
||||
// 1. Via applied member constraints
|
||||
//
|
||||
// The issuing region can flow into the choice regions, and they are either:
|
||||
// - placeholders or free regions themselves,
|
||||
// - or also transitively outlive a free region.
|
||||
//
|
||||
// That is to say, if there are member constraints here, the loan escapes the function
|
||||
// and cannot go out of scope. We can early return.
|
||||
if self.regioncx.scc_has_member_constraints(scc) {
|
||||
return;
|
||||
// That is to say, if there are applied member constraints here, the loan escapes the
|
||||
// function and cannot go out of scope. We could early return here.
|
||||
//
|
||||
// For additional insurance via fuzzing and crater, we verify that the constraint's min
|
||||
// choice indeed escapes the function. In the future, we could e.g. turn this check into
|
||||
// a debug assert and early return as an optimization.
|
||||
for constraint in self.regioncx.applied_member_constraints(scc) {
|
||||
if universal_regions.is_universal_region(constraint.min_choice) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Via regions that are live at all points: placeholders and free regions.
|
||||
|
|
@ -413,12 +420,12 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
|
|||
let mut polonius_prec = PoloniusOutOfScopePrecomputer::new(body, regioncx);
|
||||
for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
|
||||
let issuing_region = loan_data.region;
|
||||
let issued_location = loan_data.reserve_location;
|
||||
let loan_issued_at = loan_data.reserve_location;
|
||||
|
||||
polonius_prec.precompute_loans_out_of_scope(
|
||||
loan_idx,
|
||||
issuing_region,
|
||||
issued_location,
|
||||
loan_issued_at,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2633,9 +2633,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
/* Check if the mpi is initialized as an argument */
|
||||
let mut is_argument = false;
|
||||
for arg in self.body.args_iter() {
|
||||
let path = self.move_data.rev_lookup.find_local(arg);
|
||||
if mpis.contains(&path) {
|
||||
is_argument = true;
|
||||
if let Some(path) = self.move_data.rev_lookup.find_local(arg) {
|
||||
if mpis.contains(&path) {
|
||||
is_argument = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ mod mutability_errors;
|
|||
mod region_errors;
|
||||
|
||||
pub(crate) use bound_region_errors::{ToUniverseInfo, UniverseInfo};
|
||||
pub(crate) use move_errors::{IllegalMoveOriginKind, MoveError};
|
||||
pub(crate) use mutability_errors::AccessKind;
|
||||
pub(crate) use outlives_suggestion::OutlivesSuggestionBuilder;
|
||||
pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
|
||||
|
|
@ -470,7 +471,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
ty.print(printer).unwrap().into_buffer()
|
||||
ty.print(&mut printer).unwrap();
|
||||
printer.into_buffer()
|
||||
}
|
||||
|
||||
/// Returns the name of the provided `Ty` (that must be a reference)'s region with a
|
||||
|
|
@ -492,7 +494,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
bug!("ty for annotation of borrow region is not a reference");
|
||||
};
|
||||
|
||||
region.print(printer).unwrap().into_buffer()
|
||||
region.print(&mut printer).unwrap();
|
||||
printer.into_buffer()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty;
|
||||
use rustc_mir_dataflow::move_paths::{
|
||||
IllegalMoveOrigin, IllegalMoveOriginKind, LookupResult, MoveError, MovePathIndex,
|
||||
};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
|
||||
use rustc_span::{BytePos, Span};
|
||||
|
||||
use crate::diagnostics::CapturedMessageOpt;
|
||||
|
|
@ -11,6 +9,42 @@ use crate::diagnostics::{DescribePlaceOpt, UseSpans};
|
|||
use crate::prefixes::PrefixSet;
|
||||
use crate::MirBorrowckCtxt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IllegalMoveOriginKind<'tcx> {
|
||||
/// Illegal move due to attempt to move from behind a reference.
|
||||
BorrowedContent {
|
||||
/// The place the reference refers to: if erroneous code was trying to
|
||||
/// move from `(*x).f` this will be `*x`.
|
||||
target_place: Place<'tcx>,
|
||||
},
|
||||
|
||||
/// Illegal move due to attempt to move from field of an ADT that
|
||||
/// implements `Drop`. Rust maintains invariant that all `Drop`
|
||||
/// ADT's remain fully-initialized so that user-defined destructor
|
||||
/// can safely read from all of the ADT's fields.
|
||||
InteriorOfTypeWithDestructor { container_ty: Ty<'tcx> },
|
||||
|
||||
/// Illegal move due to attempt to move out of a slice or array.
|
||||
InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct MoveError<'tcx> {
|
||||
place: Place<'tcx>,
|
||||
location: Location,
|
||||
kind: IllegalMoveOriginKind<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> MoveError<'tcx> {
|
||||
pub(crate) fn new(
|
||||
place: Place<'tcx>,
|
||||
location: Location,
|
||||
kind: IllegalMoveOriginKind<'tcx>,
|
||||
) -> Self {
|
||||
MoveError { place, location, kind }
|
||||
}
|
||||
}
|
||||
|
||||
// Often when desugaring a pattern match we may have many individual moves in
|
||||
// MIR that are all part of one operation from the user's point-of-view. For
|
||||
// example:
|
||||
|
|
@ -53,20 +87,18 @@ enum GroupedMoveError<'tcx> {
|
|||
}
|
||||
|
||||
impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
pub(crate) fn report_move_errors(&mut self, move_errors: Vec<(Place<'tcx>, MoveError<'tcx>)>) {
|
||||
let grouped_errors = self.group_move_errors(move_errors);
|
||||
pub(crate) fn report_move_errors(&mut self) {
|
||||
let grouped_errors = self.group_move_errors();
|
||||
for error in grouped_errors {
|
||||
self.report(error);
|
||||
}
|
||||
}
|
||||
|
||||
fn group_move_errors(
|
||||
&self,
|
||||
errors: Vec<(Place<'tcx>, MoveError<'tcx>)>,
|
||||
) -> Vec<GroupedMoveError<'tcx>> {
|
||||
fn group_move_errors(&mut self) -> Vec<GroupedMoveError<'tcx>> {
|
||||
let mut grouped_errors = Vec::new();
|
||||
for (original_path, error) in errors {
|
||||
self.append_to_grouped_errors(&mut grouped_errors, original_path, error);
|
||||
let errors = std::mem::take(&mut self.move_errors);
|
||||
for error in errors {
|
||||
self.append_to_grouped_errors(&mut grouped_errors, error);
|
||||
}
|
||||
grouped_errors
|
||||
}
|
||||
|
|
@ -74,66 +106,58 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
fn append_to_grouped_errors(
|
||||
&self,
|
||||
grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
|
||||
original_path: Place<'tcx>,
|
||||
error: MoveError<'tcx>,
|
||||
) {
|
||||
match error {
|
||||
MoveError::UnionMove { .. } => {
|
||||
unimplemented!("don't know how to report union move errors yet.")
|
||||
}
|
||||
MoveError::IllegalMove { cannot_move_out_of: IllegalMoveOrigin { location, kind } } => {
|
||||
// Note: that the only time we assign a place isn't a temporary
|
||||
// to a user variable is when initializing it.
|
||||
// If that ever stops being the case, then the ever initialized
|
||||
// flow could be used.
|
||||
if let Some(StatementKind::Assign(box (
|
||||
place,
|
||||
Rvalue::Use(Operand::Move(move_from)),
|
||||
))) = self.body.basic_blocks[location.block]
|
||||
.statements
|
||||
.get(location.statement_index)
|
||||
.map(|stmt| &stmt.kind)
|
||||
{
|
||||
if let Some(local) = place.as_local() {
|
||||
let local_decl = &self.body.local_decls[local];
|
||||
// opt_match_place is the
|
||||
// match_span is the span of the expression being matched on
|
||||
// match *x.y { ... } match_place is Some(*x.y)
|
||||
// ^^^^ match_span is the span of *x.y
|
||||
//
|
||||
// opt_match_place is None for let [mut] x = ... statements,
|
||||
// whether or not the right-hand side is a place expression
|
||||
if let LocalInfo::User(BindingForm::Var(VarBindingForm {
|
||||
opt_match_place: Some((opt_match_place, match_span)),
|
||||
binding_mode: _,
|
||||
opt_ty_info: _,
|
||||
pat_span: _,
|
||||
})) = *local_decl.local_info()
|
||||
{
|
||||
let stmt_source_info = self.body.source_info(location);
|
||||
self.append_binding_error(
|
||||
grouped_errors,
|
||||
kind,
|
||||
original_path,
|
||||
*move_from,
|
||||
local,
|
||||
opt_match_place,
|
||||
match_span,
|
||||
stmt_source_info.span,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
let MoveError { place: original_path, location, kind } = error;
|
||||
|
||||
let move_spans = self.move_spans(original_path.as_ref(), location);
|
||||
grouped_errors.push(GroupedMoveError::OtherIllegalMove {
|
||||
use_spans: move_spans,
|
||||
original_path,
|
||||
kind,
|
||||
});
|
||||
// Note: that the only time we assign a place isn't a temporary
|
||||
// to a user variable is when initializing it.
|
||||
// If that ever stops being the case, then the ever initialized
|
||||
// flow could be used.
|
||||
if let Some(StatementKind::Assign(box (place, Rvalue::Use(Operand::Move(move_from))))) =
|
||||
self.body.basic_blocks[location.block]
|
||||
.statements
|
||||
.get(location.statement_index)
|
||||
.map(|stmt| &stmt.kind)
|
||||
{
|
||||
if let Some(local) = place.as_local() {
|
||||
let local_decl = &self.body.local_decls[local];
|
||||
// opt_match_place is the
|
||||
// match_span is the span of the expression being matched on
|
||||
// match *x.y { ... } match_place is Some(*x.y)
|
||||
// ^^^^ match_span is the span of *x.y
|
||||
//
|
||||
// opt_match_place is None for let [mut] x = ... statements,
|
||||
// whether or not the right-hand side is a place expression
|
||||
if let LocalInfo::User(BindingForm::Var(VarBindingForm {
|
||||
opt_match_place: Some((opt_match_place, match_span)),
|
||||
binding_mode: _,
|
||||
opt_ty_info: _,
|
||||
pat_span: _,
|
||||
})) = *local_decl.local_info()
|
||||
{
|
||||
let stmt_source_info = self.body.source_info(location);
|
||||
self.append_binding_error(
|
||||
grouped_errors,
|
||||
kind,
|
||||
original_path,
|
||||
*move_from,
|
||||
local,
|
||||
opt_match_place,
|
||||
match_span,
|
||||
stmt_source_info.span,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let move_spans = self.move_spans(original_path.as_ref(), location);
|
||||
grouped_errors.push(GroupedMoveError::OtherIllegalMove {
|
||||
use_spans: move_spans,
|
||||
original_path,
|
||||
kind,
|
||||
});
|
||||
}
|
||||
|
||||
fn append_binding_error(
|
||||
|
|
|
|||
|
|
@ -31,13 +31,8 @@ use rustc_index::{IndexSlice, IndexVec};
|
|||
use rustc_infer::infer::{
|
||||
InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
|
||||
};
|
||||
use rustc_middle::mir::{
|
||||
traversal, Body, ClearCrossCrate, Local, Location, MutBorrowKind, Mutability,
|
||||
NonDivergingIntrinsic, Operand, Place, PlaceElem, PlaceRef, VarDebugInfoContents,
|
||||
};
|
||||
use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
|
||||
use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
|
||||
use rustc_middle::mir::{ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
|
||||
use rustc_middle::mir::tcx::PlaceTy;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::traits::DefiningAnchor;
|
||||
use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt};
|
||||
|
|
@ -55,13 +50,13 @@ use rustc_mir_dataflow::impls::{
|
|||
EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
|
||||
};
|
||||
use rustc_mir_dataflow::move_paths::{InitIndex, MoveOutIndex, MovePathIndex};
|
||||
use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveData, MoveError};
|
||||
use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveData};
|
||||
use rustc_mir_dataflow::Analysis;
|
||||
use rustc_mir_dataflow::MoveDataParamEnv;
|
||||
|
||||
use crate::session_diagnostics::VarNeedNotMut;
|
||||
|
||||
use self::diagnostics::{AccessKind, RegionName};
|
||||
use self::diagnostics::{AccessKind, IllegalMoveOriginKind, MoveError, RegionName};
|
||||
use self::location::LocationTable;
|
||||
use self::prefixes::PrefixSet;
|
||||
use consumers::{BodyWithBorrowckFacts, ConsumerOptions};
|
||||
|
|
@ -224,14 +219,10 @@ fn do_mir_borrowck<'tcx>(
|
|||
let location_table_owned = LocationTable::new(body);
|
||||
let location_table = &location_table_owned;
|
||||
|
||||
let (move_data, move_errors): (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>) =
|
||||
match MoveData::gather_moves(&body, tcx, param_env) {
|
||||
Ok(move_data) => (move_data, Vec::new()),
|
||||
Err((move_data, move_errors)) => (move_data, move_errors),
|
||||
};
|
||||
let promoted_errors = promoted
|
||||
let move_data = MoveData::gather_moves(&body, tcx, param_env, |_| true);
|
||||
let promoted_move_data = promoted
|
||||
.iter_enumerated()
|
||||
.map(|(idx, body)| (idx, MoveData::gather_moves(&body, tcx, param_env)));
|
||||
.map(|(idx, body)| (idx, MoveData::gather_moves(&body, tcx, param_env, |_| true)));
|
||||
|
||||
let mdpe = MoveDataParamEnv { move_data, param_env };
|
||||
|
||||
|
|
@ -313,36 +304,49 @@ fn do_mir_borrowck<'tcx>(
|
|||
true
|
||||
};
|
||||
|
||||
for (idx, move_data_results) in promoted_errors {
|
||||
let promoted_body = &promoted[idx];
|
||||
for (idx, move_data) in promoted_move_data {
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
|
||||
if let Err((move_data, move_errors)) = move_data_results {
|
||||
let mut promoted_mbcx = MirBorrowckCtxt {
|
||||
infcx: &infcx,
|
||||
param_env,
|
||||
body: promoted_body,
|
||||
move_data: &move_data,
|
||||
location_table, // no need to create a real one for the promoted, it is not used
|
||||
movable_coroutine,
|
||||
fn_self_span_reported: Default::default(),
|
||||
locals_are_invalidated_at_exit,
|
||||
access_place_error_reported: Default::default(),
|
||||
reservation_error_reported: Default::default(),
|
||||
uninitialized_error_reported: Default::default(),
|
||||
regioncx: regioncx.clone(),
|
||||
used_mut: Default::default(),
|
||||
used_mut_upvars: SmallVec::new(),
|
||||
borrow_set: Rc::clone(&borrow_set),
|
||||
upvars: Vec::new(),
|
||||
local_names: IndexVec::from_elem(None, &promoted_body.local_decls),
|
||||
region_names: RefCell::default(),
|
||||
next_region_name: RefCell::new(1),
|
||||
polonius_output: None,
|
||||
errors,
|
||||
};
|
||||
promoted_mbcx.report_move_errors(move_errors);
|
||||
errors = promoted_mbcx.errors;
|
||||
let promoted_body = &promoted[idx];
|
||||
let mut promoted_mbcx = MirBorrowckCtxt {
|
||||
infcx: &infcx,
|
||||
param_env,
|
||||
body: promoted_body,
|
||||
move_data: &move_data,
|
||||
location_table, // no need to create a real one for the promoted, it is not used
|
||||
movable_coroutine,
|
||||
fn_self_span_reported: Default::default(),
|
||||
locals_are_invalidated_at_exit,
|
||||
access_place_error_reported: Default::default(),
|
||||
reservation_error_reported: Default::default(),
|
||||
uninitialized_error_reported: Default::default(),
|
||||
regioncx: regioncx.clone(),
|
||||
used_mut: Default::default(),
|
||||
used_mut_upvars: SmallVec::new(),
|
||||
borrow_set: Rc::clone(&borrow_set),
|
||||
upvars: Vec::new(),
|
||||
local_names: IndexVec::from_elem(None, &promoted_body.local_decls),
|
||||
region_names: RefCell::default(),
|
||||
next_region_name: RefCell::new(1),
|
||||
polonius_output: None,
|
||||
move_errors: Vec::new(),
|
||||
errors,
|
||||
};
|
||||
MoveVisitor { ctxt: &mut promoted_mbcx }.visit_body(promoted_body);
|
||||
promoted_mbcx.report_move_errors();
|
||||
errors = promoted_mbcx.errors;
|
||||
|
||||
struct MoveVisitor<'a, 'cx, 'tcx> {
|
||||
ctxt: &'a mut MirBorrowckCtxt<'cx, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx> {
|
||||
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
|
||||
if let Operand::Move(place) = operand {
|
||||
self.ctxt.check_movable_place(location, *place);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut mbcx = MirBorrowckCtxt {
|
||||
|
|
@ -366,6 +370,7 @@ fn do_mir_borrowck<'tcx>(
|
|||
region_names: RefCell::default(),
|
||||
next_region_name: RefCell::new(1),
|
||||
polonius_output,
|
||||
move_errors: Vec::new(),
|
||||
errors,
|
||||
};
|
||||
|
||||
|
|
@ -378,8 +383,6 @@ fn do_mir_borrowck<'tcx>(
|
|||
borrows: flow_borrows,
|
||||
};
|
||||
|
||||
mbcx.report_move_errors(move_errors);
|
||||
|
||||
rustc_mir_dataflow::visit_results(
|
||||
body,
|
||||
traversal::reverse_postorder(body).map(|(bb, _)| bb),
|
||||
|
|
@ -387,6 +390,8 @@ fn do_mir_borrowck<'tcx>(
|
|||
&mut mbcx,
|
||||
);
|
||||
|
||||
mbcx.report_move_errors();
|
||||
|
||||
// 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
|
||||
|
|
@ -595,6 +600,7 @@ struct MirBorrowckCtxt<'cx, 'tcx> {
|
|||
polonius_output: Option<Rc<PoloniusOutput>>,
|
||||
|
||||
errors: error::BorrowckErrors<'tcx>,
|
||||
move_errors: Vec<MoveError<'tcx>>,
|
||||
}
|
||||
|
||||
// Check that:
|
||||
|
|
@ -725,7 +731,6 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
|
|||
}
|
||||
TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
|
||||
self.consume_operand(loc, (cond, span), flow_state);
|
||||
use rustc_middle::mir::AssertKind;
|
||||
if let AssertKind::BoundsCheck { len, index } = &**msg {
|
||||
self.consume_operand(loc, (len, span), flow_state);
|
||||
self.consume_operand(loc, (index, span), flow_state);
|
||||
|
|
@ -1409,7 +1414,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
// As such we have to search for the local that this
|
||||
// capture comes from and mark it as being used as mut.
|
||||
|
||||
let temp_mpi = self.move_data.rev_lookup.find_local(local);
|
||||
let Some(temp_mpi) = self.move_data.rev_lookup.find_local(local) else {
|
||||
bug!("temporary should be tracked");
|
||||
};
|
||||
let init = if let [init_index] = *self.move_data.init_path_map[temp_mpi] {
|
||||
&self.move_data.inits[init_index]
|
||||
} else {
|
||||
|
|
@ -1469,6 +1476,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
);
|
||||
}
|
||||
Operand::Move(place) => {
|
||||
// Check if moving from this place makes sense.
|
||||
self.check_movable_place(location, place);
|
||||
|
||||
// move of place: check if this is move of already borrowed path
|
||||
self.access_place(
|
||||
location,
|
||||
|
|
@ -1590,6 +1600,131 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_movable_place(&mut self, location: Location, place: Place<'tcx>) {
|
||||
use IllegalMoveOriginKind::*;
|
||||
|
||||
let body = self.body;
|
||||
let tcx = self.infcx.tcx;
|
||||
let mut place_ty = PlaceTy::from_ty(body.local_decls[place.local].ty);
|
||||
for (place_ref, elem) in place.iter_projections() {
|
||||
match elem {
|
||||
ProjectionElem::Deref => match place_ty.ty.kind() {
|
||||
ty::Ref(..) | ty::RawPtr(..) => {
|
||||
self.move_errors.push(MoveError::new(
|
||||
place,
|
||||
location,
|
||||
BorrowedContent {
|
||||
target_place: place_ref.project_deeper(&[elem], tcx),
|
||||
},
|
||||
));
|
||||
return;
|
||||
}
|
||||
ty::Adt(adt, _) => {
|
||||
if !adt.is_box() {
|
||||
bug!("Adt should be a box type when Place is deref");
|
||||
}
|
||||
}
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Foreign(_)
|
||||
| ty::Str
|
||||
| ty::Array(_, _)
|
||||
| ty::Slice(_)
|
||||
| ty::FnDef(_, _)
|
||||
| ty::FnPtr(_)
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Closure(_, _)
|
||||
| ty::Coroutine(_, _, _)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::Alias(_, _)
|
||||
| ty::Param(_)
|
||||
| ty::Bound(_, _)
|
||||
| ty::Infer(_)
|
||||
| ty::Error(_)
|
||||
| ty::Placeholder(_) => {
|
||||
bug!("When Place is Deref it's type shouldn't be {place_ty:#?}")
|
||||
}
|
||||
},
|
||||
ProjectionElem::Field(_, _) => match place_ty.ty.kind() {
|
||||
ty::Adt(adt, _) => {
|
||||
if adt.has_dtor(tcx) {
|
||||
self.move_errors.push(MoveError::new(
|
||||
place,
|
||||
location,
|
||||
InteriorOfTypeWithDestructor { container_ty: place_ty.ty },
|
||||
));
|
||||
return;
|
||||
}
|
||||
}
|
||||
ty::Closure(_, _) | ty::Coroutine(_, _, _) | ty::Tuple(_) => (),
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Foreign(_)
|
||||
| ty::Str
|
||||
| ty::Array(_, _)
|
||||
| ty::Slice(_)
|
||||
| ty::RawPtr(_)
|
||||
| ty::Ref(_, _, _)
|
||||
| ty::FnDef(_, _)
|
||||
| ty::FnPtr(_)
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
| ty::Alias(_, _)
|
||||
| ty::Param(_)
|
||||
| ty::Bound(_, _)
|
||||
| ty::Infer(_)
|
||||
| ty::Error(_)
|
||||
| ty::Placeholder(_) => bug!(
|
||||
"When Place contains ProjectionElem::Field it's type shouldn't be {place_ty:#?}"
|
||||
),
|
||||
},
|
||||
ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
|
||||
match place_ty.ty.kind() {
|
||||
ty::Slice(_) => {
|
||||
self.move_errors.push(MoveError::new(
|
||||
place,
|
||||
location,
|
||||
InteriorOfSliceOrArray { ty: place_ty.ty, is_index: false },
|
||||
));
|
||||
return;
|
||||
}
|
||||
ty::Array(_, _) => (),
|
||||
_ => bug!("Unexpected type {:#?}", place_ty.ty),
|
||||
}
|
||||
}
|
||||
ProjectionElem::Index(_) => match place_ty.ty.kind() {
|
||||
ty::Array(..) | ty::Slice(..) => {
|
||||
self.move_errors.push(MoveError::new(
|
||||
place,
|
||||
location,
|
||||
InteriorOfSliceOrArray { ty: place_ty.ty, is_index: true },
|
||||
));
|
||||
return;
|
||||
}
|
||||
_ => bug!("Unexpected type {place_ty:#?}"),
|
||||
},
|
||||
// `OpaqueCast`: only transmutes the type, so no moves there.
|
||||
// `Downcast` : only changes information about a `Place` without moving.
|
||||
// `Subtype` : only transmutes the type, so no moves.
|
||||
// So it's safe to skip these.
|
||||
ProjectionElem::OpaqueCast(_)
|
||||
| ProjectionElem::Subtype(_)
|
||||
| ProjectionElem::Downcast(_, _) => (),
|
||||
}
|
||||
|
||||
place_ty = place_ty.projection_ty(tcx, elem);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_if_full_path_is_moved(
|
||||
&mut self,
|
||||
location: Location,
|
||||
|
|
@ -2074,7 +2209,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
local: Local,
|
||||
flow_state: &Flows<'cx, 'tcx>,
|
||||
) -> Option<InitIndex> {
|
||||
let mpi = self.move_data.rev_lookup.find_local(local);
|
||||
let mpi = self.move_data.rev_lookup.find_local(local)?;
|
||||
let ii = &self.move_data.init_path_map[mpi];
|
||||
ii.into_iter().find(|&&index| flow_state.ever_inits.contains(index)).copied()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -644,11 +644,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
self.scc_universes[scc]
|
||||
}
|
||||
|
||||
/// Once region solving has completed, this function will return
|
||||
/// the member constraints that were applied to the value of a given
|
||||
/// region `r`. See `AppliedMemberConstraint`.
|
||||
pub(crate) fn applied_member_constraints(&self, r: RegionVid) -> &[AppliedMemberConstraint] {
|
||||
let scc = self.constraint_sccs.scc(r);
|
||||
/// Once region solving has completed, this function will return the member constraints that
|
||||
/// were applied to the value of a given SCC `scc`. See `AppliedMemberConstraint`.
|
||||
pub(crate) fn applied_member_constraints(
|
||||
&self,
|
||||
scc: ConstraintSccIndex,
|
||||
) -> &[AppliedMemberConstraint] {
|
||||
binary_search_util::binary_search_slice(
|
||||
&self.member_constraints_applied,
|
||||
|applied| applied.member_region_scc,
|
||||
|
|
@ -1945,7 +1946,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
// Member constraints can also give rise to `'r: 'x` edges that
|
||||
// were not part of the graph initially, so watch out for those.
|
||||
// (But they are extremely rare; this loop is very cold.)
|
||||
for constraint in self.applied_member_constraints(r) {
|
||||
for constraint in self.applied_member_constraints(self.constraint_sccs.scc(r)) {
|
||||
let p_c = &self.member_constraints[constraint.member_constraint_index];
|
||||
let constraint = OutlivesConstraint {
|
||||
sup: r,
|
||||
|
|
@ -2292,11 +2293,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
self.constraint_sccs.as_ref()
|
||||
}
|
||||
|
||||
/// Returns whether the given SCC has any member constraints.
|
||||
pub(crate) fn scc_has_member_constraints(&self, scc: ConstraintSccIndex) -> bool {
|
||||
self.member_constraints.indices(scc).next().is_some()
|
||||
}
|
||||
|
||||
/// Returns whether the given SCC is live at all points: whether the representative is a
|
||||
/// placeholder or a free region.
|
||||
pub(crate) fn scc_is_live_at_all_points(&self, scc: ConstraintSccIndex) -> bool {
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> {
|
|||
fn compute_drop_live_points_for(&mut self, local: Local) {
|
||||
debug!("compute_drop_live_points_for(local={:?})", local);
|
||||
|
||||
let mpi = self.cx.move_data.rev_lookup.find_local(local);
|
||||
let Some(mpi) = self.cx.move_data.rev_lookup.find_local(local) else { return };
|
||||
debug!("compute_drop_live_points_for: mpi = {:?}", mpi);
|
||||
|
||||
// Find the drops where `local` is initialized.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ test = false
|
|||
[dependencies]
|
||||
bitflags = "1.0"
|
||||
cstr = "0.2"
|
||||
itertools = "0.10.5"
|
||||
libc = "0.2"
|
||||
measureme = "10.0.0"
|
||||
object = { version = "0.32.0", default-features = false, features = [
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
|
||||
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_middle::mir::coverage::{
|
||||
CodeRegion, CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping, Op,
|
||||
};
|
||||
use rustc_middle::ty::Instance;
|
||||
use rustc_span::Symbol;
|
||||
|
||||
/// Holds all of the coverage mapping data associated with a function instance,
|
||||
/// collected during traversal of `Coverage` statements in the function's MIR.
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionCoverage<'tcx> {
|
||||
pub struct FunctionCoverageCollector<'tcx> {
|
||||
/// Coverage info that was attached to this function by the instrumentor.
|
||||
function_coverage_info: &'tcx FunctionCoverageInfo,
|
||||
is_used: bool,
|
||||
|
|
@ -26,7 +28,7 @@ pub struct FunctionCoverage<'tcx> {
|
|||
expressions_seen: BitSet<ExpressionId>,
|
||||
}
|
||||
|
||||
impl<'tcx> FunctionCoverage<'tcx> {
|
||||
impl<'tcx> FunctionCoverageCollector<'tcx> {
|
||||
/// Creates a new set of coverage data for a used (called) function.
|
||||
pub fn new(
|
||||
instance: Instance<'tcx>,
|
||||
|
|
@ -76,11 +78,6 @@ impl<'tcx> FunctionCoverage<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns true for a used (called) function, and false for an unused function.
|
||||
pub fn is_used(&self) -> bool {
|
||||
self.is_used
|
||||
}
|
||||
|
||||
/// Marks a counter ID as having been seen in a counter-increment statement.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn mark_counter_id_seen(&mut self, id: CounterId) {
|
||||
|
|
@ -165,64 +162,71 @@ impl<'tcx> FunctionCoverage<'tcx> {
|
|||
ZeroExpressions(zero_expressions)
|
||||
}
|
||||
|
||||
pub(crate) fn into_finished(self) -> FunctionCoverage<'tcx> {
|
||||
let zero_expressions = self.identify_zero_expressions();
|
||||
let FunctionCoverageCollector { function_coverage_info, is_used, counters_seen, .. } = self;
|
||||
|
||||
FunctionCoverage { function_coverage_info, is_used, counters_seen, zero_expressions }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct FunctionCoverage<'tcx> {
|
||||
function_coverage_info: &'tcx FunctionCoverageInfo,
|
||||
is_used: bool,
|
||||
|
||||
counters_seen: BitSet<CounterId>,
|
||||
zero_expressions: ZeroExpressions,
|
||||
}
|
||||
|
||||
impl<'tcx> FunctionCoverage<'tcx> {
|
||||
/// Returns true for a used (called) function, and false for an unused function.
|
||||
pub(crate) fn is_used(&self) -> bool {
|
||||
self.is_used
|
||||
}
|
||||
|
||||
/// Return the source hash, generated from the HIR node structure, and used to indicate whether
|
||||
/// or not the source code structure changed between different compilations.
|
||||
pub fn source_hash(&self) -> u64 {
|
||||
if self.is_used { self.function_coverage_info.function_source_hash } else { 0 }
|
||||
}
|
||||
|
||||
/// Generate an array of CounterExpressions, and an iterator over all `Counter`s and their
|
||||
/// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create
|
||||
/// `CounterMappingRegion`s.
|
||||
pub fn get_expressions_and_counter_regions(
|
||||
&self,
|
||||
) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &CodeRegion)>) {
|
||||
let zero_expressions = self.identify_zero_expressions();
|
||||
|
||||
let counter_expressions = self.counter_expressions(&zero_expressions);
|
||||
// Expression IDs are indices into `self.expressions`, and on the LLVM
|
||||
// side they will be treated as indices into `counter_expressions`, so
|
||||
// the two vectors should correspond 1:1.
|
||||
assert_eq!(self.function_coverage_info.expressions.len(), counter_expressions.len());
|
||||
|
||||
let counter_regions = self.counter_regions(zero_expressions);
|
||||
|
||||
(counter_expressions, counter_regions)
|
||||
/// Returns an iterator over all filenames used by this function's mappings.
|
||||
pub(crate) fn all_file_names(&self) -> impl Iterator<Item = Symbol> + Captures<'_> {
|
||||
self.function_coverage_info.mappings.iter().map(|mapping| mapping.code_region.file_name)
|
||||
}
|
||||
|
||||
/// Convert this function's coverage expression data into a form that can be
|
||||
/// passed through FFI to LLVM.
|
||||
fn counter_expressions(&self, zero_expressions: &ZeroExpressions) -> Vec<CounterExpression> {
|
||||
pub(crate) fn counter_expressions(
|
||||
&self,
|
||||
) -> impl Iterator<Item = CounterExpression> + ExactSizeIterator + Captures<'_> {
|
||||
// We know that LLVM will optimize out any unused expressions before
|
||||
// producing the final coverage map, so there's no need to do the same
|
||||
// thing on the Rust side unless we're confident we can do much better.
|
||||
// (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.)
|
||||
|
||||
let counter_from_operand = |operand: CovTerm| match operand {
|
||||
CovTerm::Expression(id) if zero_expressions.contains(id) => Counter::ZERO,
|
||||
CovTerm::Expression(id) if self.zero_expressions.contains(id) => Counter::ZERO,
|
||||
_ => Counter::from_term(operand),
|
||||
};
|
||||
|
||||
self.function_coverage_info
|
||||
.expressions
|
||||
.iter()
|
||||
.map(|&Expression { lhs, op, rhs }| CounterExpression {
|
||||
self.function_coverage_info.expressions.iter().map(move |&Expression { lhs, op, rhs }| {
|
||||
CounterExpression {
|
||||
lhs: counter_from_operand(lhs),
|
||||
kind: match op {
|
||||
Op::Add => ExprKind::Add,
|
||||
Op::Subtract => ExprKind::Subtract,
|
||||
},
|
||||
rhs: counter_from_operand(rhs),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Converts this function's coverage mappings into an intermediate form
|
||||
/// that will be used by `mapgen` when preparing for FFI.
|
||||
fn counter_regions(
|
||||
pub(crate) fn counter_regions(
|
||||
&self,
|
||||
zero_expressions: ZeroExpressions,
|
||||
) -> impl Iterator<Item = (Counter, &CodeRegion)> {
|
||||
) -> impl Iterator<Item = (Counter, &CodeRegion)> + ExactSizeIterator {
|
||||
// Historically, mappings were stored directly in counter/expression
|
||||
// statements in MIR, and MIR optimizations would sometimes remove them.
|
||||
// That's mostly no longer true, so now we detect cases where that would
|
||||
|
|
@ -230,7 +234,7 @@ impl<'tcx> FunctionCoverage<'tcx> {
|
|||
let counter_for_term = move |term: CovTerm| {
|
||||
let force_to_zero = match term {
|
||||
CovTerm::Counter(id) => !self.counters_seen.contains(id),
|
||||
CovTerm::Expression(id) => zero_expressions.contains(id),
|
||||
CovTerm::Expression(id) => self.zero_expressions.contains(id),
|
||||
CovTerm::Zero => false,
|
||||
};
|
||||
if force_to_zero { Counter::ZERO } else { Counter::from_term(term) }
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use crate::common::CodegenCx;
|
||||
use crate::coverageinfo;
|
||||
use crate::coverageinfo::ffi::CounterMappingRegion;
|
||||
use crate::coverageinfo::map_data::FunctionCoverage;
|
||||
use crate::coverageinfo::map_data::{FunctionCoverage, FunctionCoverageCollector};
|
||||
use crate::llvm;
|
||||
|
||||
use itertools::Itertools as _;
|
||||
use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods};
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_index::IndexVec;
|
||||
|
|
@ -57,11 +58,32 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
|
|||
return;
|
||||
}
|
||||
|
||||
let mut global_file_table = GlobalFileTable::new(tcx);
|
||||
let function_coverage_entries = function_coverage_map
|
||||
.into_iter()
|
||||
.map(|(instance, function_coverage)| (instance, function_coverage.into_finished()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let all_file_names =
|
||||
function_coverage_entries.iter().flat_map(|(_, fn_cov)| fn_cov.all_file_names());
|
||||
let global_file_table = GlobalFileTable::new(all_file_names);
|
||||
|
||||
// Encode all filenames referenced by coverage mappings in this CGU.
|
||||
let filenames_buffer = global_file_table.make_filenames_buffer(tcx);
|
||||
|
||||
let filenames_size = filenames_buffer.len();
|
||||
let filenames_val = cx.const_bytes(&filenames_buffer);
|
||||
let filenames_ref = coverageinfo::hash_bytes(&filenames_buffer);
|
||||
|
||||
// Generate the coverage map header, which contains the filenames used by
|
||||
// this CGU's coverage mappings, and store it in a well-known global.
|
||||
let cov_data_val = generate_coverage_map(cx, version, filenames_size, filenames_val);
|
||||
coverageinfo::save_cov_data_to_mod(cx, cov_data_val);
|
||||
|
||||
let mut unused_function_names = Vec::new();
|
||||
let covfun_section_name = coverageinfo::covfun_section_name(cx);
|
||||
|
||||
// Encode coverage mappings and generate function records
|
||||
let mut function_data = Vec::new();
|
||||
for (instance, function_coverage) in function_coverage_map {
|
||||
for (instance, function_coverage) in function_coverage_entries {
|
||||
debug!("Generate function coverage for {}, {:?}", cx.codegen_unit.name(), instance);
|
||||
|
||||
let mangled_function_name = tcx.symbol_name(instance).name;
|
||||
|
|
@ -69,7 +91,7 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
|
|||
let is_used = function_coverage.is_used();
|
||||
|
||||
let coverage_mapping_buffer =
|
||||
encode_mappings_for_function(&mut global_file_table, &function_coverage);
|
||||
encode_mappings_for_function(&global_file_table, &function_coverage);
|
||||
|
||||
if coverage_mapping_buffer.is_empty() {
|
||||
if function_coverage.is_used() {
|
||||
|
|
@ -83,23 +105,6 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
|
|||
}
|
||||
}
|
||||
|
||||
function_data.push((mangled_function_name, source_hash, is_used, coverage_mapping_buffer));
|
||||
}
|
||||
|
||||
// Encode all filenames referenced by counters/expressions in this module
|
||||
let filenames_buffer = global_file_table.into_filenames_buffer();
|
||||
|
||||
let filenames_size = filenames_buffer.len();
|
||||
let filenames_val = cx.const_bytes(&filenames_buffer);
|
||||
let filenames_ref = coverageinfo::hash_bytes(&filenames_buffer);
|
||||
|
||||
// Generate the LLVM IR representation of the coverage map and store it in a well-known global
|
||||
let cov_data_val = generate_coverage_map(cx, version, filenames_size, filenames_val);
|
||||
|
||||
let mut unused_function_names = Vec::new();
|
||||
|
||||
let covfun_section_name = coverageinfo::covfun_section_name(cx);
|
||||
for (mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data {
|
||||
if !is_used {
|
||||
unused_function_names.push(mangled_function_name);
|
||||
}
|
||||
|
|
@ -133,92 +138,125 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
|
|||
llvm::set_linkage(array, llvm::Linkage::InternalLinkage);
|
||||
llvm::set_initializer(array, initializer);
|
||||
}
|
||||
|
||||
// Save the coverage data value to LLVM IR
|
||||
coverageinfo::save_cov_data_to_mod(cx, cov_data_val);
|
||||
}
|
||||
|
||||
/// Maps "global" (per-CGU) file ID numbers to their underlying filenames.
|
||||
struct GlobalFileTable {
|
||||
global_file_table: FxIndexSet<Symbol>,
|
||||
/// This "raw" table doesn't include the working dir, so a filename's
|
||||
/// global ID is its index in this set **plus one**.
|
||||
raw_file_table: FxIndexSet<Symbol>,
|
||||
}
|
||||
|
||||
impl GlobalFileTable {
|
||||
fn new(tcx: TyCtxt<'_>) -> Self {
|
||||
let mut global_file_table = FxIndexSet::default();
|
||||
fn new(all_file_names: impl IntoIterator<Item = Symbol>) -> Self {
|
||||
// Collect all of the filenames into a set. Filenames usually come in
|
||||
// contiguous runs, so we can dedup adjacent ones to save work.
|
||||
let mut raw_file_table = all_file_names.into_iter().dedup().collect::<FxIndexSet<Symbol>>();
|
||||
|
||||
// Sort the file table by its actual string values, not the arbitrary
|
||||
// ordering of its symbols.
|
||||
raw_file_table.sort_unstable_by(|a, b| a.as_str().cmp(b.as_str()));
|
||||
|
||||
Self { raw_file_table }
|
||||
}
|
||||
|
||||
fn global_file_id_for_file_name(&self, file_name: Symbol) -> u32 {
|
||||
let raw_id = self.raw_file_table.get_index_of(&file_name).unwrap_or_else(|| {
|
||||
bug!("file name not found in prepared global file table: {file_name}");
|
||||
});
|
||||
// The raw file table doesn't include an entry for the working dir
|
||||
// (which has ID 0), so add 1 to get the correct ID.
|
||||
(raw_id + 1) as u32
|
||||
}
|
||||
|
||||
fn make_filenames_buffer(&self, tcx: TyCtxt<'_>) -> Vec<u8> {
|
||||
// LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
|
||||
// requires setting the first filename to the compilation directory.
|
||||
// Since rustc generates coverage maps with relative paths, the
|
||||
// compilation directory can be combined with the relative paths
|
||||
// to get absolute paths, if needed.
|
||||
use rustc_session::RemapFileNameExt;
|
||||
let working_dir =
|
||||
Symbol::intern(&tcx.sess.opts.working_dir.for_codegen(&tcx.sess).to_string_lossy());
|
||||
global_file_table.insert(working_dir);
|
||||
Self { global_file_table }
|
||||
}
|
||||
|
||||
fn global_file_id_for_file_name(&mut self, file_name: Symbol) -> u32 {
|
||||
let (global_file_id, _) = self.global_file_table.insert_full(file_name);
|
||||
global_file_id as u32
|
||||
}
|
||||
|
||||
fn into_filenames_buffer(self) -> Vec<u8> {
|
||||
// This method takes `self` so that the caller can't accidentally
|
||||
// modify the original file table after encoding it into a buffer.
|
||||
let working_dir: &str = &tcx.sess.opts.working_dir.for_codegen(&tcx.sess).to_string_lossy();
|
||||
|
||||
llvm::build_byte_buffer(|buffer| {
|
||||
coverageinfo::write_filenames_section_to_buffer(
|
||||
self.global_file_table.iter().map(Symbol::as_str),
|
||||
// Insert the working dir at index 0, before the other filenames.
|
||||
std::iter::once(working_dir).chain(self.raw_file_table.iter().map(Symbol::as_str)),
|
||||
buffer,
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
// Tell the newtype macro to not generate `Encode`/`Decode` impls.
|
||||
#[custom_encodable]
|
||||
struct LocalFileId {}
|
||||
}
|
||||
|
||||
/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
|
||||
/// file IDs.
|
||||
#[derive(Default)]
|
||||
struct VirtualFileMapping {
|
||||
local_to_global: IndexVec<LocalFileId, u32>,
|
||||
global_to_local: FxIndexMap<u32, LocalFileId>,
|
||||
}
|
||||
|
||||
impl VirtualFileMapping {
|
||||
fn local_id_for_global(&mut self, global_file_id: u32) -> LocalFileId {
|
||||
*self
|
||||
.global_to_local
|
||||
.entry(global_file_id)
|
||||
.or_insert_with(|| self.local_to_global.push(global_file_id))
|
||||
}
|
||||
|
||||
fn into_vec(self) -> Vec<u32> {
|
||||
self.local_to_global.raw
|
||||
}
|
||||
}
|
||||
|
||||
/// Using the expressions and counter regions collected for a single function,
|
||||
/// generate the variable-sized payload of its corresponding `__llvm_covfun`
|
||||
/// entry. The payload is returned as a vector of bytes.
|
||||
///
|
||||
/// Newly-encountered filenames will be added to the global file table.
|
||||
fn encode_mappings_for_function(
|
||||
global_file_table: &mut GlobalFileTable,
|
||||
global_file_table: &GlobalFileTable,
|
||||
function_coverage: &FunctionCoverage<'_>,
|
||||
) -> Vec<u8> {
|
||||
let (expressions, counter_regions) = function_coverage.get_expressions_and_counter_regions();
|
||||
|
||||
let mut counter_regions = counter_regions.collect::<Vec<_>>();
|
||||
let counter_regions = function_coverage.counter_regions();
|
||||
if counter_regions.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut virtual_file_mapping = IndexVec::<u32, u32>::new();
|
||||
let expressions = function_coverage.counter_expressions().collect::<Vec<_>>();
|
||||
|
||||
let mut virtual_file_mapping = VirtualFileMapping::default();
|
||||
let mut mapping_regions = Vec::with_capacity(counter_regions.len());
|
||||
|
||||
// Sort and group the list of (counter, region) mapping pairs by filename.
|
||||
// (Preserve any further ordering imposed by `FunctionCoverage`.)
|
||||
// Group mappings into runs with the same filename, preserving the order
|
||||
// yielded by `FunctionCoverage`.
|
||||
// Prepare file IDs for each filename, and prepare the mapping data so that
|
||||
// we can pass it through FFI to LLVM.
|
||||
counter_regions.sort_by_key(|(_counter, region)| region.file_name);
|
||||
for counter_regions_for_file in
|
||||
counter_regions.group_by(|(_, a), (_, b)| a.file_name == b.file_name)
|
||||
for (file_name, counter_regions_for_file) in
|
||||
&counter_regions.group_by(|(_counter, region)| region.file_name)
|
||||
{
|
||||
// Look up (or allocate) the global file ID for this filename.
|
||||
let file_name = counter_regions_for_file[0].1.file_name;
|
||||
// Look up the global file ID for this filename.
|
||||
let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
|
||||
|
||||
// Associate that global file ID with a local file ID for this function.
|
||||
let local_file_id: u32 = virtual_file_mapping.push(global_file_id);
|
||||
debug!(" file id: local {local_file_id} => global {global_file_id} = '{file_name:?}'");
|
||||
let local_file_id = virtual_file_mapping.local_id_for_global(global_file_id);
|
||||
debug!(" file id: {local_file_id:?} => global {global_file_id} = '{file_name:?}'");
|
||||
|
||||
// For each counter/region pair in this function+file, convert it to a
|
||||
// form suitable for FFI.
|
||||
for &(counter, region) in counter_regions_for_file {
|
||||
for (counter, region) in counter_regions_for_file {
|
||||
let CodeRegion { file_name: _, start_line, start_col, end_line, end_col } = *region;
|
||||
|
||||
debug!("Adding counter {counter:?} to map for {region:?}");
|
||||
mapping_regions.push(CounterMappingRegion::code_region(
|
||||
counter,
|
||||
local_file_id,
|
||||
local_file_id.as_u32(),
|
||||
start_line,
|
||||
start_col,
|
||||
end_line,
|
||||
|
|
@ -230,7 +268,7 @@ fn encode_mappings_for_function(
|
|||
// Encode the function's coverage mappings into a buffer.
|
||||
llvm::build_byte_buffer(|buffer| {
|
||||
coverageinfo::write_mapping_to_buffer(
|
||||
virtual_file_mapping.raw,
|
||||
virtual_file_mapping.into_vec(),
|
||||
expressions,
|
||||
mapping_regions,
|
||||
buffer,
|
||||
|
|
@ -419,7 +457,7 @@ fn add_unused_function_coverage<'tcx>(
|
|||
) {
|
||||
// An unused function's mappings will automatically be rewritten to map to
|
||||
// zero, because none of its counters/expressions are marked as seen.
|
||||
let function_coverage = FunctionCoverage::unused(instance, function_coverage_info);
|
||||
let function_coverage = FunctionCoverageCollector::unused(instance, function_coverage_info);
|
||||
|
||||
if let Some(coverage_context) = cx.coverage_context() {
|
||||
coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::llvm;
|
|||
use crate::builder::Builder;
|
||||
use crate::common::CodegenCx;
|
||||
use crate::coverageinfo::ffi::{CounterExpression, CounterMappingRegion};
|
||||
use crate::coverageinfo::map_data::FunctionCoverage;
|
||||
use crate::coverageinfo::map_data::FunctionCoverageCollector;
|
||||
|
||||
use libc::c_uint;
|
||||
use rustc_codegen_ssa::traits::{
|
||||
|
|
@ -29,7 +29,8 @@ const VAR_ALIGN_BYTES: usize = 8;
|
|||
/// A context object for maintaining all state needed by the coverageinfo module.
|
||||
pub struct CrateCoverageContext<'ll, 'tcx> {
|
||||
/// Coverage data for each instrumented function identified by DefId.
|
||||
pub(crate) function_coverage_map: RefCell<FxHashMap<Instance<'tcx>, FunctionCoverage<'tcx>>>,
|
||||
pub(crate) function_coverage_map:
|
||||
RefCell<FxHashMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>>>,
|
||||
pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
|
||||
}
|
||||
|
||||
|
|
@ -41,7 +42,9 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn take_function_coverage_map(&self) -> FxHashMap<Instance<'tcx>, FunctionCoverage<'tcx>> {
|
||||
pub fn take_function_coverage_map(
|
||||
&self,
|
||||
) -> FxHashMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>> {
|
||||
self.function_coverage_map.replace(FxHashMap::default())
|
||||
}
|
||||
}
|
||||
|
|
@ -93,7 +96,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
|||
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
|
||||
let func_coverage = coverage_map
|
||||
.entry(instance)
|
||||
.or_insert_with(|| FunctionCoverage::new(instance, function_coverage_info));
|
||||
.or_insert_with(|| FunctionCoverageCollector::new(instance, function_coverage_info));
|
||||
|
||||
let Coverage { kind } = coverage;
|
||||
match *kind {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ mod utils;
|
|||
|
||||
pub use self::create_scope_map::compute_mir_scopes;
|
||||
pub use self::metadata::build_global_var_di_node;
|
||||
pub use self::metadata::extend_scope_to_file;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
const DW_TAG_auto_variable: c_uint = 0x100;
|
||||
|
|
|
|||
|
|
@ -8,12 +8,13 @@
|
|||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![feature(exact_size_is_empty)]
|
||||
#![feature(extern_types)]
|
||||
#![feature(hash_raw_entry)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(min_specialization)]
|
||||
#![feature(never_type)]
|
||||
#![feature(slice_group_by)]
|
||||
#![feature(impl_trait_in_assoc_type)]
|
||||
#![recursion_limit = "256"]
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use crate::llvm;
|
|||
use crate::type_of::LayoutLlvmExt;
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
|
||||
pub use rustc_middle::mir::mono::MonoItem;
|
||||
use rustc_middle::mir::mono::{Linkage, Visibility};
|
||||
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
|
||||
use rustc_middle::ty::{self, Instance, TypeVisitableExt};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::mem;
|
|||
use either::{Left, Right};
|
||||
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_middle::mir::interpret::{ErrorHandled, InterpErrorInfo};
|
||||
use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo};
|
||||
use rustc_middle::mir::pretty::write_allocation_bytes;
|
||||
use rustc_middle::mir::{self, ConstAlloc, ConstValue};
|
||||
use rustc_middle::traits::Reveal;
|
||||
|
|
@ -285,7 +285,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
|
|||
let def = cid.instance.def.def_id();
|
||||
let is_static = tcx.is_static(def);
|
||||
|
||||
let mut ecx = InterpCx::new(
|
||||
let ecx = InterpCx::new(
|
||||
tcx,
|
||||
tcx.def_span(def),
|
||||
key.param_env,
|
||||
|
|
@ -293,7 +293,14 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
|
|||
// they do not have to behave "as if" they were evaluated at runtime.
|
||||
CompileTimeInterpreter::new(CanAccessStatics::from(is_static), CheckAlignment::Error),
|
||||
);
|
||||
eval_in_interpreter(ecx, cid, is_static)
|
||||
}
|
||||
|
||||
pub fn eval_in_interpreter<'mir, 'tcx>(
|
||||
mut ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
|
||||
cid: GlobalId<'tcx>,
|
||||
is_static: bool,
|
||||
) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
|
||||
let res = ecx.load_mir(cid.instance.def, cid.promoted);
|
||||
match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) {
|
||||
Err(error) => {
|
||||
|
|
@ -306,7 +313,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
|
|||
// If the current item has generics, we'd like to enrich the message with the
|
||||
// instance and its args: to show the actual compile-time values, in addition to
|
||||
// the expression, leading to the const eval error.
|
||||
let instance = &key.value.instance;
|
||||
let instance = &cid.instance;
|
||||
if !instance.args.is_empty() {
|
||||
let instance = with_no_trimmed_paths!(instance.to_string());
|
||||
("const_with_path", instance)
|
||||
|
|
@ -331,56 +338,14 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
|
|||
Ok(mplace) => {
|
||||
// Since evaluation had no errors, validate the resulting constant.
|
||||
// This is a separate `try` block to provide more targeted error reporting.
|
||||
let validation: Result<_, InterpErrorInfo<'_>> = try {
|
||||
let mut ref_tracking = RefTracking::new(mplace.clone());
|
||||
let mut inner = false;
|
||||
while let Some((mplace, path)) = ref_tracking.todo.pop() {
|
||||
let mode = match tcx.static_mutability(cid.instance.def_id()) {
|
||||
Some(_) if cid.promoted.is_some() => {
|
||||
// Promoteds in statics are allowed to point to statics.
|
||||
CtfeValidationMode::Const { inner, allow_static_ptrs: true }
|
||||
}
|
||||
Some(_) => CtfeValidationMode::Regular, // a `static`
|
||||
None => CtfeValidationMode::Const { inner, allow_static_ptrs: false },
|
||||
};
|
||||
ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?;
|
||||
inner = true;
|
||||
}
|
||||
};
|
||||
let validation =
|
||||
const_validate_mplace(&ecx, &mplace, is_static, cid.promoted.is_some());
|
||||
|
||||
let alloc_id = mplace.ptr().provenance.unwrap();
|
||||
|
||||
// Validation failed, report an error.
|
||||
if let Err(error) = validation {
|
||||
let (error, backtrace) = error.into_parts();
|
||||
backtrace.print_backtrace();
|
||||
|
||||
let ub_note = matches!(error, InterpError::UndefinedBehavior(_)).then(|| {});
|
||||
|
||||
let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner();
|
||||
let mut bytes = String::new();
|
||||
if alloc.size() != abi::Size::ZERO {
|
||||
bytes = "\n".into();
|
||||
// FIXME(translation) there might be pieces that are translatable.
|
||||
write_allocation_bytes(*ecx.tcx, alloc, &mut bytes, " ").unwrap();
|
||||
}
|
||||
let raw_bytes = errors::RawBytesNote {
|
||||
size: alloc.size().bytes(),
|
||||
align: alloc.align.bytes(),
|
||||
bytes,
|
||||
};
|
||||
|
||||
Err(super::report(
|
||||
*ecx.tcx,
|
||||
error,
|
||||
None,
|
||||
|| super::get_span_and_frames(&ecx),
|
||||
move |span, frames| errors::UndefinedBehavior {
|
||||
span,
|
||||
ub_note,
|
||||
frames,
|
||||
raw_bytes,
|
||||
},
|
||||
))
|
||||
Err(const_report_error(&ecx, error, alloc_id))
|
||||
} else {
|
||||
// Convert to raw constant
|
||||
Ok(ConstAlloc { alloc_id, ty: mplace.layout.ty })
|
||||
|
|
@ -388,3 +353,61 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn const_validate_mplace<'mir, 'tcx>(
|
||||
ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
|
||||
mplace: &MPlaceTy<'tcx>,
|
||||
is_static: bool,
|
||||
is_promoted: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
let mut ref_tracking = RefTracking::new(mplace.clone());
|
||||
let mut inner = false;
|
||||
while let Some((mplace, path)) = ref_tracking.todo.pop() {
|
||||
let mode = if is_static {
|
||||
if is_promoted {
|
||||
// Promoteds in statics are allowed to point to statics.
|
||||
CtfeValidationMode::Const { inner, allow_static_ptrs: true }
|
||||
} else {
|
||||
// a `static`
|
||||
CtfeValidationMode::Regular
|
||||
}
|
||||
} else {
|
||||
CtfeValidationMode::Const { inner, allow_static_ptrs: false }
|
||||
};
|
||||
ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?;
|
||||
inner = true;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn const_report_error<'mir, 'tcx>(
|
||||
ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
|
||||
error: InterpErrorInfo<'tcx>,
|
||||
alloc_id: AllocId,
|
||||
) -> ErrorHandled {
|
||||
let (error, backtrace) = error.into_parts();
|
||||
backtrace.print_backtrace();
|
||||
|
||||
let ub_note = matches!(error, InterpError::UndefinedBehavior(_)).then(|| {});
|
||||
|
||||
let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner();
|
||||
let mut bytes = String::new();
|
||||
if alloc.size() != abi::Size::ZERO {
|
||||
bytes = "\n".into();
|
||||
// FIXME(translation) there might be pieces that are translatable.
|
||||
write_allocation_bytes(*ecx.tcx, alloc, &mut bytes, " ").unwrap();
|
||||
}
|
||||
let raw_bytes =
|
||||
errors::RawBytesNote { size: alloc.size().bytes(), align: alloc.align.bytes(), bytes };
|
||||
|
||||
crate::const_eval::report(
|
||||
*ecx.tcx,
|
||||
error,
|
||||
None,
|
||||
|| crate::const_eval::get_span_and_frames(ecx),
|
||||
move |span, frames| errors::UndefinedBehavior { span, ub_note, frames, raw_bytes },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1074,17 +1074,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
instance: ty::Instance<'tcx>,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
|
||||
let gid = GlobalId { instance, promoted: None };
|
||||
// For statics we pick `ParamEnv::reveal_all`, because statics don't have generics
|
||||
// and thus don't care about the parameter environment. While we could just use
|
||||
// `self.param_env`, that would mean we invoke the query to evaluate the static
|
||||
// with different parameter environments, thus causing the static to be evaluated
|
||||
// multiple times.
|
||||
let param_env = if self.tcx.is_static(gid.instance.def_id()) {
|
||||
ty::ParamEnv::reveal_all()
|
||||
let val = if self.tcx.is_static(gid.instance.def_id()) {
|
||||
let alloc_id = self.tcx.reserve_and_set_static_alloc(gid.instance.def_id());
|
||||
|
||||
let ty = instance.ty(self.tcx.tcx, self.param_env);
|
||||
mir::ConstAlloc { alloc_id, ty }
|
||||
} else {
|
||||
self.param_env
|
||||
self.ctfe_query(|tcx| tcx.eval_to_allocation_raw(self.param_env.and(gid)))?
|
||||
};
|
||||
let val = self.ctfe_query(|tcx| tcx.eval_to_allocation_raw(param_env.and(gid)))?;
|
||||
self.raw_const_to_mplace(val)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,10 +107,10 @@ impl<Prov: Provenance> std::fmt::Display for ImmTy<'_, Prov> {
|
|||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
/// Helper function for printing a scalar to a FmtPrinter
|
||||
fn p<'a, 'tcx, Prov: Provenance>(
|
||||
cx: FmtPrinter<'a, 'tcx>,
|
||||
cx: &mut FmtPrinter<'a, 'tcx>,
|
||||
s: Scalar<Prov>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Result<FmtPrinter<'a, 'tcx>, std::fmt::Error> {
|
||||
) -> Result<(), std::fmt::Error> {
|
||||
match s {
|
||||
Scalar::Int(int) => cx.pretty_print_const_scalar_int(int, ty, true),
|
||||
Scalar::Ptr(ptr, _sz) => {
|
||||
|
|
@ -125,8 +125,9 @@ impl<Prov: Provenance> std::fmt::Display for ImmTy<'_, Prov> {
|
|||
match self.imm {
|
||||
Immediate::Scalar(s) => {
|
||||
if let Some(ty) = tcx.lift(self.layout.ty) {
|
||||
let cx = FmtPrinter::new(tcx, Namespace::ValueNS);
|
||||
f.write_str(&p(cx, s, ty)?.into_buffer())?;
|
||||
let s =
|
||||
FmtPrinter::print_string(tcx, Namespace::ValueNS, |cx| p(cx, s, ty))?;
|
||||
f.write_str(&s)?;
|
||||
return Ok(());
|
||||
}
|
||||
write!(f, "{:x}: {}", s, self.layout.ty)
|
||||
|
|
|
|||
|
|
@ -929,7 +929,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
/// - no pointers to statics.
|
||||
/// - no `UnsafeCell` or non-ZST `&mut`.
|
||||
#[inline(always)]
|
||||
pub fn const_validate_operand(
|
||||
pub(crate) fn const_validate_operand(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
path: Vec<PathElem>,
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
|
|||
self.tcx
|
||||
}
|
||||
|
||||
fn print_region(self, _region: ty::Region<'_>) -> Result<Self, PrintError> {
|
||||
Ok(self)
|
||||
fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self, PrintError> {
|
||||
fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> {
|
||||
match *ty.kind() {
|
||||
// Types without identity.
|
||||
ty::Bool
|
||||
|
|
@ -43,7 +43,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
|
|||
// Placeholders (all printed as `_` to uniformize them).
|
||||
ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => {
|
||||
write!(self, "_")?;
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Types with identity (print the module path).
|
||||
|
|
@ -60,44 +60,44 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self, PrintError> {
|
||||
fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> {
|
||||
self.pretty_print_const(ct, false)
|
||||
}
|
||||
|
||||
fn print_dyn_existential(
|
||||
self,
|
||||
&mut self,
|
||||
predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
self.pretty_print_dyn_existential(predicates)
|
||||
}
|
||||
|
||||
fn path_crate(mut self, cnum: CrateNum) -> Result<Self, PrintError> {
|
||||
fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
|
||||
self.path.push_str(self.tcx.crate_name(cnum).as_str());
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn path_qualified(
|
||||
self,
|
||||
&mut self,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
self.pretty_path_qualified(self_ty, trait_ref)
|
||||
}
|
||||
|
||||
fn path_append_impl(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
_disambiguated_data: &DisambiguatedDefPathData,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
self.pretty_path_append_impl(
|
||||
|mut cx| {
|
||||
cx = print_prefix(cx)?;
|
||||
|cx| {
|
||||
print_prefix(cx)?;
|
||||
|
||||
cx.path.push_str("::");
|
||||
|
||||
Ok(cx)
|
||||
Ok(())
|
||||
},
|
||||
self_ty,
|
||||
trait_ref,
|
||||
|
|
@ -105,29 +105,29 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
|
|||
}
|
||||
|
||||
fn path_append(
|
||||
mut self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
disambiguated_data: &DisambiguatedDefPathData,
|
||||
) -> Result<Self, PrintError> {
|
||||
self = print_prefix(self)?;
|
||||
) -> Result<(), PrintError> {
|
||||
print_prefix(self)?;
|
||||
|
||||
write!(self.path, "::{}", disambiguated_data.data).unwrap();
|
||||
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn path_generic_args(
|
||||
mut self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
args: &[GenericArg<'tcx>],
|
||||
) -> Result<Self, PrintError> {
|
||||
self = print_prefix(self)?;
|
||||
) -> Result<(), PrintError> {
|
||||
print_prefix(self)?;
|
||||
let args =
|
||||
args.iter().cloned().filter(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
|
||||
if args.clone().next().is_some() {
|
||||
self.generic_delimiters(|cx| cx.comma_sep(args))
|
||||
} else {
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -136,31 +136,31 @@ impl<'tcx> PrettyPrinter<'tcx> for AbsolutePathPrinter<'tcx> {
|
|||
fn should_print_region(&self, _region: ty::Region<'_>) -> bool {
|
||||
false
|
||||
}
|
||||
fn comma_sep<T>(mut self, mut elems: impl Iterator<Item = T>) -> Result<Self, PrintError>
|
||||
fn comma_sep<T>(&mut self, mut elems: impl Iterator<Item = T>) -> Result<(), PrintError>
|
||||
where
|
||||
T: Print<'tcx, Self>,
|
||||
{
|
||||
if let Some(first) = elems.next() {
|
||||
self = first.print(self)?;
|
||||
first.print(self)?;
|
||||
for elem in elems {
|
||||
self.path.push_str(", ");
|
||||
self = elem.print(self)?;
|
||||
elem.print(self)?;
|
||||
}
|
||||
}
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generic_delimiters(
|
||||
mut self,
|
||||
f: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
) -> Result<Self, PrintError> {
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
) -> Result<(), PrintError> {
|
||||
write!(self, "<")?;
|
||||
|
||||
self = f(self)?;
|
||||
f(self)?;
|
||||
|
||||
write!(self, ">")?;
|
||||
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn should_print_verbose(&self) -> bool {
|
||||
|
|
@ -177,5 +177,7 @@ impl Write for AbsolutePathPrinter<'_> {
|
|||
}
|
||||
|
||||
pub fn type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> String {
|
||||
AbsolutePathPrinter { tcx, path: String::new() }.print_type(ty).unwrap().path
|
||||
let mut printer = AbsolutePathPrinter { tcx, path: String::new() };
|
||||
printer.print_type(ty).unwrap();
|
||||
printer.path
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ pub use worker_local::{Registry, WorkerLocal};
|
|||
mod parallel;
|
||||
#[cfg(parallel_compiler)]
|
||||
pub use parallel::scope;
|
||||
pub use parallel::{join, par_for_each_in, par_map, parallel_guard};
|
||||
pub use parallel::{join, par_for_each_in, par_map, parallel_guard, try_par_for_each_in};
|
||||
|
||||
pub use std::sync::atomic::Ordering;
|
||||
pub use std::sync::atomic::Ordering::SeqCst;
|
||||
|
|
|
|||
|
|
@ -77,6 +77,15 @@ mod disabled {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn try_par_for_each_in<T: IntoIterator, E: Copy>(
|
||||
t: T,
|
||||
mut for_each: impl FnMut(T::Item) -> Result<(), E>,
|
||||
) -> Result<(), E> {
|
||||
parallel_guard(|guard| {
|
||||
t.into_iter().fold(Ok(()), |ret, i| guard.run(|| for_each(i)).unwrap_or(ret).and(ret))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn par_map<T: IntoIterator, R, C: FromIterator<R>>(
|
||||
t: T,
|
||||
mut map: impl FnMut(<<T as IntoIterator>::IntoIter as Iterator>::Item) -> R,
|
||||
|
|
@ -167,6 +176,26 @@ mod enabled {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn try_par_for_each_in<
|
||||
T: IntoIterator + IntoParallelIterator<Item = <T as IntoIterator>::Item>,
|
||||
E: Copy + Send,
|
||||
>(
|
||||
t: T,
|
||||
for_each: impl Fn(<T as IntoIterator>::Item) -> Result<(), E> + DynSync + DynSend,
|
||||
) -> Result<(), E> {
|
||||
parallel_guard(|guard| {
|
||||
if mode::is_dyn_thread_safe() {
|
||||
let for_each = FromDyn::from(for_each);
|
||||
t.into_par_iter()
|
||||
.fold_with(Ok(()), |ret, i| guard.run(|| for_each(i)).unwrap_or(ret).and(ret))
|
||||
.reduce(|| Ok(()), |a, b| a.and(b))
|
||||
} else {
|
||||
t.into_iter()
|
||||
.fold(Ok(()), |ret, i| guard.run(|| for_each(i)).unwrap_or(ret).and(ret))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn par_map<
|
||||
I,
|
||||
T: IntoIterator<Item = I> + IntoParallelIterator<Item = I>,
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ declare_features! (
|
|||
/// Allows generators to be cloned.
|
||||
(removed, generator_clone, "1.65.0", Some(95360), None, Some("renamed to `coroutine_clone`")),
|
||||
/// Allows defining generators.
|
||||
(removed, generators, "1.21.0", Some(43122), None, Some("renamed to `coroutine`")),
|
||||
(removed, generators, "1.21.0", Some(43122), None, Some("renamed to `coroutines`")),
|
||||
/// Allows `impl Trait` in bindings (`let`, `const`, `static`).
|
||||
(removed, impl_trait_in_bindings, "1.55.0", Some(63065), None,
|
||||
Some("the implementation was not maintainable, the feature may get reintroduced once the current refactorings are done")),
|
||||
|
|
|
|||
|
|
@ -92,7 +92,8 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
|
|||
span: Span,
|
||||
body_def_id: LocalDefId,
|
||||
f: F,
|
||||
) where
|
||||
) -> Result<(), ErrorGuaranteed>
|
||||
where
|
||||
F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>),
|
||||
{
|
||||
let param_env = tcx.param_env(body_def_id);
|
||||
|
|
@ -106,40 +107,46 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
|
|||
}
|
||||
f(&mut wfcx);
|
||||
|
||||
let assumed_wf_types = match wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)
|
||||
{
|
||||
Ok(wf_types) => wf_types,
|
||||
Err(_guar) => return,
|
||||
};
|
||||
let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?;
|
||||
|
||||
let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, assumed_wf_types);
|
||||
|
||||
let errors = wfcx.select_all_or_error();
|
||||
if !errors.is_empty() {
|
||||
infcx.err_ctxt().report_fulfillment_errors(errors);
|
||||
return;
|
||||
let err = infcx.err_ctxt().report_fulfillment_errors(errors);
|
||||
if tcx.sess.err_count() > 0 {
|
||||
return Err(err);
|
||||
} else {
|
||||
// HACK(oli-obk): tests/ui/specialization/min_specialization/specialize_on_type_error.rs causes an
|
||||
// error (delay_span_bug) during normalization, without reporting an error, so we need to act as if
|
||||
// no error happened, in order to let our callers continue and report an error later in
|
||||
// check_impl_items_against_trait.
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
||||
|
||||
let _ = wfcx.ocx.resolve_regions_and_report_errors(body_def_id, &outlives_env);
|
||||
wfcx.ocx.resolve_regions_and_report_errors(body_def_id, &outlives_env)?;
|
||||
infcx.tainted_by_errors().error_reported()
|
||||
}
|
||||
|
||||
fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) {
|
||||
fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) -> Result<(), ErrorGuaranteed> {
|
||||
let node = tcx.hir().owner(def_id);
|
||||
match node {
|
||||
hir::OwnerNode::Crate(_) => {}
|
||||
let mut res = match node {
|
||||
hir::OwnerNode::Crate(_) => bug!("check_well_formed cannot be applied to the crate root"),
|
||||
hir::OwnerNode::Item(item) => check_item(tcx, item),
|
||||
hir::OwnerNode::TraitItem(item) => check_trait_item(tcx, item),
|
||||
hir::OwnerNode::ImplItem(item) => check_impl_item(tcx, item),
|
||||
hir::OwnerNode::ForeignItem(item) => check_foreign_item(tcx, item),
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(generics) = node.generics() {
|
||||
for param in generics.params {
|
||||
check_param_wf(tcx, param)
|
||||
res = res.and(check_param_wf(tcx, param));
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/// Checks that the field types (in a struct def'n) or argument types (in an enum def'n) are
|
||||
|
|
@ -156,7 +163,7 @@ fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) {
|
|||
/// not included it frequently leads to confusing errors in fn bodies. So it's better to check
|
||||
/// the types first.
|
||||
#[instrument(skip(tcx), level = "debug")]
|
||||
fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
|
||||
fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<(), ErrorGuaranteed> {
|
||||
let def_id = item.owner_id.def_id;
|
||||
|
||||
debug!(
|
||||
|
|
@ -186,31 +193,32 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
|
|||
let is_auto = tcx
|
||||
.impl_trait_ref(def_id)
|
||||
.is_some_and(|trait_ref| tcx.trait_is_auto(trait_ref.skip_binder().def_id));
|
||||
let mut res = Ok(());
|
||||
if let (hir::Defaultness::Default { .. }, true) = (impl_.defaultness, is_auto) {
|
||||
let sp = impl_.of_trait.as_ref().map_or(item.span, |t| t.path.span);
|
||||
let mut err =
|
||||
tcx.sess.struct_span_err(sp, "impls of auto traits cannot be default");
|
||||
err.span_labels(impl_.defaultness_span, "default because of this");
|
||||
err.span_label(sp, "auto trait");
|
||||
err.emit();
|
||||
res = Err(err.emit());
|
||||
}
|
||||
// We match on both `ty::ImplPolarity` and `ast::ImplPolarity` just to get the `!` span.
|
||||
match (tcx.impl_polarity(def_id), impl_.polarity) {
|
||||
(ty::ImplPolarity::Positive, _) => {
|
||||
check_impl(tcx, item, impl_.self_ty, &impl_.of_trait);
|
||||
res = res.and(check_impl(tcx, item, impl_.self_ty, &impl_.of_trait));
|
||||
}
|
||||
(ty::ImplPolarity::Negative, ast::ImplPolarity::Negative(span)) => {
|
||||
// FIXME(#27579): what amount of WF checking do we need for neg impls?
|
||||
if let hir::Defaultness::Default { .. } = impl_.defaultness {
|
||||
let mut spans = vec![span];
|
||||
spans.extend(impl_.defaultness_span);
|
||||
struct_span_err!(
|
||||
res = Err(struct_span_err!(
|
||||
tcx.sess,
|
||||
spans,
|
||||
E0750,
|
||||
"negative impls cannot be default impls"
|
||||
)
|
||||
.emit();
|
||||
.emit());
|
||||
}
|
||||
}
|
||||
(ty::ImplPolarity::Reservation, _) => {
|
||||
|
|
@ -218,49 +226,52 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
|
|||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
res
|
||||
}
|
||||
hir::ItemKind::Fn(ref sig, ..) => {
|
||||
check_item_fn(tcx, def_id, item.ident, item.span, sig.decl);
|
||||
check_item_fn(tcx, def_id, item.ident, item.span, sig.decl)
|
||||
}
|
||||
hir::ItemKind::Static(ty, ..) => {
|
||||
check_item_type(tcx, def_id, ty.span, UnsizedHandling::Forbid);
|
||||
check_item_type(tcx, def_id, ty.span, UnsizedHandling::Forbid)
|
||||
}
|
||||
hir::ItemKind::Const(ty, ..) => {
|
||||
check_item_type(tcx, def_id, ty.span, UnsizedHandling::Forbid);
|
||||
check_item_type(tcx, def_id, ty.span, UnsizedHandling::Forbid)
|
||||
}
|
||||
hir::ItemKind::Struct(_, ast_generics) => {
|
||||
check_type_defn(tcx, item, false);
|
||||
let res = check_type_defn(tcx, item, false);
|
||||
check_variances_for_type_defn(tcx, item, ast_generics);
|
||||
res
|
||||
}
|
||||
hir::ItemKind::Union(_, ast_generics) => {
|
||||
check_type_defn(tcx, item, true);
|
||||
let res = check_type_defn(tcx, item, true);
|
||||
check_variances_for_type_defn(tcx, item, ast_generics);
|
||||
res
|
||||
}
|
||||
hir::ItemKind::Enum(_, ast_generics) => {
|
||||
check_type_defn(tcx, item, true);
|
||||
let res = check_type_defn(tcx, item, true);
|
||||
check_variances_for_type_defn(tcx, item, ast_generics);
|
||||
res
|
||||
}
|
||||
hir::ItemKind::Trait(..) => {
|
||||
check_trait(tcx, item);
|
||||
}
|
||||
hir::ItemKind::TraitAlias(..) => {
|
||||
check_trait(tcx, item);
|
||||
}
|
||||
hir::ItemKind::Trait(..) => check_trait(tcx, item),
|
||||
hir::ItemKind::TraitAlias(..) => check_trait(tcx, item),
|
||||
// `ForeignItem`s are handled separately.
|
||||
hir::ItemKind::ForeignMod { .. } => {}
|
||||
hir::ItemKind::ForeignMod { .. } => Ok(()),
|
||||
hir::ItemKind::TyAlias(hir_ty, ast_generics) => {
|
||||
if tcx.type_alias_is_lazy(item.owner_id) {
|
||||
// Bounds of lazy type aliases and of eager ones that contain opaque types are respected.
|
||||
// E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`.
|
||||
check_item_type(tcx, def_id, hir_ty.span, UnsizedHandling::Allow);
|
||||
let res = check_item_type(tcx, def_id, hir_ty.span, UnsizedHandling::Allow);
|
||||
check_variances_for_type_defn(tcx, item, ast_generics);
|
||||
res
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_foreign_item(tcx: TyCtxt<'_>, item: &hir::ForeignItem<'_>) {
|
||||
fn check_foreign_item(tcx: TyCtxt<'_>, item: &hir::ForeignItem<'_>) -> Result<(), ErrorGuaranteed> {
|
||||
let def_id = item.owner_id.def_id;
|
||||
|
||||
debug!(
|
||||
|
|
@ -275,11 +286,14 @@ fn check_foreign_item(tcx: TyCtxt<'_>, item: &hir::ForeignItem<'_>) {
|
|||
hir::ForeignItemKind::Static(ty, ..) => {
|
||||
check_item_type(tcx, def_id, ty.span, UnsizedHandling::AllowIfForeignTail)
|
||||
}
|
||||
hir::ForeignItemKind::Type => (),
|
||||
hir::ForeignItemKind::Type => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) {
|
||||
fn check_trait_item(
|
||||
tcx: TyCtxt<'_>,
|
||||
trait_item: &hir::TraitItem<'_>,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let def_id = trait_item.owner_id.def_id;
|
||||
|
||||
let (method_sig, span) = match trait_item.kind {
|
||||
|
|
@ -288,18 +302,19 @@ fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) {
|
|||
_ => (None, trait_item.span),
|
||||
};
|
||||
check_object_unsafe_self_trait_by_name(tcx, trait_item);
|
||||
check_associated_item(tcx, def_id, span, method_sig);
|
||||
let mut res = check_associated_item(tcx, def_id, span, method_sig);
|
||||
|
||||
if matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) {
|
||||
for &assoc_ty_def_id in tcx.associated_types_for_impl_traits_in_associated_fn(def_id) {
|
||||
check_associated_item(
|
||||
res = res.and(check_associated_item(
|
||||
tcx,
|
||||
assoc_ty_def_id.expect_local(),
|
||||
tcx.def_span(assoc_ty_def_id),
|
||||
None,
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/// Require that the user writes where clauses on GATs for the implicit
|
||||
|
|
@ -826,7 +841,7 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem
|
|||
}
|
||||
}
|
||||
|
||||
fn check_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) {
|
||||
fn check_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) -> Result<(), ErrorGuaranteed> {
|
||||
let (method_sig, span) = match impl_item.kind {
|
||||
hir::ImplItemKind::Fn(ref sig, _) => (Some(sig), impl_item.span),
|
||||
// Constrain binding and overflow error spans to `<Ty>` in `type foo = <Ty>`.
|
||||
|
|
@ -834,13 +849,13 @@ fn check_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) {
|
|||
_ => (None, impl_item.span),
|
||||
};
|
||||
|
||||
check_associated_item(tcx, impl_item.owner_id.def_id, span, method_sig);
|
||||
check_associated_item(tcx, impl_item.owner_id.def_id, span, method_sig)
|
||||
}
|
||||
|
||||
fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
|
||||
fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), ErrorGuaranteed> {
|
||||
match param.kind {
|
||||
// We currently only check wf of const params here.
|
||||
hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => (),
|
||||
hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => Ok(()),
|
||||
|
||||
// Const parameters are well formed if their type is structural match.
|
||||
hir::GenericParamKind::Const { ty: hir_ty, default: _ } => {
|
||||
|
|
@ -860,68 +875,66 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
|
|||
ty,
|
||||
trait_def_id,
|
||||
);
|
||||
});
|
||||
})
|
||||
} else {
|
||||
let diag = match ty.kind() {
|
||||
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None,
|
||||
ty::FnPtr(_) => Some(tcx.sess.struct_span_err(
|
||||
let mut diag = match ty.kind() {
|
||||
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => return Ok(()),
|
||||
ty::FnPtr(_) => tcx.sess.struct_span_err(
|
||||
hir_ty.span,
|
||||
"using function pointers as const generic parameters is forbidden",
|
||||
)),
|
||||
ty::RawPtr(_) => Some(tcx.sess.struct_span_err(
|
||||
),
|
||||
ty::RawPtr(_) => tcx.sess.struct_span_err(
|
||||
hir_ty.span,
|
||||
"using raw pointers as const generic parameters is forbidden",
|
||||
)),
|
||||
_ => Some(tcx.sess.struct_span_err(
|
||||
),
|
||||
_ => tcx.sess.struct_span_err(
|
||||
hir_ty.span,
|
||||
format!("`{}` is forbidden as the type of a const generic parameter", ty),
|
||||
)),
|
||||
),
|
||||
};
|
||||
|
||||
if let Some(mut diag) = diag {
|
||||
diag.note("the only supported types are integers, `bool` and `char`");
|
||||
diag.note("the only supported types are integers, `bool` and `char`");
|
||||
|
||||
let cause = ObligationCause::misc(hir_ty.span, param.def_id);
|
||||
let may_suggest_feature = match type_allowed_to_implement_const_param_ty(
|
||||
tcx,
|
||||
tcx.param_env(param.def_id),
|
||||
ty,
|
||||
cause,
|
||||
) {
|
||||
// Can never implement `ConstParamTy`, don't suggest anything.
|
||||
Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => false,
|
||||
// May be able to implement `ConstParamTy`. Only emit the feature help
|
||||
// if the type is local, since the user may be able to fix the local type.
|
||||
Err(ConstParamTyImplementationError::InfrigingFields(..)) => {
|
||||
fn ty_is_local(ty: Ty<'_>) -> bool {
|
||||
match ty.kind() {
|
||||
ty::Adt(adt_def, ..) => adt_def.did().is_local(),
|
||||
// Arrays and slices use the inner type's `ConstParamTy`.
|
||||
ty::Array(ty, ..) => ty_is_local(*ty),
|
||||
ty::Slice(ty) => ty_is_local(*ty),
|
||||
// `&` references use the inner type's `ConstParamTy`.
|
||||
// `&mut` are not supported.
|
||||
ty::Ref(_, ty, ast::Mutability::Not) => ty_is_local(*ty),
|
||||
// Say that a tuple is local if any of its components are local.
|
||||
// This is not strictly correct, but it's likely that the user can fix the local component.
|
||||
ty::Tuple(tys) => tys.iter().any(|ty| ty_is_local(ty)),
|
||||
_ => false,
|
||||
}
|
||||
let cause = ObligationCause::misc(hir_ty.span, param.def_id);
|
||||
let may_suggest_feature = match type_allowed_to_implement_const_param_ty(
|
||||
tcx,
|
||||
tcx.param_env(param.def_id),
|
||||
ty,
|
||||
cause,
|
||||
) {
|
||||
// Can never implement `ConstParamTy`, don't suggest anything.
|
||||
Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => false,
|
||||
// May be able to implement `ConstParamTy`. Only emit the feature help
|
||||
// if the type is local, since the user may be able to fix the local type.
|
||||
Err(ConstParamTyImplementationError::InfrigingFields(..)) => {
|
||||
fn ty_is_local(ty: Ty<'_>) -> bool {
|
||||
match ty.kind() {
|
||||
ty::Adt(adt_def, ..) => adt_def.did().is_local(),
|
||||
// Arrays and slices use the inner type's `ConstParamTy`.
|
||||
ty::Array(ty, ..) => ty_is_local(*ty),
|
||||
ty::Slice(ty) => ty_is_local(*ty),
|
||||
// `&` references use the inner type's `ConstParamTy`.
|
||||
// `&mut` are not supported.
|
||||
ty::Ref(_, ty, ast::Mutability::Not) => ty_is_local(*ty),
|
||||
// Say that a tuple is local if any of its components are local.
|
||||
// This is not strictly correct, but it's likely that the user can fix the local component.
|
||||
ty::Tuple(tys) => tys.iter().any(|ty| ty_is_local(ty)),
|
||||
_ => false,
|
||||
}
|
||||
|
||||
ty_is_local(ty)
|
||||
}
|
||||
// Implments `ConstParamTy`, suggest adding the feature to enable.
|
||||
Ok(..) => true,
|
||||
};
|
||||
if may_suggest_feature && tcx.sess.is_nightly_build() {
|
||||
diag.help(
|
||||
|
||||
ty_is_local(ty)
|
||||
}
|
||||
// Implments `ConstParamTy`, suggest adding the feature to enable.
|
||||
Ok(..) => true,
|
||||
};
|
||||
if may_suggest_feature && tcx.sess.is_nightly_build() {
|
||||
diag.help(
|
||||
"add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types",
|
||||
);
|
||||
}
|
||||
|
||||
diag.emit();
|
||||
}
|
||||
|
||||
Err(diag.emit())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -933,7 +946,7 @@ fn check_associated_item(
|
|||
item_id: LocalDefId,
|
||||
span: Span,
|
||||
sig_if_method: Option<&hir::FnSig<'_>>,
|
||||
) {
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let loc = Some(WellFormedLoc::Ty(item_id));
|
||||
enter_wf_checking_ctxt(tcx, span, item_id, |wfcx| {
|
||||
let item = tcx.associated_item(item_id);
|
||||
|
|
@ -985,7 +998,11 @@ fn item_adt_kind(kind: &ItemKind<'_>) -> Option<AdtKind> {
|
|||
}
|
||||
|
||||
/// In a type definition, we check that to ensure that the types of the fields are well-formed.
|
||||
fn check_type_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, all_sized: bool) {
|
||||
fn check_type_defn<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
item: &hir::Item<'tcx>,
|
||||
all_sized: bool,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let _ = tcx.representability(item.owner_id.def_id);
|
||||
let adt_def = tcx.adt_def(item.owner_id);
|
||||
|
||||
|
|
@ -1080,11 +1097,11 @@ fn check_type_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, all_sized: b
|
|||
}
|
||||
|
||||
check_where_clauses(wfcx, item.span, item.owner_id.def_id);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx, item))]
|
||||
fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
|
||||
fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) -> Result<(), ErrorGuaranteed> {
|
||||
debug!(?item.owner_id);
|
||||
|
||||
let def_id = item.owner_id.def_id;
|
||||
|
|
@ -1103,7 +1120,7 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
|
|||
}
|
||||
}
|
||||
|
||||
enter_wf_checking_ctxt(tcx, item.span, def_id, |wfcx| {
|
||||
let res = enter_wf_checking_ctxt(tcx, item.span, def_id, |wfcx| {
|
||||
check_where_clauses(wfcx, item.span, def_id)
|
||||
});
|
||||
|
||||
|
|
@ -1111,6 +1128,7 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
|
|||
if let hir::ItemKind::Trait(..) = item.kind {
|
||||
check_gat_where_clauses(tcx, item.owner_id.def_id);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/// Checks all associated type defaults of trait `trait_def_id`.
|
||||
|
|
@ -1142,7 +1160,7 @@ fn check_item_fn(
|
|||
ident: Ident,
|
||||
span: Span,
|
||||
decl: &hir::FnDecl<'_>,
|
||||
) {
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
enter_wf_checking_ctxt(tcx, span, def_id, |wfcx| {
|
||||
let sig = tcx.fn_sig(def_id).instantiate_identity();
|
||||
check_fn_or_method(wfcx, ident.span, sig, decl, def_id);
|
||||
|
|
@ -1160,7 +1178,7 @@ fn check_item_type(
|
|||
item_id: LocalDefId,
|
||||
ty_span: Span,
|
||||
unsized_handling: UnsizedHandling,
|
||||
) {
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
debug!("check_item_type: {:?}", item_id);
|
||||
|
||||
enter_wf_checking_ctxt(tcx, ty_span, item_id, |wfcx| {
|
||||
|
|
@ -1200,7 +1218,7 @@ fn check_item_type(
|
|||
tcx.require_lang_item(LangItem::Sync, Some(ty_span)),
|
||||
);
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))]
|
||||
|
|
@ -1209,7 +1227,7 @@ fn check_impl<'tcx>(
|
|||
item: &'tcx hir::Item<'tcx>,
|
||||
ast_self_ty: &hir::Ty<'_>,
|
||||
ast_trait_ref: &Option<hir::TraitRef<'_>>,
|
||||
) {
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| {
|
||||
match ast_trait_ref {
|
||||
Some(ast_trait_ref) => {
|
||||
|
|
@ -1258,7 +1276,7 @@ fn check_impl<'tcx>(
|
|||
}
|
||||
|
||||
check_where_clauses(wfcx, item.span, item.owner_id.def_id);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks where-clauses and inline bounds that are declared on `def_id`.
|
||||
|
|
@ -1879,12 +1897,12 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) {
|
||||
fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) -> Result<(), ErrorGuaranteed> {
|
||||
let items = tcx.hir_module_items(module);
|
||||
items.par_items(|item| tcx.ensure().check_well_formed(item.owner_id));
|
||||
items.par_impl_items(|item| tcx.ensure().check_well_formed(item.owner_id));
|
||||
items.par_trait_items(|item| tcx.ensure().check_well_formed(item.owner_id));
|
||||
items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id));
|
||||
let mut res = items.par_items(|item| tcx.ensure().check_well_formed(item.owner_id));
|
||||
res = res.and(items.par_impl_items(|item| tcx.ensure().check_well_formed(item.owner_id)));
|
||||
res = res.and(items.par_trait_items(|item| tcx.ensure().check_well_formed(item.owner_id)));
|
||||
res.and(items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id)))
|
||||
}
|
||||
|
||||
fn error_392(
|
||||
|
|
|
|||
|
|
@ -205,10 +205,8 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
|
|||
})?;
|
||||
}
|
||||
|
||||
tcx.sess.track_errors(|| {
|
||||
tcx.sess.time("wf_checking", || {
|
||||
tcx.hir().par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module))
|
||||
});
|
||||
tcx.sess.time("wf_checking", || {
|
||||
tcx.hir().try_par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module))
|
||||
})?;
|
||||
|
||||
// NOTE: This is copy/pasted in librustdoc/core.rs and should be kept in sync.
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ mod arg_matrix;
|
|||
mod checks;
|
||||
mod suggestions;
|
||||
|
||||
pub use _impl::*;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
pub use suggestions::*;
|
||||
|
||||
use crate::coercion::DynamicCoerceMany;
|
||||
use crate::{Diverges, EnclosingBreakables, Inherited};
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use rustc_hir::pat_util::EnumerateAndAdjustIterator;
|
|||
use rustc_hir::{HirId, Pat, PatKind};
|
||||
use rustc_infer::infer;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeVisitableExt};
|
||||
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
||||
use rustc_span::edit_distance::find_best_match_for_name;
|
||||
|
|
@ -2164,7 +2165,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
len: ty::Const<'tcx>,
|
||||
min_len: u64,
|
||||
) -> (Option<Ty<'tcx>>, Ty<'tcx>) {
|
||||
let guar = if let Some(len) = len.try_eval_target_usize(self.tcx, self.param_env) {
|
||||
let len = match len.eval(self.tcx, self.param_env, None) {
|
||||
Ok(val) => val
|
||||
.try_to_scalar()
|
||||
.and_then(|scalar| scalar.try_to_int().ok())
|
||||
.and_then(|int| int.try_to_target_usize(self.tcx).ok()),
|
||||
Err(ErrorHandled::Reported(..)) => {
|
||||
let guar = self.error_scrutinee_unfixed_length(span);
|
||||
return (Some(Ty::new_error(self.tcx, guar)), arr_ty);
|
||||
}
|
||||
Err(ErrorHandled::TooGeneric(..)) => None,
|
||||
};
|
||||
|
||||
let guar = if let Some(len) = len {
|
||||
// Now we know the length...
|
||||
if slice.is_none() {
|
||||
// ...and since there is no variable-length pattern,
|
||||
|
|
|
|||
|
|
@ -588,60 +588,60 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
self.tcx
|
||||
}
|
||||
|
||||
fn print_region(self, _region: ty::Region<'_>) -> Result<Self, PrintError> {
|
||||
fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> {
|
||||
Err(fmt::Error)
|
||||
}
|
||||
|
||||
fn print_type(self, _ty: Ty<'tcx>) -> Result<Self, PrintError> {
|
||||
fn print_type(&mut self, _ty: Ty<'tcx>) -> Result<(), PrintError> {
|
||||
Err(fmt::Error)
|
||||
}
|
||||
|
||||
fn print_dyn_existential(
|
||||
self,
|
||||
&mut self,
|
||||
_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
Err(fmt::Error)
|
||||
}
|
||||
|
||||
fn print_const(self, _ct: ty::Const<'tcx>) -> Result<Self, PrintError> {
|
||||
fn print_const(&mut self, _ct: ty::Const<'tcx>) -> Result<(), PrintError> {
|
||||
Err(fmt::Error)
|
||||
}
|
||||
|
||||
fn path_crate(mut self, cnum: CrateNum) -> Result<Self, PrintError> {
|
||||
fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
|
||||
self.segments = vec![self.tcx.crate_name(cnum).to_string()];
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
fn path_qualified(
|
||||
self,
|
||||
&mut self,
|
||||
_self_ty: Ty<'tcx>,
|
||||
_trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
Err(fmt::Error)
|
||||
}
|
||||
|
||||
fn path_append_impl(
|
||||
self,
|
||||
_print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
_print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
_disambiguated_data: &DisambiguatedDefPathData,
|
||||
_self_ty: Ty<'tcx>,
|
||||
_trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
Err(fmt::Error)
|
||||
}
|
||||
fn path_append(
|
||||
mut self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
disambiguated_data: &DisambiguatedDefPathData,
|
||||
) -> Result<Self, PrintError> {
|
||||
self = print_prefix(self)?;
|
||||
) -> Result<(), PrintError> {
|
||||
print_prefix(self)?;
|
||||
self.segments.push(disambiguated_data.to_string());
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
fn path_generic_args(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
_args: &[GenericArg<'tcx>],
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
print_prefix(self)
|
||||
}
|
||||
}
|
||||
|
|
@ -652,9 +652,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
// let _ = [{struct Foo; Foo}, {struct Foo; Foo}];
|
||||
if did1.krate != did2.krate {
|
||||
let abs_path = |def_id| {
|
||||
AbsolutePathPrinter { tcx: self.tcx, segments: vec![] }
|
||||
.print_def_path(def_id, &[])
|
||||
.map(|p| p.segments)
|
||||
let mut printer = AbsolutePathPrinter { tcx: self.tcx, segments: vec![] };
|
||||
printer.print_def_path(def_id, &[]).map(|_| printer.segments)
|
||||
};
|
||||
|
||||
// We compare strings because DefPath can be different
|
||||
|
|
@ -1071,7 +1070,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
|
||||
let get_lifetimes = |sig| {
|
||||
use rustc_hir::def::Namespace;
|
||||
let (_, sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS)
|
||||
let (sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS)
|
||||
.name_all_regions(sig)
|
||||
.unwrap();
|
||||
let lts: Vec<String> = reg.into_values().map(|kind| kind.to_string()).collect();
|
||||
|
|
|
|||
|
|
@ -200,12 +200,15 @@ fn ty_to_string<'tcx>(
|
|||
ty: Ty<'tcx>,
|
||||
called_method_def_id: Option<DefId>,
|
||||
) -> String {
|
||||
let printer = fmt_printer(infcx, Namespace::TypeNS);
|
||||
let mut printer = fmt_printer(infcx, Namespace::TypeNS);
|
||||
let ty = infcx.resolve_vars_if_possible(ty);
|
||||
match (ty.kind(), called_method_def_id) {
|
||||
// We don't want the regular output for `fn`s because it includes its path in
|
||||
// invalid pseudo-syntax, we want the `fn`-pointer output instead.
|
||||
(ty::FnDef(..), _) => ty.fn_sig(infcx.tcx).print(printer).unwrap().into_buffer(),
|
||||
(ty::FnDef(..), _) => {
|
||||
ty.fn_sig(infcx.tcx).print(&mut printer).unwrap();
|
||||
printer.into_buffer()
|
||||
}
|
||||
(_, Some(def_id))
|
||||
if ty.is_ty_or_numeric_infer()
|
||||
&& infcx.tcx.get_diagnostic_item(sym::iterator_collect_fn) == Some(def_id) =>
|
||||
|
|
@ -218,7 +221,10 @@ fn ty_to_string<'tcx>(
|
|||
//
|
||||
// We do have to hide the `extern "rust-call"` ABI in that case though,
|
||||
// which is too much of a bother for now.
|
||||
_ => ty.print(printer).unwrap().into_buffer(),
|
||||
_ => {
|
||||
ty.print(&mut printer).unwrap();
|
||||
printer.into_buffer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -285,8 +291,9 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
if let Some(highlight) = highlight {
|
||||
printer.region_highlight_mode = highlight;
|
||||
}
|
||||
ty.print(&mut printer).unwrap();
|
||||
InferenceDiagnosticsData {
|
||||
name: ty.print(printer).unwrap().into_buffer(),
|
||||
name: printer.into_buffer(),
|
||||
span: None,
|
||||
kind: UnderspecifiedArgKind::Type { prefix: ty.prefix_string(self.tcx) },
|
||||
parent: None,
|
||||
|
|
@ -312,8 +319,9 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
if let Some(highlight) = highlight {
|
||||
printer.region_highlight_mode = highlight;
|
||||
}
|
||||
ct.print(&mut printer).unwrap();
|
||||
InferenceDiagnosticsData {
|
||||
name: ct.print(printer).unwrap().into_buffer(),
|
||||
name: printer.into_buffer(),
|
||||
span: Some(origin.span),
|
||||
kind: UnderspecifiedArgKind::Const { is_parameter: false },
|
||||
parent: None,
|
||||
|
|
@ -329,8 +337,9 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
if let Some(highlight) = highlight {
|
||||
printer.region_highlight_mode = highlight;
|
||||
}
|
||||
ct.print(&mut printer).unwrap();
|
||||
InferenceDiagnosticsData {
|
||||
name: ct.print(printer).unwrap().into_buffer(),
|
||||
name: printer.into_buffer(),
|
||||
span: None,
|
||||
kind: UnderspecifiedArgKind::Const { is_parameter: false },
|
||||
parent: None,
|
||||
|
|
@ -487,7 +496,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
{
|
||||
"Vec<_>".to_string()
|
||||
} else {
|
||||
fmt_printer(self, Namespace::TypeNS)
|
||||
let mut printer = fmt_printer(self, Namespace::TypeNS);
|
||||
printer
|
||||
.comma_sep(generic_args.iter().copied().map(|arg| {
|
||||
if arg.is_suggestable(self.tcx, true) {
|
||||
return arg;
|
||||
|
|
@ -512,8 +522,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
.into(),
|
||||
}
|
||||
}))
|
||||
.unwrap()
|
||||
.into_buffer()
|
||||
.unwrap();
|
||||
printer.into_buffer()
|
||||
};
|
||||
|
||||
if !have_turbofish {
|
||||
|
|
@ -525,8 +535,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
InferSourceKind::FullyQualifiedMethodCall { receiver, successor, args, def_id } => {
|
||||
let printer = fmt_printer(self, Namespace::ValueNS);
|
||||
let def_path = printer.print_def_path(def_id, args).unwrap().into_buffer();
|
||||
let mut printer = fmt_printer(self, Namespace::ValueNS);
|
||||
printer.print_def_path(def_id, args).unwrap();
|
||||
let def_path = printer.into_buffer();
|
||||
|
||||
// We only care about whether we have to add `&` or `&mut ` for now.
|
||||
// This is the case if the last adjustment is a borrow and the
|
||||
|
|
|
|||
|
|
@ -49,8 +49,8 @@ where
|
|||
let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS);
|
||||
printer.region_highlight_mode = self.highlight;
|
||||
|
||||
let s = self.value.print(printer)?.into_buffer();
|
||||
f.write_str(&s)
|
||||
self.value.print(&mut printer)?;
|
||||
f.write_str(&printer.into_buffer())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -763,9 +763,9 @@ fn foo(&self) -> Self::T { String::new() }
|
|||
}
|
||||
|
||||
pub fn format_generic_args(&self, args: &[ty::GenericArg<'tcx>]) -> String {
|
||||
FmtPrinter::new(self.tcx, hir::def::Namespace::TypeNS)
|
||||
.path_generic_args(Ok, args)
|
||||
.expect("could not write to `String`.")
|
||||
.into_buffer()
|
||||
FmtPrinter::print_string(self.tcx, hir::def::Namespace::TypeNS, |cx| {
|
||||
cx.path_generic_args(|_| Ok(()), args)
|
||||
})
|
||||
.expect("could not write to `String`.")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ use rustc_span::Span;
|
|||
|
||||
pub use self::FulfillmentErrorCode::*;
|
||||
pub use self::ImplSource::*;
|
||||
pub use self::ObligationCauseCode::*;
|
||||
pub use self::SelectionError::*;
|
||||
|
||||
pub use self::engine::{TraitEngine, TraitEngineExt};
|
||||
|
|
|
|||
|
|
@ -1210,35 +1210,35 @@ impl<'tcx> LateContext<'tcx> {
|
|||
self.tcx
|
||||
}
|
||||
|
||||
fn print_region(self, _region: ty::Region<'_>) -> Result<Self, PrintError> {
|
||||
Ok(self)
|
||||
fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_type(self, _ty: Ty<'tcx>) -> Result<Self, PrintError> {
|
||||
Ok(self)
|
||||
fn print_type(&mut self, _ty: Ty<'tcx>) -> Result<(), PrintError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_dyn_existential(
|
||||
self,
|
||||
&mut self,
|
||||
_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
Ok(self)
|
||||
) -> Result<(), PrintError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_const(self, _ct: ty::Const<'tcx>) -> Result<Self, PrintError> {
|
||||
Ok(self)
|
||||
fn print_const(&mut self, _ct: ty::Const<'tcx>) -> Result<(), PrintError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn path_crate(mut self, cnum: CrateNum) -> Result<Self, PrintError> {
|
||||
fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
|
||||
self.path = vec![self.tcx.crate_name(cnum)];
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn path_qualified(
|
||||
mut self,
|
||||
&mut self,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
if trait_ref.is_none() {
|
||||
if let ty::Adt(def, args) = self_ty.kind() {
|
||||
return self.print_def_path(def.did(), args);
|
||||
|
|
@ -1251,21 +1251,21 @@ impl<'tcx> LateContext<'tcx> {
|
|||
Some(trait_ref) => Symbol::intern(&format!("{trait_ref:?}")),
|
||||
None => Symbol::intern(&format!("<{self_ty}>")),
|
||||
}];
|
||||
Ok(self)
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn path_append_impl(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
_disambiguated_data: &DisambiguatedDefPathData,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
let mut path = print_prefix(self)?;
|
||||
) -> Result<(), PrintError> {
|
||||
print_prefix(self)?;
|
||||
|
||||
// This shouldn't ever be needed, but just in case:
|
||||
path.path.push(match trait_ref {
|
||||
self.path.push(match trait_ref {
|
||||
Some(trait_ref) => {
|
||||
with_no_trimmed_paths!(Symbol::intern(&format!(
|
||||
"<impl {} for {}>",
|
||||
|
|
@ -1278,38 +1278,37 @@ impl<'tcx> LateContext<'tcx> {
|
|||
}
|
||||
});
|
||||
|
||||
Ok(path)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn path_append(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
disambiguated_data: &DisambiguatedDefPathData,
|
||||
) -> Result<Self, PrintError> {
|
||||
let mut path = print_prefix(self)?;
|
||||
) -> Result<(), PrintError> {
|
||||
print_prefix(self)?;
|
||||
|
||||
// Skip `::{{extern}}` blocks and `::{{constructor}}` on tuple/unit structs.
|
||||
if let DefPathData::ForeignMod | DefPathData::Ctor = disambiguated_data.data {
|
||||
return Ok(path);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
path.path.push(Symbol::intern(&disambiguated_data.data.to_string()));
|
||||
Ok(path)
|
||||
self.path.push(Symbol::intern(&disambiguated_data.data.to_string()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn path_generic_args(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
_args: &[GenericArg<'tcx>],
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
print_prefix(self)
|
||||
}
|
||||
}
|
||||
|
||||
AbsolutePathPrinter { tcx: self.tcx, path: vec![] }
|
||||
.print_def_path(def_id, &[])
|
||||
.unwrap()
|
||||
.path
|
||||
let mut printer = AbsolutePathPrinter { tcx: self.tcx, path: vec![] };
|
||||
printer.print_def_path(def_id, &[]).unwrap();
|
||||
printer.path
|
||||
}
|
||||
|
||||
/// Returns the associated type `name` for `self_ty` as an implementation of `trait_id`.
|
||||
|
|
|
|||
|
|
@ -114,6 +114,11 @@ struct QueryModifiers {
|
|||
|
||||
/// Generate a `feed` method to set the query's value from another query.
|
||||
feedable: Option<Ident>,
|
||||
|
||||
/// Forward the result on ensure if the query gets recomputed, and
|
||||
/// return `Ok(())` otherwise. Only applicable to queries returning
|
||||
/// `Result<(), ErrorGuaranteed>`
|
||||
ensure_forwards_result_if_red: Option<Ident>,
|
||||
}
|
||||
|
||||
fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
|
||||
|
|
@ -128,6 +133,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
|
|||
let mut depth_limit = None;
|
||||
let mut separate_provide_extern = None;
|
||||
let mut feedable = None;
|
||||
let mut ensure_forwards_result_if_red = None;
|
||||
|
||||
while !input.is_empty() {
|
||||
let modifier: Ident = input.parse()?;
|
||||
|
|
@ -187,6 +193,8 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
|
|||
try_insert!(separate_provide_extern = modifier);
|
||||
} else if modifier == "feedable" {
|
||||
try_insert!(feedable = modifier);
|
||||
} else if modifier == "ensure_forwards_result_if_red" {
|
||||
try_insert!(ensure_forwards_result_if_red = modifier);
|
||||
} else {
|
||||
return Err(Error::new(modifier.span(), "unknown query modifier"));
|
||||
}
|
||||
|
|
@ -206,6 +214,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
|
|||
depth_limit,
|
||||
separate_provide_extern,
|
||||
feedable,
|
||||
ensure_forwards_result_if_red,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -325,6 +334,7 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
|
|||
eval_always,
|
||||
depth_limit,
|
||||
separate_provide_extern,
|
||||
ensure_forwards_result_if_red,
|
||||
);
|
||||
|
||||
if modifiers.cache.is_some() {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use rustc_ast as ast;
|
|||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_data_structures::sync::{par_for_each_in, DynSend, DynSync};
|
||||
use rustc_data_structures::sync::{par_for_each_in, try_par_for_each_in, DynSend, DynSync};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId, LOCAL_CRATE};
|
||||
use rustc_hir::definitions::{DefKey, DefPath, DefPathData, DefPathHash};
|
||||
|
|
@ -16,7 +16,7 @@ use rustc_index::Idx;
|
|||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_span::def_id::StableCrateId;
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{ErrorGuaranteed, Span};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
#[inline]
|
||||
|
|
@ -632,6 +632,17 @@ impl<'hir> Map<'hir> {
|
|||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_par_for_each_module(
|
||||
self,
|
||||
f: impl Fn(LocalModDefId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let crate_items = self.tcx.hir_crate_items(());
|
||||
try_par_for_each_in(&crate_items.submodules[..], |module| {
|
||||
f(LocalModDefId::new_unchecked(module.def_id))
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns an iterator for the nodes in the ancestor tree of the `current_id`
|
||||
/// until the crate root is reached. Prefer this over your own loop using `parent_id`.
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ pub mod place;
|
|||
use crate::query::Providers;
|
||||
use crate::ty::{EarlyBinder, ImplSubject, TyCtxt};
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::sync::{par_for_each_in, DynSend, DynSync};
|
||||
use rustc_data_structures::sync::{try_par_for_each_in, DynSend, DynSync};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
|
||||
use rustc_hir::*;
|
||||
use rustc_query_system::ich::StableHashingContext;
|
||||
use rustc_span::{ExpnId, DUMMY_SP};
|
||||
use rustc_span::{ErrorGuaranteed, ExpnId, DUMMY_SP};
|
||||
|
||||
/// Top-level HIR node for current owner. This only contains the node for which
|
||||
/// `HirId::local_id == 0`, and excludes bodies.
|
||||
|
|
@ -78,20 +78,32 @@ impl ModuleItems {
|
|||
self.owners().map(|id| id.def_id)
|
||||
}
|
||||
|
||||
pub fn par_items(&self, f: impl Fn(ItemId) + DynSend + DynSync) {
|
||||
par_for_each_in(&self.items[..], |&id| f(id))
|
||||
pub fn par_items(
|
||||
&self,
|
||||
f: impl Fn(ItemId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
try_par_for_each_in(&self.items[..], |&id| f(id))
|
||||
}
|
||||
|
||||
pub fn par_trait_items(&self, f: impl Fn(TraitItemId) + DynSend + DynSync) {
|
||||
par_for_each_in(&self.trait_items[..], |&id| f(id))
|
||||
pub fn par_trait_items(
|
||||
&self,
|
||||
f: impl Fn(TraitItemId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
try_par_for_each_in(&self.trait_items[..], |&id| f(id))
|
||||
}
|
||||
|
||||
pub fn par_impl_items(&self, f: impl Fn(ImplItemId) + DynSend + DynSync) {
|
||||
par_for_each_in(&self.impl_items[..], |&id| f(id))
|
||||
pub fn par_impl_items(
|
||||
&self,
|
||||
f: impl Fn(ImplItemId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
try_par_for_each_in(&self.impl_items[..], |&id| f(id))
|
||||
}
|
||||
|
||||
pub fn par_foreign_items(&self, f: impl Fn(ForeignItemId) + DynSend + DynSync) {
|
||||
par_for_each_in(&self.foreign_items[..], |&id| f(id))
|
||||
pub fn par_foreign_items(
|
||||
&self,
|
||||
f: impl Fn(ForeignItemId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
try_par_for_each_in(&self.foreign_items[..], |&id| f(id))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -998,9 +998,9 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
|||
ty::tls::with(|tcx| {
|
||||
let variant_def = &tcx.adt_def(adt_did).variant(variant);
|
||||
let args = tcx.lift(args).expect("could not lift for printing");
|
||||
let name = FmtPrinter::new(tcx, Namespace::ValueNS)
|
||||
.print_def_path(variant_def.def_id, args)?
|
||||
.into_buffer();
|
||||
let name = FmtPrinter::print_string(tcx, Namespace::ValueNS, |cx| {
|
||||
cx.print_def_path(variant_def.def_id, args)
|
||||
})?;
|
||||
|
||||
match variant_def.ctor_kind() {
|
||||
Some(CtorKind::Const) => fmt.write_str(&name),
|
||||
|
|
@ -1740,7 +1740,7 @@ fn pretty_print_const_value_tcx<'tcx>(
|
|||
let args = tcx.lift(args).unwrap();
|
||||
let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
|
||||
cx.print_alloc_ids = true;
|
||||
let cx = cx.print_value_path(variant_def.def_id, args)?;
|
||||
cx.print_value_path(variant_def.def_id, args)?;
|
||||
fmt.write_str(&cx.into_buffer())?;
|
||||
|
||||
match variant_def.ctor_kind() {
|
||||
|
|
@ -1775,14 +1775,14 @@ fn pretty_print_const_value_tcx<'tcx>(
|
|||
let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
|
||||
cx.print_alloc_ids = true;
|
||||
let ty = tcx.lift(ty).unwrap();
|
||||
cx = cx.pretty_print_const_scalar(scalar, ty)?;
|
||||
cx.pretty_print_const_scalar(scalar, ty)?;
|
||||
fmt.write_str(&cx.into_buffer())?;
|
||||
return Ok(());
|
||||
}
|
||||
(ConstValue::ZeroSized, ty::FnDef(d, s)) => {
|
||||
let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
|
||||
cx.print_alloc_ids = true;
|
||||
let cx = cx.print_value_path(*d, s)?;
|
||||
cx.print_value_path(*d, s)?;
|
||||
fmt.write_str(&cx.into_buffer())?;
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,10 @@ use rustc_hir::LangItem;
|
|||
use smallvec::SmallVec;
|
||||
|
||||
use super::{BasicBlock, InlineAsmOperand, Operand, SourceInfo, TerminatorKind, UnwindAction};
|
||||
pub use rustc_ast::Mutability;
|
||||
use rustc_macros::HashStable;
|
||||
use std::iter;
|
||||
use std::slice;
|
||||
|
||||
pub use super::query::*;
|
||||
use super::*;
|
||||
|
||||
impl SwitchTargets {
|
||||
|
|
@ -28,6 +26,15 @@ impl SwitchTargets {
|
|||
Self { values: smallvec![value], targets: smallvec![then, else_] }
|
||||
}
|
||||
|
||||
/// Inverse of `SwitchTargets::static_if`.
|
||||
pub fn as_static_if(&self) -> Option<(u128, BasicBlock, BasicBlock)> {
|
||||
if let &[value] = &self.values[..] && let &[then, else_] = &self.targets[..] {
|
||||
Some((value, then, else_))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the fallback target that is jumped to when none of the values match the operand.
|
||||
pub fn otherwise(&self) -> BasicBlock {
|
||||
*self.targets.last().unwrap()
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ use crate::mir::interpret::{
|
|||
use crate::mir::interpret::{LitToConstError, LitToConstInput};
|
||||
use crate::mir::mono::CodegenUnit;
|
||||
use crate::query::erase::{erase, restore, Erase};
|
||||
use crate::query::plumbing::{query_ensure, query_get_at, CyclePlaceholder, DynamicQuery};
|
||||
use crate::query::plumbing::{
|
||||
query_ensure, query_ensure_error_guaranteed, query_get_at, CyclePlaceholder, DynamicQuery,
|
||||
};
|
||||
use crate::thir;
|
||||
use crate::traits::query::{
|
||||
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,
|
||||
|
|
@ -965,8 +967,9 @@ rustc_queries! {
|
|||
desc { |tcx| "checking that impls are well-formed in {}", describe_as_module(key, tcx) }
|
||||
}
|
||||
|
||||
query check_mod_type_wf(key: LocalModDefId) -> () {
|
||||
query check_mod_type_wf(key: LocalModDefId) -> Result<(), ErrorGuaranteed> {
|
||||
desc { |tcx| "checking that types are well-formed in {}", describe_as_module(key, tcx) }
|
||||
ensure_forwards_result_if_red
|
||||
}
|
||||
|
||||
query collect_mod_item_types(key: LocalModDefId) -> () {
|
||||
|
|
@ -1499,8 +1502,9 @@ rustc_queries! {
|
|||
feedable
|
||||
}
|
||||
|
||||
query check_well_formed(key: hir::OwnerId) -> () {
|
||||
query check_well_formed(key: hir::OwnerId) -> Result<(), ErrorGuaranteed> {
|
||||
desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key) }
|
||||
ensure_forwards_result_if_red
|
||||
}
|
||||
|
||||
// The `DefId`s of all non-generic functions and statics in the given crate
|
||||
|
|
|
|||
|
|
@ -173,6 +173,45 @@ pub fn query_ensure<'tcx, Cache>(
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn query_ensure_error_guaranteed<'tcx, Cache>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>,
|
||||
query_cache: &Cache,
|
||||
key: Cache::Key,
|
||||
check_cache: bool,
|
||||
) -> Result<(), ErrorGuaranteed>
|
||||
where
|
||||
Cache: QueryCache<Value = super::erase::Erase<Result<(), ErrorGuaranteed>>>,
|
||||
{
|
||||
let key = key.into_query_param();
|
||||
if let Some(res) = try_get_cached(tcx, query_cache, &key) {
|
||||
super::erase::restore(res)
|
||||
} else {
|
||||
execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache })
|
||||
.map(super::erase::restore)
|
||||
// Either we actually executed the query, which means we got a full `Result`,
|
||||
// or we can just assume the query succeeded, because it was green in the
|
||||
// incremental cache. If it is green, that means that the previous compilation
|
||||
// that wrote to the incremental cache compiles successfully. That is only
|
||||
// possible if the cache entry was `Ok(())`, so we emit that here, without
|
||||
// actually encoding the `Result` in the cache or loading it from there.
|
||||
.unwrap_or(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! query_ensure {
|
||||
([]$($args:tt)*) => {
|
||||
query_ensure($($args)*)
|
||||
};
|
||||
([(ensure_forwards_result_if_red) $($rest:tt)*]$($args:tt)*) => {
|
||||
query_ensure_error_guaranteed($($args)*)
|
||||
};
|
||||
([$other:tt $($modifiers:tt)*]$($args:tt)*) => {
|
||||
query_ensure!([$($modifiers)*]$($args)*)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! query_helper_param_ty {
|
||||
(DefId) => { impl IntoQueryParam<DefId> };
|
||||
(LocalDefId) => { impl IntoQueryParam<LocalDefId> };
|
||||
|
|
@ -220,6 +259,18 @@ macro_rules! separate_provide_extern_decl {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! ensure_result {
|
||||
([][$ty:ty]) => {
|
||||
()
|
||||
};
|
||||
([(ensure_forwards_result_if_red) $($rest:tt)*][$ty:ty]) => {
|
||||
Result<(), ErrorGuaranteed>
|
||||
};
|
||||
([$other:tt $($modifiers:tt)*][$($args:tt)*]) => {
|
||||
ensure_result!([$($modifiers)*][$($args)*])
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! separate_provide_extern_default {
|
||||
([][$name:ident]) => {
|
||||
()
|
||||
|
|
@ -343,14 +394,15 @@ macro_rules! define_callbacks {
|
|||
impl<'tcx> TyCtxtEnsure<'tcx> {
|
||||
$($(#[$attr])*
|
||||
#[inline(always)]
|
||||
pub fn $name(self, key: query_helper_param_ty!($($K)*)) {
|
||||
query_ensure(
|
||||
pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> ensure_result!([$($modifiers)*][$V]) {
|
||||
query_ensure!(
|
||||
[$($modifiers)*]
|
||||
self.tcx,
|
||||
self.tcx.query_system.fns.engine.$name,
|
||||
&self.tcx.query_system.caches.$name,
|
||||
key.into_query_param(),
|
||||
false,
|
||||
);
|
||||
)
|
||||
})*
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
pub use self::AssocItemContainer::*;
|
||||
|
||||
use crate::ty;
|
||||
use rustc_data_structures::sorted_map::SortedIndexMultiMap;
|
||||
use rustc_hir as hir;
|
||||
|
|
|
|||
|
|
@ -315,26 +315,25 @@ impl<'tcx> Ty<'tcx> {
|
|||
impl<'tcx> TyCtxt<'tcx> {
|
||||
pub fn ty_string_with_limit(self, ty: Ty<'tcx>, length_limit: usize) -> String {
|
||||
let mut type_limit = 50;
|
||||
let regular = FmtPrinter::new(self, hir::def::Namespace::TypeNS)
|
||||
.pretty_print_type(ty)
|
||||
.expect("could not write to `String`")
|
||||
.into_buffer();
|
||||
let regular = FmtPrinter::print_string(self, hir::def::Namespace::TypeNS, |cx| {
|
||||
cx.pretty_print_type(ty)
|
||||
})
|
||||
.expect("could not write to `String`");
|
||||
if regular.len() <= length_limit {
|
||||
return regular;
|
||||
}
|
||||
let mut short;
|
||||
loop {
|
||||
// Look for the longest properly trimmed path that still fits in length_limit.
|
||||
short = with_forced_trimmed_paths!(
|
||||
FmtPrinter::new_with_limit(
|
||||
short = with_forced_trimmed_paths!({
|
||||
let mut cx = FmtPrinter::new_with_limit(
|
||||
self,
|
||||
hir::def::Namespace::TypeNS,
|
||||
rustc_session::Limit(type_limit),
|
||||
)
|
||||
.pretty_print_type(ty)
|
||||
.expect("could not write to `String`")
|
||||
.into_buffer()
|
||||
);
|
||||
);
|
||||
cx.pretty_print_type(ty).expect("could not write to `String`");
|
||||
cx.into_buffer()
|
||||
});
|
||||
if short.len() <= length_limit || type_limit == 0 {
|
||||
break;
|
||||
}
|
||||
|
|
@ -344,10 +343,10 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
}
|
||||
|
||||
pub fn short_ty_string(self, ty: Ty<'tcx>) -> (String, Option<PathBuf>) {
|
||||
let regular = FmtPrinter::new(self, hir::def::Namespace::TypeNS)
|
||||
.pretty_print_type(ty)
|
||||
.expect("could not write to `String`")
|
||||
.into_buffer();
|
||||
let regular = FmtPrinter::print_string(self, hir::def::Namespace::TypeNS, |cx| {
|
||||
cx.pretty_print_type(ty)
|
||||
})
|
||||
.expect("could not write to `String`");
|
||||
|
||||
if !self.sess.opts.unstable_opts.write_long_types_to_disk {
|
||||
return (regular, None);
|
||||
|
|
|
|||
|
|
@ -298,9 +298,9 @@ fn fmt_instance(
|
|||
ty::tls::with(|tcx| {
|
||||
let args = tcx.lift(instance.args).expect("could not lift for printing");
|
||||
|
||||
let s = FmtPrinter::new_with_limit(tcx, Namespace::ValueNS, type_length)
|
||||
.print_def_path(instance.def_id(), args)?
|
||||
.into_buffer();
|
||||
let mut cx = FmtPrinter::new_with_limit(tcx, Namespace::ValueNS, type_length);
|
||||
cx.print_def_path(instance.def_id(), args)?;
|
||||
let s = cx.into_buffer();
|
||||
f.write_str(&s)
|
||||
})?;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ pub type PrintError = std::fmt::Error;
|
|||
// FIXME(eddyb) false positive, the lifetime parameters are used with `P: Printer<...>`.
|
||||
#[allow(unused_lifetimes)]
|
||||
pub trait Print<'tcx, P> {
|
||||
fn print(&self, cx: P) -> Result<P, PrintError>;
|
||||
fn print(&self, cx: &mut P) -> Result<(), PrintError>;
|
||||
}
|
||||
|
||||
/// Interface for outputting user-facing "type-system entities"
|
||||
|
|
@ -31,70 +31,70 @@ pub trait Printer<'tcx>: Sized {
|
|||
fn tcx<'a>(&'a self) -> TyCtxt<'tcx>;
|
||||
|
||||
fn print_def_path(
|
||||
self,
|
||||
&mut self,
|
||||
def_id: DefId,
|
||||
args: &'tcx [GenericArg<'tcx>],
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
self.default_print_def_path(def_id, args)
|
||||
}
|
||||
|
||||
fn print_impl_path(
|
||||
self,
|
||||
&mut self,
|
||||
impl_def_id: DefId,
|
||||
args: &'tcx [GenericArg<'tcx>],
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
self.default_print_impl_path(impl_def_id, args, self_ty, trait_ref)
|
||||
}
|
||||
|
||||
fn print_region(self, region: ty::Region<'tcx>) -> Result<Self, PrintError>;
|
||||
fn print_region(&mut self, region: ty::Region<'tcx>) -> Result<(), PrintError>;
|
||||
|
||||
fn print_type(self, ty: Ty<'tcx>) -> Result<Self, PrintError>;
|
||||
fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError>;
|
||||
|
||||
fn print_dyn_existential(
|
||||
self,
|
||||
&mut self,
|
||||
predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> Result<Self, PrintError>;
|
||||
) -> Result<(), PrintError>;
|
||||
|
||||
fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self, PrintError>;
|
||||
fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError>;
|
||||
|
||||
fn path_crate(self, cnum: CrateNum) -> Result<Self, PrintError>;
|
||||
fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError>;
|
||||
|
||||
fn path_qualified(
|
||||
self,
|
||||
&mut self,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self, PrintError>;
|
||||
) -> Result<(), PrintError>;
|
||||
|
||||
fn path_append_impl(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
disambiguated_data: &DisambiguatedDefPathData,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self, PrintError>;
|
||||
) -> Result<(), PrintError>;
|
||||
|
||||
fn path_append(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
disambiguated_data: &DisambiguatedDefPathData,
|
||||
) -> Result<Self, PrintError>;
|
||||
) -> Result<(), PrintError>;
|
||||
|
||||
fn path_generic_args(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
args: &[GenericArg<'tcx>],
|
||||
) -> Result<Self, PrintError>;
|
||||
) -> Result<(), PrintError>;
|
||||
|
||||
// Defaults (should not be overridden):
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn default_print_def_path(
|
||||
self,
|
||||
&mut self,
|
||||
def_id: DefId,
|
||||
args: &'tcx [GenericArg<'tcx>],
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
let key = self.tcx().def_key(def_id);
|
||||
debug!(?key);
|
||||
|
||||
|
|
@ -161,7 +161,7 @@ pub trait Printer<'tcx>: Sized {
|
|||
}
|
||||
|
||||
self.path_append(
|
||||
|cx: Self| {
|
||||
|cx: &mut Self| {
|
||||
if trait_qualify_parent {
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
cx.tcx(),
|
||||
|
|
@ -180,12 +180,12 @@ pub trait Printer<'tcx>: Sized {
|
|||
}
|
||||
|
||||
fn default_print_impl_path(
|
||||
self,
|
||||
&mut self,
|
||||
impl_def_id: DefId,
|
||||
_args: &'tcx [GenericArg<'tcx>],
|
||||
self_ty: Ty<'tcx>,
|
||||
impl_trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
debug!(
|
||||
"default_print_impl_path: impl_def_id={:?}, self_ty={}, impl_trait_ref={:?}",
|
||||
impl_def_id, self_ty, impl_trait_ref
|
||||
|
|
@ -286,25 +286,25 @@ pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> {
|
|||
}
|
||||
|
||||
impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Region<'tcx> {
|
||||
fn print(&self, cx: P) -> Result<P, PrintError> {
|
||||
fn print(&self, cx: &mut P) -> Result<(), PrintError> {
|
||||
cx.print_region(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for Ty<'tcx> {
|
||||
fn print(&self, cx: P) -> Result<P, PrintError> {
|
||||
fn print(&self, cx: &mut P) -> Result<(), PrintError> {
|
||||
cx.print_type(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> {
|
||||
fn print(&self, cx: P) -> Result<P, PrintError> {
|
||||
fn print(&self, cx: &mut P) -> Result<(), PrintError> {
|
||||
cx.print_dyn_existential(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Const<'tcx> {
|
||||
fn print(&self, cx: P) -> Result<P, PrintError> {
|
||||
fn print(&self, cx: &mut P) -> Result<(), PrintError> {
|
||||
cx.print_const(*self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -22,11 +22,10 @@ impl fmt::Debug for ty::TraitDef {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
ty::tls::with(|tcx| {
|
||||
with_no_trimmed_paths!({
|
||||
f.write_str(
|
||||
&FmtPrinter::new(tcx, Namespace::TypeNS)
|
||||
.print_def_path(self.def_id, &[])?
|
||||
.into_buffer(),
|
||||
)
|
||||
let s = FmtPrinter::print_string(tcx, Namespace::TypeNS, |cx| {
|
||||
cx.print_def_path(self.def_id, &[])
|
||||
})?;
|
||||
f.write_str(&s)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -36,11 +35,10 @@ impl<'tcx> fmt::Debug for ty::AdtDef<'tcx> {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
ty::tls::with(|tcx| {
|
||||
with_no_trimmed_paths!({
|
||||
f.write_str(
|
||||
&FmtPrinter::new(tcx, Namespace::TypeNS)
|
||||
.print_def_path(self.did(), &[])?
|
||||
.into_buffer(),
|
||||
)
|
||||
let s = FmtPrinter::print_string(tcx, Namespace::TypeNS, |cx| {
|
||||
cx.print_def_path(self.did(), &[])
|
||||
})?;
|
||||
f.write_str(&s)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -350,9 +348,8 @@ impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::Const<'tcx> {
|
|||
let ConstKind::Value(valtree) = lifted.kind() else {
|
||||
bug!("we checked that this is a valtree")
|
||||
};
|
||||
let cx = FmtPrinter::new(tcx, Namespace::ValueNS);
|
||||
let cx =
|
||||
cx.pretty_print_const_valtree(valtree, lifted.ty(), /*print_ty*/ true)?;
|
||||
let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
|
||||
cx.pretty_print_const_valtree(valtree, lifted.ty(), /*print_ty*/ true)?;
|
||||
f.write_str(&cx.into_buffer())
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::elaborate_drops::DropFlagState;
|
||||
use rustc_middle::mir::{self, Body, Location, Terminator, TerminatorKind};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_target::abi::VariantIdx;
|
||||
|
||||
use super::indexes::MovePathIndex;
|
||||
|
|
@ -55,60 +55,6 @@ pub fn on_all_children_bits<'tcx, F>(
|
|||
) where
|
||||
F: FnMut(MovePathIndex),
|
||||
{
|
||||
#[inline]
|
||||
fn is_terminal_path<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
path: MovePathIndex,
|
||||
) -> bool {
|
||||
let place = move_data.move_paths[path].place;
|
||||
|
||||
// When enumerating the child fragments of a path, don't recurse into
|
||||
// paths (1.) past arrays, slices, and pointers, nor (2.) into a type
|
||||
// that implements `Drop`.
|
||||
//
|
||||
// Places behind references or arrays are not tracked by elaboration
|
||||
// and are always assumed to be initialized when accessible. As
|
||||
// references and indexes can be reseated, trying to track them can
|
||||
// only lead to trouble.
|
||||
//
|
||||
// Places behind ADT's with a Drop impl are not tracked by
|
||||
// elaboration since they can never have a drop-flag state that
|
||||
// differs from that of the parent with the Drop impl.
|
||||
//
|
||||
// In both cases, the contents can only be accessed if and only if
|
||||
// their parents are initialized. This implies for example that there
|
||||
// is no need to maintain separate drop flags to track such state.
|
||||
//
|
||||
// FIXME: we have to do something for moving slice patterns.
|
||||
let ty = place.ty(body, tcx).ty;
|
||||
match ty.kind() {
|
||||
ty::Adt(def, _) if (def.has_dtor(tcx) && !def.is_box()) || def.is_union() => {
|
||||
debug!(
|
||||
"place_contents_drop_state_cannot_differ place: {:?} ty: {:?} Drop => true",
|
||||
place, ty
|
||||
);
|
||||
true
|
||||
}
|
||||
ty::Array(..) => {
|
||||
debug!(
|
||||
"place_contents_drop_state_cannot_differ place: {:?} ty: {:?} => false",
|
||||
place, ty
|
||||
);
|
||||
false
|
||||
}
|
||||
ty::Slice(..) | ty::Ref(..) | ty::RawPtr(..) => {
|
||||
debug!(
|
||||
"place_contents_drop_state_cannot_differ place: {:?} ty: {:?} refd => true",
|
||||
place, ty
|
||||
);
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn on_all_children_bits<'tcx, F>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
|
|
@ -120,10 +66,6 @@ pub fn on_all_children_bits<'tcx, F>(
|
|||
{
|
||||
each_child(move_path_index);
|
||||
|
||||
if is_terminal_path(tcx, body, move_data, move_path_index) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut next_child_index = move_data.move_paths[move_path_index].first_child;
|
||||
while let Some(child_index) = next_child_index {
|
||||
on_all_children_bits(tcx, body, move_data, child_index, each_child);
|
||||
|
|
@ -133,29 +75,6 @@ pub fn on_all_children_bits<'tcx, F>(
|
|||
on_all_children_bits(tcx, body, move_data, move_path_index, &mut each_child);
|
||||
}
|
||||
|
||||
pub fn on_all_drop_children_bits<'tcx, F>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
ctxt: &MoveDataParamEnv<'tcx>,
|
||||
path: MovePathIndex,
|
||||
mut each_child: F,
|
||||
) where
|
||||
F: FnMut(MovePathIndex),
|
||||
{
|
||||
on_all_children_bits(tcx, body, &ctxt.move_data, path, |child| {
|
||||
let place = &ctxt.move_data.move_paths[path].place;
|
||||
let ty = place.ty(body, tcx).ty;
|
||||
debug!("on_all_drop_children_bits({:?}, {:?} : {:?})", path, place, ty);
|
||||
|
||||
let erased_ty = tcx.erase_regions(ty);
|
||||
if erased_ty.needs_drop(tcx, ctxt.param_env) {
|
||||
each_child(child);
|
||||
} else {
|
||||
debug!("on_all_drop_children_bits - skipping")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn drop_flag_effects_for_function_entry<'tcx, F>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ mod visitor;
|
|||
pub use self::cursor::{AnalysisResults, ResultsClonedCursor, ResultsCursor, ResultsRefCursor};
|
||||
pub use self::direction::{Backward, Direction, Forward};
|
||||
pub use self::engine::{Engine, EntrySets, Results, ResultsCloned};
|
||||
pub use self::lattice::{JoinSemiLattice, MaybeReachable, MeetSemiLattice};
|
||||
pub use self::lattice::{JoinSemiLattice, MaybeReachable};
|
||||
pub use self::visitor::{visit_results, ResultsVisitable, ResultsVisitor};
|
||||
|
||||
/// Analysis domains are all bitsets of various kinds. This trait holds
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use crate::framework::SwitchIntEdgeEffects;
|
|||
use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
|
||||
use crate::on_lookup_result_bits;
|
||||
use crate::MoveDataParamEnv;
|
||||
use crate::{drop_flag_effects, on_all_children_bits, on_all_drop_children_bits};
|
||||
use crate::{drop_flag_effects, on_all_children_bits};
|
||||
use crate::{lattice, AnalysisDomain, GenKill, GenKillAnalysis, MaybeReachable};
|
||||
|
||||
/// `MaybeInitializedPlaces` tracks all places that might be
|
||||
|
|
@ -72,7 +72,7 @@ impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
|
|||
) -> bool {
|
||||
if let LookupResult::Exact(path) = self.move_data().rev_lookup.find(place.as_ref()) {
|
||||
let mut maybe_live = false;
|
||||
on_all_drop_children_bits(self.tcx, self.body, self.mdpe, path, |child| {
|
||||
on_all_children_bits(self.tcx, self.body, self.move_data(), path, |child| {
|
||||
maybe_live |= state.contains(child);
|
||||
});
|
||||
!maybe_live
|
||||
|
|
@ -690,9 +690,13 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
|
|||
if let mir::StatementKind::StorageDead(local) = stmt.kind {
|
||||
// End inits for StorageDead, so that an immutable variable can
|
||||
// be reinitialized on the next iteration of the loop.
|
||||
let move_path_index = rev_lookup.find_local(local);
|
||||
debug!("clears the ever initialized status of {:?}", init_path_map[move_path_index]);
|
||||
trans.kill_all(init_path_map[move_path_index].iter().copied());
|
||||
if let Some(move_path_index) = rev_lookup.find_local(local) {
|
||||
debug!(
|
||||
"clears the ever initialized status of {:?}",
|
||||
init_path_map[move_path_index]
|
||||
);
|
||||
trans.kill_all(init_path_map[move_path_index].iter().copied());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@ use rustc_span::symbol::{sym, Symbol};
|
|||
|
||||
pub use self::drop_flag_effects::{
|
||||
drop_flag_effects_for_function_entry, drop_flag_effects_for_location,
|
||||
move_path_children_matching, on_all_children_bits, on_all_drop_children_bits,
|
||||
on_lookup_result_bits,
|
||||
move_path_children_matching, on_all_children_bits, on_lookup_result_bits,
|
||||
};
|
||||
pub use self::framework::{
|
||||
fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, AnalysisResults, Backward,
|
||||
|
|
|
|||
|
|
@ -1,58 +1,66 @@
|
|||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::tcx::RvalueInitializationState;
|
||||
use rustc_middle::mir::tcx::{PlaceTy, RvalueInitializationState};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use std::mem;
|
||||
|
||||
use super::abs_domain::Lift;
|
||||
use super::IllegalMoveOriginKind::*;
|
||||
use super::{Init, InitIndex, InitKind, InitLocation, LookupResult, MoveError};
|
||||
use super::{Init, InitIndex, InitKind, InitLocation, LookupResult};
|
||||
use super::{
|
||||
LocationMap, MoveData, MoveOut, MoveOutIndex, MovePath, MovePathIndex, MovePathLookup,
|
||||
};
|
||||
|
||||
struct MoveDataBuilder<'a, 'tcx> {
|
||||
struct MoveDataBuilder<'a, 'tcx, F> {
|
||||
body: &'a Body<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
data: MoveData<'tcx>,
|
||||
errors: Vec<(Place<'tcx>, MoveError<'tcx>)>,
|
||||
filter: F,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
|
||||
fn new(body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
|
||||
impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
|
||||
fn new(
|
||||
body: &'a Body<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
filter: F,
|
||||
) -> Self {
|
||||
let mut move_paths = IndexVec::new();
|
||||
let mut path_map = IndexVec::new();
|
||||
let mut init_path_map = IndexVec::new();
|
||||
|
||||
let locals = body
|
||||
.local_decls
|
||||
.iter_enumerated()
|
||||
.map(|(i, l)| {
|
||||
if l.is_deref_temp() {
|
||||
return None;
|
||||
}
|
||||
if filter(l.ty) {
|
||||
Some(new_move_path(
|
||||
&mut move_paths,
|
||||
&mut path_map,
|
||||
&mut init_path_map,
|
||||
None,
|
||||
Place::from(i),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
MoveDataBuilder {
|
||||
body,
|
||||
tcx,
|
||||
param_env,
|
||||
errors: Vec::new(),
|
||||
data: MoveData {
|
||||
moves: IndexVec::new(),
|
||||
loc_map: LocationMap::new(body),
|
||||
rev_lookup: MovePathLookup {
|
||||
locals: body
|
||||
.local_decls
|
||||
.iter_enumerated()
|
||||
.map(|(i, l)| {
|
||||
if l.is_deref_temp() {
|
||||
MovePathIndex::MAX
|
||||
} else {
|
||||
Self::new_move_path(
|
||||
&mut move_paths,
|
||||
&mut path_map,
|
||||
&mut init_path_map,
|
||||
None,
|
||||
Place::from(i),
|
||||
)
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
locals,
|
||||
projections: Default::default(),
|
||||
un_derefer: Default::default(),
|
||||
},
|
||||
|
|
@ -62,35 +70,42 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
|
|||
init_loc_map: LocationMap::new(body),
|
||||
init_path_map,
|
||||
},
|
||||
filter,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_move_path(
|
||||
move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>,
|
||||
path_map: &mut IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>,
|
||||
init_path_map: &mut IndexVec<MovePathIndex, SmallVec<[InitIndex; 4]>>,
|
||||
parent: Option<MovePathIndex>,
|
||||
place: Place<'tcx>,
|
||||
) -> MovePathIndex {
|
||||
let move_path =
|
||||
move_paths.push(MovePath { next_sibling: None, first_child: None, parent, place });
|
||||
|
||||
if let Some(parent) = parent {
|
||||
let next_sibling = mem::replace(&mut move_paths[parent].first_child, Some(move_path));
|
||||
move_paths[move_path].next_sibling = next_sibling;
|
||||
}
|
||||
|
||||
let path_map_ent = path_map.push(smallvec![]);
|
||||
assert_eq!(path_map_ent, move_path);
|
||||
|
||||
let init_path_map_ent = init_path_map.push(smallvec![]);
|
||||
assert_eq!(init_path_map_ent, move_path);
|
||||
|
||||
move_path
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
||||
fn new_move_path<'tcx>(
|
||||
move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>,
|
||||
path_map: &mut IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>,
|
||||
init_path_map: &mut IndexVec<MovePathIndex, SmallVec<[InitIndex; 4]>>,
|
||||
parent: Option<MovePathIndex>,
|
||||
place: Place<'tcx>,
|
||||
) -> MovePathIndex {
|
||||
let move_path =
|
||||
move_paths.push(MovePath { next_sibling: None, first_child: None, parent, place });
|
||||
|
||||
if let Some(parent) = parent {
|
||||
let next_sibling = mem::replace(&mut move_paths[parent].first_child, Some(move_path));
|
||||
move_paths[move_path].next_sibling = next_sibling;
|
||||
}
|
||||
|
||||
let path_map_ent = path_map.push(smallvec![]);
|
||||
assert_eq!(path_map_ent, move_path);
|
||||
|
||||
let init_path_map_ent = init_path_map.push(smallvec![]);
|
||||
assert_eq!(init_path_map_ent, move_path);
|
||||
|
||||
move_path
|
||||
}
|
||||
|
||||
enum MovePathResult {
|
||||
Path(MovePathIndex),
|
||||
Union(MovePathIndex),
|
||||
Error,
|
||||
}
|
||||
|
||||
impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> {
|
||||
/// This creates a MovePath for a given place, returning an `MovePathError`
|
||||
/// if that place can't be moved from.
|
||||
///
|
||||
|
|
@ -98,11 +113,13 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
|||
/// problematic for borrowck.
|
||||
///
|
||||
/// Maybe we should have separate "borrowck" and "moveck" modes.
|
||||
fn move_path_for(&mut self, place: Place<'tcx>) -> Result<MovePathIndex, MoveError<'tcx>> {
|
||||
fn move_path_for(&mut self, place: Place<'tcx>) -> MovePathResult {
|
||||
let data = &mut self.builder.data;
|
||||
|
||||
debug!("lookup({:?})", place);
|
||||
let mut base = data.rev_lookup.find_local(place.local);
|
||||
let Some(mut base) = data.rev_lookup.find_local(place.local) else {
|
||||
return MovePathResult::Error;
|
||||
};
|
||||
|
||||
// The move path index of the first union that we find. Once this is
|
||||
// some we stop creating child move paths, since moves from unions
|
||||
|
|
@ -118,12 +135,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
|||
match elem {
|
||||
ProjectionElem::Deref => match place_ty.kind() {
|
||||
ty::Ref(..) | ty::RawPtr(..) => {
|
||||
return Err(MoveError::cannot_move_out_of(
|
||||
self.loc,
|
||||
BorrowedContent {
|
||||
target_place: place_ref.project_deeper(&[elem], tcx),
|
||||
},
|
||||
));
|
||||
return MovePathResult::Error;
|
||||
}
|
||||
ty::Adt(adt, _) => {
|
||||
if !adt.is_box() {
|
||||
|
|
@ -159,10 +171,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
|||
ProjectionElem::Field(_, _) => match place_ty.kind() {
|
||||
ty::Adt(adt, _) => {
|
||||
if adt.has_dtor(tcx) {
|
||||
return Err(MoveError::cannot_move_out_of(
|
||||
self.loc,
|
||||
InteriorOfTypeWithDestructor { container_ty: place_ty },
|
||||
));
|
||||
return MovePathResult::Error;
|
||||
}
|
||||
if adt.is_union() {
|
||||
union_path.get_or_insert(base);
|
||||
|
|
@ -197,33 +206,15 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
|||
ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
|
||||
match place_ty.kind() {
|
||||
ty::Slice(_) => {
|
||||
return Err(MoveError::cannot_move_out_of(
|
||||
self.loc,
|
||||
InteriorOfSliceOrArray {
|
||||
ty: place_ty,
|
||||
is_index: matches!(elem, ProjectionElem::Index(..)),
|
||||
},
|
||||
));
|
||||
return MovePathResult::Error;
|
||||
}
|
||||
ty::Array(_, _) => (),
|
||||
_ => bug!("Unexpected type {:#?}", place_ty.is_array()),
|
||||
}
|
||||
}
|
||||
ProjectionElem::Index(_) => match place_ty.kind() {
|
||||
ty::Array(..) => {
|
||||
return Err(MoveError::cannot_move_out_of(
|
||||
self.loc,
|
||||
InteriorOfSliceOrArray { ty: place_ty, is_index: true },
|
||||
));
|
||||
}
|
||||
ty::Slice(_) => {
|
||||
return Err(MoveError::cannot_move_out_of(
|
||||
self.loc,
|
||||
InteriorOfSliceOrArray {
|
||||
ty: place_ty,
|
||||
is_index: matches!(elem, ProjectionElem::Index(..)),
|
||||
},
|
||||
));
|
||||
ty::Array(..) | ty::Slice(_) => {
|
||||
return MovePathResult::Error;
|
||||
}
|
||||
_ => bug!("Unexpected type {place_ty:#?}"),
|
||||
},
|
||||
|
|
@ -235,11 +226,15 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
|||
| ProjectionElem::Subtype(_)
|
||||
| ProjectionElem::Downcast(_, _) => (),
|
||||
}
|
||||
let elem_ty = PlaceTy::from_ty(place_ty).projection_ty(tcx, elem).ty;
|
||||
if !(self.builder.filter)(elem_ty) {
|
||||
return MovePathResult::Error;
|
||||
}
|
||||
if union_path.is_none() {
|
||||
// inlined from add_move_path because of a borrowck conflict with the iterator
|
||||
base =
|
||||
*data.rev_lookup.projections.entry((base, elem.lift())).or_insert_with(|| {
|
||||
MoveDataBuilder::new_move_path(
|
||||
new_move_path(
|
||||
&mut data.move_paths,
|
||||
&mut data.path_map,
|
||||
&mut data.init_path_map,
|
||||
|
|
@ -252,9 +247,9 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
|||
|
||||
if let Some(base) = union_path {
|
||||
// Move out of union - always move the entire union.
|
||||
Err(MoveError::UnionMove { path: base })
|
||||
MovePathResult::Union(base)
|
||||
} else {
|
||||
Ok(base)
|
||||
MovePathResult::Path(base)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -270,13 +265,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
|||
..
|
||||
} = self.builder;
|
||||
*rev_lookup.projections.entry((base, elem.lift())).or_insert_with(move || {
|
||||
MoveDataBuilder::new_move_path(
|
||||
move_paths,
|
||||
path_map,
|
||||
init_path_map,
|
||||
Some(base),
|
||||
mk_place(*tcx),
|
||||
)
|
||||
new_move_path(move_paths, path_map, init_path_map, Some(base), mk_place(*tcx))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -287,11 +276,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub type MoveDat<'tcx> =
|
||||
Result<MoveData<'tcx>, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)>;
|
||||
|
||||
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
|
||||
fn finalize(self) -> MoveDat<'tcx> {
|
||||
impl<'a, 'tcx, F> MoveDataBuilder<'a, 'tcx, F> {
|
||||
fn finalize(self) -> MoveData<'tcx> {
|
||||
debug!("{}", {
|
||||
debug!("moves for {:?}:", self.body.span);
|
||||
for (j, mo) in self.data.moves.iter_enumerated() {
|
||||
|
|
@ -304,7 +290,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
|
|||
"done dumping moves"
|
||||
});
|
||||
|
||||
if self.errors.is_empty() { Ok(self.data) } else { Err((self.data, self.errors)) }
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -312,8 +298,9 @@ pub(super) fn gather_moves<'tcx>(
|
|||
body: &Body<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> MoveDat<'tcx> {
|
||||
let mut builder = MoveDataBuilder::new(body, tcx, param_env);
|
||||
filter: impl Fn(Ty<'tcx>) -> bool,
|
||||
) -> MoveData<'tcx> {
|
||||
let mut builder = MoveDataBuilder::new(body, tcx, param_env, filter);
|
||||
|
||||
builder.gather_args();
|
||||
|
||||
|
|
@ -330,20 +317,20 @@ pub(super) fn gather_moves<'tcx>(
|
|||
builder.finalize()
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
|
||||
impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
|
||||
fn gather_args(&mut self) {
|
||||
for arg in self.body.args_iter() {
|
||||
let path = self.data.rev_lookup.find_local(arg);
|
||||
if let Some(path) = self.data.rev_lookup.find_local(arg) {
|
||||
let init = self.data.inits.push(Init {
|
||||
path,
|
||||
kind: InitKind::Deep,
|
||||
location: InitLocation::Argument(arg),
|
||||
});
|
||||
|
||||
let init = self.data.inits.push(Init {
|
||||
path,
|
||||
kind: InitKind::Deep,
|
||||
location: InitLocation::Argument(arg),
|
||||
});
|
||||
debug!("gather_args: adding init {:?} of {:?} for argument {:?}", init, path, arg);
|
||||
|
||||
debug!("gather_args: adding init {:?} of {:?} for argument {:?}", init, path, arg);
|
||||
|
||||
self.data.init_path_map[path].push(init);
|
||||
self.data.init_path_map[path].push(init);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -358,12 +345,12 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
struct Gatherer<'b, 'a, 'tcx> {
|
||||
builder: &'b mut MoveDataBuilder<'a, 'tcx>,
|
||||
struct Gatherer<'b, 'a, 'tcx, F> {
|
||||
builder: &'b mut MoveDataBuilder<'a, 'tcx, F>,
|
||||
loc: Location,
|
||||
}
|
||||
|
||||
impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
||||
impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> {
|
||||
fn gather_statement(&mut self, stmt: &Statement<'tcx>) {
|
||||
match &stmt.kind {
|
||||
StatementKind::Assign(box (place, Rvalue::CopyForDeref(reffed))) => {
|
||||
|
|
@ -546,13 +533,12 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
|||
let base_place =
|
||||
Place { local: place.local, projection: self.builder.tcx.mk_place_elems(base) };
|
||||
let base_path = match self.move_path_for(base_place) {
|
||||
Ok(path) => path,
|
||||
Err(MoveError::UnionMove { path }) => {
|
||||
MovePathResult::Path(path) => path,
|
||||
MovePathResult::Union(path) => {
|
||||
self.record_move(place, path);
|
||||
return;
|
||||
}
|
||||
Err(error @ MoveError::IllegalMove { .. }) => {
|
||||
self.builder.errors.push((base_place, error));
|
||||
MovePathResult::Error => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
|
@ -572,10 +558,10 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
|||
}
|
||||
} else {
|
||||
match self.move_path_for(place) {
|
||||
Ok(path) | Err(MoveError::UnionMove { path }) => self.record_move(place, path),
|
||||
Err(error @ MoveError::IllegalMove { .. }) => {
|
||||
self.builder.errors.push((place, error));
|
||||
MovePathResult::Path(path) | MovePathResult::Union(path) => {
|
||||
self.record_move(place, path)
|
||||
}
|
||||
MovePathResult::Error => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use crate::move_paths::builder::MoveDat;
|
||||
use crate::un_derefer::UnDerefer;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
|
|
@ -291,7 +290,7 @@ impl Init {
|
|||
/// Tables mapping from a place to its MovePathIndex.
|
||||
#[derive(Debug)]
|
||||
pub struct MovePathLookup<'tcx> {
|
||||
locals: IndexVec<Local, MovePathIndex>,
|
||||
locals: IndexVec<Local, Option<MovePathIndex>>,
|
||||
|
||||
/// projections are made from a base-place and a projection
|
||||
/// elem. The base-place will have a unique MovePathIndex; we use
|
||||
|
|
@ -318,7 +317,9 @@ impl<'tcx> MovePathLookup<'tcx> {
|
|||
// unknown place, but will rather return the nearest available
|
||||
// parent.
|
||||
pub fn find(&self, place: PlaceRef<'tcx>) -> LookupResult {
|
||||
let mut result = self.find_local(place.local);
|
||||
let Some(mut result) = self.find_local(place.local) else {
|
||||
return LookupResult::Parent(None);
|
||||
};
|
||||
|
||||
for (_, elem) in self.un_derefer.iter_projections(place) {
|
||||
if let Some(&subpath) = self.projections.get(&(result, elem.lift())) {
|
||||
|
|
@ -332,7 +333,7 @@ impl<'tcx> MovePathLookup<'tcx> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn find_local(&self, local: Local) -> MovePathIndex {
|
||||
pub fn find_local(&self, local: Local) -> Option<MovePathIndex> {
|
||||
self.locals[local]
|
||||
}
|
||||
|
||||
|
|
@ -340,46 +341,8 @@ impl<'tcx> MovePathLookup<'tcx> {
|
|||
/// `MovePathIndex`es.
|
||||
pub fn iter_locals_enumerated(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (Local, MovePathIndex)> + ExactSizeIterator + '_ {
|
||||
self.locals.iter_enumerated().map(|(l, &idx)| (l, idx))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IllegalMoveOrigin<'tcx> {
|
||||
pub location: Location,
|
||||
pub kind: IllegalMoveOriginKind<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IllegalMoveOriginKind<'tcx> {
|
||||
/// Illegal move due to attempt to move from behind a reference.
|
||||
BorrowedContent {
|
||||
/// The place the reference refers to: if erroneous code was trying to
|
||||
/// move from `(*x).f` this will be `*x`.
|
||||
target_place: Place<'tcx>,
|
||||
},
|
||||
|
||||
/// Illegal move due to attempt to move from field of an ADT that
|
||||
/// implements `Drop`. Rust maintains invariant that all `Drop`
|
||||
/// ADT's remain fully-initialized so that user-defined destructor
|
||||
/// can safely read from all of the ADT's fields.
|
||||
InteriorOfTypeWithDestructor { container_ty: Ty<'tcx> },
|
||||
|
||||
/// Illegal move due to attempt to move out of a slice or array.
|
||||
InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MoveError<'tcx> {
|
||||
IllegalMove { cannot_move_out_of: IllegalMoveOrigin<'tcx> },
|
||||
UnionMove { path: MovePathIndex },
|
||||
}
|
||||
|
||||
impl<'tcx> MoveError<'tcx> {
|
||||
fn cannot_move_out_of(location: Location, kind: IllegalMoveOriginKind<'tcx>) -> Self {
|
||||
let origin = IllegalMoveOrigin { location, kind };
|
||||
MoveError::IllegalMove { cannot_move_out_of: origin }
|
||||
) -> impl DoubleEndedIterator<Item = (Local, MovePathIndex)> + '_ {
|
||||
self.locals.iter_enumerated().filter_map(|(l, &idx)| Some((l, idx?)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -388,8 +351,9 @@ impl<'tcx> MoveData<'tcx> {
|
|||
body: &Body<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
) -> MoveDat<'tcx> {
|
||||
builder::gather_moves(body, tcx, param_env)
|
||||
filter: impl Fn(Ty<'tcx>) -> bool,
|
||||
) -> MoveData<'tcx> {
|
||||
builder::gather_moves(body, tcx, param_env, filter)
|
||||
}
|
||||
|
||||
/// For the move path `mpi`, returns the root local variable (if any) that starts the path.
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
|
|||
}
|
||||
|
||||
let param_env = tcx.param_env(def_id);
|
||||
let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap();
|
||||
let move_data = MoveData::gather_moves(&body, tcx, param_env, |_| true);
|
||||
let mdpe = MoveDataParamEnv { move_data, param_env };
|
||||
|
||||
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() {
|
||||
|
|
|
|||
|
|
@ -463,7 +463,19 @@ impl<V: Clone> Clone for State<V> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<V: Clone + HasTop + HasBottom> State<V> {
|
||||
impl<V: Clone> State<V> {
|
||||
pub fn new(init: V, map: &Map) -> State<V> {
|
||||
let values = IndexVec::from_elem_n(init, map.value_count);
|
||||
State(StateData::Reachable(values))
|
||||
}
|
||||
|
||||
pub fn all(&self, f: impl Fn(&V) -> bool) -> bool {
|
||||
match self.0 {
|
||||
StateData::Unreachable => true,
|
||||
StateData::Reachable(ref values) => values.iter().all(f),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_reachable(&self) -> bool {
|
||||
matches!(&self.0, StateData::Reachable(_))
|
||||
}
|
||||
|
|
@ -472,7 +484,10 @@ impl<V: Clone + HasTop + HasBottom> State<V> {
|
|||
self.0 = StateData::Unreachable;
|
||||
}
|
||||
|
||||
pub fn flood_all(&mut self) {
|
||||
pub fn flood_all(&mut self)
|
||||
where
|
||||
V: HasTop,
|
||||
{
|
||||
self.flood_all_with(V::TOP)
|
||||
}
|
||||
|
||||
|
|
@ -481,28 +496,52 @@ impl<V: Clone + HasTop + HasBottom> State<V> {
|
|||
values.raw.fill(value);
|
||||
}
|
||||
|
||||
/// Assign `value` to all places that are contained in `place` or may alias one.
|
||||
pub fn flood_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V) {
|
||||
let StateData::Reachable(values) = &mut self.0 else { return };
|
||||
map.for_each_aliasing_place(place, None, &mut |vi| {
|
||||
values[vi] = value.clone();
|
||||
});
|
||||
self.flood_with_tail_elem(place, None, map, value)
|
||||
}
|
||||
|
||||
pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map) {
|
||||
/// Assign `TOP` to all places that are contained in `place` or may alias one.
|
||||
pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map)
|
||||
where
|
||||
V: HasTop,
|
||||
{
|
||||
self.flood_with(place, map, V::TOP)
|
||||
}
|
||||
|
||||
/// Assign `value` to the discriminant of `place` and all places that may alias it.
|
||||
pub fn flood_discr_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V) {
|
||||
let StateData::Reachable(values) = &mut self.0 else { return };
|
||||
map.for_each_aliasing_place(place, Some(TrackElem::Discriminant), &mut |vi| {
|
||||
values[vi] = value.clone();
|
||||
});
|
||||
self.flood_with_tail_elem(place, Some(TrackElem::Discriminant), map, value)
|
||||
}
|
||||
|
||||
pub fn flood_discr(&mut self, place: PlaceRef<'_>, map: &Map) {
|
||||
/// Assign `TOP` to the discriminant of `place` and all places that may alias it.
|
||||
pub fn flood_discr(&mut self, place: PlaceRef<'_>, map: &Map)
|
||||
where
|
||||
V: HasTop,
|
||||
{
|
||||
self.flood_discr_with(place, map, V::TOP)
|
||||
}
|
||||
|
||||
/// This method is the most general version of the `flood_*` method.
|
||||
///
|
||||
/// Assign `value` on the given place and all places that may alias it. In particular, when
|
||||
/// the given place has a variant downcast, we invoke the function on all the other variants.
|
||||
///
|
||||
/// `tail_elem` allows to support discriminants that are not a place in MIR, but that we track
|
||||
/// as such.
|
||||
pub fn flood_with_tail_elem(
|
||||
&mut self,
|
||||
place: PlaceRef<'_>,
|
||||
tail_elem: Option<TrackElem>,
|
||||
map: &Map,
|
||||
value: V,
|
||||
) {
|
||||
let StateData::Reachable(values) = &mut self.0 else { return };
|
||||
map.for_each_aliasing_place(place, tail_elem, &mut |vi| {
|
||||
values[vi] = value.clone();
|
||||
});
|
||||
}
|
||||
|
||||
/// Low-level method that assigns to a place.
|
||||
/// This does nothing if the place is not tracked.
|
||||
///
|
||||
|
|
@ -553,7 +592,10 @@ impl<V: Clone + HasTop + HasBottom> State<V> {
|
|||
}
|
||||
|
||||
/// Helper method to interpret `target = result`.
|
||||
pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map) {
|
||||
pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map)
|
||||
where
|
||||
V: HasTop,
|
||||
{
|
||||
self.flood(target, map);
|
||||
if let Some(target) = map.find(target) {
|
||||
self.insert_idx(target, result, map);
|
||||
|
|
@ -561,36 +603,93 @@ impl<V: Clone + HasTop + HasBottom> State<V> {
|
|||
}
|
||||
|
||||
/// Helper method for assignments to a discriminant.
|
||||
pub fn assign_discr(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map) {
|
||||
pub fn assign_discr(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map)
|
||||
where
|
||||
V: HasTop,
|
||||
{
|
||||
self.flood_discr(target, map);
|
||||
if let Some(target) = map.find_discr(target) {
|
||||
self.insert_idx(target, result, map);
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the value stored for a place, or ⊤ if it is not tracked.
|
||||
pub fn get(&self, place: PlaceRef<'_>, map: &Map) -> V {
|
||||
map.find(place).map(|place| self.get_idx(place, map)).unwrap_or(V::TOP)
|
||||
/// Retrieve the value stored for a place, or `None` if it is not tracked.
|
||||
pub fn try_get(&self, place: PlaceRef<'_>, map: &Map) -> Option<V> {
|
||||
let place = map.find(place)?;
|
||||
self.try_get_idx(place, map)
|
||||
}
|
||||
|
||||
/// Retrieve the value stored for a place, or ⊤ if it is not tracked.
|
||||
pub fn get_discr(&self, place: PlaceRef<'_>, map: &Map) -> V {
|
||||
match map.find_discr(place) {
|
||||
Some(place) => self.get_idx(place, map),
|
||||
None => V::TOP,
|
||||
/// Retrieve the discriminant stored for a place, or `None` if it is not tracked.
|
||||
pub fn try_get_discr(&self, place: PlaceRef<'_>, map: &Map) -> Option<V> {
|
||||
let place = map.find_discr(place)?;
|
||||
self.try_get_idx(place, map)
|
||||
}
|
||||
|
||||
/// Retrieve the slice length stored for a place, or `None` if it is not tracked.
|
||||
pub fn try_get_len(&self, place: PlaceRef<'_>, map: &Map) -> Option<V> {
|
||||
let place = map.find_len(place)?;
|
||||
self.try_get_idx(place, map)
|
||||
}
|
||||
|
||||
/// Retrieve the value stored for a place index, or `None` if it is not tracked.
|
||||
pub fn try_get_idx(&self, place: PlaceIndex, map: &Map) -> Option<V> {
|
||||
match &self.0 {
|
||||
StateData::Reachable(values) => {
|
||||
map.places[place].value_index.map(|v| values[v].clone())
|
||||
}
|
||||
StateData::Unreachable => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the value stored for a place, or ⊤ if it is not tracked.
|
||||
pub fn get_len(&self, place: PlaceRef<'_>, map: &Map) -> V {
|
||||
match map.find_len(place) {
|
||||
Some(place) => self.get_idx(place, map),
|
||||
None => V::TOP,
|
||||
///
|
||||
/// This method returns ⊥ if the place is tracked and the state is unreachable.
|
||||
pub fn get(&self, place: PlaceRef<'_>, map: &Map) -> V
|
||||
where
|
||||
V: HasBottom + HasTop,
|
||||
{
|
||||
match &self.0 {
|
||||
StateData::Reachable(_) => self.try_get(place, map).unwrap_or(V::TOP),
|
||||
// Because this is unreachable, we can return any value we want.
|
||||
StateData::Unreachable => V::BOTTOM,
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the value stored for a place, or ⊤ if it is not tracked.
|
||||
///
|
||||
/// This method returns ⊥ the current state is unreachable.
|
||||
pub fn get_discr(&self, place: PlaceRef<'_>, map: &Map) -> V
|
||||
where
|
||||
V: HasBottom + HasTop,
|
||||
{
|
||||
match &self.0 {
|
||||
StateData::Reachable(_) => self.try_get_discr(place, map).unwrap_or(V::TOP),
|
||||
// Because this is unreachable, we can return any value we want.
|
||||
StateData::Unreachable => V::BOTTOM,
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the value stored for a place, or ⊤ if it is not tracked.
|
||||
///
|
||||
/// This method returns ⊥ the current state is unreachable.
|
||||
pub fn get_len(&self, place: PlaceRef<'_>, map: &Map) -> V
|
||||
where
|
||||
V: HasBottom + HasTop,
|
||||
{
|
||||
match &self.0 {
|
||||
StateData::Reachable(_) => self.try_get_len(place, map).unwrap_or(V::TOP),
|
||||
// Because this is unreachable, we can return any value we want.
|
||||
StateData::Unreachable => V::BOTTOM,
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the value stored for a place index, or ⊤ if it is not tracked.
|
||||
pub fn get_idx(&self, place: PlaceIndex, map: &Map) -> V {
|
||||
///
|
||||
/// This method returns ⊥ the current state is unreachable.
|
||||
pub fn get_idx(&self, place: PlaceIndex, map: &Map) -> V
|
||||
where
|
||||
V: HasBottom + HasTop,
|
||||
{
|
||||
match &self.0 {
|
||||
StateData::Reachable(values) => {
|
||||
map.places[place].value_index.map(|v| values[v].clone()).unwrap_or(V::TOP)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
|
|||
tracing = "0.1"
|
||||
either = "1"
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_arena = { path = "../rustc_arena" }
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
|
|
|
|||
98
compiler/rustc_mir_transform/src/cost_checker.rs
Normal file
98
compiler/rustc_mir_transform/src/cost_checker.rs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
use rustc_middle::mir::visit::*;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
|
||||
|
||||
const INSTR_COST: usize = 5;
|
||||
const CALL_PENALTY: usize = 25;
|
||||
const LANDINGPAD_PENALTY: usize = 50;
|
||||
const RESUME_PENALTY: usize = 45;
|
||||
|
||||
/// Verify that the callee body is compatible with the caller.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct CostChecker<'b, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
cost: usize,
|
||||
callee_body: &'b Body<'tcx>,
|
||||
instance: Option<ty::Instance<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'b, 'tcx> CostChecker<'b, 'tcx> {
|
||||
pub fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
instance: Option<ty::Instance<'tcx>>,
|
||||
callee_body: &'b Body<'tcx>,
|
||||
) -> CostChecker<'b, 'tcx> {
|
||||
CostChecker { tcx, param_env, callee_body, instance, cost: 0 }
|
||||
}
|
||||
|
||||
pub fn cost(&self) -> usize {
|
||||
self.cost
|
||||
}
|
||||
|
||||
fn instantiate_ty(&self, v: Ty<'tcx>) -> Ty<'tcx> {
|
||||
if let Some(instance) = self.instance {
|
||||
instance.instantiate_mir(self.tcx, ty::EarlyBinder::bind(&v))
|
||||
} else {
|
||||
v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
|
||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
|
||||
// Don't count StorageLive/StorageDead in the inlining cost.
|
||||
match statement.kind {
|
||||
StatementKind::StorageLive(_)
|
||||
| StatementKind::StorageDead(_)
|
||||
| StatementKind::Deinit(_)
|
||||
| StatementKind::Nop => {}
|
||||
_ => self.cost += INSTR_COST,
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _: Location) {
|
||||
let tcx = self.tcx;
|
||||
match terminator.kind {
|
||||
TerminatorKind::Drop { ref place, unwind, .. } => {
|
||||
// If the place doesn't actually need dropping, treat it like a regular goto.
|
||||
let ty = self.instantiate_ty(place.ty(self.callee_body, tcx).ty);
|
||||
if ty.needs_drop(tcx, self.param_env) {
|
||||
self.cost += CALL_PENALTY;
|
||||
if let UnwindAction::Cleanup(_) = unwind {
|
||||
self.cost += LANDINGPAD_PENALTY;
|
||||
}
|
||||
} else {
|
||||
self.cost += INSTR_COST;
|
||||
}
|
||||
}
|
||||
TerminatorKind::Call { func: Operand::Constant(ref f), unwind, .. } => {
|
||||
let fn_ty = self.instantiate_ty(f.const_.ty());
|
||||
self.cost += if let ty::FnDef(def_id, _) = *fn_ty.kind() && tcx.is_intrinsic(def_id) {
|
||||
// Don't give intrinsics the extra penalty for calls
|
||||
INSTR_COST
|
||||
} else {
|
||||
CALL_PENALTY
|
||||
};
|
||||
if let UnwindAction::Cleanup(_) = unwind {
|
||||
self.cost += LANDINGPAD_PENALTY;
|
||||
}
|
||||
}
|
||||
TerminatorKind::Assert { unwind, .. } => {
|
||||
self.cost += CALL_PENALTY;
|
||||
if let UnwindAction::Cleanup(_) = unwind {
|
||||
self.cost += LANDINGPAD_PENALTY;
|
||||
}
|
||||
}
|
||||
TerminatorKind::UnwindResume => self.cost += RESUME_PENALTY,
|
||||
TerminatorKind::InlineAsm { unwind, .. } => {
|
||||
self.cost += INSTR_COST;
|
||||
if let UnwindAction::Cleanup(_) = unwind {
|
||||
self.cost += LANDINGPAD_PENALTY;
|
||||
}
|
||||
}
|
||||
_ => self.cost += INSTR_COST,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,9 +9,9 @@ use rustc_mir_dataflow::elaborate_drops::{elaborate_drop, DropFlagState, Unwind}
|
|||
use rustc_mir_dataflow::elaborate_drops::{DropElaborator, DropFlagMode, DropStyle};
|
||||
use rustc_mir_dataflow::impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
|
||||
use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex};
|
||||
use rustc_mir_dataflow::on_all_children_bits;
|
||||
use rustc_mir_dataflow::on_lookup_result_bits;
|
||||
use rustc_mir_dataflow::MoveDataParamEnv;
|
||||
use rustc_mir_dataflow::{on_all_children_bits, on_all_drop_children_bits};
|
||||
use rustc_mir_dataflow::{Analysis, ResultsCursor};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::{FieldIdx, VariantIdx};
|
||||
|
|
@ -54,16 +54,10 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
|
|||
|
||||
let def_id = body.source.def_id();
|
||||
let param_env = tcx.param_env_reveal_all_normalized(def_id);
|
||||
let move_data = match MoveData::gather_moves(body, tcx, param_env) {
|
||||
Ok(move_data) => move_data,
|
||||
Err((move_data, _)) => {
|
||||
tcx.sess.delay_span_bug(
|
||||
body.span,
|
||||
"No `move_errors` should be allowed in MIR borrowck",
|
||||
);
|
||||
move_data
|
||||
}
|
||||
};
|
||||
// For types that do not need dropping, the behaviour is trivial. So we only need to track
|
||||
// init/uninit for types that do need dropping.
|
||||
let move_data =
|
||||
MoveData::gather_moves(&body, tcx, param_env, |ty| ty.needs_drop(tcx, param_env));
|
||||
let elaborate_patch = {
|
||||
let env = MoveDataParamEnv { move_data, param_env };
|
||||
|
||||
|
|
@ -178,13 +172,19 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, '_, 'tcx> {
|
|||
let mut some_live = false;
|
||||
let mut some_dead = false;
|
||||
let mut children_count = 0;
|
||||
on_all_drop_children_bits(self.tcx(), self.body(), self.ctxt.env, path, |child| {
|
||||
let (live, dead) = self.ctxt.init_data.maybe_live_dead(child);
|
||||
debug!("elaborate_drop: state({:?}) = {:?}", child, (live, dead));
|
||||
some_live |= live;
|
||||
some_dead |= dead;
|
||||
children_count += 1;
|
||||
});
|
||||
on_all_children_bits(
|
||||
self.tcx(),
|
||||
self.body(),
|
||||
self.ctxt.move_data(),
|
||||
path,
|
||||
|child| {
|
||||
let (live, dead) = self.ctxt.init_data.maybe_live_dead(child);
|
||||
debug!("elaborate_drop: state({:?}) = {:?}", child, (live, dead));
|
||||
some_live |= live;
|
||||
some_dead |= dead;
|
||||
children_count += 1;
|
||||
},
|
||||
);
|
||||
((some_live, some_dead), children_count != 1)
|
||||
}
|
||||
};
|
||||
|
|
@ -296,26 +296,36 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
|
|||
fn collect_drop_flags(&mut self) {
|
||||
for (bb, data) in self.body.basic_blocks.iter_enumerated() {
|
||||
let terminator = data.terminator();
|
||||
let place = match terminator.kind {
|
||||
TerminatorKind::Drop { ref place, .. } => place,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
self.init_data.seek_before(self.body.terminator_loc(bb));
|
||||
let TerminatorKind::Drop { ref place, .. } = terminator.kind else { continue };
|
||||
|
||||
let path = self.move_data().rev_lookup.find(place.as_ref());
|
||||
debug!("collect_drop_flags: {:?}, place {:?} ({:?})", bb, place, path);
|
||||
|
||||
let path = match path {
|
||||
LookupResult::Exact(e) => e,
|
||||
LookupResult::Parent(None) => continue,
|
||||
match path {
|
||||
LookupResult::Exact(path) => {
|
||||
self.init_data.seek_before(self.body.terminator_loc(bb));
|
||||
on_all_children_bits(self.tcx, self.body, self.move_data(), path, |child| {
|
||||
let (maybe_live, maybe_dead) = self.init_data.maybe_live_dead(child);
|
||||
debug!(
|
||||
"collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}",
|
||||
child,
|
||||
place,
|
||||
path,
|
||||
(maybe_live, maybe_dead)
|
||||
);
|
||||
if maybe_live && maybe_dead {
|
||||
self.create_drop_flag(child, terminator.source_info.span)
|
||||
}
|
||||
});
|
||||
}
|
||||
LookupResult::Parent(None) => {}
|
||||
LookupResult::Parent(Some(parent)) => {
|
||||
let (_maybe_live, maybe_dead) = self.init_data.maybe_live_dead(parent);
|
||||
|
||||
if self.body.local_decls[place.local].is_deref_temp() {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.init_data.seek_before(self.body.terminator_loc(bb));
|
||||
let (_maybe_live, maybe_dead) = self.init_data.maybe_live_dead(parent);
|
||||
if maybe_dead {
|
||||
self.tcx.sess.delay_span_bug(
|
||||
terminator.source_info.span,
|
||||
|
|
@ -324,80 +334,74 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
|
|||
),
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
on_all_drop_children_bits(self.tcx, self.body, self.env, path, |child| {
|
||||
let (maybe_live, maybe_dead) = self.init_data.maybe_live_dead(child);
|
||||
debug!(
|
||||
"collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}",
|
||||
child,
|
||||
place,
|
||||
path,
|
||||
(maybe_live, maybe_dead)
|
||||
);
|
||||
if maybe_live && maybe_dead {
|
||||
self.create_drop_flag(child, terminator.source_info.span)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn elaborate_drops(&mut self) {
|
||||
// This function should mirror what `collect_drop_flags` does.
|
||||
for (bb, data) in self.body.basic_blocks.iter_enumerated() {
|
||||
let loc = Location { block: bb, statement_index: data.statements.len() };
|
||||
let terminator = data.terminator();
|
||||
let TerminatorKind::Drop { place, target, unwind, replace } = terminator.kind else {
|
||||
continue;
|
||||
};
|
||||
|
||||
match terminator.kind {
|
||||
TerminatorKind::Drop { place, target, unwind, replace } => {
|
||||
self.init_data.seek_before(loc);
|
||||
match self.move_data().rev_lookup.find(place.as_ref()) {
|
||||
LookupResult::Exact(path) => {
|
||||
let unwind = if data.is_cleanup {
|
||||
Unwind::InCleanup
|
||||
} else {
|
||||
match unwind {
|
||||
UnwindAction::Cleanup(cleanup) => Unwind::To(cleanup),
|
||||
UnwindAction::Continue => Unwind::To(self.patch.resume_block()),
|
||||
UnwindAction::Unreachable => {
|
||||
Unwind::To(self.patch.unreachable_cleanup_block())
|
||||
}
|
||||
UnwindAction::Terminate(reason) => {
|
||||
debug_assert_ne!(
|
||||
reason,
|
||||
UnwindTerminateReason::InCleanup,
|
||||
"we are not in a cleanup block, InCleanup reason should be impossible"
|
||||
);
|
||||
Unwind::To(self.patch.terminate_block(reason))
|
||||
}
|
||||
}
|
||||
};
|
||||
elaborate_drop(
|
||||
&mut Elaborator { ctxt: self },
|
||||
terminator.source_info,
|
||||
place,
|
||||
path,
|
||||
target,
|
||||
unwind,
|
||||
bb,
|
||||
)
|
||||
// This place does not need dropping. It does not have an associated move-path, so the
|
||||
// match below will conservatively keep an unconditional drop. As that drop is useless,
|
||||
// just remove it here and now.
|
||||
if !place
|
||||
.ty(&self.body.local_decls, self.tcx)
|
||||
.ty
|
||||
.needs_drop(self.tcx, self.env.param_env)
|
||||
{
|
||||
self.patch.patch_terminator(bb, TerminatorKind::Goto { target });
|
||||
continue;
|
||||
}
|
||||
|
||||
let path = self.move_data().rev_lookup.find(place.as_ref());
|
||||
match path {
|
||||
LookupResult::Exact(path) => {
|
||||
let unwind = match unwind {
|
||||
_ if data.is_cleanup => Unwind::InCleanup,
|
||||
UnwindAction::Cleanup(cleanup) => Unwind::To(cleanup),
|
||||
UnwindAction::Continue => Unwind::To(self.patch.resume_block()),
|
||||
UnwindAction::Unreachable => {
|
||||
Unwind::To(self.patch.unreachable_cleanup_block())
|
||||
}
|
||||
LookupResult::Parent(..) => {
|
||||
if !replace {
|
||||
self.tcx.sess.delay_span_bug(
|
||||
terminator.source_info.span,
|
||||
format!("drop of untracked value {bb:?}"),
|
||||
);
|
||||
}
|
||||
// A drop and replace behind a pointer/array/whatever.
|
||||
// The borrow checker requires that these locations are initialized before the assignment,
|
||||
// so we just leave an unconditional drop.
|
||||
assert!(!data.is_cleanup);
|
||||
UnwindAction::Terminate(reason) => {
|
||||
debug_assert_ne!(
|
||||
reason,
|
||||
UnwindTerminateReason::InCleanup,
|
||||
"we are not in a cleanup block, InCleanup reason should be impossible"
|
||||
);
|
||||
Unwind::To(self.patch.terminate_block(reason))
|
||||
}
|
||||
}
|
||||
};
|
||||
self.init_data.seek_before(self.body.terminator_loc(bb));
|
||||
elaborate_drop(
|
||||
&mut Elaborator { ctxt: self },
|
||||
terminator.source_info,
|
||||
place,
|
||||
path,
|
||||
target,
|
||||
unwind,
|
||||
bb,
|
||||
)
|
||||
}
|
||||
LookupResult::Parent(None) => {}
|
||||
LookupResult::Parent(Some(_)) => {
|
||||
if !replace {
|
||||
self.tcx.sess.delay_span_bug(
|
||||
terminator.source_info.span,
|
||||
format!("drop of untracked value {bb:?}"),
|
||||
);
|
||||
}
|
||||
// A drop and replace behind a pointer/array/whatever.
|
||||
// The borrow checker requires that these locations are initialized before the assignment,
|
||||
// so we just leave an unconditional drop.
|
||||
assert!(!data.is_cleanup);
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use rustc_session::config::OptLevel;
|
|||
use rustc_target::abi::FieldIdx;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use crate::cost_checker::CostChecker;
|
||||
use crate::simplify::{remove_dead_blocks, CfgSimplifier};
|
||||
use crate::util;
|
||||
use crate::MirPass;
|
||||
|
|
@ -22,11 +23,6 @@ use std::ops::{Range, RangeFrom};
|
|||
|
||||
pub(crate) mod cycle;
|
||||
|
||||
const INSTR_COST: usize = 5;
|
||||
const CALL_PENALTY: usize = 25;
|
||||
const LANDINGPAD_PENALTY: usize = 50;
|
||||
const RESUME_PENALTY: usize = 45;
|
||||
|
||||
const TOP_DOWN_DEPTH_LIMIT: usize = 5;
|
||||
|
||||
pub struct Inline;
|
||||
|
|
@ -479,13 +475,8 @@ impl<'tcx> Inliner<'tcx> {
|
|||
|
||||
// FIXME: Give a bonus to functions with only a single caller
|
||||
|
||||
let mut checker = CostChecker {
|
||||
tcx: self.tcx,
|
||||
param_env: self.param_env,
|
||||
instance: callsite.callee,
|
||||
callee_body,
|
||||
cost: 0,
|
||||
};
|
||||
let mut checker =
|
||||
CostChecker::new(self.tcx, self.param_env, Some(callsite.callee), callee_body);
|
||||
|
||||
// Traverse the MIR manually so we can account for the effects of inlining on the CFG.
|
||||
let mut work_list = vec![START_BLOCK];
|
||||
|
|
@ -530,7 +521,7 @@ impl<'tcx> Inliner<'tcx> {
|
|||
// That attribute is often applied to very large functions that exceed LLVM's (very
|
||||
// generous) inlining threshold. Such functions are very poor MIR inlining candidates.
|
||||
// Always inlining #[inline(always)] functions in MIR, on net, slows down the compiler.
|
||||
let cost = checker.cost;
|
||||
let cost = checker.cost();
|
||||
if cost <= threshold {
|
||||
debug!("INLINING {:?} [cost={} <= threshold={}]", callsite, cost, threshold);
|
||||
Ok(())
|
||||
|
|
@ -803,81 +794,6 @@ impl<'tcx> Inliner<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Verify that the callee body is compatible with the caller.
|
||||
///
|
||||
/// This visitor mostly computes the inlining cost,
|
||||
/// but also needs to verify that types match because of normalization failure.
|
||||
struct CostChecker<'b, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
cost: usize,
|
||||
callee_body: &'b Body<'tcx>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
|
||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
|
||||
// Don't count StorageLive/StorageDead in the inlining cost.
|
||||
match statement.kind {
|
||||
StatementKind::StorageLive(_)
|
||||
| StatementKind::StorageDead(_)
|
||||
| StatementKind::Deinit(_)
|
||||
| StatementKind::Nop => {}
|
||||
_ => self.cost += INSTR_COST,
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _: Location) {
|
||||
let tcx = self.tcx;
|
||||
match terminator.kind {
|
||||
TerminatorKind::Drop { ref place, unwind, .. } => {
|
||||
// If the place doesn't actually need dropping, treat it like a regular goto.
|
||||
let ty = self.instance.instantiate_mir(
|
||||
tcx,
|
||||
ty::EarlyBinder::bind(&place.ty(self.callee_body, tcx).ty),
|
||||
);
|
||||
if ty.needs_drop(tcx, self.param_env) {
|
||||
self.cost += CALL_PENALTY;
|
||||
if let UnwindAction::Cleanup(_) = unwind {
|
||||
self.cost += LANDINGPAD_PENALTY;
|
||||
}
|
||||
} else {
|
||||
self.cost += INSTR_COST;
|
||||
}
|
||||
}
|
||||
TerminatorKind::Call { func: Operand::Constant(ref f), unwind, .. } => {
|
||||
let fn_ty =
|
||||
self.instance.instantiate_mir(tcx, ty::EarlyBinder::bind(&f.const_.ty()));
|
||||
self.cost += if let ty::FnDef(def_id, _) = *fn_ty.kind()
|
||||
&& tcx.is_intrinsic(def_id)
|
||||
{
|
||||
// Don't give intrinsics the extra penalty for calls
|
||||
INSTR_COST
|
||||
} else {
|
||||
CALL_PENALTY
|
||||
};
|
||||
if let UnwindAction::Cleanup(_) = unwind {
|
||||
self.cost += LANDINGPAD_PENALTY;
|
||||
}
|
||||
}
|
||||
TerminatorKind::Assert { unwind, .. } => {
|
||||
self.cost += CALL_PENALTY;
|
||||
if let UnwindAction::Cleanup(_) = unwind {
|
||||
self.cost += LANDINGPAD_PENALTY;
|
||||
}
|
||||
}
|
||||
TerminatorKind::UnwindResume => self.cost += RESUME_PENALTY,
|
||||
TerminatorKind::InlineAsm { unwind, .. } => {
|
||||
self.cost += INSTR_COST;
|
||||
if let UnwindAction::Cleanup(_) = unwind {
|
||||
self.cost += LANDINGPAD_PENALTY;
|
||||
}
|
||||
}
|
||||
_ => self.cost += INSTR_COST,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Integrator.
|
||||
*
|
||||
|
|
|
|||
759
compiler/rustc_mir_transform/src/jump_threading.rs
Normal file
759
compiler/rustc_mir_transform/src/jump_threading.rs
Normal file
|
|
@ -0,0 +1,759 @@
|
|||
//! A jump threading optimization.
|
||||
//!
|
||||
//! This optimization seeks to replace join-then-switch control flow patterns by straight jumps
|
||||
//! X = 0 X = 0
|
||||
//! ------------\ /-------- ------------
|
||||
//! X = 1 X----X SwitchInt(X) => X = 1
|
||||
//! ------------/ \-------- ------------
|
||||
//!
|
||||
//!
|
||||
//! We proceed by walking the cfg backwards starting from each `SwitchInt` terminator,
|
||||
//! looking for assignments that will turn the `SwitchInt` into a simple `Goto`.
|
||||
//!
|
||||
//! The algorithm maintains a set of replacement conditions:
|
||||
//! - `conditions[place]` contains `Condition { value, polarity: Eq, target }`
|
||||
//! if assigning `value` to `place` turns the `SwitchInt` into `Goto { target }`.
|
||||
//! - `conditions[place]` contains `Condition { value, polarity: Ne, target }`
|
||||
//! if assigning anything different from `value` to `place` turns the `SwitchInt`
|
||||
//! into `Goto { target }`.
|
||||
//!
|
||||
//! In this file, we denote as `place ?= value` the existence of a replacement condition
|
||||
//! on `place` with given `value`, irrespective of the polarity and target of that
|
||||
//! replacement condition.
|
||||
//!
|
||||
//! We then walk the CFG backwards transforming the set of conditions.
|
||||
//! When we find a fulfilling assignment, we record a `ThreadingOpportunity`.
|
||||
//! All `ThreadingOpportunity`s are applied to the body, by duplicating blocks if required.
|
||||
//!
|
||||
//! The optimization search can be very heavy, as it performs a DFS on MIR starting from
|
||||
//! each `SwitchInt` terminator. To manage the complexity, we:
|
||||
//! - bound the maximum depth by a constant `MAX_BACKTRACK`;
|
||||
//! - we only traverse `Goto` terminators.
|
||||
//!
|
||||
//! We try to avoid creating irreducible control-flow by not threading through a loop header.
|
||||
//!
|
||||
//! Likewise, applying the optimisation can create a lot of new MIR, so we bound the instruction
|
||||
//! cost by `MAX_COST`.
|
||||
|
||||
use rustc_arena::DroplessArena;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
|
||||
use rustc_mir_dataflow::value_analysis::{Map, PlaceIndex, State, TrackElem};
|
||||
|
||||
use crate::cost_checker::CostChecker;
|
||||
use crate::MirPass;
|
||||
|
||||
pub struct JumpThreading;
|
||||
|
||||
const MAX_BACKTRACK: usize = 5;
|
||||
const MAX_COST: usize = 100;
|
||||
const MAX_PLACES: usize = 100;
|
||||
|
||||
impl<'tcx> MirPass<'tcx> for JumpThreading {
|
||||
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
|
||||
sess.mir_opt_level() >= 4
|
||||
}
|
||||
|
||||
#[instrument(skip_all level = "debug")]
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
let def_id = body.source.def_id();
|
||||
debug!(?def_id);
|
||||
|
||||
let param_env = tcx.param_env_reveal_all_normalized(def_id);
|
||||
let map = Map::new(tcx, body, Some(MAX_PLACES));
|
||||
let loop_headers = loop_headers(body);
|
||||
|
||||
let arena = DroplessArena::default();
|
||||
let mut finder = TOFinder {
|
||||
tcx,
|
||||
param_env,
|
||||
body,
|
||||
arena: &arena,
|
||||
map: &map,
|
||||
loop_headers: &loop_headers,
|
||||
opportunities: Vec::new(),
|
||||
};
|
||||
|
||||
for (bb, bbdata) in body.basic_blocks.iter_enumerated() {
|
||||
debug!(?bb, term = ?bbdata.terminator());
|
||||
if bbdata.is_cleanup || loop_headers.contains(bb) {
|
||||
continue;
|
||||
}
|
||||
let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { continue };
|
||||
let Some(discr) = discr.place() else { continue };
|
||||
debug!(?discr, ?bb);
|
||||
|
||||
let discr_ty = discr.ty(body, tcx).ty;
|
||||
let Ok(discr_layout) = tcx.layout_of(param_env.and(discr_ty)) else { continue };
|
||||
|
||||
let Some(discr) = finder.map.find(discr.as_ref()) else { continue };
|
||||
debug!(?discr);
|
||||
|
||||
let cost = CostChecker::new(tcx, param_env, None, body);
|
||||
|
||||
let mut state = State::new(ConditionSet::default(), &finder.map);
|
||||
|
||||
let conds = if let Some((value, then, else_)) = targets.as_static_if() {
|
||||
let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else {
|
||||
continue;
|
||||
};
|
||||
arena.alloc_from_iter([
|
||||
Condition { value, polarity: Polarity::Eq, target: then },
|
||||
Condition { value, polarity: Polarity::Ne, target: else_ },
|
||||
])
|
||||
} else {
|
||||
arena.alloc_from_iter(targets.iter().filter_map(|(value, target)| {
|
||||
let value = ScalarInt::try_from_uint(value, discr_layout.size)?;
|
||||
Some(Condition { value, polarity: Polarity::Eq, target })
|
||||
}))
|
||||
};
|
||||
let conds = ConditionSet(conds);
|
||||
state.insert_value_idx(discr, conds, &finder.map);
|
||||
|
||||
finder.find_opportunity(bb, state, cost, 0);
|
||||
}
|
||||
|
||||
let opportunities = finder.opportunities;
|
||||
debug!(?opportunities);
|
||||
if opportunities.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify that we do not thread through a loop header.
|
||||
for to in opportunities.iter() {
|
||||
assert!(to.chain.iter().all(|&block| !loop_headers.contains(block)));
|
||||
}
|
||||
OpportunitySet::new(body, opportunities).apply(body);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ThreadingOpportunity {
|
||||
/// The list of `BasicBlock`s from the one that found the opportunity to the `SwitchInt`.
|
||||
chain: Vec<BasicBlock>,
|
||||
/// The `SwitchInt` will be replaced by `Goto { target }`.
|
||||
target: BasicBlock,
|
||||
}
|
||||
|
||||
struct TOFinder<'tcx, 'a> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
body: &'a Body<'tcx>,
|
||||
map: &'a Map,
|
||||
loop_headers: &'a BitSet<BasicBlock>,
|
||||
/// We use an arena to avoid cloning the slices when cloning `state`.
|
||||
arena: &'a DroplessArena,
|
||||
opportunities: Vec<ThreadingOpportunity>,
|
||||
}
|
||||
|
||||
/// Represent the following statement. If we can prove that the current local is equal/not-equal
|
||||
/// to `value`, jump to `target`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct Condition {
|
||||
value: ScalarInt,
|
||||
polarity: Polarity,
|
||||
target: BasicBlock,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
enum Polarity {
|
||||
Ne,
|
||||
Eq,
|
||||
}
|
||||
|
||||
impl Condition {
|
||||
fn matches(&self, value: ScalarInt) -> bool {
|
||||
(self.value == value) == (self.polarity == Polarity::Eq)
|
||||
}
|
||||
|
||||
fn inv(mut self) -> Self {
|
||||
self.polarity = match self.polarity {
|
||||
Polarity::Eq => Polarity::Ne,
|
||||
Polarity::Ne => Polarity::Eq,
|
||||
};
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
struct ConditionSet<'a>(&'a [Condition]);
|
||||
|
||||
impl<'a> ConditionSet<'a> {
|
||||
fn iter(self) -> impl Iterator<Item = Condition> + 'a {
|
||||
self.0.iter().copied()
|
||||
}
|
||||
|
||||
fn iter_matches(self, value: ScalarInt) -> impl Iterator<Item = Condition> + 'a {
|
||||
self.iter().filter(move |c| c.matches(value))
|
||||
}
|
||||
|
||||
fn map(self, arena: &'a DroplessArena, f: impl Fn(Condition) -> Condition) -> ConditionSet<'a> {
|
||||
ConditionSet(arena.alloc_from_iter(self.iter().map(f)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, 'a> TOFinder<'tcx, 'a> {
|
||||
fn is_empty(&self, state: &State<ConditionSet<'a>>) -> bool {
|
||||
state.all(|cs| cs.0.is_empty())
|
||||
}
|
||||
|
||||
/// Recursion entry point to find threading opportunities.
|
||||
#[instrument(level = "trace", skip(self, cost), ret)]
|
||||
fn find_opportunity(
|
||||
&mut self,
|
||||
bb: BasicBlock,
|
||||
mut state: State<ConditionSet<'a>>,
|
||||
mut cost: CostChecker<'_, 'tcx>,
|
||||
depth: usize,
|
||||
) {
|
||||
// Do not thread through loop headers.
|
||||
if self.loop_headers.contains(bb) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug!(cost = ?cost.cost());
|
||||
for (statement_index, stmt) in
|
||||
self.body.basic_blocks[bb].statements.iter().enumerate().rev()
|
||||
{
|
||||
if self.is_empty(&state) {
|
||||
return;
|
||||
}
|
||||
|
||||
cost.visit_statement(stmt, Location { block: bb, statement_index });
|
||||
if cost.cost() > MAX_COST {
|
||||
return;
|
||||
}
|
||||
|
||||
// Attempt to turn the `current_condition` on `lhs` into a condition on another place.
|
||||
self.process_statement(bb, stmt, &mut state);
|
||||
|
||||
// When a statement mutates a place, assignments to that place that happen
|
||||
// above the mutation cannot fulfill a condition.
|
||||
// _1 = 5 // Whatever happens here, it won't change the result of a `SwitchInt`.
|
||||
// _1 = 6
|
||||
if let Some((lhs, tail)) = self.mutated_statement(stmt) {
|
||||
state.flood_with_tail_elem(lhs.as_ref(), tail, self.map, ConditionSet::default());
|
||||
}
|
||||
}
|
||||
|
||||
if self.is_empty(&state) || depth >= MAX_BACKTRACK {
|
||||
return;
|
||||
}
|
||||
|
||||
let last_non_rec = self.opportunities.len();
|
||||
|
||||
let predecessors = &self.body.basic_blocks.predecessors()[bb];
|
||||
if let &[pred] = &predecessors[..] && bb != START_BLOCK {
|
||||
let term = self.body.basic_blocks[pred].terminator();
|
||||
match term.kind {
|
||||
TerminatorKind::SwitchInt { ref discr, ref targets } => {
|
||||
self.process_switch_int(discr, targets, bb, &mut state);
|
||||
self.find_opportunity(pred, state, cost, depth + 1);
|
||||
}
|
||||
_ => self.recurse_through_terminator(pred, &state, &cost, depth),
|
||||
}
|
||||
} else {
|
||||
for &pred in predecessors {
|
||||
self.recurse_through_terminator(pred, &state, &cost, depth);
|
||||
}
|
||||
}
|
||||
|
||||
let new_tos = &mut self.opportunities[last_non_rec..];
|
||||
debug!(?new_tos);
|
||||
|
||||
// Try to deduplicate threading opportunities.
|
||||
if new_tos.len() > 1
|
||||
&& new_tos.len() == predecessors.len()
|
||||
&& predecessors
|
||||
.iter()
|
||||
.zip(new_tos.iter())
|
||||
.all(|(&pred, to)| to.chain == &[pred] && to.target == new_tos[0].target)
|
||||
{
|
||||
// All predecessors have a threading opportunity, and they all point to the same block.
|
||||
debug!(?new_tos, "dedup");
|
||||
let first = &mut new_tos[0];
|
||||
*first = ThreadingOpportunity { chain: vec![bb], target: first.target };
|
||||
self.opportunities.truncate(last_non_rec + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
for op in self.opportunities[last_non_rec..].iter_mut() {
|
||||
op.chain.push(bb);
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the mutated place from a statement.
|
||||
///
|
||||
/// This method returns the `Place` so we can flood the state in case of a partial assignment.
|
||||
/// (_1 as Ok).0 = _5;
|
||||
/// (_1 as Err).0 = _6;
|
||||
/// We want to ensure that a `SwitchInt((_1 as Ok).0)` does not see the first assignment, as
|
||||
/// the value may have been mangled by the second assignment.
|
||||
///
|
||||
/// In case we assign to a discriminant, we return `Some(TrackElem::Discriminant)`, so we can
|
||||
/// stop at flooding the discriminant, and preserve the variant fields.
|
||||
/// (_1 as Some).0 = _6;
|
||||
/// SetDiscriminant(_1, 1);
|
||||
/// switchInt((_1 as Some).0)
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
fn mutated_statement(
|
||||
&self,
|
||||
stmt: &Statement<'tcx>,
|
||||
) -> Option<(Place<'tcx>, Option<TrackElem>)> {
|
||||
match stmt.kind {
|
||||
StatementKind::Assign(box (place, _))
|
||||
| StatementKind::Deinit(box place) => Some((place, None)),
|
||||
StatementKind::SetDiscriminant { box place, variant_index: _ } => {
|
||||
Some((place, Some(TrackElem::Discriminant)))
|
||||
}
|
||||
StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
|
||||
Some((Place::from(local), None))
|
||||
}
|
||||
StatementKind::Retag(..)
|
||||
| StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(..))
|
||||
// copy_nonoverlapping takes pointers and mutated the pointed-to value.
|
||||
| StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(..))
|
||||
| StatementKind::AscribeUserType(..)
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::FakeRead(..)
|
||||
| StatementKind::ConstEvalCounter
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::Nop => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self))]
|
||||
fn process_operand(
|
||||
&mut self,
|
||||
bb: BasicBlock,
|
||||
lhs: PlaceIndex,
|
||||
rhs: &Operand<'tcx>,
|
||||
state: &mut State<ConditionSet<'a>>,
|
||||
) -> Option<!> {
|
||||
let register_opportunity = |c: Condition| {
|
||||
debug!(?bb, ?c.target, "register");
|
||||
self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target })
|
||||
};
|
||||
|
||||
match rhs {
|
||||
// If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
|
||||
Operand::Constant(constant) => {
|
||||
let conditions = state.try_get_idx(lhs, self.map)?;
|
||||
let constant =
|
||||
constant.const_.normalize(self.tcx, self.param_env).try_to_scalar_int()?;
|
||||
conditions.iter_matches(constant).for_each(register_opportunity);
|
||||
}
|
||||
// Transfer the conditions on the copied rhs.
|
||||
Operand::Move(rhs) | Operand::Copy(rhs) => {
|
||||
let rhs = self.map.find(rhs.as_ref())?;
|
||||
state.insert_place_idx(rhs, lhs, self.map);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self))]
|
||||
fn process_statement(
|
||||
&mut self,
|
||||
bb: BasicBlock,
|
||||
stmt: &Statement<'tcx>,
|
||||
state: &mut State<ConditionSet<'a>>,
|
||||
) -> Option<!> {
|
||||
let register_opportunity = |c: Condition| {
|
||||
debug!(?bb, ?c.target, "register");
|
||||
self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target })
|
||||
};
|
||||
|
||||
// Below, `lhs` is the return value of `mutated_statement`,
|
||||
// the place to which `conditions` apply.
|
||||
|
||||
let discriminant_for_variant = |enum_ty: Ty<'tcx>, variant_index| {
|
||||
let discr = enum_ty.discriminant_for_variant(self.tcx, variant_index)?;
|
||||
let discr_layout = self.tcx.layout_of(self.param_env.and(discr.ty)).ok()?;
|
||||
let scalar = ScalarInt::try_from_uint(discr.val, discr_layout.size)?;
|
||||
Some(Operand::const_from_scalar(
|
||||
self.tcx,
|
||||
discr.ty,
|
||||
scalar.into(),
|
||||
rustc_span::DUMMY_SP,
|
||||
))
|
||||
};
|
||||
|
||||
match &stmt.kind {
|
||||
// If we expect `discriminant(place) ?= A`,
|
||||
// we have an opportunity if `variant_index ?= A`.
|
||||
StatementKind::SetDiscriminant { box place, variant_index } => {
|
||||
let discr_target = self.map.find_discr(place.as_ref())?;
|
||||
let enum_ty = place.ty(self.body, self.tcx).ty;
|
||||
let discr = discriminant_for_variant(enum_ty, *variant_index)?;
|
||||
self.process_operand(bb, discr_target, &discr, state)?;
|
||||
}
|
||||
// If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`.
|
||||
StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(
|
||||
Operand::Copy(place) | Operand::Move(place),
|
||||
)) => {
|
||||
let conditions = state.try_get(place.as_ref(), self.map)?;
|
||||
conditions.iter_matches(ScalarInt::TRUE).for_each(register_opportunity);
|
||||
}
|
||||
StatementKind::Assign(box (lhs_place, rhs)) => {
|
||||
if let Some(lhs) = self.map.find(lhs_place.as_ref()) {
|
||||
match rhs {
|
||||
Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state)?,
|
||||
// Transfer the conditions on the copy rhs.
|
||||
Rvalue::CopyForDeref(rhs) => {
|
||||
self.process_operand(bb, lhs, &Operand::Copy(*rhs), state)?
|
||||
}
|
||||
Rvalue::Discriminant(rhs) => {
|
||||
let rhs = self.map.find_discr(rhs.as_ref())?;
|
||||
state.insert_place_idx(rhs, lhs, self.map);
|
||||
}
|
||||
// If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
|
||||
Rvalue::Aggregate(box ref kind, ref operands) => {
|
||||
let agg_ty = lhs_place.ty(self.body, self.tcx).ty;
|
||||
let lhs = match kind {
|
||||
// Do not support unions.
|
||||
AggregateKind::Adt(.., Some(_)) => return None,
|
||||
AggregateKind::Adt(_, variant_index, ..) if agg_ty.is_enum() => {
|
||||
if let Some(discr_target) = self.map.apply(lhs, TrackElem::Discriminant)
|
||||
&& let Some(discr_value) = discriminant_for_variant(agg_ty, *variant_index)
|
||||
{
|
||||
self.process_operand(bb, discr_target, &discr_value, state);
|
||||
}
|
||||
self.map.apply(lhs, TrackElem::Variant(*variant_index))?
|
||||
}
|
||||
_ => lhs,
|
||||
};
|
||||
for (field_index, operand) in operands.iter_enumerated() {
|
||||
if let Some(field) =
|
||||
self.map.apply(lhs, TrackElem::Field(field_index))
|
||||
{
|
||||
self.process_operand(bb, field, operand, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Transfer the conditions on the copy rhs, after inversing polarity.
|
||||
Rvalue::UnaryOp(UnOp::Not, Operand::Move(place) | Operand::Copy(place)) => {
|
||||
let conditions = state.try_get_idx(lhs, self.map)?;
|
||||
let place = self.map.find(place.as_ref())?;
|
||||
let conds = conditions.map(self.arena, Condition::inv);
|
||||
state.insert_value_idx(place, conds, self.map);
|
||||
}
|
||||
// We expect `lhs ?= A`. We found `lhs = Eq(rhs, B)`.
|
||||
// Create a condition on `rhs ?= B`.
|
||||
Rvalue::BinaryOp(
|
||||
op,
|
||||
box (
|
||||
Operand::Move(place) | Operand::Copy(place),
|
||||
Operand::Constant(value),
|
||||
)
|
||||
| box (
|
||||
Operand::Constant(value),
|
||||
Operand::Move(place) | Operand::Copy(place),
|
||||
),
|
||||
) => {
|
||||
let conditions = state.try_get_idx(lhs, self.map)?;
|
||||
let place = self.map.find(place.as_ref())?;
|
||||
let equals = match op {
|
||||
BinOp::Eq => ScalarInt::TRUE,
|
||||
BinOp::Ne => ScalarInt::FALSE,
|
||||
_ => return None,
|
||||
};
|
||||
let value = value
|
||||
.const_
|
||||
.normalize(self.tcx, self.param_env)
|
||||
.try_to_scalar_int()?;
|
||||
let conds = conditions.map(self.arena, |c| Condition {
|
||||
value,
|
||||
polarity: if c.matches(equals) {
|
||||
Polarity::Eq
|
||||
} else {
|
||||
Polarity::Ne
|
||||
},
|
||||
..c
|
||||
});
|
||||
state.insert_value_idx(place, conds, self.map);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self, cost))]
|
||||
fn recurse_through_terminator(
|
||||
&mut self,
|
||||
bb: BasicBlock,
|
||||
state: &State<ConditionSet<'a>>,
|
||||
cost: &CostChecker<'_, 'tcx>,
|
||||
depth: usize,
|
||||
) {
|
||||
let register_opportunity = |c: Condition| {
|
||||
debug!(?bb, ?c.target, "register");
|
||||
self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target })
|
||||
};
|
||||
|
||||
let term = self.body.basic_blocks[bb].terminator();
|
||||
let place_to_flood = match term.kind {
|
||||
// We come from a target, so those are not possible.
|
||||
TerminatorKind::UnwindResume
|
||||
| TerminatorKind::UnwindTerminate(_)
|
||||
| TerminatorKind::Return
|
||||
| TerminatorKind::Unreachable
|
||||
| TerminatorKind::CoroutineDrop => bug!("{term:?} has no terminators"),
|
||||
// Disallowed during optimizations.
|
||||
TerminatorKind::FalseEdge { .. }
|
||||
| TerminatorKind::FalseUnwind { .. }
|
||||
| TerminatorKind::Yield { .. } => bug!("{term:?} invalid"),
|
||||
// Cannot reason about inline asm.
|
||||
TerminatorKind::InlineAsm { .. } => return,
|
||||
// `SwitchInt` is handled specially.
|
||||
TerminatorKind::SwitchInt { .. } => return,
|
||||
// We can recurse, no thing particular to do.
|
||||
TerminatorKind::Goto { .. } => None,
|
||||
// Flood the overwritten place, and progress through.
|
||||
TerminatorKind::Drop { place: destination, .. }
|
||||
| TerminatorKind::Call { destination, .. } => Some(destination),
|
||||
// Treat as an `assume(cond == expected)`.
|
||||
TerminatorKind::Assert { ref cond, expected, .. } => {
|
||||
if let Some(place) = cond.place()
|
||||
&& let Some(conditions) = state.try_get(place.as_ref(), self.map)
|
||||
{
|
||||
let expected = if expected { ScalarInt::TRUE } else { ScalarInt::FALSE };
|
||||
conditions.iter_matches(expected).for_each(register_opportunity);
|
||||
}
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
// We can recurse through this terminator.
|
||||
let mut state = state.clone();
|
||||
if let Some(place_to_flood) = place_to_flood {
|
||||
state.flood_with(place_to_flood.as_ref(), self.map, ConditionSet::default());
|
||||
}
|
||||
self.find_opportunity(bb, state, cost.clone(), depth + 1);
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self))]
|
||||
fn process_switch_int(
|
||||
&mut self,
|
||||
discr: &Operand<'tcx>,
|
||||
targets: &SwitchTargets,
|
||||
target_bb: BasicBlock,
|
||||
state: &mut State<ConditionSet<'a>>,
|
||||
) -> Option<!> {
|
||||
debug_assert_ne!(target_bb, START_BLOCK);
|
||||
debug_assert_eq!(self.body.basic_blocks.predecessors()[target_bb].len(), 1);
|
||||
|
||||
let discr = discr.place()?;
|
||||
let discr_ty = discr.ty(self.body, self.tcx).ty;
|
||||
let discr_layout = self.tcx.layout_of(self.param_env.and(discr_ty)).ok()?;
|
||||
let conditions = state.try_get(discr.as_ref(), self.map)?;
|
||||
|
||||
if let Some((value, _)) = targets.iter().find(|&(_, target)| target == target_bb) {
|
||||
let value = ScalarInt::try_from_uint(value, discr_layout.size)?;
|
||||
debug_assert_eq!(targets.iter().filter(|&(_, target)| target == target_bb).count(), 1);
|
||||
|
||||
// We are inside `target_bb`. Since we have a single predecessor, we know we passed
|
||||
// through the `SwitchInt` before arriving here. Therefore, we know that
|
||||
// `discr == value`. If one condition can be fulfilled by `discr == value`,
|
||||
// that's an opportunity.
|
||||
for c in conditions.iter_matches(value) {
|
||||
debug!(?target_bb, ?c.target, "register");
|
||||
self.opportunities.push(ThreadingOpportunity { chain: vec![], target: c.target });
|
||||
}
|
||||
} else if let Some((value, _, else_bb)) = targets.as_static_if()
|
||||
&& target_bb == else_bb
|
||||
{
|
||||
let value = ScalarInt::try_from_uint(value, discr_layout.size)?;
|
||||
|
||||
// We only know that `discr != value`. That's much weaker information than
|
||||
// the equality we had in the previous arm. All we can conclude is that
|
||||
// the replacement condition `discr != value` can be threaded, and nothing else.
|
||||
for c in conditions.iter() {
|
||||
if c.value == value && c.polarity == Polarity::Ne {
|
||||
debug!(?target_bb, ?c.target, "register");
|
||||
self.opportunities
|
||||
.push(ThreadingOpportunity { chain: vec![], target: c.target });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
struct OpportunitySet {
|
||||
opportunities: Vec<ThreadingOpportunity>,
|
||||
/// For each bb, give the TOs in which it appears. The pair corresponds to the index
|
||||
/// in `opportunities` and the index in `ThreadingOpportunity::chain`.
|
||||
involving_tos: IndexVec<BasicBlock, Vec<(usize, usize)>>,
|
||||
/// Cache the number of predecessors for each block, as we clear the basic block cache..
|
||||
predecessors: IndexVec<BasicBlock, usize>,
|
||||
}
|
||||
|
||||
impl OpportunitySet {
|
||||
fn new(body: &Body<'_>, opportunities: Vec<ThreadingOpportunity>) -> OpportunitySet {
|
||||
let mut involving_tos = IndexVec::from_elem(Vec::new(), &body.basic_blocks);
|
||||
for (index, to) in opportunities.iter().enumerate() {
|
||||
for (ibb, &bb) in to.chain.iter().enumerate() {
|
||||
involving_tos[bb].push((index, ibb));
|
||||
}
|
||||
involving_tos[to.target].push((index, to.chain.len()));
|
||||
}
|
||||
let predecessors = predecessor_count(body);
|
||||
OpportunitySet { opportunities, involving_tos, predecessors }
|
||||
}
|
||||
|
||||
/// Apply the opportunities on the graph.
|
||||
fn apply(&mut self, body: &mut Body<'_>) {
|
||||
for i in 0..self.opportunities.len() {
|
||||
self.apply_once(i, body);
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self, body))]
|
||||
fn apply_once(&mut self, index: usize, body: &mut Body<'_>) {
|
||||
debug!(?self.predecessors);
|
||||
debug!(?self.involving_tos);
|
||||
|
||||
// Check that `predecessors` satisfies its invariant.
|
||||
debug_assert_eq!(self.predecessors, predecessor_count(body));
|
||||
|
||||
// Remove the TO from the vector to allow modifying the other ones later.
|
||||
let op = &mut self.opportunities[index];
|
||||
debug!(?op);
|
||||
let op_chain = std::mem::take(&mut op.chain);
|
||||
let op_target = op.target;
|
||||
debug_assert_eq!(op_chain.len(), op_chain.iter().collect::<FxHashSet<_>>().len());
|
||||
|
||||
let Some((current, chain)) = op_chain.split_first() else { return };
|
||||
let basic_blocks = body.basic_blocks.as_mut();
|
||||
|
||||
// Invariant: the control-flow is well-formed at the end of each iteration.
|
||||
let mut current = *current;
|
||||
for &succ in chain {
|
||||
debug!(?current, ?succ);
|
||||
|
||||
// `succ` must be a successor of `current`. If it is not, this means this TO is not
|
||||
// satisfiable and a previous TO erased this edge, so we bail out.
|
||||
if basic_blocks[current].terminator().successors().find(|s| *s == succ).is_none() {
|
||||
debug!("impossible");
|
||||
return;
|
||||
}
|
||||
|
||||
// Fast path: `succ` is only used once, so we can reuse it directly.
|
||||
if self.predecessors[succ] == 1 {
|
||||
debug!("single");
|
||||
current = succ;
|
||||
continue;
|
||||
}
|
||||
|
||||
let new_succ = basic_blocks.push(basic_blocks[succ].clone());
|
||||
debug!(?new_succ);
|
||||
|
||||
// Replace `succ` by `new_succ` where it appears.
|
||||
let mut num_edges = 0;
|
||||
for s in basic_blocks[current].terminator_mut().successors_mut() {
|
||||
if *s == succ {
|
||||
*s = new_succ;
|
||||
num_edges += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Update predecessors with the new block.
|
||||
let _new_succ = self.predecessors.push(num_edges);
|
||||
debug_assert_eq!(new_succ, _new_succ);
|
||||
self.predecessors[succ] -= num_edges;
|
||||
self.update_predecessor_count(basic_blocks[new_succ].terminator(), Update::Incr);
|
||||
|
||||
// Replace the `current -> succ` edge by `current -> new_succ` in all the following
|
||||
// TOs. This is necessary to avoid trying to thread through a non-existing edge. We
|
||||
// use `involving_tos` here to avoid traversing the full set of TOs on each iteration.
|
||||
let mut new_involved = Vec::new();
|
||||
for &(to_index, in_to_index) in &self.involving_tos[current] {
|
||||
// That TO has already been applied, do nothing.
|
||||
if to_index <= index {
|
||||
continue;
|
||||
}
|
||||
|
||||
let other_to = &mut self.opportunities[to_index];
|
||||
if other_to.chain.get(in_to_index) != Some(¤t) {
|
||||
continue;
|
||||
}
|
||||
let s = other_to.chain.get_mut(in_to_index + 1).unwrap_or(&mut other_to.target);
|
||||
if *s == succ {
|
||||
// `other_to` references the `current -> succ` edge, so replace `succ`.
|
||||
*s = new_succ;
|
||||
new_involved.push((to_index, in_to_index + 1));
|
||||
}
|
||||
}
|
||||
|
||||
// The TOs that we just updated now reference `new_succ`. Update `involving_tos`
|
||||
// in case we need to duplicate an edge starting at `new_succ` later.
|
||||
let _new_succ = self.involving_tos.push(new_involved);
|
||||
debug_assert_eq!(new_succ, _new_succ);
|
||||
|
||||
current = new_succ;
|
||||
}
|
||||
|
||||
let current = &mut basic_blocks[current];
|
||||
self.update_predecessor_count(current.terminator(), Update::Decr);
|
||||
current.terminator_mut().kind = TerminatorKind::Goto { target: op_target };
|
||||
self.predecessors[op_target] += 1;
|
||||
}
|
||||
|
||||
fn update_predecessor_count(&mut self, terminator: &Terminator<'_>, incr: Update) {
|
||||
match incr {
|
||||
Update::Incr => {
|
||||
for s in terminator.successors() {
|
||||
self.predecessors[s] += 1;
|
||||
}
|
||||
}
|
||||
Update::Decr => {
|
||||
for s in terminator.successors() {
|
||||
self.predecessors[s] -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn predecessor_count(body: &Body<'_>) -> IndexVec<BasicBlock, usize> {
|
||||
let mut predecessors: IndexVec<_, _> =
|
||||
body.basic_blocks.predecessors().iter().map(|ps| ps.len()).collect();
|
||||
predecessors[START_BLOCK] += 1; // Account for the implicit entry edge.
|
||||
predecessors
|
||||
}
|
||||
|
||||
enum Update {
|
||||
Incr,
|
||||
Decr,
|
||||
}
|
||||
|
||||
/// Compute the set of loop headers in the given body. We define a loop header as a block which has
|
||||
/// at least a predecessor which it dominates. This definition is only correct for reducible CFGs.
|
||||
/// But if the CFG is already irreducible, there is no point in trying much harder.
|
||||
/// is already irreducible.
|
||||
fn loop_headers(body: &Body<'_>) -> BitSet<BasicBlock> {
|
||||
let mut loop_headers = BitSet::new_empty(body.basic_blocks.len());
|
||||
let dominators = body.basic_blocks.dominators();
|
||||
// Only visit reachable blocks.
|
||||
for (bb, bbdata) in traversal::preorder(body) {
|
||||
for succ in bbdata.terminator().successors() {
|
||||
if dominators.dominates(succ, bb) {
|
||||
loop_headers.insert(succ);
|
||||
}
|
||||
}
|
||||
}
|
||||
loop_headers
|
||||
}
|
||||
|
|
@ -62,6 +62,7 @@ mod const_prop;
|
|||
mod const_prop_lint;
|
||||
mod copy_prop;
|
||||
mod coroutine;
|
||||
mod cost_checker;
|
||||
mod coverage;
|
||||
mod cross_crate_inline;
|
||||
mod ctfe_limit;
|
||||
|
|
@ -81,6 +82,7 @@ mod function_item_references;
|
|||
mod gvn;
|
||||
pub mod inline;
|
||||
mod instsimplify;
|
||||
mod jump_threading;
|
||||
mod large_enums;
|
||||
mod lower_intrinsics;
|
||||
mod lower_slice_len;
|
||||
|
|
@ -571,6 +573,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
|||
&dataflow_const_prop::DataflowConstProp,
|
||||
&const_debuginfo::ConstDebugInfo,
|
||||
&o1(simplify_branches::SimplifyConstCondition::AfterConstProp),
|
||||
&jump_threading::JumpThreading,
|
||||
&early_otherwise_branch::EarlyOtherwiseBranch,
|
||||
&simplify_comparison_integral::SimplifyComparisonIntegral,
|
||||
&dead_store_elimination::DeadStoreElimination,
|
||||
|
|
|
|||
|
|
@ -24,11 +24,8 @@ pub struct RemoveUninitDrops;
|
|||
impl<'tcx> MirPass<'tcx> for RemoveUninitDrops {
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
let param_env = tcx.param_env(body.source.def_id());
|
||||
let Ok(move_data) = MoveData::gather_moves(body, tcx, param_env) else {
|
||||
// We could continue if there are move errors, but there's not much point since our
|
||||
// init data isn't complete.
|
||||
return;
|
||||
};
|
||||
let move_data =
|
||||
MoveData::gather_moves(&body, tcx, param_env, |ty| ty.needs_drop(tcx, param_env));
|
||||
|
||||
let mdpe = MoveDataParamEnv { move_data, param_env };
|
||||
let mut maybe_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe)
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ impl<D: Deps> DepGraphData<D> {
|
|||
/// - If you need 3+ arguments, use a tuple for the
|
||||
/// `arg` parameter.
|
||||
///
|
||||
/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/incremental-compilation.html
|
||||
/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation.html
|
||||
#[inline(always)]
|
||||
pub fn with_task<Ctxt: HasDepContext<Deps = D>, A: Debug, R>(
|
||||
&self,
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ struct UnusedImportCheckVisitor<'a, 'b, 'tcx> {
|
|||
base_use_tree: Option<&'a ast::UseTree>,
|
||||
base_id: ast::NodeId,
|
||||
item_span: Span,
|
||||
base_use_is_pub: bool,
|
||||
}
|
||||
|
||||
struct ExternCrateToLint {
|
||||
|
|
@ -146,7 +145,6 @@ impl<'a, 'b, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b, 'tcx> {
|
|||
// because this means that they were generated in some fashion by the
|
||||
// compiler and we don't need to consider them.
|
||||
ast::ItemKind::Use(..) if item.span.is_dummy() => return,
|
||||
ast::ItemKind::Use(..) => self.base_use_is_pub = item.vis.kind.is_pub(),
|
||||
ast::ItemKind::ExternCrate(orig_name) => {
|
||||
self.extern_crate_items.push(ExternCrateToLint {
|
||||
id: item.id,
|
||||
|
|
@ -173,7 +171,7 @@ impl<'a, 'b, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b, 'tcx> {
|
|||
self.base_use_tree = Some(use_tree);
|
||||
}
|
||||
|
||||
if self.base_use_is_pub {
|
||||
if self.r.effective_visibilities.is_exported(self.r.local_def_id(id)) {
|
||||
self.check_import_as_underscore(use_tree, id);
|
||||
return;
|
||||
}
|
||||
|
|
@ -332,7 +330,6 @@ impl Resolver<'_, '_> {
|
|||
base_use_tree: None,
|
||||
base_id: ast::DUMMY_NODE_ID,
|
||||
item_span: DUMMY_SP,
|
||||
base_use_is_pub: false,
|
||||
};
|
||||
visit::walk_crate(&mut visitor, krate);
|
||||
|
||||
|
|
|
|||
|
|
@ -3463,9 +3463,10 @@ impl DumpMonoStatsFormat {
|
|||
|
||||
/// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy,
|
||||
/// or future prototype.
|
||||
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
|
||||
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
|
||||
pub enum Polonius {
|
||||
/// The default value: disabled.
|
||||
#[default]
|
||||
Off,
|
||||
|
||||
/// Legacy version, using datalog and the `polonius-engine` crate. Historical value for `-Zpolonius`.
|
||||
|
|
@ -3475,12 +3476,6 @@ pub enum Polonius {
|
|||
Next,
|
||||
}
|
||||
|
||||
impl Default for Polonius {
|
||||
fn default() -> Self {
|
||||
Polonius::Off
|
||||
}
|
||||
}
|
||||
|
||||
impl Polonius {
|
||||
/// Returns whether the legacy version of polonius is enabled
|
||||
pub fn is_legacy_enabled(&self) -> bool {
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_driver = { path = "../rustc_driver" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_interface = { path = "../rustc_interface" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
|
|
|
|||
|
|
@ -3,22 +3,18 @@
|
|||
//! For that, we define APIs that will temporarily be public to 3P that exposes rustc internal APIs
|
||||
//! until stable MIR is complete.
|
||||
|
||||
use crate::rustc_internal;
|
||||
use crate::rustc_smir::Tables;
|
||||
use rustc_data_structures::fx;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_driver::{Callbacks, Compilation, RunCompiler};
|
||||
use rustc_interface::{interface, Queries};
|
||||
use rustc_middle::mir::interpret::AllocId;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::def_id::{CrateNum, DefId};
|
||||
use rustc_span::Span;
|
||||
use stable_mir::ty::IndexedVal;
|
||||
use stable_mir::CompilerError;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::ops::{ControlFlow, Index};
|
||||
use std::ops::Index;
|
||||
|
||||
mod internal;
|
||||
|
||||
|
|
@ -143,63 +139,81 @@ pub fn run(tcx: TyCtxt<'_>, f: impl FnOnce()) {
|
|||
);
|
||||
}
|
||||
|
||||
pub struct StableMir<B = (), C = ()>
|
||||
where
|
||||
B: Send,
|
||||
C: Send,
|
||||
{
|
||||
args: Vec<String>,
|
||||
callback: fn(TyCtxt<'_>) -> ControlFlow<B, C>,
|
||||
result: Option<ControlFlow<B, C>>,
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! run {
|
||||
($args:expr, $callback:expr) => {
|
||||
run!($args, tcx, $callback)
|
||||
};
|
||||
($args:expr, $tcx:ident, $callback:expr) => {{
|
||||
use rustc_driver::{Callbacks, Compilation, RunCompiler};
|
||||
use rustc_interface::{interface, Queries};
|
||||
use stable_mir::CompilerError;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
impl<B, C> StableMir<B, C>
|
||||
where
|
||||
B: Send,
|
||||
C: Send,
|
||||
{
|
||||
/// Creates a new `StableMir` instance, with given test_function and arguments.
|
||||
pub fn new(args: Vec<String>, callback: fn(TyCtxt<'_>) -> ControlFlow<B, C>) -> Self {
|
||||
StableMir { args, callback, result: None }
|
||||
}
|
||||
|
||||
/// Runs the compiler against given target and tests it with `test_function`
|
||||
pub fn run(&mut self) -> Result<C, CompilerError<B>> {
|
||||
let compiler_result =
|
||||
rustc_driver::catch_fatal_errors(|| RunCompiler::new(&self.args.clone(), self).run());
|
||||
match (compiler_result, self.result.take()) {
|
||||
(Ok(Ok(())), Some(ControlFlow::Continue(value))) => Ok(value),
|
||||
(Ok(Ok(())), Some(ControlFlow::Break(value))) => Err(CompilerError::Interrupted(value)),
|
||||
(Ok(Ok(_)), None) => Err(CompilerError::Skipped),
|
||||
(Ok(Err(_)), _) => Err(CompilerError::CompilationFailed),
|
||||
(Err(_), _) => Err(CompilerError::ICE),
|
||||
pub struct StableMir<B = (), C = ()>
|
||||
where
|
||||
B: Send,
|
||||
C: Send,
|
||||
{
|
||||
args: Vec<String>,
|
||||
callback: fn(TyCtxt<'_>) -> ControlFlow<B, C>,
|
||||
result: Option<ControlFlow<B, C>>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, C> Callbacks for StableMir<B, C>
|
||||
where
|
||||
B: Send,
|
||||
C: Send,
|
||||
{
|
||||
/// Called after analysis. Return value instructs the compiler whether to
|
||||
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
|
||||
fn after_analysis<'tcx>(
|
||||
&mut self,
|
||||
_compiler: &interface::Compiler,
|
||||
queries: &'tcx Queries<'tcx>,
|
||||
) -> Compilation {
|
||||
queries.global_ctxt().unwrap().enter(|tcx| {
|
||||
rustc_internal::run(tcx, || {
|
||||
self.result = Some((self.callback)(tcx));
|
||||
});
|
||||
if self.result.as_ref().is_some_and(|val| val.is_continue()) {
|
||||
Compilation::Continue
|
||||
} else {
|
||||
Compilation::Stop
|
||||
impl<B, C> StableMir<B, C>
|
||||
where
|
||||
B: Send,
|
||||
C: Send,
|
||||
{
|
||||
/// Creates a new `StableMir` instance, with given test_function and arguments.
|
||||
pub fn new(args: Vec<String>, callback: fn(TyCtxt<'_>) -> ControlFlow<B, C>) -> Self {
|
||||
StableMir { args, callback, result: None }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Runs the compiler against given target and tests it with `test_function`
|
||||
pub fn run(&mut self) -> Result<C, CompilerError<B>> {
|
||||
let compiler_result = rustc_driver::catch_fatal_errors(|| {
|
||||
RunCompiler::new(&self.args.clone(), self).run()
|
||||
});
|
||||
match (compiler_result, self.result.take()) {
|
||||
(Ok(Ok(())), Some(ControlFlow::Continue(value))) => Ok(value),
|
||||
(Ok(Ok(())), Some(ControlFlow::Break(value))) => {
|
||||
Err(CompilerError::Interrupted(value))
|
||||
}
|
||||
(Ok(Ok(_)), None) => Err(CompilerError::Skipped),
|
||||
(Ok(Err(_)), _) => Err(CompilerError::CompilationFailed),
|
||||
(Err(_), _) => Err(CompilerError::ICE),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, C> Callbacks for StableMir<B, C>
|
||||
where
|
||||
B: Send,
|
||||
C: Send,
|
||||
{
|
||||
/// Called after analysis. Return value instructs the compiler whether to
|
||||
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
|
||||
fn after_analysis<'tcx>(
|
||||
&mut self,
|
||||
_compiler: &interface::Compiler,
|
||||
queries: &'tcx Queries<'tcx>,
|
||||
) -> Compilation {
|
||||
queries.global_ctxt().unwrap().enter(|tcx| {
|
||||
rustc_internal::run(tcx, || {
|
||||
self.result = Some((self.callback)(tcx));
|
||||
});
|
||||
if self.result.as_ref().is_some_and(|val| val.is_continue()) {
|
||||
Compilation::Continue
|
||||
} else {
|
||||
Compilation::Stop
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
StableMir::new($args, |$tcx| $callback).run()
|
||||
}};
|
||||
}
|
||||
|
||||
/// Simmilar to rustc's `FxIndexMap`, `IndexMap` with extra
|
||||
|
|
|
|||
|
|
@ -2247,7 +2247,7 @@ where
|
|||
|
||||
/// Useful type to use with `Result<>` indicate that an error has already
|
||||
/// been reported to the user, so no need to continue checking.
|
||||
#[derive(Clone, Copy, Debug, Encodable, Decodable, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(HashStable_Generic)]
|
||||
pub struct ErrorGuaranteed(());
|
||||
|
||||
|
|
@ -2259,3 +2259,20 @@ impl ErrorGuaranteed {
|
|||
ErrorGuaranteed(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: rustc_serialize::Encoder> Encodable<E> for ErrorGuaranteed {
|
||||
#[inline]
|
||||
fn encode(&self, _e: &mut E) {
|
||||
panic!(
|
||||
"should never serialize an `ErrorGuaranteed`, as we do not write metadata or incremental caches in case errors occurred"
|
||||
)
|
||||
}
|
||||
}
|
||||
impl<D: rustc_serialize::Decoder> Decodable<D> for ErrorGuaranteed {
|
||||
#[inline]
|
||||
fn decode(_d: &mut D) -> ErrorGuaranteed {
|
||||
panic!(
|
||||
"`ErrorGuaranteed` should never have been serialized to metadata or incremental caches"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -199,16 +199,16 @@ struct SymbolPrinter<'tcx> {
|
|||
// `PrettyPrinter` aka pretty printing of e.g. types in paths,
|
||||
// symbol names should have their own printing machinery.
|
||||
|
||||
impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
|
||||
impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> {
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn print_region(self, _region: ty::Region<'_>) -> Result<Self, PrintError> {
|
||||
Ok(self)
|
||||
fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self, PrintError> {
|
||||
fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> {
|
||||
match *ty.kind() {
|
||||
// Print all nominal types as paths (unlike `pretty_print_type`).
|
||||
ty::FnDef(def_id, args)
|
||||
|
|
@ -220,17 +220,17 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
|
|||
// -Zverbose flag, so we cannot reuse it here.
|
||||
ty::Array(ty, size) => {
|
||||
self.write_str("[")?;
|
||||
self = self.print_type(ty)?;
|
||||
self.print_type(ty)?;
|
||||
self.write_str("; ")?;
|
||||
if let Some(size) = size.try_to_target_usize(self.tcx()) {
|
||||
write!(self, "{size}")?
|
||||
} else if let ty::ConstKind::Param(param) = size.kind() {
|
||||
self = param.print(self)?
|
||||
param.print(self)?
|
||||
} else {
|
||||
self.write_str("_")?
|
||||
}
|
||||
self.write_str("]")?;
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
ty::Alias(ty::Inherent, _) => panic!("unexpected inherent projection"),
|
||||
|
|
@ -240,21 +240,21 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
|
|||
}
|
||||
|
||||
fn print_dyn_existential(
|
||||
mut self,
|
||||
&mut self,
|
||||
predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
let mut first = true;
|
||||
for p in predicates {
|
||||
if !first {
|
||||
write!(self, "+")?;
|
||||
}
|
||||
first = false;
|
||||
self = p.print(self)?;
|
||||
p.print(self)?;
|
||||
}
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self, PrintError> {
|
||||
fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> {
|
||||
// only print integers
|
||||
match (ct.kind(), ct.ty().kind()) {
|
||||
(ty::ConstKind::Value(ty::ValTree::Leaf(scalar)), ty::Int(_) | ty::Uint(_)) => {
|
||||
|
|
@ -269,18 +269,18 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
|
|||
}
|
||||
_ => self.write_str("_")?,
|
||||
}
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn path_crate(self, cnum: CrateNum) -> Result<Self, PrintError> {
|
||||
fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
|
||||
self.write_str(self.tcx.crate_name(cnum).as_str())?;
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
fn path_qualified(
|
||||
self,
|
||||
&mut self,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
// Similar to `pretty_path_qualified`, but for the other
|
||||
// types that are printed as paths (see `print_type` above).
|
||||
match self_ty.kind() {
|
||||
|
|
@ -295,15 +295,15 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
|
|||
}
|
||||
|
||||
fn path_append_impl(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
_disambiguated_data: &DisambiguatedDefPathData,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
self.pretty_path_append_impl(
|
||||
|mut cx| {
|
||||
cx = print_prefix(cx)?;
|
||||
|cx| {
|
||||
print_prefix(cx)?;
|
||||
|
||||
if cx.keep_within_component {
|
||||
// HACK(eddyb) print the path similarly to how `FmtPrinter` prints it.
|
||||
|
|
@ -312,22 +312,22 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
|
|||
cx.path.finalize_pending_component();
|
||||
}
|
||||
|
||||
Ok(cx)
|
||||
Ok(())
|
||||
},
|
||||
self_ty,
|
||||
trait_ref,
|
||||
)
|
||||
}
|
||||
fn path_append(
|
||||
mut self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
disambiguated_data: &DisambiguatedDefPathData,
|
||||
) -> Result<Self, PrintError> {
|
||||
self = print_prefix(self)?;
|
||||
) -> Result<(), PrintError> {
|
||||
print_prefix(self)?;
|
||||
|
||||
// Skip `::{{extern}}` blocks and `::{{constructor}}` on tuple/unit structs.
|
||||
if let DefPathData::ForeignMod | DefPathData::Ctor = disambiguated_data.data {
|
||||
return Ok(self);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.keep_within_component {
|
||||
|
|
@ -339,14 +339,14 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
|
|||
|
||||
write!(self, "{}", disambiguated_data.data)?;
|
||||
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
fn path_generic_args(
|
||||
mut self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
args: &[GenericArg<'tcx>],
|
||||
) -> Result<Self, PrintError> {
|
||||
self = print_prefix(self)?;
|
||||
) -> Result<(), PrintError> {
|
||||
print_prefix(self)?;
|
||||
|
||||
let args =
|
||||
args.iter().cloned().filter(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
|
||||
|
|
@ -354,42 +354,42 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
|
|||
if args.clone().next().is_some() {
|
||||
self.generic_delimiters(|cx| cx.comma_sep(args))
|
||||
} else {
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> PrettyPrinter<'tcx> for &mut SymbolPrinter<'tcx> {
|
||||
impl<'tcx> PrettyPrinter<'tcx> for SymbolPrinter<'tcx> {
|
||||
fn should_print_region(&self, _region: ty::Region<'_>) -> bool {
|
||||
false
|
||||
}
|
||||
fn comma_sep<T>(mut self, mut elems: impl Iterator<Item = T>) -> Result<Self, PrintError>
|
||||
fn comma_sep<T>(&mut self, mut elems: impl Iterator<Item = T>) -> Result<(), PrintError>
|
||||
where
|
||||
T: Print<'tcx, Self>,
|
||||
{
|
||||
if let Some(first) = elems.next() {
|
||||
self = first.print(self)?;
|
||||
first.print(self)?;
|
||||
for elem in elems {
|
||||
self.write_str(",")?;
|
||||
self = elem.print(self)?;
|
||||
elem.print(self)?;
|
||||
}
|
||||
}
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generic_delimiters(
|
||||
mut self,
|
||||
f: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
) -> Result<Self, PrintError> {
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
) -> Result<(), PrintError> {
|
||||
write!(self, "<")?;
|
||||
|
||||
let kept_within_component = mem::replace(&mut self.keep_within_component, true);
|
||||
self = f(self)?;
|
||||
f(self)?;
|
||||
self.keep_within_component = kept_within_component;
|
||||
|
||||
write!(self, ">")?;
|
||||
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ pub(super) fn mangle<'tcx>(
|
|||
let args = tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), instance.args);
|
||||
|
||||
let prefix = "_R";
|
||||
let mut cx = &mut SymbolMangler {
|
||||
let mut cx: SymbolMangler<'_> = SymbolMangler {
|
||||
tcx,
|
||||
start_offset: prefix.len(),
|
||||
paths: FxHashMap::default(),
|
||||
|
|
@ -49,13 +49,13 @@ pub(super) fn mangle<'tcx>(
|
|||
_ => None,
|
||||
};
|
||||
|
||||
cx = if let Some(shim_kind) = shim_kind {
|
||||
if let Some(shim_kind) = shim_kind {
|
||||
cx.path_append_ns(|cx| cx.print_def_path(def_id, args), 'S', 0, shim_kind).unwrap()
|
||||
} else {
|
||||
cx.print_def_path(def_id, args).unwrap()
|
||||
};
|
||||
if let Some(instantiating_crate) = instantiating_crate {
|
||||
cx = cx.print_def_path(instantiating_crate.as_def_id(), &[]).unwrap();
|
||||
cx.print_def_path(instantiating_crate.as_def_id(), &[]).unwrap();
|
||||
}
|
||||
std::mem::take(&mut cx.out)
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ pub(super) fn mangle_typeid_for_trait_ref<'tcx>(
|
|||
trait_ref: ty::PolyExistentialTraitRef<'tcx>,
|
||||
) -> String {
|
||||
// FIXME(flip1995): See comment in `mangle_typeid_for_fnabi`.
|
||||
let mut cx = &mut SymbolMangler {
|
||||
let mut cx = SymbolMangler {
|
||||
tcx,
|
||||
start_offset: 0,
|
||||
paths: FxHashMap::default(),
|
||||
|
|
@ -74,7 +74,7 @@ pub(super) fn mangle_typeid_for_trait_ref<'tcx>(
|
|||
binders: vec![],
|
||||
out: String::new(),
|
||||
};
|
||||
cx = cx.print_def_path(trait_ref.def_id(), &[]).unwrap();
|
||||
cx.print_def_path(trait_ref.def_id(), &[]).unwrap();
|
||||
std::mem::take(&mut cx.out)
|
||||
}
|
||||
|
||||
|
|
@ -179,32 +179,32 @@ impl<'tcx> SymbolMangler<'tcx> {
|
|||
self.push(ident);
|
||||
}
|
||||
|
||||
fn path_append_ns<'a>(
|
||||
mut self: &'a mut Self,
|
||||
print_prefix: impl FnOnce(&'a mut Self) -> Result<&'a mut Self, PrintError>,
|
||||
fn path_append_ns(
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
ns: char,
|
||||
disambiguator: u64,
|
||||
name: &str,
|
||||
) -> Result<&'a mut Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
self.push("N");
|
||||
self.out.push(ns);
|
||||
self = print_prefix(self)?;
|
||||
print_prefix(self)?;
|
||||
self.push_disambiguator(disambiguator as u64);
|
||||
self.push_ident(name);
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_backref(&mut self, i: usize) -> Result<&mut Self, PrintError> {
|
||||
fn print_backref(&mut self, i: usize) -> Result<(), PrintError> {
|
||||
self.push("B");
|
||||
self.push_integer_62((i - self.start_offset) as u64);
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn in_binder<'a, T>(
|
||||
mut self: &'a mut Self,
|
||||
fn in_binder<T>(
|
||||
&mut self,
|
||||
value: &ty::Binder<'tcx, T>,
|
||||
print_value: impl FnOnce(&'a mut Self, &T) -> Result<&'a mut Self, PrintError>,
|
||||
) -> Result<&'a mut Self, PrintError>
|
||||
print_value: impl FnOnce(&mut Self, &T) -> Result<(), PrintError>,
|
||||
) -> Result<(), PrintError>
|
||||
where
|
||||
T: TypeVisitable<TyCtxt<'tcx>>,
|
||||
{
|
||||
|
|
@ -222,45 +222,45 @@ impl<'tcx> SymbolMangler<'tcx> {
|
|||
lifetime_depths.end += lifetimes;
|
||||
|
||||
self.binders.push(BinderLevel { lifetime_depths });
|
||||
self = print_value(self, value.as_ref().skip_binder())?;
|
||||
print_value(self, value.as_ref().skip_binder())?;
|
||||
self.binders.pop();
|
||||
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
||||
impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn print_def_path(
|
||||
mut self,
|
||||
&mut self,
|
||||
def_id: DefId,
|
||||
args: &'tcx [GenericArg<'tcx>],
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
if let Some(&i) = self.paths.get(&(def_id, args)) {
|
||||
return self.print_backref(i);
|
||||
}
|
||||
let start = self.out.len();
|
||||
|
||||
self = self.default_print_def_path(def_id, args)?;
|
||||
self.default_print_def_path(def_id, args)?;
|
||||
|
||||
// Only cache paths that do not refer to an enclosing
|
||||
// binder (which would change depending on context).
|
||||
if !args.iter().any(|k| k.has_escaping_bound_vars()) {
|
||||
self.paths.insert((def_id, args), start);
|
||||
}
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_impl_path(
|
||||
mut self,
|
||||
&mut self,
|
||||
impl_def_id: DefId,
|
||||
args: &'tcx [GenericArg<'tcx>],
|
||||
mut self_ty: Ty<'tcx>,
|
||||
mut impl_trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
let key = self.tcx.def_key(impl_def_id);
|
||||
let parent_def_id = DefId { index: key.parent.unwrap(), ..impl_def_id };
|
||||
|
||||
|
|
@ -288,7 +288,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
// Encode impl generic params if the substitutions contain parameters (implying
|
||||
// polymorphization is enabled) and this isn't an inherent impl.
|
||||
if impl_trait_ref.is_some() && args.iter().any(|a| a.has_non_region_param()) {
|
||||
self = self.path_generic_args(
|
||||
self.path_generic_args(
|
||||
|this| {
|
||||
this.path_append_ns(
|
||||
|cx| cx.print_def_path(parent_def_id, &[]),
|
||||
|
|
@ -301,19 +301,19 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
)?;
|
||||
} else {
|
||||
self.push_disambiguator(key.disambiguated_data.disambiguator as u64);
|
||||
self = self.print_def_path(parent_def_id, &[])?;
|
||||
self.print_def_path(parent_def_id, &[])?;
|
||||
}
|
||||
|
||||
self = self_ty.print(self)?;
|
||||
self_ty.print(self)?;
|
||||
|
||||
if let Some(trait_ref) = impl_trait_ref {
|
||||
self = self.print_def_path(trait_ref.def_id, trait_ref.args)?;
|
||||
self.print_def_path(trait_ref.def_id, trait_ref.args)?;
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_region(self, region: ty::Region<'_>) -> Result<Self, PrintError> {
|
||||
fn print_region(&mut self, region: ty::Region<'_>) -> Result<(), PrintError> {
|
||||
let i = match *region {
|
||||
// Erased lifetimes use the index 0, for a
|
||||
// shorter mangling of `L_`.
|
||||
|
|
@ -332,10 +332,10 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
};
|
||||
self.push("L");
|
||||
self.push_integer_62(i as u64);
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self, PrintError> {
|
||||
fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> {
|
||||
// Basic types, never cached (single-character).
|
||||
let basic_type = match ty.kind() {
|
||||
ty::Bool => "b",
|
||||
|
|
@ -365,7 +365,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
};
|
||||
if !basic_type.is_empty() {
|
||||
self.push(basic_type);
|
||||
return Ok(self);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(&i) = self.types.get(&ty) {
|
||||
|
|
@ -391,9 +391,9 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
hir::Mutability::Mut => "Q",
|
||||
});
|
||||
if !r.is_erased() {
|
||||
self = r.print(self)?;
|
||||
r.print(self)?;
|
||||
}
|
||||
self = ty.print(self)?;
|
||||
ty.print(self)?;
|
||||
}
|
||||
|
||||
ty::RawPtr(mt) => {
|
||||
|
|
@ -401,23 +401,23 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
hir::Mutability::Not => "P",
|
||||
hir::Mutability::Mut => "O",
|
||||
});
|
||||
self = mt.ty.print(self)?;
|
||||
mt.ty.print(self)?;
|
||||
}
|
||||
|
||||
ty::Array(ty, len) => {
|
||||
self.push("A");
|
||||
self = ty.print(self)?;
|
||||
self = self.print_const(len)?;
|
||||
ty.print(self)?;
|
||||
self.print_const(len)?;
|
||||
}
|
||||
ty::Slice(ty) => {
|
||||
self.push("S");
|
||||
self = ty.print(self)?;
|
||||
ty.print(self)?;
|
||||
}
|
||||
|
||||
ty::Tuple(tys) => {
|
||||
self.push("T");
|
||||
for ty in tys.iter() {
|
||||
self = ty.print(self)?;
|
||||
ty.print(self)?;
|
||||
}
|
||||
self.push("E");
|
||||
}
|
||||
|
|
@ -428,15 +428,15 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
| ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. })
|
||||
| ty::Closure(def_id, args)
|
||||
| ty::Coroutine(def_id, args, _) => {
|
||||
self = self.print_def_path(def_id, args)?;
|
||||
self.print_def_path(def_id, args)?;
|
||||
}
|
||||
ty::Foreign(def_id) => {
|
||||
self = self.print_def_path(def_id, &[])?;
|
||||
self.print_def_path(def_id, &[])?;
|
||||
}
|
||||
|
||||
ty::FnPtr(sig) => {
|
||||
self.push("F");
|
||||
self = self.in_binder(&sig, |mut cx, sig| {
|
||||
self.in_binder(&sig, |cx, sig| {
|
||||
if sig.unsafety == hir::Unsafety::Unsafe {
|
||||
cx.push("U");
|
||||
}
|
||||
|
|
@ -454,7 +454,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
}
|
||||
}
|
||||
for &ty in sig.inputs() {
|
||||
cx = ty.print(cx)?;
|
||||
ty.print(cx)?;
|
||||
}
|
||||
if sig.c_variadic {
|
||||
cx.push("v");
|
||||
|
|
@ -470,8 +470,8 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
// FIXME(dyn-star): need to update v0 mangling docs
|
||||
ty::DynStar => "D*",
|
||||
});
|
||||
self = self.print_dyn_existential(predicates)?;
|
||||
self = r.print(self)?;
|
||||
self.print_dyn_existential(predicates)?;
|
||||
r.print(self)?;
|
||||
}
|
||||
|
||||
ty::Alias(ty::Inherent, _) => bug!("symbol_names: unexpected inherent projection"),
|
||||
|
|
@ -484,13 +484,13 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
if !ty.has_escaping_bound_vars() {
|
||||
self.types.insert(ty, start);
|
||||
}
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_dyn_existential(
|
||||
mut self,
|
||||
&mut self,
|
||||
predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
// Okay, so this is a bit tricky. Imagine we have a trait object like
|
||||
// `dyn for<'a> Foo<'a, Bar = &'a ()>`. When we mangle this, the
|
||||
// output looks really close to the syntax, where the `Bar = &'a ()` bit
|
||||
|
|
@ -517,7 +517,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
// [<Trait> [{<Projection>}]] [{<Auto>}]
|
||||
// Since any predicates after the first one shouldn't change the binders,
|
||||
// just put them all in the binders of the first.
|
||||
self = self.in_binder(&predicates[0], |mut cx, _| {
|
||||
self.in_binder(&predicates[0], |cx, _| {
|
||||
for predicate in predicates.iter() {
|
||||
// It would be nice to be able to validate bound vars here, but
|
||||
// projections can actually include bound vars from super traits
|
||||
|
|
@ -528,30 +528,30 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
// Use a type that can't appear in defaults of type parameters.
|
||||
let dummy_self = Ty::new_fresh(cx.tcx, 0);
|
||||
let trait_ref = trait_ref.with_self_ty(cx.tcx, dummy_self);
|
||||
cx = cx.print_def_path(trait_ref.def_id, trait_ref.args)?;
|
||||
cx.print_def_path(trait_ref.def_id, trait_ref.args)?;
|
||||
}
|
||||
ty::ExistentialPredicate::Projection(projection) => {
|
||||
let name = cx.tcx.associated_item(projection.def_id).name;
|
||||
cx.push("p");
|
||||
cx.push_ident(name.as_str());
|
||||
cx = match projection.term.unpack() {
|
||||
match projection.term.unpack() {
|
||||
ty::TermKind::Ty(ty) => ty.print(cx),
|
||||
ty::TermKind::Const(c) => c.print(cx),
|
||||
}?;
|
||||
}
|
||||
ty::ExistentialPredicate::AutoTrait(def_id) => {
|
||||
cx = cx.print_def_path(*def_id, &[])?;
|
||||
cx.print_def_path(*def_id, &[])?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(cx)
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.push("E");
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_const(mut self, ct: ty::Const<'tcx>) -> Result<Self, PrintError> {
|
||||
fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> {
|
||||
// We only mangle a typed value if the const can be evaluated.
|
||||
let ct = ct.normalize(self.tcx, ty::ParamEnv::reveal_all());
|
||||
match ct.kind() {
|
||||
|
|
@ -570,12 +570,13 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
| ty::ConstKind::Error(_) => {
|
||||
// Never cached (single-character).
|
||||
self.push("p");
|
||||
return Ok(self);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(&i) = self.consts.get(&ct) {
|
||||
return self.print_backref(i);
|
||||
self.print_backref(i)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let start = self.out.len();
|
||||
|
|
@ -583,7 +584,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
|
||||
match ty.kind() {
|
||||
ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => {
|
||||
self = ty.print(self)?;
|
||||
ty.print(self)?;
|
||||
|
||||
let mut bits = ct.eval_bits(self.tcx, ty::ParamEnv::reveal_all());
|
||||
|
||||
|
|
@ -645,7 +646,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
.ty;
|
||||
// FIXME(const_generics): add an assert that we only do this for valtrees.
|
||||
let dereferenced_const = self.tcx.mk_ct_from_kind(ct.kind(), pointee_ty);
|
||||
self = dereferenced_const.print(self)?;
|
||||
dereferenced_const.print(self)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -654,22 +655,22 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
let contents = self.tcx.destructure_const(ct);
|
||||
let fields = contents.fields.iter().copied();
|
||||
|
||||
let print_field_list = |mut this: Self| {
|
||||
let print_field_list = |this: &mut Self| {
|
||||
for field in fields.clone() {
|
||||
this = field.print(this)?;
|
||||
field.print(this)?;
|
||||
}
|
||||
this.push("E");
|
||||
Ok(this)
|
||||
Ok(())
|
||||
};
|
||||
|
||||
match *ct.ty().kind() {
|
||||
ty::Array(..) | ty::Slice(_) => {
|
||||
self.push("A");
|
||||
self = print_field_list(self)?;
|
||||
print_field_list(self)?;
|
||||
}
|
||||
ty::Tuple(..) => {
|
||||
self.push("T");
|
||||
self = print_field_list(self)?;
|
||||
print_field_list(self)?;
|
||||
}
|
||||
ty::Adt(def, args) => {
|
||||
let variant_idx =
|
||||
|
|
@ -677,7 +678,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
let variant_def = &def.variant(variant_idx);
|
||||
|
||||
self.push("V");
|
||||
self = self.print_def_path(variant_def.def_id, args)?;
|
||||
self.print_def_path(variant_def.def_id, args)?;
|
||||
|
||||
match variant_def.ctor_kind() {
|
||||
Some(CtorKind::Const) => {
|
||||
|
|
@ -685,7 +686,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
}
|
||||
Some(CtorKind::Fn) => {
|
||||
self.push("T");
|
||||
self = print_field_list(self)?;
|
||||
print_field_list(self)?;
|
||||
}
|
||||
None => {
|
||||
self.push("S");
|
||||
|
|
@ -701,7 +702,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
);
|
||||
self.push_ident(field_name.unwrap_or(kw::Empty).as_str());
|
||||
|
||||
self = field.print(self)?;
|
||||
field.print(self)?;
|
||||
}
|
||||
self.push("E");
|
||||
}
|
||||
|
|
@ -720,47 +721,47 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
if !ct.has_escaping_bound_vars() {
|
||||
self.consts.insert(ct, start);
|
||||
}
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn path_crate(self, cnum: CrateNum) -> Result<Self, PrintError> {
|
||||
fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
|
||||
self.push("C");
|
||||
let stable_crate_id = self.tcx.def_path_hash(cnum.as_def_id()).stable_crate_id();
|
||||
self.push_disambiguator(stable_crate_id.as_u64());
|
||||
let name = self.tcx.crate_name(cnum);
|
||||
self.push_ident(name.as_str());
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn path_qualified(
|
||||
mut self,
|
||||
&mut self,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
assert!(trait_ref.is_some());
|
||||
let trait_ref = trait_ref.unwrap();
|
||||
|
||||
self.push("Y");
|
||||
self = self_ty.print(self)?;
|
||||
self_ty.print(self)?;
|
||||
self.print_def_path(trait_ref.def_id, trait_ref.args)
|
||||
}
|
||||
|
||||
fn path_append_impl(
|
||||
self,
|
||||
_: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
_: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
_: &DisambiguatedDefPathData,
|
||||
_: Ty<'tcx>,
|
||||
_: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
// Inlined into `print_impl_path`
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn path_append(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
disambiguated_data: &DisambiguatedDefPathData,
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
let ns = match disambiguated_data.data {
|
||||
// Extern block segments can be skipped, names from extern blocks
|
||||
// are effectively living in their parent modules.
|
||||
|
|
@ -797,10 +798,10 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
}
|
||||
|
||||
fn path_generic_args(
|
||||
mut self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
&mut self,
|
||||
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
|
||||
args: &[GenericArg<'tcx>],
|
||||
) -> Result<Self, PrintError> {
|
||||
) -> Result<(), PrintError> {
|
||||
// Don't print any regions if they're all erased.
|
||||
let print_regions = args.iter().any(|arg| match arg.unpack() {
|
||||
GenericArgKind::Lifetime(r) => !r.is_erased(),
|
||||
|
|
@ -816,23 +817,23 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
}
|
||||
|
||||
self.push("I");
|
||||
self = print_prefix(self)?;
|
||||
print_prefix(self)?;
|
||||
for arg in args {
|
||||
match arg.unpack() {
|
||||
GenericArgKind::Lifetime(lt) => {
|
||||
self = lt.print(self)?;
|
||||
lt.print(self)?;
|
||||
}
|
||||
GenericArgKind::Type(ty) => {
|
||||
self = ty.print(self)?;
|
||||
ty.print(self)?;
|
||||
}
|
||||
GenericArgKind::Const(c) => {
|
||||
self.push("K");
|
||||
self = c.print(self)?;
|
||||
c.print(self)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.push("E");
|
||||
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ pub fn target() -> Target {
|
|||
options: TargetOptions {
|
||||
abi: "abiv2".into(),
|
||||
features: "+2e3,+3e7,+7e10,+cache,+dsp1e2,+dspe60,+e1,+e2,+edsp,+elrw,+hard-tp,+high-registers,+hwdiv,+mp,+mp1e2,+nvic,+trust".into(),
|
||||
late_link_args_static: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-l:libatomic.a"]),
|
||||
late_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-l:libatomic.a"]),
|
||||
max_atomic_width: Some(32),
|
||||
..super::linux_gnu_base::opts()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
use crate::spec::{Cc, LinkerFlavor, Lld, Target, TargetOptions};
|
||||
|
||||
// This target is for glibc Linux on Csky
|
||||
|
||||
pub fn target() -> Target {
|
||||
Target {
|
||||
//https://github.com/llvm/llvm-project/blob/8b76aea8d8b1b71f6220bc2845abc749f18a19b7/clang/lib/Basic/Targets/CSKY.h
|
||||
llvm_target: "csky-unknown-linux-gnuabiv2".into(),
|
||||
pointer_width: 32,
|
||||
data_layout: "e-m:e-S32-p:32:32-i32:32:32-i64:32:32-f32:32:32-f64:32:32-v64:32:32-v128:32:32-a:0:32-Fi32-n32".into(),
|
||||
arch: "csky".into(),
|
||||
options: TargetOptions {
|
||||
abi: "abiv2hf".into(),
|
||||
cpu: "ck860fv".into(),
|
||||
features: "+hard-float,+hard-float-abi,+2e3,+3e7,+7e10,+cache,+dsp1e2,+dspe60,+e1,+e2,+edsp,+elrw,+hard-tp,+high-registers,+hwdiv,+mp,+mp1e2,+nvic,+trust".into(),
|
||||
late_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-l:libatomic.a", "-mhard-float"]),
|
||||
max_atomic_width: Some(32),
|
||||
..super::linux_gnu_base::opts()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -1344,6 +1344,7 @@ supported_targets! {
|
|||
("loongarch64-unknown-linux-gnu", loongarch64_unknown_linux_gnu),
|
||||
("m68k-unknown-linux-gnu", m68k_unknown_linux_gnu),
|
||||
("csky-unknown-linux-gnuabiv2", csky_unknown_linux_gnuabiv2),
|
||||
("csky-unknown-linux-gnuabiv2hf", csky_unknown_linux_gnuabiv2hf),
|
||||
("mips-unknown-linux-gnu", mips_unknown_linux_gnu),
|
||||
("mips64-unknown-linux-gnuabi64", mips64_unknown_linux_gnuabi64),
|
||||
("mips64el-unknown-linux-gnuabi64", mips64el_unknown_linux_gnuabi64),
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ use std::ops::ControlFlow;
|
|||
|
||||
pub use self::infer_ctxt_ext::*;
|
||||
pub use self::type_err_ctxt_ext::*;
|
||||
pub use rustc_infer::traits::error_reporting::*;
|
||||
|
||||
// When outputting impl candidates, prefer showing those that are more similar.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -246,14 +246,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
if pred_str.len() > 50 {
|
||||
// We don't need to save the type to a file, we will be talking about this type already
|
||||
// in a separate note when we explain the obligation, so it will be available that way.
|
||||
pred_str = predicate
|
||||
.print(FmtPrinter::new_with_limit(
|
||||
self.tcx,
|
||||
Namespace::TypeNS,
|
||||
rustc_session::Limit(6),
|
||||
))
|
||||
.unwrap()
|
||||
.into_buffer();
|
||||
let mut cx: FmtPrinter<'_, '_> =
|
||||
FmtPrinter::new_with_limit(self.tcx, Namespace::TypeNS, rustc_session::Limit(6));
|
||||
predicate.print(&mut cx).unwrap();
|
||||
pred_str = cx.into_buffer();
|
||||
}
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
|
|
@ -1408,17 +1404,15 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term)
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
with_forced_trimmed_paths!(format!(
|
||||
"type mismatch resolving `{}`",
|
||||
self.resolve_vars_if_possible(predicate)
|
||||
.print(FmtPrinter::new_with_limit(
|
||||
self.tcx,
|
||||
Namespace::TypeNS,
|
||||
rustc_session::Limit(10),
|
||||
))
|
||||
.unwrap()
|
||||
.into_buffer()
|
||||
))
|
||||
let mut cx = FmtPrinter::new_with_limit(
|
||||
self.tcx,
|
||||
Namespace::TypeNS,
|
||||
rustc_session::Limit(10),
|
||||
);
|
||||
with_forced_trimmed_paths!(format!("type mismatch resolving `{}`", {
|
||||
self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap();
|
||||
cx.into_buffer()
|
||||
}))
|
||||
});
|
||||
let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}");
|
||||
|
||||
|
|
@ -1463,14 +1457,15 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
ty.span,
|
||||
with_forced_trimmed_paths!(Cow::from(format!(
|
||||
"type mismatch resolving `{}`",
|
||||
self.resolve_vars_if_possible(predicate)
|
||||
.print(FmtPrinter::new_with_limit(
|
||||
{
|
||||
let mut cx = FmtPrinter::new_with_limit(
|
||||
self.tcx,
|
||||
Namespace::TypeNS,
|
||||
rustc_session::Limit(5),
|
||||
))
|
||||
.unwrap()
|
||||
.into_buffer()
|
||||
);
|
||||
self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap();
|
||||
cx.into_buffer()
|
||||
}
|
||||
))),
|
||||
)),
|
||||
_ => None,
|
||||
|
|
|
|||
|
|
@ -41,11 +41,6 @@ use std::ops::ControlFlow;
|
|||
|
||||
pub(crate) use self::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
|
||||
|
||||
pub use self::FulfillmentErrorCode::*;
|
||||
pub use self::ImplSource::*;
|
||||
pub use self::ObligationCauseCode::*;
|
||||
pub use self::SelectionError::*;
|
||||
|
||||
pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls};
|
||||
pub use self::coherence::{OrphanCheckErr, OverlapResult};
|
||||
pub use self::engine::{ObligationCtxt, TraitEngineExt};
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitable
|
|||
use rustc_span::Span;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
pub use rustc_infer::traits::{self, util::*};
|
||||
pub use rustc_infer::traits::util::*;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// `TraitAliasExpander` iterator
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
#
|
||||
# If `change-id` does not match the version that is currently running,
|
||||
# `x.py` will prompt you to update it and check the related PR for more details.
|
||||
change-id = 115898
|
||||
change-id = 116998
|
||||
|
||||
# =============================================================================
|
||||
# Tweaking how LLVM is compiled
|
||||
|
|
@ -377,6 +377,9 @@ change-id = 115898
|
|||
# this is not intended to be used during local development.
|
||||
#metrics = false
|
||||
|
||||
# Specify the location of the Android NDK. Used when targeting Android.
|
||||
#android-ndk = "/path/to/android-ndk-r25b"
|
||||
|
||||
# =============================================================================
|
||||
# General install configuration options
|
||||
# =============================================================================
|
||||
|
|
@ -756,12 +759,6 @@ change-id = 115898
|
|||
# it must link to `libgcc_eh.a` to get a working output, and this option have no effect.
|
||||
#llvm-libunwind = 'no' if Linux, 'in-tree' if Fuchsia
|
||||
|
||||
# If this target is for Android, this option will be required to specify where
|
||||
# the NDK for the target lives. This is used to find the C compiler to link and
|
||||
# build native code.
|
||||
# See `src/bootstrap/cc_detect.rs` for details.
|
||||
#android-ndk = <none> (path)
|
||||
|
||||
# Build the sanitizer runtimes for this target.
|
||||
# This option will override the same option under [build] section.
|
||||
#sanitizers = build.sanitizers (bool)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#![doc = include_str!("../../stdarch/crates/core_arch/src/core_arch_docs.md")]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[stable(feature = "simd_arch", since = "1.27.0")]
|
||||
pub use crate::core_arch::arch::*;
|
||||
|
||||
|
|
|
|||
|
|
@ -120,8 +120,6 @@
|
|||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![deny(fuzzy_provenance_casts)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
mod alloc;
|
||||
mod any;
|
||||
mod array;
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ use core::num::flt2dec::{
|
|||
};
|
||||
use core::num::fmt::{Formatted, Part};
|
||||
|
||||
pub use test::Bencher;
|
||||
|
||||
mod estimator;
|
||||
mod strategy {
|
||||
mod dragon;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,5 @@ pub mod simd {
|
|||
pub use crate::core_simd::masks::*;
|
||||
pub use crate::core_simd::ord::*;
|
||||
pub use crate::core_simd::swizzle::*;
|
||||
pub use crate::core_simd::swizzle_dyn::*;
|
||||
pub use crate::core_simd::vector::*;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
pub mod alloc;
|
||||
pub mod small_c_string;
|
||||
#[allow(unused_imports)]
|
||||
pub mod thread_local;
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -241,6 +241,7 @@ pub unsafe fn cleanup() {
|
|||
|
||||
#[cfg(target_os = "android")]
|
||||
pub use crate::sys::android::signal;
|
||||
#[allow(unused_imports)]
|
||||
#[cfg(not(target_os = "android"))]
|
||||
pub use libc::signal;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes};
|
||||
pub use self::process_inner::{ExitStatus, ExitStatusError, Process};
|
||||
pub use crate::ffi::OsString as EnvKey;
|
||||
pub use crate::sys_common::process::CommandEnvs;
|
||||
|
||||
#[cfg_attr(any(target_os = "espidf", target_os = "horizon"), allow(unused))]
|
||||
mod process_common;
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ cfg_if::cfg_if! {
|
|||
return 0;
|
||||
}
|
||||
} else {
|
||||
#[allow(unused_imports)]
|
||||
pub use libc::{sigemptyset, sigaddset};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,18 +62,15 @@ mod imp {
|
|||
unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) }
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "espidf", target_os = "horizon"))]
|
||||
#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "freebsd"))]
|
||||
fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
|
||||
unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }
|
||||
}
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
|
||||
// FIXME: using the above when libary std's libc is updated
|
||||
#[cfg(not(target_os = "freebsd"))]
|
||||
use libc::getrandom;
|
||||
#[cfg(target_os = "freebsd")]
|
||||
extern "C" {
|
||||
fn getrandom(
|
||||
buffer: *mut libc::c_void,
|
||||
length: libc::size_t,
|
||||
buf: *mut libc::c_void,
|
||||
buflen: libc::size_t,
|
||||
flags: libc::c_uint,
|
||||
) -> libc::ssize_t;
|
||||
}
|
||||
|
|
@ -236,6 +233,7 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: once the 10.x release becomes the minimum, this can be dropped for simplification.
|
||||
#[cfg(target_os = "netbsd")]
|
||||
mod imp {
|
||||
use crate::ptr;
|
||||
|
|
|
|||
|
|
@ -1066,6 +1066,14 @@ impl DirBuilder {
|
|||
}
|
||||
|
||||
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
|
||||
// We push a `*` to the end of the path which cause the empty path to be
|
||||
// treated as the current directory. So, for consistency with other platforms,
|
||||
// we explicitly error on the empty path.
|
||||
if p.as_os_str().is_empty() {
|
||||
// Return an error code consistent with other ways of opening files.
|
||||
// E.g. fs::metadata or File::open.
|
||||
return Err(io::Error::from_raw_os_error(c::ERROR_PATH_NOT_FOUND as i32));
|
||||
}
|
||||
let root = p.to_path_buf();
|
||||
let star = p.join("*");
|
||||
let path = maybe_verbatim(&star)?;
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ class GenerateAndParseConfig(unittest.TestCase):
|
|||
"""Test that we can serialize and deserialize a config.toml file"""
|
||||
def test_no_args(self):
|
||||
build = serialize_and_parse([])
|
||||
self.assertEqual(build.get_toml("change-id"), '115898')
|
||||
self.assertEqual(build.get_toml("change-id"), '116998')
|
||||
self.assertEqual(build.get_toml("profile"), 'dist')
|
||||
self.assertIsNone(build.get_toml("llvm.download-ci-llvm"))
|
||||
|
||||
|
|
|
|||
|
|
@ -97,20 +97,7 @@ v("llvm-root", None, "set LLVM root")
|
|||
v("llvm-config", None, "set path to llvm-config")
|
||||
v("llvm-filecheck", None, "set path to LLVM's FileCheck utility")
|
||||
v("python", "build.python", "set path to python")
|
||||
v("android-cross-path", "target.arm-linux-androideabi.android-ndk",
|
||||
"Android NDK standalone path (deprecated)")
|
||||
v("i686-linux-android-ndk", "target.i686-linux-android.android-ndk",
|
||||
"i686-linux-android NDK standalone path")
|
||||
v("arm-linux-androideabi-ndk", "target.arm-linux-androideabi.android-ndk",
|
||||
"arm-linux-androideabi NDK standalone path")
|
||||
v("armv7-linux-androideabi-ndk", "target.armv7-linux-androideabi.android-ndk",
|
||||
"armv7-linux-androideabi NDK standalone path")
|
||||
v("thumbv7neon-linux-androideabi-ndk", "target.thumbv7neon-linux-androideabi.android-ndk",
|
||||
"thumbv7neon-linux-androideabi NDK standalone path")
|
||||
v("aarch64-linux-android-ndk", "target.aarch64-linux-android.android-ndk",
|
||||
"aarch64-linux-android NDK standalone path")
|
||||
v("x86_64-linux-android-ndk", "target.x86_64-linux-android.android-ndk",
|
||||
"x86_64-linux-android NDK standalone path")
|
||||
v("android-ndk", "build.android-ndk", "set path to Android NDK")
|
||||
v("musl-root", "target.x86_64-unknown-linux-musl.musl-root",
|
||||
"MUSL root installation directory (deprecated)")
|
||||
v("musl-root-x86_64", "target.x86_64-unknown-linux-musl.musl-root",
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ use std::str::FromStr;
|
|||
use crate::core::build_steps::compile::CODEGEN_BACKEND_PREFIX;
|
||||
use crate::core::config::flags::{Color, Flags, Warnings};
|
||||
use crate::utils::cache::{Interned, INTERNER};
|
||||
use crate::utils::cc_detect::{ndk_compiler, Language};
|
||||
use crate::utils::channel::{self, GitInfo};
|
||||
use crate::utils::helpers::{exe, output, t};
|
||||
use build_helper::exit;
|
||||
|
|
@ -142,6 +141,7 @@ pub struct Config {
|
|||
pub color: Color,
|
||||
pub patch_binaries_for_nix: Option<bool>,
|
||||
pub stage0_metadata: Stage0Metadata,
|
||||
pub android_ndk: Option<PathBuf>,
|
||||
|
||||
pub stdout_is_tty: bool,
|
||||
pub stderr_is_tty: bool,
|
||||
|
|
@ -521,7 +521,6 @@ pub struct Target {
|
|||
pub ranlib: Option<PathBuf>,
|
||||
pub default_linker: Option<PathBuf>,
|
||||
pub linker: Option<PathBuf>,
|
||||
pub ndk: Option<PathBuf>,
|
||||
pub sanitizers: Option<bool>,
|
||||
pub profiler: Option<StringOrBool>,
|
||||
pub rpath: Option<bool>,
|
||||
|
|
@ -799,6 +798,7 @@ define_config! {
|
|||
patch_binaries_for_nix: Option<bool> = "patch-binaries-for-nix",
|
||||
// NOTE: only parsed by bootstrap.py, `--feature build-metrics` enables metrics unconditionally
|
||||
metrics: Option<bool> = "metrics",
|
||||
android_ndk: Option<PathBuf> = "android-ndk",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1039,7 +1039,6 @@ define_config! {
|
|||
llvm_has_rust_patches: Option<bool> = "llvm-has-rust-patches",
|
||||
llvm_filecheck: Option<String> = "llvm-filecheck",
|
||||
llvm_libunwind: Option<String> = "llvm-libunwind",
|
||||
android_ndk: Option<String> = "android-ndk",
|
||||
sanitizers: Option<bool> = "sanitizers",
|
||||
profiler: Option<StringOrBool> = "profiler",
|
||||
rpath: Option<bool> = "rpath",
|
||||
|
|
@ -1320,6 +1319,7 @@ impl Config {
|
|||
config.python = build.python.map(PathBuf::from);
|
||||
config.reuse = build.reuse.map(PathBuf::from);
|
||||
config.submodules = build.submodules;
|
||||
config.android_ndk = build.android_ndk;
|
||||
set(&mut config.low_priority, build.low_priority);
|
||||
set(&mut config.compiler_docs, build.compiler_docs);
|
||||
set(&mut config.library_docs_private_items, build.library_docs_private_items);
|
||||
|
|
@ -1600,18 +1600,11 @@ impl Config {
|
|||
.llvm_libunwind
|
||||
.as_ref()
|
||||
.map(|v| v.parse().expect("failed to parse rust.llvm-libunwind"));
|
||||
if let Some(ref s) = cfg.android_ndk {
|
||||
target.ndk = Some(config.src.join(s));
|
||||
}
|
||||
if let Some(s) = cfg.no_std {
|
||||
target.no_std = s;
|
||||
}
|
||||
target.cc = cfg.cc.map(PathBuf::from).or_else(|| {
|
||||
target.ndk.as_ref().map(|ndk| ndk_compiler(Language::C, &triple, ndk))
|
||||
});
|
||||
target.cxx = cfg.cxx.map(PathBuf::from).or_else(|| {
|
||||
target.ndk.as_ref().map(|ndk| ndk_compiler(Language::CPlusPlus, &triple, ndk))
|
||||
});
|
||||
target.cc = cfg.cc.map(PathBuf::from);
|
||||
target.cxx = cfg.cxx.map(PathBuf::from);
|
||||
target.ar = cfg.ar.map(PathBuf::from);
|
||||
target.ranlib = cfg.ranlib.map(PathBuf::from);
|
||||
target.linker = cfg.linker.map(PathBuf::from);
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"];
|
|||
///
|
||||
/// If you make any major changes (such as adding new values or changing default values), please
|
||||
/// ensure that the associated PR ID is added to the end of this list.
|
||||
pub const CONFIG_CHANGE_HISTORY: &[usize] = &[115898];
|
||||
pub const CONFIG_CHANGE_HISTORY: &[usize] = &[115898, 116998];
|
||||
|
||||
/// Extra --check-cfg to add when building
|
||||
/// (Mode restriction, config name, config values (if any))
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ use std::path::{Path, PathBuf};
|
|||
use std::process::Command;
|
||||
use std::{env, iter};
|
||||
|
||||
use crate::core::config::{Target, TargetSelection};
|
||||
use crate::core::config::TargetSelection;
|
||||
use crate::utils::helpers::output;
|
||||
use crate::{Build, CLang, GitRepo};
|
||||
|
||||
|
|
@ -107,10 +107,11 @@ pub fn find(build: &Build) {
|
|||
pub fn find_target(build: &Build, target: TargetSelection) {
|
||||
let mut cfg = new_cc_build(build, target);
|
||||
let config = build.config.target_config.get(&target);
|
||||
if let Some(cc) = config.and_then(|c| c.cc.as_ref()) {
|
||||
if let Some(cc) = config
|
||||
.and_then(|c| c.cc.clone())
|
||||
.or_else(|| default_compiler(&mut cfg, Language::C, target, build))
|
||||
{
|
||||
cfg.compiler(cc);
|
||||
} else {
|
||||
set_compiler(&mut cfg, Language::C, target, config, build);
|
||||
}
|
||||
|
||||
let compiler = cfg.get_compiler();
|
||||
|
|
@ -127,12 +128,12 @@ pub fn find_target(build: &Build, target: TargetSelection) {
|
|||
// We'll need one anyways if the target triple is also a host triple
|
||||
let mut cfg = new_cc_build(build, target);
|
||||
cfg.cpp(true);
|
||||
let cxx_configured = if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
|
||||
let cxx_configured = if let Some(cxx) = config
|
||||
.and_then(|c| c.cxx.clone())
|
||||
.or_else(|| default_compiler(&mut cfg, Language::CPlusPlus, target, build))
|
||||
{
|
||||
cfg.compiler(cxx);
|
||||
true
|
||||
} else if build.hosts.contains(&target) || build.build == target {
|
||||
set_compiler(&mut cfg, Language::CPlusPlus, target, config, build);
|
||||
true
|
||||
} else {
|
||||
// Use an auto-detected compiler (or one configured via `CXX_target_triple` env vars).
|
||||
cfg.try_get_compiler().is_ok()
|
||||
|
|
@ -161,22 +162,21 @@ pub fn find_target(build: &Build, target: TargetSelection) {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_compiler(
|
||||
fn default_compiler(
|
||||
cfg: &mut cc::Build,
|
||||
compiler: Language,
|
||||
target: TargetSelection,
|
||||
config: Option<&Target>,
|
||||
build: &Build,
|
||||
) {
|
||||
) -> Option<PathBuf> {
|
||||
match &*target.triple {
|
||||
// When compiling for android we may have the NDK configured in the
|
||||
// config.toml in which case we look there. Otherwise the default
|
||||
// compiler already takes into account the triple in question.
|
||||
t if t.contains("android") => {
|
||||
if let Some(ndk) = config.and_then(|c| c.ndk.as_ref()) {
|
||||
cfg.compiler(ndk_compiler(compiler, &*target.triple, ndk));
|
||||
}
|
||||
}
|
||||
t if t.contains("android") => build
|
||||
.config
|
||||
.android_ndk
|
||||
.as_ref()
|
||||
.map(|ndk| ndk_compiler(compiler, &*target.triple, ndk)),
|
||||
|
||||
// The default gcc version from OpenBSD may be too old, try using egcc,
|
||||
// which is a gcc version from ports, if this is the case.
|
||||
|
|
@ -184,45 +184,48 @@ fn set_compiler(
|
|||
let c = cfg.get_compiler();
|
||||
let gnu_compiler = compiler.gcc();
|
||||
if !c.path().ends_with(gnu_compiler) {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
let output = output(c.to_command().arg("--version"));
|
||||
let i = match output.find(" 4.") {
|
||||
Some(i) => i,
|
||||
None => return,
|
||||
};
|
||||
let i = output.find(" 4.")?;
|
||||
match output[i + 3..].chars().next().unwrap() {
|
||||
'0'..='6' => {}
|
||||
_ => return,
|
||||
_ => return None,
|
||||
}
|
||||
let alternative = format!("e{gnu_compiler}");
|
||||
if Command::new(&alternative).output().is_ok() {
|
||||
cfg.compiler(alternative);
|
||||
Some(PathBuf::from(alternative))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
"mips-unknown-linux-musl" => {
|
||||
"mips-unknown-linux-musl" if compiler == Language::C => {
|
||||
if cfg.get_compiler().path().to_str() == Some("gcc") {
|
||||
cfg.compiler("mips-linux-musl-gcc");
|
||||
Some(PathBuf::from("mips-linux-musl-gcc"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
"mipsel-unknown-linux-musl" => {
|
||||
"mipsel-unknown-linux-musl" if compiler == Language::C => {
|
||||
if cfg.get_compiler().path().to_str() == Some("gcc") {
|
||||
cfg.compiler("mipsel-linux-musl-gcc");
|
||||
Some(PathBuf::from("mipsel-linux-musl-gcc"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
t if t.contains("musl") => {
|
||||
t if t.contains("musl") && compiler == Language::C => {
|
||||
if let Some(root) = build.musl_root(target) {
|
||||
let guess = root.join("bin/musl-gcc");
|
||||
if guess.exists() {
|
||||
cfg.compiler(guess);
|
||||
}
|
||||
if guess.exists() { Some(guess) } else { None }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -243,10 +246,22 @@ pub(crate) fn ndk_compiler(compiler: Language, triple: &str, ndk: &Path) -> Path
|
|||
let api_level =
|
||||
if triple.contains("aarch64") || triple.contains("x86_64") { "21" } else { "19" };
|
||||
let compiler = format!("{}{}-{}", triple_translated, api_level, compiler.clang());
|
||||
ndk.join("bin").join(compiler)
|
||||
let host_tag = if cfg!(target_os = "macos") {
|
||||
// The NDK uses universal binaries, so this is correct even on ARM.
|
||||
"darwin-x86_64"
|
||||
} else if cfg!(target_os = "windows") {
|
||||
"windows-x86_64"
|
||||
} else {
|
||||
// NDK r25b only has official releases for macOS, Windows and Linux.
|
||||
// Try the Linux directory everywhere else, on the assumption that the OS has an
|
||||
// emulation layer that can cope (e.g. BSDs).
|
||||
"linux-x86_64"
|
||||
};
|
||||
ndk.join("toolchains").join("llvm").join("prebuilt").join(host_tag).join("bin").join(compiler)
|
||||
}
|
||||
|
||||
/// The target programming language for a native compiler.
|
||||
#[derive(PartialEq)]
|
||||
pub(crate) enum Language {
|
||||
/// The compiler is targeting C.
|
||||
C,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ENV PATH=$PATH:/android/sdk/platform-tools
|
|||
|
||||
ENV TARGETS=arm-linux-androideabi
|
||||
|
||||
ENV RUST_CONFIGURE_ARGS --arm-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/
|
||||
ENV RUST_CONFIGURE_ARGS --android-ndk=/android/ndk/
|
||||
|
||||
ENV SCRIPT python3 ../x.py --stage 2 test --host='' --target $TARGETS
|
||||
|
||||
|
|
|
|||
|
|
@ -19,12 +19,7 @@ ENV TARGETS=$TARGETS,x86_64-linux-android
|
|||
ENV RUST_CONFIGURE_ARGS \
|
||||
--enable-extended \
|
||||
--enable-profiler \
|
||||
--arm-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
|
||||
--armv7-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
|
||||
--thumbv7neon-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
|
||||
--i686-linux-android-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
|
||||
--aarch64-linux-android-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
|
||||
--x86_64-linux-android-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
|
||||
--android-ndk=/android/ndk/ \
|
||||
--disable-docs
|
||||
|
||||
ENV SCRIPT python3 ../x.py dist --host='' --target $TARGETS
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
- [\*-esp-espidf](platform-support/esp-idf.md)
|
||||
- [\*-unknown-fuchsia](platform-support/fuchsia.md)
|
||||
- [\*-kmc-solid_\*](platform-support/kmc-solid.md)
|
||||
- [csky-unknown-linux-gnuabiv2](platform-support/csky-unknown-linux-gnuabiv2.md)
|
||||
- [csky-unknown-linux-gnuabiv2\*](platform-support/csky-unknown-linux-gnuabiv2.md)
|
||||
- [loongarch\*-unknown-linux-\*](platform-support/loongarch-linux.md)
|
||||
- [loongarch\*-unknown-none\*](platform-support/loongarch-none.md)
|
||||
- [m68k-unknown-linux-gnu](platform-support/m68k-unknown-linux-gnu.md)
|
||||
|
|
|
|||
|
|
@ -259,7 +259,8 @@ target | std | host | notes
|
|||
`avr-unknown-gnu-atmega328` | * | | AVR. Requires `-Z build-std=core`
|
||||
`bpfeb-unknown-none` | * | | BPF (big endian)
|
||||
`bpfel-unknown-none` | * | | BPF (little endian)
|
||||
`csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux(little endian)
|
||||
`csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux (little endian)
|
||||
`csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian)
|
||||
`hexagon-unknown-linux-musl` | ? | |
|
||||
`i386-apple-ios` | ✓ | | 32-bit x86 iOS [^x86_32-floats-return-ABI]
|
||||
[`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS [^x86_32-floats-return-ABI]
|
||||
|
|
|
|||
|
|
@ -4,7 +4,14 @@
|
|||
|
||||
This target supports [C-SKY](https://github.com/c-sky) CPUs with `abi` v2 and `glibc`.
|
||||
|
||||
target | std | host | notes
|
||||
-------|:---:|:----:|-------
|
||||
`csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux (little endian)
|
||||
`csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian)
|
||||
|
||||
Reference:
|
||||
https://c-sky.github.io/
|
||||
|
||||
https://gitlab.com/c-sky/
|
||||
|
||||
## Target maintainers
|
||||
|
|
@ -13,7 +20,6 @@ https://gitlab.com/c-sky/
|
|||
|
||||
## Requirements
|
||||
|
||||
|
||||
## Building the target
|
||||
|
||||
### Get a C toolchain
|
||||
|
|
@ -28,13 +34,17 @@ The target can be built by enabling it for a `rustc` build, by placing the follo
|
|||
|
||||
```toml
|
||||
[build]
|
||||
target = ["x86_64-unknown-linux-gnu", "csky-unknown-linux-gnuabiv2"]
|
||||
target = ["x86_64-unknown-linux-gnu", "csky-unknown-linux-gnuabiv2", "csky-unknown-linux-gnuabiv2hf"]
|
||||
stage = 2
|
||||
|
||||
[target.csky-unknown-linux-gnuabiv2]
|
||||
# ADJUST THIS PATH TO POINT AT YOUR TOOLCHAIN
|
||||
cc = "${TOOLCHAIN_PATH}/bin/csky-linux-gnuabiv2-gcc"
|
||||
|
||||
[target.csky-unknown-linux-gnuabiv2hf]
|
||||
# ADJUST THIS PATH TO POINT AT YOUR TOOLCHAIN
|
||||
cc = "${TOOLCHAIN_PATH}/bin/csky-linux-gnuabiv2-gcc"
|
||||
|
||||
### Build
|
||||
|
||||
```sh
|
||||
|
|
|
|||
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