Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2025-07-17 05:06:45 +00:00
commit 52db0ce24f
226 changed files with 3053 additions and 3111 deletions

View file

@ -182,6 +182,11 @@ jobs:
- name: install MinGW
run: src/ci/scripts/install-mingw.sh
# Workaround for spurious ci failures after mingw install
# see https://rust-lang.zulipchat.com/#narrow/channel/242791-t-infra/topic/Spurious.20bors.20CI.20failures/near/528915775
- name: ensure home dir exists
run: mkdir -p ~
- name: install ninja
run: src/ci/scripts/install-ninja.sh

View file

@ -5069,14 +5069,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "suggest-tests"
version = "0.1.0"
dependencies = [
"build_helper",
"glob",
]
[[package]]
name = "syn"
version = "1.0.109"

View file

@ -39,7 +39,6 @@ members = [
"src/tools/rustdoc-gui-test",
"src/tools/rustdoc-themes",
"src/tools/rustfmt",
"src/tools/suggest-tests",
"src/tools/test-float-parse",
"src/tools/tidy",
"src/tools/tier-check",

View file

@ -1,24 +1,7 @@
//! Definitions shared by macros / syntax extensions and e.g. `rustc_middle`.
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
use rustc_span::Ident;
use rustc_span::def_id::DefId;
use crate::MetaItem;
pub mod allocator;
pub mod autodiff_attrs;
pub mod typetree;
#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)]
pub struct StrippedCfgItem<ModId = DefId> {
pub parent_module: ModId,
pub ident: Ident,
pub cfg: MetaItem,
}
impl<ModId> StrippedCfgItem<ModId> {
pub fn map_mod_id<New>(self, f: impl FnOnce(ModId) -> New) -> StrippedCfgItem<New> {
StrippedCfgItem { parent_module: f(self.parent_module), ident: self.ident, cfg: self.cfg }
}
}

View file

@ -42,7 +42,7 @@ use std::sync::Arc;
use rustc_ast::node_id::NodeMap;
use rustc_ast::{self as ast, *};
use rustc_attr_parsing::{AttributeParser, OmitDoc};
use rustc_attr_parsing::{AttributeParser, Late, OmitDoc};
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
@ -192,7 +192,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// interact with `gen`/`async gen` blocks
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
attribute_parser: AttributeParser::new(tcx.sess, tcx.features(), registered_tools),
attribute_parser: AttributeParser::new(
tcx.sess,
tcx.features(),
registered_tools,
Late,
),
delayed_lints: Vec::new(),
}
}

View file

@ -3,7 +3,7 @@ use rustc_ast::token::CommentKind;
use rustc_ast::{self as ast, AttrStyle};
use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
use rustc_span::hygiene::Transparency;
use rustc_span::{Span, Symbol};
use rustc_span::{Ident, Span, Symbol};
use thin_vec::ThinVec;
use crate::{DefaultBodyStability, PartialConstStability, PrintAttribute, RustcVersion, Stability};
@ -69,6 +69,7 @@ pub enum ReprAttr {
ReprAlign(Align),
}
pub use ReprAttr::*;
use rustc_span::def_id::DefId;
pub enum TransparencyError {
UnknownTransparency(Symbol, Span),
@ -140,6 +141,30 @@ pub enum UsedBy {
Linker,
}
#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)]
pub struct StrippedCfgItem<ModId = DefId> {
pub parent_module: ModId,
pub ident: Ident,
pub cfg: (CfgEntry, Span),
}
impl<ModId> StrippedCfgItem<ModId> {
pub fn map_mod_id<New>(self, f: impl FnOnce(ModId) -> New) -> StrippedCfgItem<New> {
StrippedCfgItem { parent_module: f(self.parent_module), ident: self.ident, cfg: self.cfg }
}
}
#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(HashStable_Generic, PrintAttribute)]
pub enum CfgEntry {
All(ThinVec<CfgEntry>, Span),
Any(ThinVec<CfgEntry>, Span),
Not(Box<CfgEntry>, Span),
Bool(bool, Span),
NameValue { name: Symbol, name_span: Span, value: Option<(Symbol, Span)>, span: Span },
Version(Option<RustcVersion>, Span),
}
/// Represents parsed *built-in* inert attributes.
///
/// ## Overview
@ -349,6 +374,9 @@ pub enum AttributeKind {
/// Represents `#[path]`
Path(Symbol, Span),
/// Represents `#[pointee]`
Pointee(Span),
/// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
PubTransparent(Span),

View file

@ -57,6 +57,7 @@ impl AttributeKind {
ParenSugar(..) => No,
PassByValue(..) => Yes,
Path(..) => No,
Pointee(..) => No,
PubTransparent(..) => Yes,
Repr { .. } => No,
RustcLayoutScalarValidRangeEnd(..) => Yes,

View file

@ -1,247 +1,291 @@
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
use rustc_ast_pretty::pprust;
use rustc_attr_data_structures::RustcVersion;
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
use rustc_ast::{LitKind, NodeId};
use rustc_attr_data_structures::{CfgEntry, RustcVersion};
use rustc_feature::{AttributeTemplate, Features, template};
use rustc_session::Session;
use rustc_session::config::ExpectedValues;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
use rustc_session::lint::{BuiltinLintDiag, Lint};
use rustc_session::parse::feature_err;
use rustc_span::{Span, Symbol, sym};
use thin_vec::ThinVec;
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
use crate::{fluent_generated, parse_version};
use crate::context::{AcceptContext, Stage};
use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser};
use crate::{
CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics, try_gate_cfg,
};
/// Emitter of a builtin lint from `cfg_matches`.
///
/// Used to support emitting a lint (currently on check-cfg), either:
/// - as an early buffered lint (in `rustc`)
/// - or has a "normal" lint from HIR (in `rustdoc`)
pub trait CfgMatchesLintEmitter {
fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag);
pub const CFG_TEMPLATE: AttributeTemplate = template!(List: "predicate");
pub fn parse_cfg_attr<'c, S: Stage>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> Option<CfgEntry> {
let ArgParser::List(list) = args else {
cx.expected_list(cx.attr_span);
return None;
};
let Some(single) = list.single() else {
cx.expected_single_argument(list.span);
return None;
};
parse_cfg_entry(cx, single)
}
impl CfgMatchesLintEmitter for NodeId {
fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag) {
sess.psess.buffer_lint(lint, sp, *self, diag);
}
}
#[derive(Clone, Debug)]
pub struct Condition {
pub name: Symbol,
pub name_span: Span,
pub value: Option<Symbol>,
pub value_span: Option<Span>,
pub span: Span,
}
/// Tests if a cfg-pattern matches the cfg set
pub fn cfg_matches(
cfg: &MetaItemInner,
sess: &Session,
lint_emitter: impl CfgMatchesLintEmitter,
features: Option<&Features>,
) -> bool {
eval_condition(cfg, sess, features, &mut |cfg| {
try_gate_cfg(cfg.name, cfg.span, sess, features);
match sess.psess.check_config.expecteds.get(&cfg.name) {
Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
lint_emitter.emit_span_lint(
sess,
UNEXPECTED_CFGS,
cfg.span,
BuiltinLintDiag::UnexpectedCfgValue(
(cfg.name, cfg.name_span),
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
),
);
fn parse_cfg_entry<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
item: &MetaItemOrLitParser<'_>,
) -> Option<CfgEntry> {
Some(match item {
MetaItemOrLitParser::MetaItemParser(meta) => match meta.args() {
ArgParser::List(list) => match meta.path().word_sym() {
Some(sym::not) => {
let Some(single) = list.single() else {
cx.expected_single_argument(list.span);
return None;
};
CfgEntry::Not(Box::new(parse_cfg_entry(cx, single)?), list.span)
}
Some(sym::any) => CfgEntry::Any(
list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(),
list.span,
),
Some(sym::all) => CfgEntry::All(
list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(),
list.span,
),
Some(sym::target) => parse_cfg_entry_target(cx, list, meta.span())?,
Some(sym::version) => parse_cfg_entry_version(cx, list, meta.span())?,
_ => {
cx.emit_err(session_diagnostics::InvalidPredicate {
span: meta.span(),
predicate: meta.path().to_string(),
});
return None;
}
},
a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => {
let Some(name) = meta.path().word_sym() else {
cx.emit_err(session_diagnostics::CfgPredicateIdentifier {
span: meta.path().span(),
});
return None;
};
parse_name_value(name, meta.path().span(), a.name_value(), meta.span(), cx)?
}
None if sess.psess.check_config.exhaustive_names => {
lint_emitter.emit_span_lint(
sess,
UNEXPECTED_CFGS,
cfg.span,
BuiltinLintDiag::UnexpectedCfgName(
(cfg.name, cfg.name_span),
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
),
);
},
MetaItemOrLitParser::Lit(lit) => match lit.kind {
LitKind::Bool(b) => CfgEntry::Bool(b, lit.span),
_ => {
cx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: lit.span });
return None;
}
_ => { /* not unexpected */ }
}
sess.psess.config.contains(&(cfg.name, cfg.value))
},
MetaItemOrLitParser::Err(_, _) => return None,
})
}
fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) {
let gate = find_gated_cfg(|sym| sym == name);
if let (Some(feats), Some(gated_cfg)) = (features, gate) {
gate_cfg(gated_cfg, span, sess, feats);
}
fn parse_cfg_entry_version<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
list: &MetaItemListParser<'_>,
meta_span: Span,
) -> Option<CfgEntry> {
try_gate_cfg(sym::version, meta_span, cx.sess(), Some(cx.features()));
let Some(version) = list.single() else {
cx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { span: list.span });
return None;
};
let Some(version_lit) = version.lit() else {
cx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: version.span() });
return None;
};
let Some(version_str) = version_lit.value_str() else {
cx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: version_lit.span });
return None;
};
let min_version = parse_version(version_str).or_else(|| {
cx.sess()
.dcx()
.emit_warn(session_diagnostics::UnknownVersionLiteral { span: version_lit.span });
None
});
Some(CfgEntry::Version(min_version, list.span))
}
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) {
let (cfg, feature, has_feature) = gated_cfg;
if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
let explain = format!("`cfg({cfg})` is experimental and subject to change");
feature_err(sess, *feature, cfg_span, explain).emit();
fn parse_cfg_entry_target<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
list: &MetaItemListParser<'_>,
meta_span: Span,
) -> Option<CfgEntry> {
if !cx.features().cfg_target_compact() {
feature_err(
cx.sess(),
sym::cfg_target_compact,
meta_span,
fluent_generated::attr_parsing_unstable_cfg_target_compact,
)
.emit();
}
}
/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
/// evaluate individual items.
pub fn eval_condition(
cfg: &MetaItemInner,
sess: &Session,
features: Option<&Features>,
eval: &mut impl FnMut(Condition) -> bool,
) -> bool {
let dcx = sess.dcx();
let mut result = ThinVec::new();
for sub_item in list.mixed() {
// First, validate that this is a NameValue item
let Some(sub_item) = sub_item.meta_item() else {
cx.expected_name_value(sub_item.span(), None);
continue;
};
let Some(nv) = sub_item.args().name_value() else {
cx.expected_name_value(sub_item.span(), None);
continue;
};
let cfg = match cfg {
MetaItemInner::MetaItem(meta_item) => meta_item,
MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
return *b;
}
_ => {
dcx.emit_err(session_diagnostics::UnsupportedLiteral {
span: cfg.span(),
reason: UnsupportedLiteralReason::CfgBoolean,
is_bytestr: false,
start_point_span: sess.source_map().start_point(cfg.span()),
// Then, parse it as a name-value item
let Some(name) = sub_item.path().word_sym() else {
cx.emit_err(session_diagnostics::CfgPredicateIdentifier {
span: sub_item.path().span(),
});
return false;
return None;
};
let name = Symbol::intern(&format!("target_{name}"));
if let Some(cfg) =
parse_name_value(name, sub_item.path().span(), Some(nv), sub_item.span(), cx)
{
result.push(cfg);
}
}
Some(CfgEntry::All(result, list.span))
}
fn parse_name_value<S: Stage>(
name: Symbol,
name_span: Span,
value: Option<&NameValueParser>,
span: Span,
cx: &mut AcceptContext<'_, '_, S>,
) -> Option<CfgEntry> {
try_gate_cfg(name, span, cx.sess(), cx.features_option());
let value = match value {
None => None,
Some(value) => {
let Some(value_str) = value.value_as_str() else {
cx.expected_string_literal(value.value_span, Some(value.value_as_lit()));
return None;
};
Some((value_str, value.value_span))
}
};
match &cfg.kind {
MetaItemKind::List(mis) if cfg.has_name(sym::version) => {
try_gate_cfg(sym::version, cfg.span, sess, features);
let (min_version, span) = match &mis[..] {
[MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => {
(sym, span)
}
[
MetaItemInner::Lit(MetaItemLit { span, .. })
| MetaItemInner::MetaItem(MetaItem { span, .. }),
] => {
dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span });
return false;
}
[..] => {
dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral {
span: cfg.span,
});
return false;
}
};
let Some(min_version) = parse_version(*min_version) else {
dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span });
return false;
};
Some(CfgEntry::NameValue { name, name_span, value, span })
}
// See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
if sess.psess.assume_incomplete_release {
RustcVersion::current_overridable() > min_version
} else {
RustcVersion::current_overridable() >= min_version
}
}
MetaItemKind::List(mis) => {
for mi in mis.iter() {
if mi.meta_item_or_bool().is_none() {
dcx.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;
pub fn eval_config_entry(
sess: &Session,
cfg_entry: &CfgEntry,
id: NodeId,
features: Option<&Features>,
) -> EvalConfigResult {
match cfg_entry {
CfgEntry::All(subs, ..) => {
let mut all = None;
for sub in subs {
let res = eval_config_entry(sess, sub, id, features);
// We cannot short-circuit because `eval_config_entry` emits some lints
if !res.as_bool() {
all.get_or_insert(res);
}
}
// The unwraps below may look dangerous, but we've already asserted
// that they won't fail with the loop above.
match cfg.name() {
Some(sym::any) => mis
.iter()
// We don't use any() here, because we want to evaluate all cfg condition
// as eval_condition can (and does) extra checks
.fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)),
Some(sym::all) => mis
.iter()
// We don't use all() here, because we want to evaluate all cfg condition
// as eval_condition can (and does) extra checks
.fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)),
Some(sym::not) => {
let [mi] = mis.as_slice() else {
dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span });
return false;
};
!eval_condition(mi, sess, features, eval)
}
Some(sym::target) => {
if let Some(features) = features
&& !features.cfg_target_compact()
{
feature_err(
sess,
sym::cfg_target_compact,
cfg.span,
fluent_generated::attr_parsing_unstable_cfg_target_compact,
)
.emit();
}
mis.iter().fold(true, |res, mi| {
let Some(mut mi) = mi.meta_item().cloned() else {
dcx.emit_err(session_diagnostics::CfgPredicateIdentifier {
span: mi.span(),
});
return false;
};
if let [seg, ..] = &mut mi.path.segments[..] {
seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
}
res & eval_condition(&MetaItemInner::MetaItem(mi), sess, features, eval)
})
}
_ => {
dcx.emit_err(session_diagnostics::InvalidPredicate {
span: cfg.span,
predicate: pprust::path_to_string(&cfg.path),
});
false
all.unwrap_or_else(|| EvalConfigResult::True)
}
CfgEntry::Any(subs, span) => {
let mut any = None;
for sub in subs {
let res = eval_config_entry(sess, sub, id, features);
// We cannot short-circuit because `eval_config_entry` emits some lints
if res.as_bool() {
any.get_or_insert(res);
}
}
}
MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
true
}
MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
dcx.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
}
MetaItemKind::Word | MetaItemKind::NameValue(..) => {
let ident = cfg.ident().expect("multi-segment cfg predicate");
eval(Condition {
name: ident.name,
name_span: ident.span,
value: cfg.value_str(),
value_span: cfg.name_value_literal_span(),
span: cfg.span,
any.unwrap_or_else(|| EvalConfigResult::False {
reason: cfg_entry.clone(),
reason_span: *span,
})
}
CfgEntry::Not(sub, span) => {
if eval_config_entry(sess, sub, id, features).as_bool() {
EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span }
} else {
EvalConfigResult::True
}
}
CfgEntry::Bool(b, span) => {
if *b {
EvalConfigResult::True
} else {
EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span }
}
}
CfgEntry::NameValue { name, name_span, value, span } => {
match sess.psess.check_config.expecteds.get(name) {
Some(ExpectedValues::Some(values)) if !values.contains(&value.map(|(v, _)| v)) => {
id.emit_span_lint(
sess,
UNEXPECTED_CFGS,
*span,
BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value),
);
}
None if sess.psess.check_config.exhaustive_names => {
id.emit_span_lint(
sess,
UNEXPECTED_CFGS,
*span,
BuiltinLintDiag::UnexpectedCfgName((*name, *name_span), *value),
);
}
_ => { /* not unexpected */ }
}
if sess.psess.config.contains(&(*name, value.map(|(v, _)| v))) {
EvalConfigResult::True
} else {
EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span }
}
}
CfgEntry::Version(min_version, version_span) => {
let Some(min_version) = min_version else {
return EvalConfigResult::False {
reason: cfg_entry.clone(),
reason_span: *version_span,
};
};
// See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
let min_version_ok = if sess.psess.assume_incomplete_release {
RustcVersion::current_overridable() > *min_version
} else {
RustcVersion::current_overridable() >= *min_version
};
if min_version_ok {
EvalConfigResult::True
} else {
EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *version_span }
}
}
}
}
pub enum EvalConfigResult {
True,
False { reason: CfgEntry, reason_span: Span },
}
impl EvalConfigResult {
pub fn as_bool(&self) -> bool {
match self {
EvalConfigResult::True => true,
EvalConfigResult::False { .. } => false,
}
}
}

View file

@ -0,0 +1,247 @@
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
use rustc_ast_pretty::pprust;
use rustc_attr_data_structures::RustcVersion;
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
use rustc_session::Session;
use rustc_session::config::ExpectedValues;
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
use rustc_session::lint::{BuiltinLintDiag, Lint};
use rustc_session::parse::feature_err;
use rustc_span::{Span, Symbol, sym};
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
use crate::{fluent_generated, parse_version};
/// Emitter of a builtin lint from `cfg_matches`.
///
/// Used to support emitting a lint (currently on check-cfg), either:
/// - as an early buffered lint (in `rustc`)
/// - or has a "normal" lint from HIR (in `rustdoc`)
pub trait CfgMatchesLintEmitter {
fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag);
}
impl CfgMatchesLintEmitter for NodeId {
fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag) {
sess.psess.buffer_lint(lint, sp, *self, diag);
}
}
#[derive(Clone, Debug)]
pub struct Condition {
pub name: Symbol,
pub name_span: Span,
pub value: Option<Symbol>,
pub value_span: Option<Span>,
pub span: Span,
}
/// Tests if a cfg-pattern matches the cfg set
pub fn cfg_matches(
cfg: &MetaItemInner,
sess: &Session,
lint_emitter: impl CfgMatchesLintEmitter,
features: Option<&Features>,
) -> bool {
eval_condition(cfg, sess, features, &mut |cfg| {
try_gate_cfg(cfg.name, cfg.span, sess, features);
match sess.psess.check_config.expecteds.get(&cfg.name) {
Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
lint_emitter.emit_span_lint(
sess,
UNEXPECTED_CFGS,
cfg.span,
BuiltinLintDiag::UnexpectedCfgValue(
(cfg.name, cfg.name_span),
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
),
);
}
None if sess.psess.check_config.exhaustive_names => {
lint_emitter.emit_span_lint(
sess,
UNEXPECTED_CFGS,
cfg.span,
BuiltinLintDiag::UnexpectedCfgName(
(cfg.name, cfg.name_span),
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
),
);
}
_ => { /* not unexpected */ }
}
sess.psess.config.contains(&(cfg.name, cfg.value))
})
}
pub fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) {
let gate = find_gated_cfg(|sym| sym == name);
if let (Some(feats), Some(gated_cfg)) = (features, gate) {
gate_cfg(gated_cfg, span, sess, feats);
}
}
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) {
let (cfg, feature, has_feature) = gated_cfg;
if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
let explain = format!("`cfg({cfg})` is experimental and subject to change");
feature_err(sess, *feature, cfg_span, explain).emit();
}
}
/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
/// evaluate individual items.
pub fn eval_condition(
cfg: &MetaItemInner,
sess: &Session,
features: Option<&Features>,
eval: &mut impl FnMut(Condition) -> bool,
) -> bool {
let dcx = sess.dcx();
let cfg = match cfg {
MetaItemInner::MetaItem(meta_item) => meta_item,
MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
return *b;
}
_ => {
dcx.emit_err(session_diagnostics::UnsupportedLiteral {
span: cfg.span(),
reason: UnsupportedLiteralReason::CfgBoolean,
is_bytestr: false,
start_point_span: sess.source_map().start_point(cfg.span()),
});
return false;
}
};
match &cfg.kind {
MetaItemKind::List(mis) if cfg.has_name(sym::version) => {
try_gate_cfg(sym::version, cfg.span, sess, features);
let (min_version, span) = match &mis[..] {
[MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => {
(sym, span)
}
[
MetaItemInner::Lit(MetaItemLit { span, .. })
| MetaItemInner::MetaItem(MetaItem { span, .. }),
] => {
dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span });
return false;
}
[..] => {
dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral {
span: cfg.span,
});
return false;
}
};
let Some(min_version) = parse_version(*min_version) else {
dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span });
return false;
};
// See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
if sess.psess.assume_incomplete_release {
RustcVersion::current_overridable() > min_version
} else {
RustcVersion::current_overridable() >= min_version
}
}
MetaItemKind::List(mis) => {
for mi in mis.iter() {
if mi.meta_item_or_bool().is_none() {
dcx.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;
}
}
// The unwraps below may look dangerous, but we've already asserted
// that they won't fail with the loop above.
match cfg.name() {
Some(sym::any) => mis
.iter()
// We don't use any() here, because we want to evaluate all cfg condition
// as eval_condition can (and does) extra checks
.fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)),
Some(sym::all) => mis
.iter()
// We don't use all() here, because we want to evaluate all cfg condition
// as eval_condition can (and does) extra checks
.fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)),
Some(sym::not) => {
let [mi] = mis.as_slice() else {
dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span });
return false;
};
!eval_condition(mi, sess, features, eval)
}
Some(sym::target) => {
if let Some(features) = features
&& !features.cfg_target_compact()
{
feature_err(
sess,
sym::cfg_target_compact,
cfg.span,
fluent_generated::attr_parsing_unstable_cfg_target_compact,
)
.emit();
}
mis.iter().fold(true, |res, mi| {
let Some(mut mi) = mi.meta_item().cloned() else {
dcx.emit_err(session_diagnostics::CfgPredicateIdentifier {
span: mi.span(),
});
return false;
};
if let [seg, ..] = &mut mi.path.segments[..] {
seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
}
res & eval_condition(&MetaItemInner::MetaItem(mi), sess, features, eval)
})
}
_ => {
dcx.emit_err(session_diagnostics::InvalidPredicate {
span: cfg.span,
predicate: pprust::path_to_string(&cfg.path),
});
false
}
}
}
MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
true
}
MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
dcx.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
}
MetaItemKind::Word | MetaItemKind::NameValue(..) => {
let ident = cfg.ident().expect("multi-segment cfg predicate");
eval(Condition {
name: ident.name,
name_span: ident.span,
value: cfg.value_str(),
value_span: cfg.name_value_literal_span(),
span: cfg.span,
})
}
}
}

View file

@ -27,6 +27,7 @@ use crate::session_diagnostics::UnusedMultiple;
pub(crate) mod allow_unstable;
pub(crate) mod cfg;
pub(crate) mod cfg_old;
pub(crate) mod codegen_attrs;
pub(crate) mod confusables;
pub(crate) mod deprecation;

View file

@ -145,3 +145,10 @@ impl<S: Stage> NoArgsAttributeParser<S> for FundamentalParser {
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::Fundamental;
}
pub(crate) struct PointeeParser;
impl<S: Stage> NoArgsAttributeParser<S> for PointeeParser {
const PATH: &[Symbol] = &[sym::pointee];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Pointee;
}

View file

@ -1,6 +1,5 @@
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;
@ -48,8 +47,8 @@ use crate::attributes::test_attrs::IgnoreParser;
use crate::attributes::traits::{
AllowIncoherentImplParser, CoherenceIsCoreParser, CoinductiveParser, ConstTraitParser,
DenyExplicitImplParser, DoNotImplementViaObjectParser, FundamentalParser, MarkerParser,
ParenSugarParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser,
UnsafeSpecializationMarkerParser,
ParenSugarParser, PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser,
TypeConstParser, UnsafeSpecializationMarkerParser,
};
use crate::attributes::transparency::TransparencyParser;
use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs};
@ -178,6 +177,7 @@ attribute_parsers!(
Single<WithoutArgs<OmitGdbPrettyPrinterSectionParser>>,
Single<WithoutArgs<ParenSugarParser>>,
Single<WithoutArgs<PassByValueParser>>,
Single<WithoutArgs<PointeeParser>>,
Single<WithoutArgs<PubTransparentParser>>,
Single<WithoutArgs<SpecializationTraitParser>>,
Single<WithoutArgs<StdInternalSymbolParser>>,
@ -202,7 +202,11 @@ pub trait Stage: Sized + 'static + Sealed {
fn parsers() -> &'static group_type!(Self);
fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed;
fn emit_err<'sess>(
&self,
sess: &'sess Session,
diag: impl for<'x> Diagnostic<'x>,
) -> ErrorGuaranteed;
}
// allow because it's a sealed trait
@ -214,8 +218,16 @@ impl Stage for Early {
fn parsers() -> &'static group_type!(Self) {
&early::ATTRIBUTE_PARSERS
}
fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
sess.dcx().create_err(diag).delay_as_bug()
fn emit_err<'sess>(
&self,
sess: &'sess Session,
diag: impl for<'x> Diagnostic<'x>,
) -> ErrorGuaranteed {
if self.emit_errors {
sess.dcx().emit_err(diag)
} else {
sess.dcx().create_err(diag).delay_as_bug()
}
}
}
@ -228,20 +240,29 @@ impl Stage for Late {
fn parsers() -> &'static group_type!(Self) {
&late::ATTRIBUTE_PARSERS
}
fn emit_err<'sess>(tcx: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
fn emit_err<'sess>(
&self,
tcx: &'sess Session,
diag: impl for<'x> Diagnostic<'x>,
) -> ErrorGuaranteed {
tcx.dcx().emit_err(diag)
}
}
/// used when parsing attributes for miscellaneous things *before* ast lowering
pub struct Early;
pub struct Early {
/// Whether to emit errors or delay them as a bug
/// For most attributes, the attribute will be parsed again in the `Late` stage and in this case the errors should be delayed
/// But for some, such as `cfg`, the attribute will be removed before the `Late` stage so errors must be emitted
pub emit_errors: bool,
}
/// used when parsing attributes during ast lowering
pub struct Late;
/// Context given to every attribute parser when accepting
///
/// Gives [`AttributeParser`]s enough information to create errors, for example.
pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
pub struct AcceptContext<'f, 'sess, S: Stage> {
pub(crate) shared: SharedContext<'f, 'sess, S>,
/// The span of the attribute currently being parsed
pub(crate) attr_span: Span,
@ -257,7 +278,7 @@ pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
S::emit_err(&self.sess, diag)
self.stage.emit_err(&self.sess, diag)
}
/// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing
@ -472,7 +493,7 @@ impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> {
///
/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create
/// errors, for example.
pub(crate) struct SharedContext<'p, 'sess, S: Stage> {
pub struct SharedContext<'p, 'sess, S: Stage> {
/// The parse context, gives access to the session and the
/// diagnostics context.
pub(crate) cx: &'p mut AttributeParser<'sess, S>,
@ -540,7 +561,7 @@ pub struct AttributeParser<'sess, S: Stage = Late> {
pub(crate) tools: Vec<Symbol>,
features: Option<&'sess Features>,
sess: &'sess Session,
stage: PhantomData<S>,
stage: S,
/// *Only* parse attributes with this symbol.
///
@ -569,13 +590,14 @@ impl<'sess> AttributeParser<'sess, Early> {
sym: Symbol,
target_span: Span,
target_node_id: NodeId,
features: Option<&'sess Features>,
) -> Option<Attribute> {
let mut p = Self {
features: None,
features,
tools: Vec::new(),
parse_only: Some(sym),
sess,
stage: PhantomData,
stage: Early { emit_errors: false },
};
let mut parsed = p.parse_attribute_list(
attrs,
@ -591,11 +613,55 @@ impl<'sess> AttributeParser<'sess, Early> {
parsed.pop()
}
pub fn parse_single<T>(
sess: &'sess Session,
attr: &ast::Attribute,
target_span: Span,
target_node_id: NodeId,
features: Option<&'sess Features>,
emit_errors: bool,
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> T,
template: &AttributeTemplate,
) -> T {
let mut parser = Self {
features,
tools: Vec::new(),
parse_only: None,
sess,
stage: Early { emit_errors },
};
let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
panic!("parse_single called on a doc attr")
};
let meta_parser = MetaItemParser::from_attr(normal_attr, parser.dcx());
let path = meta_parser.path();
let args = meta_parser.args();
let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
shared: SharedContext {
cx: &mut parser,
target_span,
target_id: target_node_id,
emit_lint: &mut |_lint| {
panic!("can't emit lints here for now (nothing uses this atm)");
},
},
attr_span: attr.span,
template,
attr_path: path.get_attribute_path(),
};
parse_fn(&mut cx, args)
}
}
impl<'sess, S: Stage> AttributeParser<'sess, S> {
pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData }
pub fn new(
sess: &'sess Session,
features: &'sess Features,
tools: Vec<Symbol>,
stage: S,
) -> Self {
Self { features: Some(features), tools, parse_only: None, sess, stage }
}
pub(crate) fn sess(&self) -> &'sess Session {
@ -606,6 +672,10 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
self.features.expect("features not available at this point in the compiler")
}
pub(crate) fn features_option(&self) -> Option<&'sess Features> {
self.features
}
pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
self.sess().dcx()
}

View file

@ -90,7 +90,8 @@ mod lints;
pub mod parser;
mod session_diagnostics;
pub use attributes::cfg::*;
pub use attributes::cfg::{CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr};
pub use attributes::cfg_old::*;
pub use attributes::util::{
find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version,
};

View file

@ -593,7 +593,13 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
diag.code(E0565);
}
AttributeParseErrorReason::ExpectedNameValue(None) => {
// The suggestion we add below this match already contains enough information
// If the span is the entire attribute, the suggestion we add below this match already contains enough information
if self.span != self.attr_span {
diag.span_label(
self.span,
format!("expected this to be of the form `... = \"...\"`"),
);
}
}
AttributeParseErrorReason::ExpectedNameValue(Some(name)) => {
diag.span_label(

View file

@ -81,11 +81,11 @@ builtin_macros_cfg_accessible_literal_path = `cfg_accessible` path cannot be a l
builtin_macros_cfg_accessible_multiple_paths = multiple `cfg_accessible` paths are specified
builtin_macros_cfg_accessible_unspecified_path = `cfg_accessible` path is not specified
builtin_macros_cfg_select_no_matches = none of the rules in this `cfg_select` evaluated to true
builtin_macros_cfg_select_no_matches = none of the predicates in this `cfg_select` evaluated to true
builtin_macros_cfg_select_unreachable = unreachable rule
builtin_macros_cfg_select_unreachable = unreachable predicate
.label = always matches
.label2 = this rules is never reached
.label2 = this predicate is never reached
builtin_macros_coerce_pointee_requires_maybe_sized = `derive(CoercePointee)` requires `{$name}` to be marked `?Sized`

View file

@ -1,12 +1,12 @@
use rustc_ast::tokenstream::TokenStream;
use rustc_attr_parsing as attr;
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
use rustc_parse::parser::cfg_select::{CfgSelectBranches, CfgSelectRule, parse_cfg_select};
use rustc_parse::parser::cfg_select::{CfgSelectBranches, CfgSelectPredicate, parse_cfg_select};
use rustc_span::{Ident, Span, sym};
use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable};
/// Selects the first arm whose rule evaluates to true.
/// Selects the first arm whose predicate evaluates to true.
fn select_arm(ecx: &ExtCtxt<'_>, branches: CfgSelectBranches) -> Option<(TokenStream, Span)> {
for (cfg, tt, arm_span) in branches.reachable {
if attr::cfg_matches(
@ -30,11 +30,11 @@ pub(super) fn expand_cfg_select<'cx>(
ExpandResult::Ready(match parse_cfg_select(&mut ecx.new_parser_from_tts(tts)) {
Ok(branches) => {
if let Some((underscore, _, _)) = branches.wildcard {
// Warn for every unreachable rule. We store the fully parsed branch for rustfmt.
for (rule, _, _) in &branches.unreachable {
let span = match rule {
CfgSelectRule::Wildcard(underscore) => underscore.span,
CfgSelectRule::Cfg(cfg) => cfg.span(),
// Warn for every unreachable predicate. We store the fully parsed branch for rustfmt.
for (predicate, _, _) in &branches.unreachable {
let span = match predicate {
CfgSelectPredicate::Wildcard(underscore) => underscore.span,
CfgSelectPredicate::Cfg(cfg) => cfg.span(),
};
let err = CfgSelectUnreachable { span, wildcard_span: underscore.span };
ecx.dcx().emit_warn(err);
@ -50,7 +50,7 @@ pub(super) fn expand_cfg_select<'cx>(
Ident::with_dummy_span(sym::cfg_select),
);
} else {
// Emit a compiler error when none of the rules matched.
// Emit a compiler error when none of the predicates matched.
let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp });
DummyResult::any(sp, guar)
}

View file

@ -484,7 +484,7 @@ impl<'a> TraitDef<'a> {
match item {
Annotatable::Item(item) => {
let is_packed = matches!(
AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, item.id),
AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, item.id, None),
Some(Attribute::Parsed(AttributeKind::Repr { reprs, .. })) if reprs.iter().any(|(x, _)| matches!(x, ReprPacked(..)))
);

View file

@ -879,9 +879,7 @@ pub(crate) fn codegen(
.generic_activity_with_arg("LLVM_module_codegen_embed_bitcode", &*module.name);
let thin_bc =
module.thin_lto_buffer.as_deref().expect("cannot find embedded bitcode");
unsafe {
embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, &thin_bc);
}
embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, &thin_bc);
}
}
@ -945,7 +943,7 @@ pub(crate) fn codegen(
// binaries. So we must clone the module to produce the asm output
// if we are also producing object code.
let llmod = if let EmitObj::ObjectCode(_) = config.emit_obj {
unsafe { llvm::LLVMCloneModule(llmod) }
llvm::LLVMCloneModule(llmod)
} else {
llmod
};
@ -1073,7 +1071,7 @@ pub(crate) fn bitcode_section_name(cgcx: &CodegenContext<LlvmCodegenBackend>) ->
}
/// Embed the bitcode of an LLVM module for LTO in the LLVM module itself.
unsafe fn embed_bitcode(
fn embed_bitcode(
cgcx: &CodegenContext<LlvmCodegenBackend>,
llcx: &llvm::Context,
llmod: &llvm::Module,
@ -1115,43 +1113,40 @@ unsafe fn embed_bitcode(
// Unfortunately, LLVM provides no way to set custom section flags. For ELF
// and COFF we emit the sections using module level inline assembly for that
// reason (see issue #90326 for historical background).
unsafe {
if cgcx.target_is_like_darwin
|| cgcx.target_is_like_aix
|| cgcx.target_arch == "wasm32"
|| cgcx.target_arch == "wasm64"
{
// We don't need custom section flags, create LLVM globals.
let llconst = common::bytes_in_context(llcx, bitcode);
let llglobal =
llvm::add_global(llmod, common::val_ty(llconst), c"rustc.embedded.module");
llvm::set_initializer(llglobal, llconst);
llvm::set_section(llglobal, bitcode_section_name(cgcx));
llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
llvm::LLVMSetGlobalConstant(llglobal, llvm::True);
if cgcx.target_is_like_darwin
|| cgcx.target_is_like_aix
|| cgcx.target_arch == "wasm32"
|| cgcx.target_arch == "wasm64"
{
// We don't need custom section flags, create LLVM globals.
let llconst = common::bytes_in_context(llcx, bitcode);
let llglobal = llvm::add_global(llmod, common::val_ty(llconst), c"rustc.embedded.module");
llvm::set_initializer(llglobal, llconst);
let llconst = common::bytes_in_context(llcx, cmdline.as_bytes());
let llglobal =
llvm::add_global(llmod, common::val_ty(llconst), c"rustc.embedded.cmdline");
llvm::set_initializer(llglobal, llconst);
let section = if cgcx.target_is_like_darwin {
c"__LLVM,__cmdline"
} else if cgcx.target_is_like_aix {
c".info"
} else {
c".llvmcmd"
};
llvm::set_section(llglobal, section);
llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
llvm::set_section(llglobal, bitcode_section_name(cgcx));
llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
llvm::LLVMSetGlobalConstant(llglobal, llvm::True);
let llconst = common::bytes_in_context(llcx, cmdline.as_bytes());
let llglobal = llvm::add_global(llmod, common::val_ty(llconst), c"rustc.embedded.cmdline");
llvm::set_initializer(llglobal, llconst);
let section = if cgcx.target_is_like_darwin {
c"__LLVM,__cmdline"
} else if cgcx.target_is_like_aix {
c".info"
} else {
// We need custom section flags, so emit module-level inline assembly.
let section_flags = if cgcx.is_pe_coff { "n" } else { "e" };
let asm = create_section_with_flags_asm(".llvmbc", section_flags, bitcode);
llvm::append_module_inline_asm(llmod, &asm);
let asm = create_section_with_flags_asm(".llvmcmd", section_flags, cmdline.as_bytes());
llvm::append_module_inline_asm(llmod, &asm);
}
c".llvmcmd"
};
llvm::set_section(llglobal, section);
llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
} else {
// We need custom section flags, so emit module-level inline assembly.
let section_flags = if cgcx.is_pe_coff { "n" } else { "e" };
let asm = create_section_with_flags_asm(".llvmbc", section_flags, bitcode);
llvm::append_module_inline_asm(llmod, &asm);
let asm = create_section_with_flags_asm(".llvmcmd", section_flags, cmdline.as_bytes());
llvm::append_module_inline_asm(llmod, &asm);
}
}

View file

@ -302,10 +302,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
return;
}
let id_str = "branch_weights";
let id = unsafe {
llvm::LLVMMDStringInContext2(self.cx.llcx, id_str.as_ptr().cast(), id_str.len())
};
let id = self.cx.create_metadata(b"branch_weights");
// For switch instructions with 2 targets, the `llvm.expect` intrinsic is used.
// This function handles switch instructions with more than 2 targets and it needs to
@ -637,17 +634,16 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
} else if place.layout.is_llvm_immediate() {
let mut const_llval = None;
let llty = place.layout.llvm_type(self);
unsafe {
if let Some(global) = llvm::LLVMIsAGlobalVariable(place.val.llval) {
if llvm::LLVMIsGlobalConstant(global) == llvm::True {
if let Some(init) = llvm::LLVMGetInitializer(global) {
if self.val_ty(init) == llty {
const_llval = Some(init);
}
if let Some(global) = llvm::LLVMIsAGlobalVariable(place.val.llval) {
if llvm::LLVMIsGlobalConstant(global) == llvm::True {
if let Some(init) = llvm::LLVMGetInitializer(global) {
if self.val_ty(init) == llty {
const_llval = Some(init);
}
}
}
}
let llval = const_llval.unwrap_or_else(|| {
let load = self.load(llty, place.val.llval, place.val.align);
if let abi::BackendRepr::Scalar(scalar) = place.layout.backend_repr {
@ -1721,7 +1717,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
} else {
cfi::typeid_for_fnabi(self.tcx, fn_abi, options)
};
let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap();
let typeid_metadata = self.cx.create_metadata(typeid.as_bytes());
let dbg_loc = self.get_dbg_loc();
// Test whether the function pointer is associated with the type identifier using the

View file

@ -76,12 +76,12 @@ fn match_args_from_caller_to_enzyme<'ll>(
outer_pos = 1;
}
let enzyme_const = cx.create_metadata("enzyme_const".to_string()).unwrap();
let enzyme_out = cx.create_metadata("enzyme_out".to_string()).unwrap();
let enzyme_dup = cx.create_metadata("enzyme_dup".to_string()).unwrap();
let enzyme_dupv = cx.create_metadata("enzyme_dupv".to_string()).unwrap();
let enzyme_dupnoneed = cx.create_metadata("enzyme_dupnoneed".to_string()).unwrap();
let enzyme_dupnoneedv = cx.create_metadata("enzyme_dupnoneedv".to_string()).unwrap();
let enzyme_const = cx.create_metadata(b"enzyme_const");
let enzyme_out = cx.create_metadata(b"enzyme_out");
let enzyme_dup = cx.create_metadata(b"enzyme_dup");
let enzyme_dupv = cx.create_metadata(b"enzyme_dupv");
let enzyme_dupnoneed = cx.create_metadata(b"enzyme_dupnoneed");
let enzyme_dupnoneedv = cx.create_metadata(b"enzyme_dupnoneedv");
while activity_pos < inputs.len() {
let diff_activity = inputs[activity_pos as usize];
@ -378,12 +378,12 @@ fn generate_enzyme_call<'ll>(
let mut args = Vec::with_capacity(num_args as usize + 1);
args.push(fn_to_diff);
let enzyme_primal_ret = cx.create_metadata("enzyme_primal_return".to_string()).unwrap();
let enzyme_primal_ret = cx.create_metadata(b"enzyme_primal_return");
if matches!(attrs.ret_activity, DiffActivity::Dual | DiffActivity::Active) {
args.push(cx.get_metadata_value(enzyme_primal_ret));
}
if attrs.width > 1 {
let enzyme_width = cx.create_metadata("enzyme_width".to_string()).unwrap();
let enzyme_width = cx.create_metadata(b"enzyme_width");
args.push(cx.get_metadata_value(enzyme_width));
args.push(cx.get_const_int(cx.type_i64(), attrs.width as u64));
}

View file

@ -215,10 +215,10 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> {
bug!("symbol `{}` is already defined", sym);
});
llvm::set_initializer(g, sc);
unsafe {
llvm::LLVMSetGlobalConstant(g, True);
llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global);
}
llvm::set_global_constant(g, true);
llvm::set_unnamed_address(g, llvm::UnnamedAddr::Global);
llvm::set_linkage(g, llvm::Linkage::InternalLinkage);
// Cast to default address space if globals are in a different addrspace
let g = self.const_pointercast(g, self.type_ptr());

View file

@ -17,13 +17,12 @@ use rustc_middle::ty::{self, Instance};
use rustc_middle::{bug, span_bug};
use tracing::{debug, instrument, trace};
use crate::common::{AsCCharPtr, CodegenCx};
use crate::common::CodegenCx;
use crate::errors::SymbolAlreadyDefined;
use crate::llvm::{self, True};
use crate::type_::Type;
use crate::type_of::LayoutLlvmExt;
use crate::value::Value;
use crate::{base, debuginfo};
use crate::{base, debuginfo, llvm};
pub(crate) fn const_alloc_to_llvm<'ll>(
cx: &CodegenCx<'ll, '_>,
@ -247,7 +246,7 @@ impl<'ll> CodegenCx<'ll, '_> {
};
llvm::set_initializer(gv, cv);
set_global_alignment(self, gv, align);
llvm::SetUnnamedAddress(gv, llvm::UnnamedAddr::Global);
llvm::set_unnamed_address(gv, llvm::UnnamedAddr::Global);
gv
}
@ -272,9 +271,8 @@ impl<'ll> CodegenCx<'ll, '_> {
return gv;
}
let gv = self.static_addr_of_mut(cv, align, kind);
unsafe {
llvm::LLVMSetGlobalConstant(gv, True);
}
llvm::set_global_constant(gv, true);
self.const_globals.borrow_mut().insert(cv, gv);
gv
}
@ -398,149 +396,140 @@ impl<'ll> CodegenCx<'ll, '_> {
}
fn codegen_static_item(&mut self, def_id: DefId) {
unsafe {
assert!(
llvm::LLVMGetInitializer(
self.instances.borrow().get(&Instance::mono(self.tcx, def_id)).unwrap()
)
.is_none()
);
let attrs = self.tcx.codegen_fn_attrs(def_id);
assert!(
llvm::LLVMGetInitializer(
self.instances.borrow().get(&Instance::mono(self.tcx, def_id)).unwrap()
)
.is_none()
);
let attrs = self.tcx.codegen_fn_attrs(def_id);
let Ok((v, alloc)) = codegen_static_initializer(self, def_id) else {
// Error has already been reported
return;
};
let alloc = alloc.inner();
let Ok((v, alloc)) = codegen_static_initializer(self, def_id) else {
// Error has already been reported
return;
};
let alloc = alloc.inner();
let val_llty = self.val_ty(v);
let val_llty = self.val_ty(v);
let g = self.get_static_inner(def_id, val_llty);
let llty = self.get_type_of_global(g);
let g = self.get_static_inner(def_id, val_llty);
let llty = self.get_type_of_global(g);
let g = if val_llty == llty {
g
} else {
// codegen_static_initializer creates the global value just from the
// `Allocation` data by generating one big struct value that is just
// all the bytes and pointers after each other. This will almost never
// match the type that the static was declared with. Unfortunately
// we can't just LLVMConstBitCast our way out of it because that has very
// specific rules on what can be cast. So instead of adding a new way to
// generate static initializers that match the static's type, we picked
// the easier option and retroactively change the type of the static item itself.
let name = llvm::get_value_name(g);
llvm::set_value_name(g, b"");
let g = if val_llty == llty {
g
} else {
// codegen_static_initializer creates the global value just from the
// `Allocation` data by generating one big struct value that is just
// all the bytes and pointers after each other. This will almost never
// match the type that the static was declared with. Unfortunately
// we can't just LLVMConstBitCast our way out of it because that has very
// specific rules on what can be cast. So instead of adding a new way to
// generate static initializers that match the static's type, we picked
// the easier option and retroactively change the type of the static item itself.
let name = String::from_utf8(llvm::get_value_name(g))
.expect("we declare our statics with a utf8-valid name");
llvm::set_value_name(g, b"");
let linkage = llvm::get_linkage(g);
let visibility = llvm::get_visibility(g);
let linkage = llvm::get_linkage(g);
let visibility = llvm::get_visibility(g);
let new_g = llvm::LLVMRustGetOrInsertGlobal(
self.llmod,
name.as_c_char_ptr(),
name.len(),
val_llty,
);
let new_g = self.declare_global(&name, val_llty);
llvm::set_linkage(new_g, linkage);
llvm::set_visibility(new_g, visibility);
llvm::set_linkage(new_g, linkage);
llvm::set_visibility(new_g, visibility);
// The old global has had its name removed but is returned by
// get_static since it is in the instance cache. Provide an
// alternative lookup that points to the new global so that
// global_asm! can compute the correct mangled symbol name
// for the global.
self.renamed_statics.borrow_mut().insert(def_id, new_g);
// The old global has had its name removed but is returned by
// get_static since it is in the instance cache. Provide an
// alternative lookup that points to the new global so that
// global_asm! can compute the correct mangled symbol name
// for the global.
self.renamed_statics.borrow_mut().insert(def_id, new_g);
// To avoid breaking any invariants, we leave around the old
// global for the moment; we'll replace all references to it
// with the new global later. (See base::codegen_backend.)
self.statics_to_rauw.borrow_mut().push((g, new_g));
new_g
};
set_global_alignment(self, g, alloc.align);
llvm::set_initializer(g, v);
// To avoid breaking any invariants, we leave around the old
// global for the moment; we'll replace all references to it
// with the new global later. (See base::codegen_backend.)
self.statics_to_rauw.borrow_mut().push((g, new_g));
new_g
};
set_global_alignment(self, g, alloc.align);
llvm::set_initializer(g, v);
self.assume_dso_local(g, true);
self.assume_dso_local(g, true);
// Forward the allocation's mutability (picked by the const interner) to LLVM.
if alloc.mutability.is_not() {
llvm::LLVMSetGlobalConstant(g, llvm::True);
}
// Forward the allocation's mutability (picked by the const interner) to LLVM.
if alloc.mutability.is_not() {
llvm::set_global_constant(g, true);
}
debuginfo::build_global_var_di_node(self, def_id, g);
debuginfo::build_global_var_di_node(self, def_id, g);
if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
llvm::set_thread_local_mode(g, self.tls_model);
}
if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
llvm::set_thread_local_mode(g, self.tls_model);
}
// Wasm statics with custom link sections get special treatment as they
// go into custom sections of the wasm executable. The exception to this
// is the `.init_array` section which are treated specially by the wasm linker.
if self.tcx.sess.target.is_like_wasm
&& attrs
.link_section
.map(|link_section| !link_section.as_str().starts_with(".init_array"))
.unwrap_or(true)
{
if let Some(section) = attrs.link_section {
let section = llvm::LLVMMDStringInContext2(
self.llcx,
section.as_str().as_c_char_ptr(),
section.as_str().len(),
);
assert!(alloc.provenance().ptrs().is_empty());
// Wasm statics with custom link sections get special treatment as they
// go into custom sections of the wasm executable. The exception to this
// is the `.init_array` section which are treated specially by the wasm linker.
if self.tcx.sess.target.is_like_wasm
&& attrs
.link_section
.map(|link_section| !link_section.as_str().starts_with(".init_array"))
.unwrap_or(true)
{
if let Some(section) = attrs.link_section {
let section = self.create_metadata(section.as_str().as_bytes());
assert!(alloc.provenance().ptrs().is_empty());
// The `inspect` method is okay here because we checked for provenance, and
// because we are doing this access to inspect the final interpreter state (not
// as part of the interpreter execution).
let bytes =
alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len());
let alloc =
llvm::LLVMMDStringInContext2(self.llcx, bytes.as_c_char_ptr(), bytes.len());
let data = [section, alloc];
let meta = llvm::LLVMMDNodeInContext2(self.llcx, data.as_ptr(), data.len());
let val = self.get_metadata_value(meta);
// The `inspect` method is okay here because we checked for provenance, and
// because we are doing this access to inspect the final interpreter state (not
// as part of the interpreter execution).
let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len());
let alloc = self.create_metadata(bytes);
let data = [section, alloc];
let meta =
unsafe { llvm::LLVMMDNodeInContext2(self.llcx, data.as_ptr(), data.len()) };
let val = self.get_metadata_value(meta);
unsafe {
llvm::LLVMAddNamedMetadataOperand(
self.llmod,
c"wasm.custom_sections".as_ptr(),
val,
);
}
} else {
base::set_link_section(g, attrs);
)
};
}
} else {
base::set_link_section(g, attrs);
}
base::set_variable_sanitizer_attrs(g, attrs);
base::set_variable_sanitizer_attrs(g, attrs);
if attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) {
// `USED` and `USED_LINKER` can't be used together.
assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER));
if attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) {
// `USED` and `USED_LINKER` can't be used together.
assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER));
// The semantics of #[used] in Rust only require the symbol to make it into the
// object file. It is explicitly allowed for the linker to strip the symbol if it
// is dead, which means we are allowed to use `llvm.compiler.used` instead of
// `llvm.used` here.
//
// Additionally, https://reviews.llvm.org/D97448 in LLVM 13 started emitting unique
// sections with SHF_GNU_RETAIN flag for llvm.used symbols, which may trigger bugs
// in the handling of `.init_array` (the static constructor list) in versions of
// the gold linker (prior to the one released with binutils 2.36).
//
// That said, we only ever emit these when `#[used(compiler)]` is explicitly
// requested. This is to avoid similar breakage on other targets, in particular
// MachO targets have *their* static constructor lists broken if `llvm.compiler.used`
// is emitted rather than `llvm.used`. However, that check happens when assigning
// the `CodegenFnAttrFlags` in the `codegen_fn_attrs` query, so we don't need to
// take care of it here.
self.add_compiler_used_global(g);
}
if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) {
// `USED` and `USED_LINKER` can't be used together.
assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER));
// The semantics of #[used] in Rust only require the symbol to make it into the
// object file. It is explicitly allowed for the linker to strip the symbol if it
// is dead, which means we are allowed to use `llvm.compiler.used` instead of
// `llvm.used` here.
//
// Additionally, https://reviews.llvm.org/D97448 in LLVM 13 started emitting unique
// sections with SHF_GNU_RETAIN flag for llvm.used symbols, which may trigger bugs
// in the handling of `.init_array` (the static constructor list) in versions of
// the gold linker (prior to the one released with binutils 2.36).
//
// That said, we only ever emit these when `#[used(compiler)]` is explicitly
// requested. This is to avoid similar breakage on other targets, in particular
// MachO targets have *their* static constructor lists broken if `llvm.compiler.used`
// is emitted rather than `llvm.used`. However, that check happens when assigning
// the `CodegenFnAttrFlags` in the `codegen_fn_attrs` query, so we don't need to
// take care of it here.
self.add_compiler_used_global(g);
}
if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) {
// `USED` and `USED_LINKER` can't be used together.
assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER));
self.add_used_global(g);
}
self.add_used_global(g);
}
}

View file

@ -34,7 +34,6 @@ use smallvec::SmallVec;
use crate::back::write::to_llvm_code_model;
use crate::callee::get_fn;
use crate::common::AsCCharPtr;
use crate::debuginfo::metadata::apply_vcall_visibility_metadata;
use crate::llvm::Metadata;
use crate::type_::Type;
@ -169,6 +168,8 @@ pub(crate) unsafe fn create_module<'ll>(
let mod_name = SmallCStr::new(mod_name);
let llmod = unsafe { llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx) };
let cx = SimpleCx::new(llmod, llcx, tcx.data_layout.pointer_size());
let mut target_data_layout = sess.target.data_layout.to_string();
let llvm_version = llvm_util::get_version();
@ -473,18 +474,14 @@ pub(crate) unsafe fn create_module<'ll>(
#[allow(clippy::option_env_unwrap)]
let rustc_producer =
format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION"));
let name_metadata = unsafe {
llvm::LLVMMDStringInContext2(
llcx,
rustc_producer.as_c_char_ptr(),
rustc_producer.as_bytes().len(),
)
};
let name_metadata = cx.create_metadata(rustc_producer.as_bytes());
unsafe {
llvm::LLVMAddNamedMetadataOperand(
llmod,
c"llvm.ident".as_ptr(),
&llvm::LLVMMetadataAsValue(llcx, llvm::LLVMMDNodeInContext2(llcx, &name_metadata, 1)),
&cx.get_metadata_value(llvm::LLVMMDNodeInContext2(llcx, &name_metadata, 1)),
);
}
@ -698,10 +695,10 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
}
}
pub(crate) fn create_metadata(&self, name: String) -> Option<&'ll Metadata> {
Some(unsafe {
pub(crate) fn create_metadata(&self, name: &[u8]) -> &'ll Metadata {
unsafe {
llvm::LLVMMDStringInContext2(self.llcx(), name.as_ptr() as *const c_char, name.len())
})
}
}
pub(crate) fn get_functions(&self) -> Vec<&'ll Value> {

View file

@ -74,7 +74,7 @@ pub(crate) fn get_or_insert_gdb_debug_scripts_section_global<'ll>(
llvm::set_section(section_var, c".debug_gdb_scripts");
llvm::set_initializer(section_var, cx.const_bytes(section_contents));
llvm::LLVMSetGlobalConstant(section_var, llvm::True);
llvm::LLVMSetUnnamedAddress(section_var, llvm::UnnamedAddr::Global);
llvm::set_unnamed_address(section_var, llvm::UnnamedAddr::Global);
llvm::set_linkage(section_var, llvm::Linkage::LinkOnceODRLinkage);
// This should make sure that the whole section is not larger than
// the string it contains. Otherwise we get a warning from GDB.

View file

@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::{iter, ptr};
use libc::{c_char, c_longlong, c_uint};
use libc::{c_longlong, c_uint};
use rustc_abi::{Align, Size};
use rustc_codegen_ssa::debuginfo::type_names::{VTableNameKind, cpp_like_debuginfo};
use rustc_codegen_ssa::traits::*;
@ -1582,13 +1582,9 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>(
};
let trait_ref_typeid = typeid_for_trait_ref(cx.tcx, trait_ref);
let typeid = cx.create_metadata(trait_ref_typeid.as_bytes());
unsafe {
let typeid = llvm::LLVMMDStringInContext2(
cx.llcx,
trait_ref_typeid.as_ptr() as *const c_char,
trait_ref_typeid.as_bytes().len(),
);
let v = [llvm::LLVMValueAsMetadata(cx.const_usize(0)), typeid];
llvm::LLVMRustGlobalAddMetadata(
vtable,
@ -1630,7 +1626,7 @@ pub(crate) fn create_vtable_di_node<'ll, 'tcx>(
// When full debuginfo is enabled, we want to try and prevent vtables from being
// merged. Otherwise debuggers will have a hard time mapping from dyn pointer
// to concrete type.
llvm::SetUnnamedAddress(vtable, llvm::UnnamedAddr::No);
llvm::set_unnamed_address(vtable, llvm::UnnamedAddr::No);
let vtable_name =
compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref, VTableNameKind::GlobalVariable);

View file

@ -49,7 +49,7 @@ pub(crate) fn declare_simple_fn<'ll>(
};
llvm::SetFunctionCallConv(llfn, callconv);
llvm::SetUnnamedAddress(llfn, unnamed);
llvm::set_unnamed_address(llfn, unnamed);
llvm::set_visibility(llfn, visibility);
llfn
@ -176,7 +176,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
{
let typeid = cfi::typeid_for_instance(self.tcx, instance, options);
if typeids.insert(typeid.clone()) {
self.add_type_metadata(llfn, typeid);
self.add_type_metadata(llfn, typeid.as_bytes());
}
}
} else {
@ -189,7 +189,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
.map(cfi::TypeIdOptions::from_iter)
{
let typeid = cfi::typeid_for_fnabi(self.tcx, fn_abi, options);
self.add_type_metadata(llfn, typeid);
self.add_type_metadata(llfn, typeid.as_bytes());
}
}
}

View file

@ -1009,7 +1009,7 @@ unsafe extern "C" {
ModuleID: *const c_char,
C: &Context,
) -> &Module;
pub(crate) fn LLVMCloneModule(M: &Module) -> &Module;
pub(crate) safe fn LLVMCloneModule(M: &Module) -> &Module;
/// Data layout. See Module::getDataLayout.
pub(crate) fn LLVMGetDataLayoutStr(M: &Module) -> *const c_char;
@ -1168,18 +1168,18 @@ unsafe extern "C" {
pub(crate) fn LLVMGlobalGetValueType(Global: &Value) -> &Type;
// Operations on global variables
pub(crate) fn LLVMIsAGlobalVariable(GlobalVar: &Value) -> Option<&Value>;
pub(crate) safe fn LLVMIsAGlobalVariable(GlobalVar: &Value) -> Option<&Value>;
pub(crate) fn LLVMAddGlobal<'a>(M: &'a Module, Ty: &'a Type, Name: *const c_char) -> &'a Value;
pub(crate) fn LLVMGetNamedGlobal(M: &Module, Name: *const c_char) -> Option<&Value>;
pub(crate) fn LLVMGetFirstGlobal(M: &Module) -> Option<&Value>;
pub(crate) fn LLVMGetNextGlobal(GlobalVar: &Value) -> Option<&Value>;
pub(crate) fn LLVMDeleteGlobal(GlobalVar: &Value);
pub(crate) fn LLVMGetInitializer(GlobalVar: &Value) -> Option<&Value>;
pub(crate) safe fn LLVMGetInitializer(GlobalVar: &Value) -> Option<&Value>;
pub(crate) fn LLVMSetInitializer<'a>(GlobalVar: &'a Value, ConstantVal: &'a Value);
pub(crate) fn LLVMIsThreadLocal(GlobalVar: &Value) -> Bool;
pub(crate) safe fn LLVMIsThreadLocal(GlobalVar: &Value) -> Bool;
pub(crate) fn LLVMSetThreadLocalMode(GlobalVar: &Value, Mode: ThreadLocalMode);
pub(crate) fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool;
pub(crate) fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool);
pub(crate) safe fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool;
pub(crate) safe fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool);
pub(crate) safe fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool);
// Operations on attributes
@ -1718,7 +1718,7 @@ unsafe extern "C" {
pub(crate) safe fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value;
pub(crate) fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr);
pub(crate) safe fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr);
pub(crate) fn LLVMIsAConstantInt(value_ref: &Value) -> Option<&ConstantInt>;

View file

@ -217,10 +217,8 @@ pub(crate) fn SetUniqueComdat(llmod: &Module, val: &Value) {
set_comdat(llmod, val, &name);
}
pub(crate) fn SetUnnamedAddress(global: &Value, unnamed: UnnamedAddr) {
unsafe {
LLVMSetUnnamedAddress(global, unnamed);
}
pub(crate) fn set_unnamed_address(global: &Value, unnamed: UnnamedAddr) {
LLVMSetUnnamedAddress(global, unnamed);
}
pub(crate) fn set_thread_local_mode(global: &Value, mode: ThreadLocalMode) {
@ -260,9 +258,7 @@ pub(crate) fn set_initializer(llglobal: &Value, constant_val: &Value) {
}
pub(crate) fn set_global_constant(llglobal: &Value, is_constant: bool) {
unsafe {
LLVMSetGlobalConstant(llglobal, if is_constant { ffi::True } else { ffi::False });
}
LLVMSetGlobalConstant(llglobal, if is_constant { ffi::True } else { ffi::False });
}
pub(crate) fn get_linkage(llglobal: &Value) -> Linkage {

View file

@ -131,8 +131,8 @@ impl CodegenCx<'_, '_> {
}
// Thread-local variables generally don't support copy relocations.
let is_thread_local_var = unsafe { llvm::LLVMIsAGlobalVariable(llval) }
.is_some_and(|v| unsafe { llvm::LLVMIsThreadLocal(v) } == llvm::True);
let is_thread_local_var = llvm::LLVMIsAGlobalVariable(llval)
.is_some_and(|v| llvm::LLVMIsThreadLocal(v) == llvm::True);
if is_thread_local_var {
return false;
}

View file

@ -2,7 +2,7 @@ use std::borrow::Borrow;
use std::hash::{Hash, Hasher};
use std::{fmt, ptr};
use libc::{c_char, c_uint};
use libc::c_uint;
use rustc_abi::{AddressSpace, Align, Integer, Reg, Size};
use rustc_codegen_ssa::common::TypeKind;
use rustc_codegen_ssa::traits::*;
@ -298,8 +298,8 @@ impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
}
impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
fn add_type_metadata(&self, function: &'ll Value, typeid: String) {
let typeid_metadata = self.typeid_metadata(typeid).unwrap();
fn add_type_metadata(&self, function: &'ll Value, typeid: &[u8]) {
let typeid_metadata = self.create_metadata(typeid);
unsafe {
let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
llvm::LLVMRustGlobalAddMetadata(
@ -310,8 +310,8 @@ impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
}
}
fn set_type_metadata(&self, function: &'ll Value, typeid: String) {
let typeid_metadata = self.typeid_metadata(typeid).unwrap();
fn set_type_metadata(&self, function: &'ll Value, typeid: &[u8]) {
let typeid_metadata = self.create_metadata(typeid);
unsafe {
let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata];
llvm::LLVMGlobalSetMetadata(
@ -322,10 +322,8 @@ impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
}
}
fn typeid_metadata(&self, typeid: String) -> Option<&'ll Metadata> {
Some(unsafe {
llvm::LLVMMDStringInContext2(self.llcx, typeid.as_ptr() as *const c_char, typeid.len())
})
fn typeid_metadata(&self, typeid: &[u8]) -> Option<&'ll Metadata> {
Some(self.create_metadata(typeid))
}
fn add_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) {

View file

@ -139,7 +139,8 @@ pub(crate) fn load_vtable<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
&& bx.cx().sess().lto() == Lto::Fat
{
if let Some(trait_ref) = dyn_trait_in_self(bx.tcx(), ty) {
let typeid = bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), trait_ref)).unwrap();
let typeid =
bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), trait_ref).as_bytes()).unwrap();
let func = bx.type_checked_load(llvtable, vtable_byte_offset, typeid);
return func;
} else if nonnull {

View file

@ -154,9 +154,9 @@ pub trait LayoutTypeCodegenMethods<'tcx>: BackendTypes {
// For backends that support CFI using type membership (i.e., testing whether a given pointer is
// associated with a type identifier).
pub trait TypeMembershipCodegenMethods<'tcx>: BackendTypes {
fn add_type_metadata(&self, _function: Self::Function, _typeid: String) {}
fn set_type_metadata(&self, _function: Self::Function, _typeid: String) {}
fn typeid_metadata(&self, _typeid: String) -> Option<Self::Metadata> {
fn add_type_metadata(&self, _function: Self::Function, _typeid: &[u8]) {}
fn set_type_metadata(&self, _function: Self::Function, _typeid: &[u8]) {}
fn typeid_metadata(&self, _typeid: &[u8]) -> Option<Self::Metadata> {
None
}
fn add_kcfi_type_metadata(&self, _function: Self::Function, _typeid: u32) {}

View file

@ -296,19 +296,22 @@ const_eval_pointer_arithmetic_overflow =
overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize`
const_eval_pointer_out_of_bounds =
{const_eval_bad_pointer_op_attempting}, but got {$pointer} which {$inbounds_size_is_neg ->
[false] {$alloc_size_minus_ptr_offset ->
[0] is at or beyond the end of the allocation of size {$alloc_size ->
[1] 1 byte
*[x] {$alloc_size} bytes
{const_eval_bad_pointer_op_attempting}, but got {$pointer} which {$ptr_offset_is_neg ->
[true] points to before the beginning of the allocation
*[false] {$inbounds_size_is_neg ->
[false] {$alloc_size_minus_ptr_offset ->
[0] is at or beyond the end of the allocation of size {$alloc_size ->
[1] 1 byte
*[x] {$alloc_size} bytes
}
[1] is only 1 byte from the end of the allocation
*[x] is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation
}
*[true] {$ptr_offset_abs ->
[0] is at the beginning of the allocation
*[other] is only {$ptr_offset_abs} bytes from the beginning of the allocation
}
[1] is only 1 byte from the end of the allocation
*[x] is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation
}
*[true] {$ptr_offset_abs ->
[0] is at the beginning of the allocation
*[other] is only {$ptr_offset_abs} bytes from the beginning of the allocation
}
}
}
const_eval_pointer_use_after_free =

View file

@ -6,7 +6,7 @@ use std::borrow::Cow;
use either::{Left, Right};
use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx};
use rustc_hir::def_id::DefId;
use rustc_middle::ty::layout::{FnAbiOf, IntegerExt, TyAndLayout};
use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef};
use rustc_middle::{bug, mir, span_bug};
use rustc_span::sym;

View file

@ -7,8 +7,8 @@ use rustc_hir::def_id::DefId;
use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo};
use rustc_middle::query::TyCtxtAt;
use rustc_middle::ty::layout::{
self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers,
TyAndLayout,
self, FnAbiError, FnAbiOf, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf,
LayoutOfHelpers, TyAndLayout,
};
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypingEnv, Variance};
use rustc_middle::{mir, span_bug};
@ -92,20 +92,6 @@ impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> {
}
}
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// This inherent method takes priority over the trait method with the same name in LayoutOf,
/// and allows wrapping the actual [LayoutOf::layout_of] with a tracing span.
/// See [LayoutOf::layout_of] for the original documentation.
#[inline(always)]
pub fn layout_of(
&self,
ty: Ty<'tcx>,
) -> <InterpCx<'tcx, M> as LayoutOfHelpers<'tcx>>::LayoutOfResult {
let _span = enter_trace_span!(M, "InterpCx::layout_of", "ty = {:?}", ty.kind());
LayoutOf::layout_of(self, ty)
}
}
impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> {
type FnAbiOfResult = Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, InterpErrorKind<'tcx>>;
@ -121,6 +107,43 @@ impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> {
}
}
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// This inherent method takes priority over the trait method with the same name in LayoutOf,
/// and allows wrapping the actual [LayoutOf::layout_of] with a tracing span.
/// See [LayoutOf::layout_of] for the original documentation.
#[inline(always)]
pub fn layout_of(&self, ty: Ty<'tcx>) -> <Self as LayoutOfHelpers<'tcx>>::LayoutOfResult {
let _span = enter_trace_span!(M, "InterpCx::layout_of", ty = ?ty.kind());
LayoutOf::layout_of(self, ty)
}
/// This inherent method takes priority over the trait method with the same name in FnAbiOf,
/// and allows wrapping the actual [FnAbiOf::fn_abi_of_fn_ptr] with a tracing span.
/// See [FnAbiOf::fn_abi_of_fn_ptr] for the original documentation.
#[inline(always)]
pub fn fn_abi_of_fn_ptr(
&self,
sig: ty::PolyFnSig<'tcx>,
extra_args: &'tcx ty::List<Ty<'tcx>>,
) -> <Self as FnAbiOfHelpers<'tcx>>::FnAbiOfResult {
let _span = enter_trace_span!(M, "InterpCx::fn_abi_of_fn_ptr", ?sig, ?extra_args);
FnAbiOf::fn_abi_of_fn_ptr(self, sig, extra_args)
}
/// This inherent method takes priority over the trait method with the same name in FnAbiOf,
/// and allows wrapping the actual [FnAbiOf::fn_abi_of_instance] with a tracing span.
/// See [FnAbiOf::fn_abi_of_instance] for the original documentation.
#[inline(always)]
pub fn fn_abi_of_instance(
&self,
instance: ty::Instance<'tcx>,
extra_args: &'tcx ty::List<Ty<'tcx>>,
) -> <Self as FnAbiOfHelpers<'tcx>>::FnAbiOfResult {
let _span = enter_trace_span!(M, "InterpCx::fn_abi_of_instance", ?instance, ?extra_args);
FnAbiOf::fn_abi_of_instance(self, instance, extra_args)
}
}
/// Test if it is valid for a MIR assignment to assign `src`-typed place to `dest`-typed value.
/// This test should be symmetric, as it is primarily about layout compatibility.
pub(super) fn mir_assign_valid_types<'tcx>(

View file

@ -44,17 +44,21 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
)?;
self.copy_op_allow_transmute(&op, dest)?;
// Give the first pointer-size bytes provenance that knows about the type id.
// Give the each pointer-sized chunk provenance that knows about the type id.
// Here we rely on `TypeId` being a newtype around an array of pointers, so we
// first project to its only field and then the first array element.
// first project to its only field and then the array elements.
let alloc_id = tcx.reserve_and_set_type_id_alloc(ty);
let first = self.project_field(dest, FieldIdx::ZERO)?;
let first = self.project_index(&first, 0)?;
let offset = self.read_scalar(&first)?.to_target_usize(&tcx)?;
let ptr = Pointer::new(alloc_id.into(), Size::from_bytes(offset));
let ptr = self.global_root_pointer(ptr)?;
let val = Scalar::from_pointer(ptr, &tcx);
self.write_scalar(val, &first)
let mut elem_iter = self.project_array_fields(&first)?;
while let Some((_, elem)) = elem_iter.next(self)? {
// Decorate this part of the hash with provenance; leave the integer part unchanged.
let hash_fragment = self.read_scalar(&elem)?.to_target_usize(&tcx)?;
let ptr = Pointer::new(alloc_id.into(), Size::from_bytes(hash_fragment));
let ptr = self.global_root_pointer(ptr)?;
let val = Scalar::from_pointer(ptr, &tcx);
self.write_scalar(val, &elem)?;
}
interp_ok(())
}
/// Returns `true` if emulation happened.
@ -101,34 +105,36 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let mut a_fields = self.project_array_fields(&a_fields)?;
let mut b_fields = self.project_array_fields(&b_fields)?;
let (_idx, a) = a_fields
.next(self)?
.expect("we know the layout of TypeId has at least 2 array elements");
let a = self.deref_pointer(&a)?;
let (a, offset_a) = self.get_ptr_type_id(a.ptr())?;
let mut provenance_a = None;
let mut provenance_b = None;
let mut provenance_matches = true;
let (_idx, b) = b_fields
.next(self)?
.expect("we know the layout of TypeId has at least 2 array elements");
let b = self.deref_pointer(&b)?;
let (b, offset_b) = self.get_ptr_type_id(b.ptr())?;
let provenance_matches = a == b;
let mut eq_id = offset_a == offset_b;
while let Some((_, a)) = a_fields.next(self)? {
while let Some((i, a)) = a_fields.next(self)? {
let (_, b) = b_fields.next(self)?.unwrap();
let a = self.read_target_usize(&a)?;
let b = self.read_target_usize(&b)?;
eq_id &= a == b;
}
let a = self.deref_pointer(&a)?;
let (a, offset_a) = self.get_ptr_type_id(a.ptr())?;
if !eq_id && provenance_matches {
throw_ub_format!(
"type_id_eq: one of the TypeId arguments is invalid, the hash does not match the type it represents"
)
let b = self.deref_pointer(&b)?;
let (b, offset_b) = self.get_ptr_type_id(b.ptr())?;
if *provenance_a.get_or_insert(a) != a {
throw_ub_format!(
"type_id_eq: the first TypeId argument is invalid, the provenance of chunk {i} does not match the first chunk's"
)
}
if *provenance_b.get_or_insert(b) != b {
throw_ub_format!(
"type_id_eq: the second TypeId argument is invalid, the provenance of chunk {i} does not match the first chunk's"
)
}
provenance_matches &= a == b;
if offset_a != offset_b && provenance_matches {
throw_ub_format!(
"type_id_eq: one of the TypeId arguments is invalid, chunk {i} of the hash does not match the type it represents"
)
}
}
self.write_scalar(Scalar::from_bool(provenance_matches), dest)?;

View file

@ -5,7 +5,6 @@
use either::Either;
use rustc_abi::{FIRST_VARIANT, FieldIdx};
use rustc_index::IndexSlice;
use rustc_middle::ty::layout::FnAbiOf;
use rustc_middle::ty::{self, Instance, Ty};
use rustc_middle::{bug, mir, span_bug};
use rustc_span::source_map::Spanned;

View file

@ -3,22 +3,20 @@ The `not` cfg-predicate was malformed.
Erroneous code example:
```compile_fail,E0536
#[cfg(not())] // error: expected 1 cfg-pattern
pub fn something() {}
pub fn main() {}
pub fn main() {
if cfg!(not()) { }
}
```
The `not` predicate expects one cfg-pattern. Example:
```
#[cfg(not(target_os = "linux"))] // ok!
pub fn something() {}
pub fn main() {}
pub fn main() {
if cfg!(not(target_os = "linux")) { } // ok!
}
```
For more information about the `cfg` attribute, read the section on
For more information about the `cfg` macro, read the section on
[Conditional Compilation][conditional-compilation] in the Reference.
[conditional-compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html

View file

@ -11,7 +11,7 @@ use rustc_ast::token::MetaVarKind;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{AssocCtxt, Visitor};
use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind};
use rustc_attr_data_structures::{AttributeKind, Deprecation, Stability, find_attr};
use rustc_attr_data_structures::{AttributeKind, CfgEntry, Deprecation, Stability, find_attr};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_data_structures::sync;
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult};
@ -1128,7 +1128,13 @@ pub trait ResolverExpand {
/// HIR proc macros items back to their harness items.
fn declare_proc_macro(&mut self, id: NodeId);
fn append_stripped_cfg_item(&mut self, parent_node: NodeId, ident: Ident, cfg: ast::MetaItem);
fn append_stripped_cfg_item(
&mut self,
parent_node: NodeId,
ident: Ident,
cfg: CfgEntry,
cfg_span: Span,
);
/// Tools registered with `#![register_tool]` and used by tool attributes and lints.
fn registered_tools(&self) -> &RegisteredTools;

View file

@ -11,6 +11,9 @@ use rustc_ast::{
NodeId, NormalAttr,
};
use rustc_attr_parsing as attr;
use rustc_attr_parsing::{
AttributeParser, CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr,
};
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
use rustc_feature::{
ACCEPTED_LANG_FEATURES, AttributeSafety, EnabledLangFeature, EnabledLibFeature, Features,
@ -18,6 +21,7 @@ use rustc_feature::{
};
use rustc_lint_defs::BuiltinLintDiag;
use rustc_parse::validate_attr;
use rustc_parse::validate_attr::deny_builtin_meta_unsafety;
use rustc_session::Session;
use rustc_session::parse::feature_err;
use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym};
@ -161,7 +165,10 @@ pub fn pre_configure_attrs(sess: &Session, attrs: &[Attribute]) -> ast::AttrVec
attrs
.iter()
.flat_map(|attr| strip_unconfigured.process_cfg_attr(attr))
.take_while(|attr| !is_cfg(attr) || strip_unconfigured.cfg_true(attr).0)
.take_while(|attr| {
!is_cfg(attr)
|| strip_unconfigured.cfg_true(attr, strip_unconfigured.lint_node_id).as_bool()
})
.collect()
}
@ -394,26 +401,42 @@ impl<'a> StripUnconfigured<'a> {
/// Determines if a node with the given attributes should be included in this configuration.
fn in_cfg(&self, attrs: &[Attribute]) -> bool {
attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr).0)
attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr, self.lint_node_id).as_bool())
}
pub(crate) fn cfg_true(&self, attr: &Attribute) -> (bool, Option<MetaItem>) {
let meta_item = match validate_attr::parse_meta(&self.sess.psess, attr) {
Ok(meta_item) => meta_item,
pub(crate) fn cfg_true(&self, attr: &Attribute, node: NodeId) -> EvalConfigResult {
// We need to run this to do basic validation of the attribute, such as that lits are valid, etc
// FIXME(jdonszelmann) this should not be necessary in the future
match validate_attr::parse_meta(&self.sess.psess, attr) {
Ok(_) => {}
Err(err) => {
err.emit();
return (true, None);
return EvalConfigResult::True;
}
}
// Unsafety check needs to be done explicitly here because this attribute will be removed before the normal check
deny_builtin_meta_unsafety(
self.sess.dcx(),
attr.get_normal_item().unsafety,
&rustc_ast::Path::from_ident(attr.ident().unwrap()),
);
let Some(cfg) = AttributeParser::parse_single(
self.sess,
attr,
attr.span,
node,
self.features,
true,
parse_cfg_attr,
&CFG_TEMPLATE,
) else {
// Cfg attribute was not parsable, give up
return EvalConfigResult::True;
};
validate_attr::deny_builtin_meta_unsafety(&self.sess.psess, &meta_item);
(
parse_cfg(&meta_item, self.sess).is_none_or(|meta_item| {
attr::cfg_matches(meta_item, &self.sess, self.lint_node_id, self.features)
}),
Some(meta_item),
)
eval_config_entry(self.sess, &cfg, self.lint_node_id, self.features)
}
/// If attributes are not allowed on expressions, emit an error for `attr`
@ -465,6 +488,7 @@ impl<'a> StripUnconfigured<'a> {
}
}
/// FIXME: Still used by Rustdoc, should be removed after
pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a MetaItemInner> {
let span = meta_item.span;
match meta_item.meta_item_list() {

View file

@ -13,6 +13,7 @@ use rustc_ast::{
MetaItemKind, ModKind, NodeId, PatKind, StmtKind, TyKind, token,
};
use rustc_ast_pretty::pprust;
use rustc_attr_parsing::EvalConfigResult;
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
use rustc_errors::PResult;
use rustc_feature::Features;
@ -2166,19 +2167,19 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
fn expand_cfg_true(
&mut self,
node: &mut impl HasAttrs,
node: &mut (impl HasAttrs + HasNodeId),
attr: ast::Attribute,
pos: usize,
) -> (bool, Option<ast::MetaItem>) {
let (res, meta_item) = self.cfg().cfg_true(&attr);
if res {
) -> EvalConfigResult {
let res = self.cfg().cfg_true(&attr, node.node_id());
if res.as_bool() {
// A trace attribute left in AST in place of the original `cfg` attribute.
// It can later be used by lints or other diagnostics.
let trace_attr = attr_into_trace(attr, sym::cfg_trace);
node.visit_attrs(|attrs| attrs.insert(pos, trace_attr));
}
(res, meta_item)
res
}
fn expand_cfg_attr(&self, node: &mut impl HasAttrs, attr: &ast::Attribute, pos: usize) {
@ -2199,20 +2200,21 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
return match self.take_first_attr(&mut node) {
Some((attr, pos, derives)) => match attr.name() {
Some(sym::cfg) => {
let (res, meta_item) = self.expand_cfg_true(&mut node, attr, pos);
if res {
continue;
}
if let Some(meta_item) = meta_item {
for ident in node.declared_idents() {
self.cx.resolver.append_stripped_cfg_item(
self.cx.current_expansion.lint_node_id,
ident,
meta_item.clone(),
)
let res = self.expand_cfg_true(&mut node, attr, pos);
match res {
EvalConfigResult::True => continue,
EvalConfigResult::False { reason, reason_span } => {
for ident in node.declared_idents() {
self.cx.resolver.append_stripped_cfg_item(
self.cx.current_expansion.lint_node_id,
ident,
reason.clone(),
reason_span,
)
}
}
}
Default::default()
}
Some(sym::cfg_attr) => {
@ -2291,7 +2293,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
Some((attr, pos, derives)) => match attr.name() {
Some(sym::cfg) => {
let span = attr.span;
if self.expand_cfg_true(node, attr, pos).0 {
if self.expand_cfg_true(node, attr, pos).as_bool() {
continue;
}

View file

@ -295,6 +295,12 @@ impl DefKind {
}
}
/// This is a "module" in name resolution sense.
#[inline]
pub fn is_module_like(self) -> bool {
matches!(self, DefKind::Mod | DefKind::Enum | DefKind::Trait)
}
#[inline]
pub fn is_fn_like(self) -> bool {
matches!(
@ -720,6 +726,15 @@ impl<Id> Res<Id> {
}
}
/// If this is a "module" in name resolution sense, return its `DefId`.
#[inline]
pub fn module_like_def_id(&self) -> Option<DefId> {
match self {
Res::Def(def_kind, def_id) if def_kind.is_module_like() => Some(*def_id),
_ => None,
}
}
/// A human readable name for the res kind ("function", "module", etc.).
pub fn descr(&self) -> &'static str {
match *self {

View file

@ -49,7 +49,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
} = self.lower_poly_trait_ref(
&trait_bound.trait_ref,
trait_bound.span,
hir::BoundConstness::Never,
trait_bound.modifiers.constness,
hir::BoundPolarity::Positive,
dummy_self,
&mut user_written_bounds,

View file

@ -167,7 +167,7 @@ impl NonCamelCaseTypes {
impl EarlyLintPass for NonCamelCaseTypes {
fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
let has_repr_c = matches!(
AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, it.id),
AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, it.id, None),
Some(Attribute::Parsed(AttributeKind::Repr { reprs, ..})) if reprs.iter().any(|(r, _)| r == &ReprAttr::ReprC)
);

View file

@ -3,7 +3,7 @@ use std::mem;
use std::sync::Arc;
use rustc_attr_data_structures::Deprecation;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def::{CtorKind, DefKind};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE};
use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
use rustc_middle::arena::ArenaAllocatable;
@ -510,10 +510,7 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
}
Entry::Vacant(entry) => {
entry.insert(parent);
if matches!(
child.res,
Res::Def(DefKind::Mod | DefKind::Enum | DefKind::Trait, _)
) {
if child.res.module_like_def_id().is_some() {
bfs_queue.push_back(def_id);
}
}

View file

@ -7,7 +7,7 @@ use def_path_hash_map::DefPathHashMapRef;
use encoder::EncodeContext;
pub use encoder::{EncodedMetadata, encode_metadata, rendered_const};
use rustc_abi::{FieldIdx, ReprOptions, VariantIdx};
use rustc_ast::expand::StrippedCfgItem;
use rustc_attr_data_structures::StrippedCfgItem;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::svh::Svh;
use rustc_hir::PreciseCapturingArgKind;

View file

@ -110,7 +110,7 @@ macro_rules! arena_types {
[] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData<rustc_middle::ty::TyCtxt<'tcx>>,
[] predefined_opaques_in_body: rustc_middle::traits::solve::PredefinedOpaquesData<rustc_middle::ty::TyCtxt<'tcx>>,
[decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap,
[] stripped_cfg_items: rustc_ast::expand::StrippedCfgItem,
[] stripped_cfg_items: rustc_attr_data_structures::StrippedCfgItem,
[] mod_child: rustc_middle::metadata::ModChild,
[] features: rustc_feature::Features,
[decode] specialization_graph: rustc_middle::traits::specialization_graph::Graph,

View file

@ -69,8 +69,8 @@ use std::sync::Arc;
use rustc_abi::Align;
use rustc_arena::TypedArena;
use rustc_ast::expand::StrippedCfgItem;
use rustc_ast::expand::allocator::AllocatorKind;
use rustc_attr_data_structures::StrippedCfgItem;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::sorted_map::SortedMap;

View file

@ -25,10 +25,9 @@ pub use generic_args::{GenericArgKind, TermKind, *};
pub use generics::*;
pub use intrinsic::IntrinsicDef;
use rustc_abi::{Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, VariantIdx};
use rustc_ast::expand::StrippedCfgItem;
use rustc_ast::node_id::NodeMap;
pub use rustc_ast_ir::{Movability, Mutability, try_visit};
use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_attr_data_structures::{AttributeKind, StrippedCfgItem, find_attr};
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::intern::Interned;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};

View file

@ -87,7 +87,7 @@ trivially_parameterized_over_tcx! {
ty::IntrinsicDef,
rustc_ast::Attribute,
rustc_ast::DelimArgs,
rustc_ast::expand::StrippedCfgItem<rustc_hir::def_id::DefIndex>,
rustc_attr_data_structures::StrippedCfgItem<rustc_hir::def_id::DefIndex>,
rustc_attr_data_structures::ConstStability,
rustc_attr_data_structures::DefaultBodyStability,
rustc_attr_data_structures::Deprecation,

View file

@ -3454,9 +3454,7 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N
collect_fn(&child.ident, ns, def_id);
}
if matches!(defkind, DefKind::Mod | DefKind::Enum | DefKind::Trait)
&& seen_defs.insert(def_id)
{
if defkind.is_module_like() && seen_defs.insert(def_id) {
queue.push(def_id);
}
}

View file

@ -7,7 +7,7 @@ use rustc_span::Span;
use crate::exp;
use crate::parser::Parser;
pub enum CfgSelectRule {
pub enum CfgSelectPredicate {
Cfg(MetaItemInner),
Wildcard(Token),
}
@ -20,7 +20,7 @@ pub struct CfgSelectBranches {
pub wildcard: Option<(Token, TokenStream, Span)>,
/// All branches after the first wildcard, including further wildcards.
/// These branches are kept for formatting.
pub unreachable: Vec<(CfgSelectRule, TokenStream, Span)>,
pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>,
}
/// Parses a `TokenTree` that must be of the form `{ /* ... */ }`, and returns a `TokenStream` where
@ -52,7 +52,7 @@ pub fn parse_cfg_select<'a>(p: &mut Parser<'a>) -> PResult<'a, CfgSelectBranches
match branches.wildcard {
None => branches.wildcard = Some((underscore, tts, span)),
Some(_) => {
branches.unreachable.push((CfgSelectRule::Wildcard(underscore), tts, span))
branches.unreachable.push((CfgSelectPredicate::Wildcard(underscore), tts, span))
}
}
} else {
@ -64,7 +64,9 @@ pub fn parse_cfg_select<'a>(p: &mut Parser<'a>) -> PResult<'a, CfgSelectBranches
match branches.wildcard {
None => branches.reachable.push((meta_item, tts, span)),
Some(_) => branches.unreachable.push((CfgSelectRule::Cfg(meta_item), tts, span)),
Some(_) => {
branches.unreachable.push((CfgSelectPredicate::Cfg(meta_item), tts, span))
}
}
}
}

View file

@ -575,14 +575,69 @@ impl<'a> Parser<'a> {
self.expect(exp!(CloseBracket))?;
}
TyKind::Array(elt_ty, length)
} else {
self.expect(exp!(CloseBracket))?;
} else if self.eat(exp!(CloseBracket)) {
TyKind::Slice(elt_ty)
} else {
self.maybe_recover_array_ty_without_semi(elt_ty)?
};
Ok(ty)
}
/// Recover from malformed array type syntax.
///
/// This method attempts to recover from cases like:
/// - `[u8, 5]` → suggests using `;`, return a Array type
/// - `[u8 5]` → suggests using `;`, return a Array type
/// Consider to add more cases in the future.
fn maybe_recover_array_ty_without_semi(&mut self, elt_ty: P<Ty>) -> PResult<'a, TyKind> {
let span = self.token.span;
let token_descr = super::token_descr(&self.token);
let mut err =
self.dcx().struct_span_err(span, format!("expected `;` or `]`, found {}", token_descr));
err.span_label(span, "expected `;` or `]`");
err.note("you might have meant to write a slice or array type");
// If we cannot recover, return the error immediately.
if !self.may_recover() {
return Err(err);
}
let snapshot = self.create_snapshot_for_diagnostic();
let suggestion_span = if self.eat(exp!(Comma)) || self.eat(exp!(Star)) {
// Consume common erroneous separators.
self.prev_token.span
} else {
self.token.span.shrink_to_lo()
};
// we first try to parse pattern like `[u8 5]`
let length = match self.parse_expr_anon_const() {
Ok(length) => length,
Err(e) => {
e.cancel();
self.restore_snapshot(snapshot);
return Err(err);
}
};
if let Err(e) = self.expect(exp!(CloseBracket)) {
e.cancel();
self.restore_snapshot(snapshot);
return Err(err);
}
err.span_suggestion_verbose(
suggestion_span,
"you might have meant to use `;` as the separator",
";",
Applicability::MaybeIncorrect,
);
err.emit();
Ok(TyKind::Array(elt_ty, length))
}
fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> {
let and_span = self.prev_token.span;
let mut opt_lifetime = self.check_lifetime().then(|| self.expect_lifetime());

View file

@ -4,9 +4,9 @@ use rustc_ast::token::Delimiter;
use rustc_ast::tokenstream::DelimSpan;
use rustc_ast::{
self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, NodeId,
Safety,
Path, Safety,
};
use rustc_errors::{Applicability, FatalError, PResult};
use rustc_errors::{Applicability, DiagCtxtHandle, FatalError, PResult};
use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
use rustc_session::errors::report_lit_error;
use rustc_session::lint::BuiltinLintDiag;
@ -247,14 +247,12 @@ pub fn check_attribute_safety(
// Called by `check_builtin_meta_item` and code that manually denies
// `unsafe(...)` in `cfg`
pub fn deny_builtin_meta_unsafety(psess: &ParseSess, meta: &MetaItem) {
pub fn deny_builtin_meta_unsafety(diag: DiagCtxtHandle<'_>, unsafety: Safety, name: &Path) {
// This only supports denying unsafety right now - making builtin attributes
// support unsafety will requite us to thread the actual `Attribute` through
// for the nice diagnostics.
if let Safety::Unsafe(unsafe_span) = meta.unsafety {
psess
.dcx()
.emit_err(errors::InvalidAttrUnsafe { span: unsafe_span, name: meta.path.clone() });
if let Safety::Unsafe(unsafe_span) = unsafety {
diag.emit_err(errors::InvalidAttrUnsafe { span: unsafe_span, name: name.clone() });
}
}
@ -299,6 +297,7 @@ pub fn check_builtin_meta_item(
| sym::align
| sym::deprecated
| sym::optimize
| sym::pointee
| sym::cold
| sym::target_feature
| sym::rustc_allow_const_fn_unstable
@ -326,7 +325,7 @@ pub fn check_builtin_meta_item(
}
if deny_unsafety {
deny_builtin_meta_unsafety(psess, meta);
deny_builtin_meta_unsafety(psess.dcx(), meta.unsafety, &meta.path);
}
}

View file

@ -251,6 +251,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
AttributeKind::BodyStability { .. }
| AttributeKind::ConstStabilityIndirect
| AttributeKind::MacroTransparency(_)
| AttributeKind::Pointee(..)
| AttributeKind::Dummy
| AttributeKind::OmitGdbPrettyPrinterSection,
) => { /* do nothing */ }
@ -381,7 +382,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| sym::cfg_attr_trace
// need to be fixed
| sym::cfi_encoding // FIXME(cfi_encoding)
| sym::pointee // FIXME(derive_coerce_pointee)
| sym::instruction_set // broken on stable!!!
| sym::windows_subsystem // broken on stable!!!
| sym::patchable_function_entry // FIXME(patchable_function_entry)

View file

@ -20,9 +20,9 @@ use rustc_hir::def::{self, *};
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
use rustc_index::bit_set::DenseBitSet;
use rustc_metadata::creader::LoadedMacro;
use rustc_middle::bug;
use rustc_middle::metadata::ModChild;
use rustc_middle::ty::Feed;
use rustc_middle::{bug, ty};
use rustc_middle::ty::{Feed, Visibility};
use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind};
use rustc_span::{Ident, Span, Symbol, kw, sym};
use tracing::debug;
@ -33,54 +33,42 @@ use crate::imports::{ImportData, ImportKind};
use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
use crate::{
BindingKey, ExternPreludeEntry, Finalize, MacroData, Module, ModuleKind, ModuleOrUniformRoot,
NameBinding, NameBindingData, NameBindingKind, ParentScope, PathResult, ResolutionError,
Resolver, ResolverArenas, Segment, ToNameBinding, Used, VisResolutionError, errors,
NameBinding, ParentScope, PathResult, ResolutionError, Resolver, Segment, Used,
VisResolutionError, errors,
};
type Res = def::Res<NodeId>;
impl<'ra, Id: Into<DefId>> ToNameBinding<'ra>
for (Module<'ra>, ty::Visibility<Id>, Span, LocalExpnId)
{
fn to_name_binding(self, arenas: &'ra ResolverArenas<'ra>) -> NameBinding<'ra> {
arenas.alloc_name_binding(NameBindingData {
kind: NameBindingKind::Module(self.0),
ambiguity: None,
warn_ambiguity: false,
vis: self.1.to_def_id(),
span: self.2,
expansion: self.3,
})
}
}
impl<'ra, Id: Into<DefId>> ToNameBinding<'ra> for (Res, ty::Visibility<Id>, Span, LocalExpnId) {
fn to_name_binding(self, arenas: &'ra ResolverArenas<'ra>) -> NameBinding<'ra> {
arenas.alloc_name_binding(NameBindingData {
kind: NameBindingKind::Res(self.0),
ambiguity: None,
warn_ambiguity: false,
vis: self.1.to_def_id(),
span: self.2,
expansion: self.3,
})
}
}
impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
/// Defines `name` in namespace `ns` of module `parent` to be `def` if it is not yet defined;
/// otherwise, reports an error.
pub(crate) fn define<T>(&mut self, parent: Module<'ra>, ident: Ident, ns: Namespace, def: T)
where
T: ToNameBinding<'ra>,
{
let binding = def.to_name_binding(self.arenas);
pub(crate) fn define_binding(
&mut self,
parent: Module<'ra>,
ident: Ident,
ns: Namespace,
binding: NameBinding<'ra>,
) {
let key = self.new_disambiguated_key(ident, ns);
if let Err(old_binding) = self.try_define(parent, key, binding, false) {
self.report_conflict(parent, ident, ns, old_binding, binding);
}
}
fn define(
&mut self,
parent: Module<'ra>,
ident: Ident,
ns: Namespace,
res: Res,
vis: Visibility<impl Into<DefId>>,
span: Span,
expn_id: LocalExpnId,
) {
let binding = self.arenas.new_res_binding(res, vis.to_def_id(), span, expn_id);
self.define_binding(parent, ident, ns, binding)
}
/// Walks up the tree of definitions starting at `def_id`,
/// stopping at the first encountered module.
/// Parent block modules for arbitrary def-ids are not recorded for the local crate,
@ -121,7 +109,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
if !def_id.is_local() {
// Query `def_kind` is not used because query system overhead is too expensive here.
let def_kind = self.cstore().def_kind_untracked(def_id);
if let DefKind::Mod | DefKind::Enum | DefKind::Trait = def_kind {
if def_kind.is_module_like() {
let parent = self
.tcx
.opt_parent(def_id)
@ -223,12 +211,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let expansion = parent_scope.expansion;
// Record primary definitions.
match res {
Res::Def(DefKind::Mod | DefKind::Enum | DefKind::Trait, def_id) => {
let module = self.expect_module(def_id);
self.define(parent, ident, TypeNS, (module, vis, span, expansion));
}
Res::Def(
DefKind::Struct
DefKind::Mod
| DefKind::Enum
| DefKind::Trait
| DefKind::Struct
| DefKind::Union
| DefKind::Variant
| DefKind::TyAlias
@ -239,7 +226,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
_,
)
| Res::PrimTy(..)
| Res::ToolMod => self.define(parent, ident, TypeNS, (res, vis, span, expansion)),
| Res::ToolMod => self.define(parent, ident, TypeNS, res, vis, span, expansion),
Res::Def(
DefKind::Fn
| DefKind::AssocFn
@ -248,9 +235,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
| DefKind::AssocConst
| DefKind::Ctor(..),
_,
) => self.define(parent, ident, ValueNS, (res, vis, span, expansion)),
) => self.define(parent, ident, ValueNS, res, vis, span, expansion),
Res::Def(DefKind::Macro(..), _) | Res::NonMacroAttr(..) => {
self.define(parent, ident, MacroNS, (res, vis, span, expansion))
self.define(parent, ident, MacroNS, res, vis, span, expansion)
}
Res::Def(
DefKind::TyParam
@ -294,10 +281,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
Res::Def(self.r.tcx.def_kind(def_id), def_id)
}
fn resolve_visibility(&mut self, vis: &ast::Visibility) -> ty::Visibility {
fn resolve_visibility(&mut self, vis: &ast::Visibility) -> Visibility {
self.try_resolve_visibility(vis, true).unwrap_or_else(|err| {
self.r.report_vis_error(err);
ty::Visibility::Public
Visibility::Public
})
}
@ -305,10 +292,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
&mut self,
vis: &'ast ast::Visibility,
finalize: bool,
) -> Result<ty::Visibility, VisResolutionError<'ast>> {
) -> Result<Visibility, VisResolutionError<'ast>> {
let parent_scope = &self.parent_scope;
match vis.kind {
ast::VisibilityKind::Public => Ok(ty::Visibility::Public),
ast::VisibilityKind::Public => Ok(Visibility::Public),
ast::VisibilityKind::Inherited => {
Ok(match self.parent_scope.module.kind {
// Any inherited visibility resolved directly inside an enum or trait
@ -318,7 +305,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
self.r.tcx.visibility(def_id).expect_local()
}
// Otherwise, the visibility is restricted to the nearest parent `mod` item.
_ => ty::Visibility::Restricted(
_ => Visibility::Restricted(
self.parent_scope.module.nearest_parent_mod().expect_local(),
),
})
@ -366,9 +353,9 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
}
if module.is_normal() {
match res {
Res::Err => Ok(ty::Visibility::Public),
Res::Err => Ok(Visibility::Public),
_ => {
let vis = ty::Visibility::Restricted(res.def_id());
let vis = Visibility::Restricted(res.def_id());
if self.r.is_accessible_from(vis, parent_scope.module) {
Ok(vis.expect_local())
} else {
@ -433,7 +420,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
item: &ast::Item,
root_span: Span,
root_id: NodeId,
vis: ty::Visibility,
vis: Visibility,
) {
let current_module = self.parent_scope.module;
let import = self.r.arenas.alloc_import(ImportData {
@ -481,7 +468,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
list_stem: bool,
// The whole `use` item
item: &Item,
vis: ty::Visibility,
vis: Visibility,
root_span: Span,
) {
debug!(
@ -690,7 +677,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
true,
// The whole `use` item
item,
ty::Visibility::Restricted(
Visibility::Restricted(
self.parent_scope.module.nearest_parent_mod().expect_local(),
),
root_span,
@ -706,7 +693,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
ident: Ident,
feed: Feed<'tcx, LocalDefId>,
adt_res: Res,
adt_vis: ty::Visibility,
adt_vis: Visibility,
adt_span: Span,
) {
let parent_scope = &self.parent_scope;
@ -714,7 +701,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
let expansion = parent_scope.expansion;
// Define a name in the type namespace if it is not anonymous.
self.r.define(parent, ident, TypeNS, (adt_res, adt_vis, adt_span, expansion));
self.r.define(parent, ident, TypeNS, adt_res, adt_vis, adt_span, expansion);
self.r.feed_visibility(feed, adt_vis);
let def_id = feed.key();
@ -766,7 +753,12 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
}
ItemKind::Mod(_, ident, ref mod_kind) => {
let module = self.r.new_module(
self.r.define(parent, ident, TypeNS, res, vis, sp, expansion);
if let ast::ModKind::Loaded(_, _, _, Err(_)) = mod_kind {
self.r.mods_with_parse_errors.insert(def_id);
}
self.parent_scope.module = self.r.new_module(
Some(parent),
ModuleKind::Def(def_kind, def_id, Some(ident.name)),
expansion.to_expn_id(),
@ -774,24 +766,16 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
parent.no_implicit_prelude
|| ast::attr::contains_name(&item.attrs, sym::no_implicit_prelude),
);
self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion));
if let ast::ModKind::Loaded(_, _, _, Err(_)) = mod_kind {
self.r.mods_with_parse_errors.insert(def_id);
}
// Descend into the module.
self.parent_scope.module = module;
}
// These items live in the value namespace.
ItemKind::Const(box ConstItem { ident, .. })
| ItemKind::Delegation(box Delegation { ident, .. })
| ItemKind::Static(box StaticItem { ident, .. }) => {
self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion));
self.r.define(parent, ident, ValueNS, res, vis, sp, expansion);
}
ItemKind::Fn(box Fn { ident, .. }) => {
self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion));
self.r.define(parent, ident, ValueNS, res, vis, sp, expansion);
// Functions introducing procedural macros reserve a slot
// in the macro namespace as well (see #52225).
@ -800,19 +784,19 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
// These items live in the type namespace.
ItemKind::TyAlias(box TyAlias { ident, .. }) | ItemKind::TraitAlias(ident, ..) => {
self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion));
self.r.define(parent, ident, TypeNS, res, vis, sp, expansion);
}
ItemKind::Enum(ident, _, _) | ItemKind::Trait(box ast::Trait { ident, .. }) => {
let module = self.r.new_module(
self.r.define(parent, ident, TypeNS, res, vis, sp, expansion);
self.parent_scope.module = self.r.new_module(
Some(parent),
ModuleKind::Def(def_kind, def_id, Some(ident.name)),
expansion.to_expn_id(),
item.span,
parent.no_implicit_prelude,
);
self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion));
self.parent_scope.module = module;
}
// These items live in both the type and value namespaces.
@ -834,7 +818,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
let mut ctor_vis = if vis.is_public()
&& ast::attr::contains_name(&item.attrs, sym::non_exhaustive)
{
ty::Visibility::Restricted(CRATE_DEF_ID)
Visibility::Restricted(CRATE_DEF_ID)
} else {
vis
};
@ -847,7 +831,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
// constructor visibility should still be determined correctly.
let field_vis = self
.try_resolve_visibility(&field.vis, false)
.unwrap_or(ty::Visibility::Public);
.unwrap_or(Visibility::Public);
if ctor_vis.is_at_least(field_vis, self.r.tcx) {
ctor_vis = field_vis;
}
@ -856,7 +840,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
let feed = self.r.feed(ctor_node_id);
let ctor_def_id = feed.key();
let ctor_res = self.res(ctor_def_id);
self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, sp, expansion));
self.r.define(parent, ident, ValueNS, ctor_res, ctor_vis, sp, expansion);
self.r.feed_visibility(feed, ctor_vis);
// We need the field visibility spans also for the constructor for E0603.
self.insert_field_visibilities_local(ctor_def_id.to_def_id(), vdata.fields());
@ -896,7 +880,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
item: &Item,
ident: Ident,
local_def_id: LocalDefId,
vis: ty::Visibility,
vis: Visibility,
parent: Module<'ra>,
) {
let sp = item.span;
@ -920,8 +904,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
}
.map(|module| {
let used = self.process_macro_use_imports(item, module);
let vis = ty::Visibility::<LocalDefId>::Public;
let binding = (module, vis, sp, expansion).to_name_binding(self.r.arenas);
let binding = self.r.arenas.new_pub_res_binding(module.res().unwrap(), sp, expansion);
(used, Some(ModuleOrUniformRoot::Module(module)), binding)
})
.unwrap_or((true, None, self.r.dummy_binding));
@ -978,7 +961,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
);
}
}
self.r.define(parent, ident, TypeNS, imported_binding);
self.r.define_binding(parent, ident, TypeNS, imported_binding);
}
/// Constructs the reduced graph for one foreign item.
@ -995,7 +978,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
let parent = self.parent_scope.module;
let expansion = self.parent_scope.expansion;
let vis = self.resolve_visibility(&item.vis);
self.r.define(parent, ident, ns, (self.res(def_id), vis, item.span, expansion));
self.r.define(parent, ident, ns, self.res(def_id), vis, item.span, expansion);
self.r.feed_visibility(feed, vis);
}
@ -1081,7 +1064,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
root_span: span,
span,
module_path: Vec::new(),
vis: ty::Visibility::Restricted(CRATE_DEF_ID),
vis: Visibility::Restricted(CRATE_DEF_ID),
})
};
@ -1231,11 +1214,11 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
self.r.macro_names.insert(ident);
let is_macro_export = ast::attr::contains_name(&item.attrs, sym::macro_export);
let vis = if is_macro_export {
ty::Visibility::Public
Visibility::Public
} else {
ty::Visibility::Restricted(CRATE_DEF_ID)
Visibility::Restricted(CRATE_DEF_ID)
};
let binding = (res, vis, span, expansion).to_name_binding(self.r.arenas);
let binding = self.r.arenas.new_res_binding(res, vis.to_def_id(), span, expansion);
self.r.set_binding_parent_module(binding, parent_scope.module);
self.r.all_macro_rules.insert(ident.name);
if is_macro_export {
@ -1254,7 +1237,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
});
self.r.import_use_map.insert(import, Used::Other);
let import_binding = self.r.import(binding, import);
self.r.define(self.r.graph_root, ident, MacroNS, import_binding);
self.r.define_binding(self.r.graph_root, ident, MacroNS, import_binding);
} else {
self.r.check_reserved_macro_name(ident, res);
self.insert_unused_macro(ident, def_id, item.id);
@ -1275,14 +1258,14 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
// Visibilities must not be resolved non-speculatively twice
// and we already resolved this one as a `fn` item visibility.
ItemKind::Fn(..) => {
self.try_resolve_visibility(&item.vis, false).unwrap_or(ty::Visibility::Public)
self.try_resolve_visibility(&item.vis, false).unwrap_or(Visibility::Public)
}
_ => self.resolve_visibility(&item.vis),
};
if !vis.is_public() {
self.insert_unused_macro(ident, def_id, item.id);
}
self.r.define(module, ident, MacroNS, (res, vis, span, expansion));
self.r.define(module, ident, MacroNS, res, vis, span, expansion);
self.r.feed_visibility(feed, vis);
self.parent_scope.macro_rules
}
@ -1418,7 +1401,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
if ctxt == AssocCtxt::Trait {
let parent = self.parent_scope.module;
let expansion = self.parent_scope.expansion;
self.r.define(parent, ident, ns, (self.res(def_id), vis, item.span, expansion));
self.r.define(parent, ident, ns, self.res(def_id), vis, item.span, expansion);
} else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob) {
let impl_def_id = self.r.tcx.local_parent(local_def_id);
let key = BindingKey::new(ident.normalize_to_macros_2_0(), ns);
@ -1503,13 +1486,13 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
let feed = self.r.feed(variant.id);
let def_id = feed.key();
let vis = self.resolve_visibility(&variant.vis);
self.r.define(parent, ident, TypeNS, (self.res(def_id), vis, variant.span, expn_id));
self.r.define(parent, ident, TypeNS, self.res(def_id), vis, variant.span, expn_id);
self.r.feed_visibility(feed, vis);
// If the variant is marked as non_exhaustive then lower the visibility to within the crate.
let ctor_vis =
if vis.is_public() && ast::attr::contains_name(&variant.attrs, sym::non_exhaustive) {
ty::Visibility::Restricted(CRATE_DEF_ID)
Visibility::Restricted(CRATE_DEF_ID)
} else {
vis
};
@ -1519,7 +1502,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
let feed = self.r.feed(ctor_node_id);
let ctor_def_id = feed.key();
let ctor_res = self.res(ctor_def_id);
self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, variant.span, expn_id));
self.r.define(parent, ident, ValueNS, ctor_res, ctor_vis, variant.span, expn_id);
self.r.feed_visibility(feed, ctor_vis);
}

View file

@ -132,6 +132,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
&self.resolver.tcx.sess,
self.resolver.tcx.features(),
Vec::new(),
Early { emit_errors: false },
);
let attrs = parser.parse_attribute_list(
&i.attrs,

View file

@ -1,11 +1,10 @@
use rustc_ast::expand::StrippedCfgItem;
use rustc_ast::ptr::P;
use rustc_ast::visit::{self, Visitor};
use rustc_ast::{
self as ast, CRATE_NODE_ID, Crate, ItemKind, MetaItemInner, MetaItemKind, ModKind, NodeId, Path,
};
use rustc_ast::{self as ast, CRATE_NODE_ID, Crate, ItemKind, ModKind, NodeId, Path};
use rustc_ast_pretty::pprust;
use rustc_attr_data_structures::{self as attr, AttributeKind, Stability, find_attr};
use rustc_attr_data_structures::{
self as attr, AttributeKind, CfgEntry, Stability, StrippedCfgItem, find_attr,
};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_errors::codes::*;
@ -233,12 +232,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
return;
}
let old_kind = match (ns, old_binding.module()) {
let old_kind = match (ns, old_binding.res()) {
(ValueNS, _) => "value",
(MacroNS, _) => "macro",
(TypeNS, _) if old_binding.is_extern_crate() => "extern crate",
(TypeNS, Some(module)) if module.is_normal() => "module",
(TypeNS, Some(module)) if module.is_trait() => "trait",
(TypeNS, Res::Def(DefKind::Mod, _)) => "module",
(TypeNS, Res::Def(DefKind::Trait, _)) => "trait",
(TypeNS, _) => "type",
};
@ -1320,7 +1319,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
// collect submodules to explore
if let Some(module) = name_binding.module() {
if let Some(def_id) = name_binding.res().module_like_def_id() {
// form the path
let mut path_segments = path_segments.clone();
path_segments.push(ast::PathSegment::from_ident(ident));
@ -1340,14 +1339,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
if !is_extern_crate_that_also_appears_in_prelude || alias_import {
// add the module to the lookup
if seen_modules.insert(module.def_id()) {
if seen_modules.insert(def_id) {
if via_import { &mut worklist_via_import } else { &mut worklist }.push(
(
module,
this.expect_module(def_id),
path_segments,
child_accessible,
child_doc_visible,
is_stable && this.is_stable(module.def_id(), name_binding.span),
is_stable && this.is_stable(def_id, name_binding.span),
),
);
}
@ -2095,7 +2094,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
true, // re-export
));
}
NameBindingKind::Res(_) | NameBindingKind::Module(_) => {}
NameBindingKind::Res(_) => {}
}
let first = binding == first_binding;
let def_span = self.tcx.sess.source_map().guess_head_span(binding.span);
@ -2307,25 +2306,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
.ok()
};
if let Some(binding) = binding {
let mut found = |what| {
msg = format!(
"expected {}, found {} `{}` in {}",
ns.descr(),
what,
ident,
parent
)
};
if binding.module().is_some() {
found("module")
} else {
match binding.res() {
// Avoid using TyCtxt::def_kind_descr in the resolver, because it
// indirectly *calls* the resolver, and would cause a query cycle.
Res::Def(kind, id) => found(kind.descr(id)),
_ => found(ns_to_try.descr()),
}
}
msg = format!(
"expected {}, found {} `{ident}` in {parent}",
ns.descr(),
binding.res().descr(),
);
};
}
(msg, None)
@ -2860,17 +2845,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let note = errors::FoundItemConfigureOut { span: ident.span };
err.subdiagnostic(note);
if let MetaItemKind::List(nested) = &cfg.kind
&& let MetaItemInner::MetaItem(meta_item) = &nested[0]
&& let MetaItemKind::NameValue(feature_name) = &meta_item.kind
{
let note = errors::ItemWasBehindFeature {
feature: feature_name.symbol,
span: meta_item.span,
};
if let CfgEntry::NameValue { value: Some((feature, _)), .. } = cfg.0 {
let note = errors::ItemWasBehindFeature { feature, span: cfg.1 };
err.subdiagnostic(note);
} else {
let note = errors::ItemWasCfgOut { span: cfg.span };
let note = errors::ItemWasCfgOut { span: cfg.1 };
err.subdiagnostic(note);
}
}

View file

@ -3,11 +3,10 @@ use Namespace::*;
use rustc_ast::{self as ast, NodeId};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::{DefKind, Namespace, NonMacroAttrKind, PartialRes, PerNS};
use rustc_middle::{bug, ty};
use rustc_middle::bug;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK;
use rustc_session::parse::feature_err;
use rustc_span::def_id::LocalDefId;
use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext};
use rustc_span::{Ident, Span, kw, sym};
use tracing::{debug, instrument};
@ -20,11 +19,9 @@ use crate::{
AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingKey, Determinacy, Finalize,
ImportKind, LexicalScopeBinding, Module, ModuleKind, ModuleOrUniformRoot, NameBinding,
NameBindingKind, ParentScope, PathResult, PrivacyError, Res, ResolutionError, Resolver, Scope,
ScopeSet, Segment, ToNameBinding, Used, Weak, errors,
ScopeSet, Segment, Used, Weak, errors,
};
type Visibility = ty::Visibility<LocalDefId>;
#[derive(Copy, Clone)]
pub enum UsePrelude {
No,
@ -464,13 +461,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
) {
Ok((Some(ext), _)) => {
if ext.helper_attrs.contains(&ident.name) {
let binding = (
let binding = this.arenas.new_pub_res_binding(
Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat),
Visibility::Public,
derive.span,
LocalExpnId::ROOT,
)
.to_name_binding(this.arenas);
);
result = Ok((binding, Flags::empty()));
break;
}
@ -1637,11 +1632,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(res);
if let Some(next_module) = binding.module() {
if self.mods_with_parse_errors.contains(&next_module.def_id()) {
if let Some(def_id) = binding.res().module_like_def_id() {
if self.mods_with_parse_errors.contains(&def_id) {
module_had_parse_errors = true;
}
module = Some(ModuleOrUniformRoot::Module(next_module));
module = Some(ModuleOrUniformRoot::Module(self.expect_module(def_id)));
record_segment_res(self, res);
} else if res == Res::ToolMod && !is_last && opt_ns.is_some() {
if binding.is_import() {

View file

@ -11,7 +11,8 @@ use rustc_errors::{Applicability, MultiSpan, pluralize, struct_span_code_err};
use rustc_hir::def::{self, DefKind, PartialRes};
use rustc_hir::def_id::DefId;
use rustc_middle::metadata::{ModChild, Reexport};
use rustc_middle::{span_bug, ty};
use rustc_middle::span_bug;
use rustc_middle::ty::Visibility;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::{
AMBIGUOUS_GLOB_REEXPORTS, HIDDEN_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE,
@ -88,7 +89,7 @@ pub(crate) enum ImportKind<'ra> {
is_prelude: bool,
// The visibility of the greatest re-export.
// n.b. `max_vis` is only used in `finalize_import` to check for re-export errors.
max_vis: Cell<Option<ty::Visibility>>,
max_vis: Cell<Option<Visibility>>,
id: NodeId,
},
ExternCrate {
@ -185,7 +186,7 @@ pub(crate) struct ImportData<'ra> {
/// |`use ::foo` | `ModuleOrUniformRoot::CrateRootAndExternPrelude` | a special case in 2015 edition |
/// |`use foo` | `ModuleOrUniformRoot::CurrentScope` | - |
pub imported_module: Cell<Option<ModuleOrUniformRoot<'ra>>>,
pub vis: ty::Visibility,
pub vis: Visibility,
}
/// All imports are unique and allocated on a same arena,
@ -679,9 +680,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
NameBindingKind::Res(res) => {
Some(self.def_id_to_node_id(res.def_id().expect_local()))
}
NameBindingKind::Module(module) => {
Some(self.def_id_to_node_id(module.def_id().expect_local()))
}
NameBindingKind::Import { import, .. } => import.id(),
};
if let Some(binding_id) = binding_id {
@ -875,7 +873,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
// We need the `target`, `source` can be extracted.
let imported_binding = this.import(binding, import);
this.define(parent, target, ns, imported_binding);
this.define_binding(parent, target, ns, imported_binding);
PendingBinding::Ready(Some(imported_binding))
}
Err(Determinacy::Determined) => {
@ -1283,7 +1281,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
if !binding.vis.is_at_least(import.vis, this.tcx) {
reexport_error = Some((ns, binding));
if let ty::Visibility::Restricted(binding_def_id) = binding.vis
if let Visibility::Restricted(binding_def_id) = binding.vis
&& binding_def_id.is_top_level_module()
{
crate_private_reexport = true;

View file

@ -28,7 +28,7 @@ use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, Par
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::{MissingLifetimeKind, PrimTy, TraitCandidate};
use rustc_middle::middle::resolve_bound_vars::Set1;
use rustc_middle::ty::DelegationFnSig;
use rustc_middle::ty::{DelegationFnSig, Visibility};
use rustc_middle::{bug, span_bug};
use rustc_session::config::{CrateType, ResolveDocLinks};
use rustc_session::lint::{self, BuiltinLintDiag};
@ -638,8 +638,8 @@ impl PathSource<'_, '_, '_> {
enum MaybeExported<'a> {
Ok(NodeId),
Impl(Option<DefId>),
ImplItem(Result<DefId, &'a Visibility>),
NestedUse(&'a Visibility),
ImplItem(Result<DefId, &'a ast::Visibility>),
NestedUse(&'a ast::Visibility),
}
impl MaybeExported<'_> {
@ -3463,7 +3463,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
span,
"error should be emitted when an unexpected trait item is used",
);
rustc_middle::ty::Visibility::Public
Visibility::Public
};
this.r.feed_visibility(this.r.feed(id), vis);
};

View file

@ -2656,18 +2656,17 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
if result.is_some() || !name_binding.vis.is_visible_locally() {
return;
}
if let Some(module) = name_binding.module() {
if let Some(module_def_id) = name_binding.res().module_like_def_id() {
// form the path
let mut path_segments = path_segments.clone();
path_segments.push(ast::PathSegment::from_ident(ident));
let module_def_id = module.def_id();
let doc_visible = doc_visible
&& (module_def_id.is_local() || !r.tcx.is_doc_hidden(module_def_id));
if module_def_id == def_id {
let path =
Path { span: name_binding.span, segments: path_segments, tokens: None };
result = Some((
module,
r.expect_module(module_def_id),
ImportSuggestion {
did: Some(def_id),
descr: "module",
@ -2682,6 +2681,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
} else {
// add the module to the lookup
if seen_modules.insert(module_def_id) {
let module = r.expect_module(module_def_id);
worklist.push((module, path_segments, doc_visible));
}
}

View file

@ -36,12 +36,12 @@ use late::{
};
use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
use rustc_arena::{DroplessArena, TypedArena};
use rustc_ast::expand::StrippedCfgItem;
use rustc_ast::node_id::NodeMap;
use rustc_ast::{
self as ast, AngleBracketedArg, CRATE_NODE_ID, Crate, Expr, ExprKind, GenericArg, GenericArgs,
LitKind, NodeId, Path, attr,
};
use rustc_attr_data_structures::StrippedCfgItem;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::intern::Interned;
use rustc_data_structures::steal::Steal;
@ -65,7 +65,7 @@ use rustc_middle::query::Providers;
use rustc_middle::span_bug;
use rustc_middle::ty::{
self, DelegationFnSig, Feed, MainDefinition, RegisteredTools, ResolverGlobalCtxt,
ResolverOutputs, TyCtxt, TyCtxtFeed,
ResolverOutputs, TyCtxt, TyCtxtFeed, Visibility,
};
use rustc_query_system::ich::StableHashingContext;
use rustc_session::lint::builtin::PRIVATE_MACRO_USE;
@ -579,7 +579,7 @@ struct ModuleData<'ra> {
globs: RefCell<Vec<Import<'ra>>>,
/// Used to memoize the traits in this module for faster searches through all traits in scope.
traits: RefCell<Option<Box<[(Ident, NameBinding<'ra>)]>>>,
traits: RefCell<Option<Box<[(Ident, NameBinding<'ra>, Option<Module<'ra>>)]>>>,
/// Span of the module itself. Used for error reporting.
span: Span,
@ -655,12 +655,12 @@ impl<'ra> Module<'ra> {
let mut traits = self.traits.borrow_mut();
if traits.is_none() {
let mut collected_traits = Vec::new();
self.for_each_child(resolver, |_, name, ns, binding| {
self.for_each_child(resolver, |r, name, ns, binding| {
if ns != TypeNS {
return;
}
if let Res::Def(DefKind::Trait | DefKind::TraitAlias, _) = binding.res() {
collected_traits.push((name, binding))
if let Res::Def(DefKind::Trait | DefKind::TraitAlias, def_id) = binding.res() {
collected_traits.push((name, binding, r.as_mut().get_module(def_id)))
}
});
*traits = Some(collected_traits.into_boxed_slice());
@ -674,7 +674,6 @@ impl<'ra> Module<'ra> {
}
}
// Public for rustdoc.
fn def_id(self) -> DefId {
self.opt_def_id().expect("`ModuleData::def_id` is called on a block module")
}
@ -749,7 +748,7 @@ struct NameBindingData<'ra> {
warn_ambiguity: bool,
expansion: LocalExpnId,
span: Span,
vis: ty::Visibility<DefId>,
vis: Visibility<DefId>,
}
/// All name bindings are unique and allocated on a same arena,
@ -769,20 +768,9 @@ impl std::hash::Hash for NameBindingData<'_> {
}
}
trait ToNameBinding<'ra> {
fn to_name_binding(self, arenas: &'ra ResolverArenas<'ra>) -> NameBinding<'ra>;
}
impl<'ra> ToNameBinding<'ra> for NameBinding<'ra> {
fn to_name_binding(self, _: &'ra ResolverArenas<'ra>) -> NameBinding<'ra> {
self
}
}
#[derive(Clone, Copy, Debug)]
enum NameBindingKind<'ra> {
Res(Res),
Module(Module<'ra>),
Import { binding: NameBinding<'ra>, import: Import<'ra> },
}
@ -875,18 +863,9 @@ struct AmbiguityError<'ra> {
}
impl<'ra> NameBindingData<'ra> {
fn module(&self) -> Option<Module<'ra>> {
match self.kind {
NameBindingKind::Module(module) => Some(module),
NameBindingKind::Import { binding, .. } => binding.module(),
_ => None,
}
}
fn res(&self) -> Res {
match self.kind {
NameBindingKind::Res(res) => res,
NameBindingKind::Module(module) => module.res().unwrap(),
NameBindingKind::Import { binding, .. } => binding.res(),
}
}
@ -921,7 +900,7 @@ impl<'ra> NameBindingData<'ra> {
DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..),
_,
)) => true,
NameBindingKind::Res(..) | NameBindingKind::Module(..) => false,
NameBindingKind::Res(..) => false,
}
}
@ -930,11 +909,7 @@ impl<'ra> NameBindingData<'ra> {
NameBindingKind::Import { import, .. } => {
matches!(import.kind, ImportKind::ExternCrate { .. })
}
NameBindingKind::Module(module)
if let ModuleKind::Def(DefKind::Mod, def_id, _) = module.kind =>
{
def_id.is_crate_root()
}
NameBindingKind::Res(Res::Def(_, def_id)) => def_id.is_crate_root(),
_ => false,
}
}
@ -1108,7 +1083,7 @@ pub struct Resolver<'ra, 'tcx> {
/// Maps glob imports to the names of items actually imported.
glob_map: FxIndexMap<LocalDefId, FxIndexSet<Symbol>>,
glob_error: Option<ErrorGuaranteed>,
visibilities_for_hashing: Vec<(LocalDefId, ty::Visibility)>,
visibilities_for_hashing: Vec<(LocalDefId, Visibility)>,
used_imports: FxHashSet<NodeId>,
maybe_unused_trait_imports: FxIndexSet<LocalDefId>,
@ -1181,7 +1156,7 @@ pub struct Resolver<'ra, 'tcx> {
/// Table for mapping struct IDs into struct constructor IDs,
/// it's not used during normal resolution, only for better error reporting.
/// Also includes of list of each fields visibility
struct_constructors: LocalDefIdMap<(Res, ty::Visibility<DefId>, Vec<ty::Visibility<DefId>>)>,
struct_constructors: LocalDefIdMap<(Res, Visibility<DefId>, Vec<Visibility<DefId>>)>,
lint_buffer: LintBuffer,
@ -1255,6 +1230,32 @@ pub struct ResolverArenas<'ra> {
}
impl<'ra> ResolverArenas<'ra> {
fn new_res_binding(
&'ra self,
res: Res,
vis: Visibility<DefId>,
span: Span,
expansion: LocalExpnId,
) -> NameBinding<'ra> {
self.alloc_name_binding(NameBindingData {
kind: NameBindingKind::Res(res),
ambiguity: None,
warn_ambiguity: false,
vis,
span,
expansion,
})
}
fn new_pub_res_binding(
&'ra self,
res: Res,
span: Span,
expn_id: LocalExpnId,
) -> NameBinding<'ra> {
self.new_res_binding(res, Visibility::Public, span, expn_id)
}
fn new_module(
&'ra self,
parent: Option<Module<'ra>>,
@ -1278,8 +1279,8 @@ impl<'ra> ResolverArenas<'ra> {
}
if let Some(def_id) = def_id {
module_map.insert(def_id, module);
let vis = ty::Visibility::<DefId>::Public;
let binding = (module, vis, module.span, LocalExpnId::ROOT).to_name_binding(self);
let res = module.res().unwrap();
let binding = self.new_pub_res_binding(res, module.span, LocalExpnId::ROOT);
module_self_bindings.insert(module, binding);
}
module
@ -1470,8 +1471,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
let registered_tools = tcx.registered_tools(());
let pub_vis = ty::Visibility::<DefId>::Public;
let edition = tcx.sess.edition();
let mut resolver = Resolver {
@ -1520,12 +1519,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
macro_expanded_macro_export_errors: BTreeSet::new(),
arenas,
dummy_binding: (Res::Err, pub_vis, DUMMY_SP, LocalExpnId::ROOT).to_name_binding(arenas),
dummy_binding: arenas.new_pub_res_binding(Res::Err, DUMMY_SP, LocalExpnId::ROOT),
builtin_types_bindings: PrimTy::ALL
.iter()
.map(|prim_ty| {
let binding = (Res::PrimTy(*prim_ty), pub_vis, DUMMY_SP, LocalExpnId::ROOT)
.to_name_binding(arenas);
let res = Res::PrimTy(*prim_ty);
let binding = arenas.new_pub_res_binding(res, DUMMY_SP, LocalExpnId::ROOT);
(prim_ty.name(), binding)
})
.collect(),
@ -1533,16 +1532,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
.iter()
.map(|builtin_attr| {
let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(builtin_attr.name));
let binding =
(res, pub_vis, DUMMY_SP, LocalExpnId::ROOT).to_name_binding(arenas);
let binding = arenas.new_pub_res_binding(res, DUMMY_SP, LocalExpnId::ROOT);
(builtin_attr.name, binding)
})
.collect(),
registered_tool_bindings: registered_tools
.iter()
.map(|ident| {
let binding = (Res::ToolMod, pub_vis, ident.span, LocalExpnId::ROOT)
.to_name_binding(arenas);
let res = Res::ToolMod;
let binding = arenas.new_pub_res_binding(res, ident.span, LocalExpnId::ROOT);
(*ident, binding)
})
.collect(),
@ -1605,7 +1603,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let root_parent_scope = ParentScope::module(graph_root, &resolver);
resolver.invocation_parent_scopes.insert(LocalExpnId::ROOT, root_parent_scope);
resolver.feed_visibility(crate_feed, ty::Visibility::Public);
resolver.feed_visibility(crate_feed, Visibility::Public);
resolver
}
@ -1659,7 +1657,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
Default::default()
}
fn feed_visibility(&mut self, feed: Feed<'tcx, LocalDefId>, vis: ty::Visibility) {
fn feed_visibility(&mut self, feed: Feed<'tcx, LocalDefId>, vis: Visibility) {
let feed = feed.upgrade(self.tcx);
feed.visibility(vis.to_def_id());
self.visibilities_for_hashing.push((feed.def_id(), vis));
@ -1837,10 +1835,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
) {
module.ensure_traits(self);
let traits = module.traits.borrow();
for (trait_name, trait_binding) in traits.as_ref().unwrap().iter() {
if self.trait_may_have_item(trait_binding.module(), assoc_item) {
for &(trait_name, trait_binding, trait_module) in traits.as_ref().unwrap().iter() {
if self.trait_may_have_item(trait_module, assoc_item) {
let def_id = trait_binding.res().def_id();
let import_ids = self.find_transitive_imports(&trait_binding.kind, *trait_name);
let import_ids = self.find_transitive_imports(&trait_binding.kind, trait_name);
found_traits.push(TraitCandidate { def_id, import_ids });
}
}
@ -2110,11 +2108,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
self.pat_span_map.insert(node, span);
}
fn is_accessible_from(
&self,
vis: ty::Visibility<impl Into<DefId>>,
module: Module<'ra>,
) -> bool {
fn is_accessible_from(&self, vis: Visibility<impl Into<DefId>>, module: Module<'ra>) -> bool {
vis.is_accessible_from(module.nearest_parent_mod(), self.tcx)
}
@ -2174,9 +2168,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
} else {
self.crate_loader(|c| c.maybe_process_path_extern(ident.name))?
};
let crate_root = self.expect_module(crate_id.as_def_id());
let vis = ty::Visibility::<DefId>::Public;
(crate_root, vis, DUMMY_SP, LocalExpnId::ROOT).to_name_binding(self.arenas)
let res = Res::Def(DefKind::Mod, crate_id.as_def_id());
self.arenas.new_pub_res_binding(res, DUMMY_SP, LocalExpnId::ROOT)
})
});

View file

@ -5,10 +5,9 @@ use std::cell::Cell;
use std::mem;
use std::sync::Arc;
use rustc_ast::expand::StrippedCfgItem;
use rustc_ast::{self as ast, Crate, NodeId, attr};
use rustc_ast_pretty::pprust;
use rustc_attr_data_structures::StabilityLevel;
use rustc_attr_data_structures::{CfgEntry, StabilityLevel, StrippedCfgItem};
use rustc_errors::{Applicability, DiagCtxtHandle, StashKey};
use rustc_expand::base::{
Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension,
@ -21,7 +20,7 @@ use rustc_expand::expand::{
use rustc_hir::def::{self, DefKind, Namespace, NonMacroAttrKind};
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
use rustc_middle::middle::stability;
use rustc_middle::ty::{RegisteredTools, TyCtxt, Visibility};
use rustc_middle::ty::{RegisteredTools, TyCtxt};
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::{
LEGACY_DERIVE_HELPERS, OUT_OF_SCOPE_MACRO_CALLS, UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
@ -42,7 +41,7 @@ use crate::imports::Import;
use crate::{
BindingKey, DeriveData, Determinacy, Finalize, InvocationParent, MacroData, ModuleKind,
ModuleOrUniformRoot, NameBinding, NameBindingKind, ParentScope, PathResult, ResolutionError,
Resolver, ScopeSet, Segment, ToNameBinding, Used,
Resolver, ScopeSet, Segment, Used,
};
type Res = def::Res<NodeId>;
@ -437,8 +436,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
.iter()
.map(|(_, ident)| {
let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper);
let binding = (res, Visibility::<DefId>::Public, ident.span, expn_id)
.to_name_binding(self.arenas);
let binding = self.arenas.new_pub_res_binding(res, ident.span, expn_id);
(*ident, binding)
})
.collect();
@ -485,8 +483,18 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
self.proc_macros.push(self.local_def_id(id))
}
fn append_stripped_cfg_item(&mut self, parent_node: NodeId, ident: Ident, cfg: ast::MetaItem) {
self.stripped_cfg_items.push(StrippedCfgItem { parent_module: parent_node, ident, cfg });
fn append_stripped_cfg_item(
&mut self,
parent_node: NodeId,
ident: Ident,
cfg: CfgEntry,
cfg_span: Span,
) {
self.stripped_cfg_items.push(StrippedCfgItem {
parent_module: parent_node,
ident,
cfg: (cfg, cfg_span),
});
}
fn registered_tools(&self) -> &RegisteredTools {

View file

@ -12,12 +12,6 @@ use crate::{self as ty, Interner};
// avoid heap allocations.
type TypeWalkerStack<I> = SmallVec<[<I as Interner>::GenericArg; 8]>;
pub struct TypeWalker<I: Interner> {
stack: TypeWalkerStack<I>,
last_subtree: usize,
pub visited: SsoHashSet<I::GenericArg>,
}
/// An iterator for walking the type tree.
///
/// It's very easy to produce a deeply
@ -26,6 +20,12 @@ pub struct TypeWalker<I: Interner> {
/// in this situation walker only visits each type once.
/// It maintains a set of visited types and
/// skips any types that are already there.
pub struct TypeWalker<I: Interner> {
stack: TypeWalkerStack<I>,
last_subtree: usize,
pub visited: SsoHashSet<I::GenericArg>,
}
impl<I: Interner> TypeWalker<I> {
pub fn new(root: I::GenericArg) -> Self {
Self { stack: smallvec![root], last_subtree: 1, visited: SsoHashSet::new() }

View file

@ -155,6 +155,7 @@
#![feature(try_trait_v2)]
#![feature(try_with_capacity)]
#![feature(tuple_trait)]
#![feature(ub_checks)]
#![feature(unicode_internals)]
#![feature(unsize)]
#![feature(unwrap_infallible)]

View file

@ -64,7 +64,7 @@ use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties};
use core::ops::{self, Index, IndexMut, Range, RangeBounds};
use core::ptr::{self, NonNull};
use core::slice::{self, SliceIndex};
use core::{fmt, intrinsics};
use core::{fmt, intrinsics, ub_checks};
#[stable(feature = "extract_if", since = "1.87.0")]
pub use self::extract_if::ExtractIf;
@ -1058,6 +1058,11 @@ impl<T, A: Allocator> Vec<T, A> {
#[inline]
#[unstable(feature = "allocator_api", issue = "32838")]
pub unsafe fn from_raw_parts_in(ptr: *mut T, length: usize, capacity: usize, alloc: A) -> Self {
ub_checks::assert_unsafe_precondition!(
check_library_ub,
"Vec::from_raw_parts_in requires that length <= capacity",
(length: usize = length, capacity: usize = capacity) => length <= capacity
);
unsafe { Vec { buf: RawVec::from_raw_parts_in(ptr, capacity, alloc), len: length } }
}
@ -1174,6 +1179,11 @@ impl<T, A: Allocator> Vec<T, A> {
#[unstable(feature = "allocator_api", reason = "new API", issue = "32838")]
// #[unstable(feature = "box_vec_non_null", issue = "130364")]
pub unsafe fn from_parts_in(ptr: NonNull<T>, length: usize, capacity: usize, alloc: A) -> Self {
ub_checks::assert_unsafe_precondition!(
check_library_ub,
"Vec::from_parts_in requires that length <= capacity",
(length: usize = length, capacity: usize = capacity) => length <= capacity
);
unsafe { Vec { buf: RawVec::from_nonnull_in(ptr, capacity, alloc), len: length } }
}
@ -1950,7 +1960,11 @@ impl<T, A: Allocator> Vec<T, A> {
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn set_len(&mut self, new_len: usize) {
debug_assert!(new_len <= self.capacity());
ub_checks::assert_unsafe_precondition!(
check_library_ub,
"Vec::set_len requires that new_len <= capacity()",
(new_len: usize = new_len, capacity: usize = self.capacity()) => new_len <= capacity
);
self.len = new_len;
}
@ -3695,7 +3709,7 @@ impl<T, A: Allocator> Vec<T, A> {
/// This is optimal if:
///
/// * The tail (elements in the vector after `range`) is empty,
/// * or `replace_with` yields fewer or equal elements than `range`s length
/// * or `replace_with` yields fewer or equal elements than `range`'s length
/// * or the lower bound of its `size_hint()` is exact.
///
/// Otherwise, a temporary vector is allocated and the tail is moved twice.

View file

@ -374,9 +374,10 @@ impl<'a, T, const N: usize> IntoIterator for &'a mut [T; N] {
}
#[stable(feature = "index_trait_on_arrays", since = "1.50.0")]
impl<T, I, const N: usize> Index<I> for [T; N]
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
impl<T, I, const N: usize> const Index<I> for [T; N]
where
[T]: Index<I>,
[T]: ~const Index<I>,
{
type Output = <[T] as Index<I>>::Output;
@ -387,9 +388,10 @@ where
}
#[stable(feature = "index_trait_on_arrays", since = "1.50.0")]
impl<T, I, const N: usize> IndexMut<I> for [T; N]
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
impl<T, I, const N: usize> const IndexMut<I> for [T; N]
where
[T]: IndexMut<I>,
[T]: ~const IndexMut<I>,
{
#[inline]
fn index_mut(&mut self, index: I) -> &mut Self::Output {

View file

@ -196,16 +196,14 @@ pub macro assert_matches {
},
}
/// A macro for defining `#[cfg]` match-like statements.
/// Selects code at compile-time based on `cfg` predicates.
///
/// It is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade of
/// `#[cfg]` cases, emitting the implementation which matches first.
/// This macro evaluates, at compile-time, a series of `cfg` predicates,
/// selects the first that is true, and emits the code guarded by that
/// predicate. The code guarded by other predicates is not emitted.
///
/// This allows you to conveniently provide a long list `#[cfg]`'d blocks of code
/// without having to rewrite each clause multiple times.
///
/// Trailing `_` wildcard match arms are **optional** and they indicate a fallback branch when
/// all previous declarations do not evaluate to true.
/// An optional trailing `_` wildcard can be used to specify a fallback. If
/// none of the predicates are true, a [`compile_error`] is emitted.
///
/// # Example
///
@ -225,7 +223,7 @@ pub macro assert_matches {
/// }
/// ```
///
/// If desired, it is possible to return expressions through the use of surrounding braces:
/// The `cfg_select!` macro can also be used in expression position:
///
/// ```
/// #![feature(cfg_select)]

View file

@ -94,9 +94,9 @@ macro_rules! sh_impl_signed {
#[inline]
fn shl(self, other: $f) -> Wrapping<$t> {
if other < 0 {
Wrapping(self.0.wrapping_shr((-other & self::shift_max::$t as $f) as u32))
Wrapping(self.0.wrapping_shr(-other as u32))
} else {
Wrapping(self.0.wrapping_shl((other & self::shift_max::$t as $f) as u32))
Wrapping(self.0.wrapping_shl(other as u32))
}
}
}
@ -119,9 +119,9 @@ macro_rules! sh_impl_signed {
#[inline]
fn shr(self, other: $f) -> Wrapping<$t> {
if other < 0 {
Wrapping(self.0.wrapping_shl((-other & self::shift_max::$t as $f) as u32))
Wrapping(self.0.wrapping_shl(-other as u32))
} else {
Wrapping(self.0.wrapping_shr((other & self::shift_max::$t as $f) as u32))
Wrapping(self.0.wrapping_shr(other as u32))
}
}
}
@ -147,7 +147,7 @@ macro_rules! sh_impl_unsigned {
#[inline]
fn shl(self, other: $f) -> Wrapping<$t> {
Wrapping(self.0.wrapping_shl((other & self::shift_max::$t as $f) as u32))
Wrapping(self.0.wrapping_shl(other as u32))
}
}
forward_ref_binop! { impl Shl, shl for Wrapping<$t>, $f,
@ -168,7 +168,7 @@ macro_rules! sh_impl_unsigned {
#[inline]
fn shr(self, other: $f) -> Wrapping<$t> {
Wrapping(self.0.wrapping_shr((other & self::shift_max::$t as $f) as u32))
Wrapping(self.0.wrapping_shr(other as u32))
}
}
forward_ref_binop! { impl Shr, shr for Wrapping<$t>, $f,
@ -1052,39 +1052,3 @@ macro_rules! wrapping_int_impl_unsigned {
}
wrapping_int_impl_unsigned! { usize u8 u16 u32 u64 u128 }
mod shift_max {
#![allow(non_upper_case_globals)]
#[cfg(target_pointer_width = "16")]
mod platform {
pub(crate) const usize: u32 = super::u16;
pub(crate) const isize: u32 = super::i16;
}
#[cfg(target_pointer_width = "32")]
mod platform {
pub(crate) const usize: u32 = super::u32;
pub(crate) const isize: u32 = super::i32;
}
#[cfg(target_pointer_width = "64")]
mod platform {
pub(crate) const usize: u32 = super::u64;
pub(crate) const isize: u32 = super::i64;
}
pub(super) const i8: u32 = (1 << 3) - 1;
pub(super) const i16: u32 = (1 << 4) - 1;
pub(super) const i32: u32 = (1 << 5) - 1;
pub(super) const i64: u32 = (1 << 6) - 1;
pub(super) const i128: u32 = (1 << 7) - 1;
pub(super) use self::platform::isize;
pub(super) const u8: u32 = i8;
pub(super) const u16: u32 = i16;
pub(super) const u32: u32 = i32;
pub(super) const u64: u32 = i64;
pub(super) const u128: u32 = i128;
pub(super) use self::platform::usize;
}

View file

@ -55,6 +55,8 @@
#[doc(alias = "]")]
#[doc(alias = "[")]
#[doc(alias = "[]")]
#[const_trait]
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
pub trait Index<Idx: ?Sized> {
/// The returned type after indexing.
#[stable(feature = "rust1", since = "1.0.0")]
@ -165,7 +167,9 @@ see chapter in The Book <https://doc.rust-lang.org/book/ch08-02-strings.html#ind
#[doc(alias = "[")]
#[doc(alias = "]")]
#[doc(alias = "[]")]
pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
#[const_trait]
pub trait IndexMut<Idx: ?Sized>: ~const Index<Idx> {
/// Performs the mutable indexing (`container[index]`) operation.
///
/// # Panics

View file

@ -1524,10 +1524,11 @@ impl<T> *const [T] {
/// }
/// ```
#[unstable(feature = "slice_ptr_get", issue = "74265")]
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
#[inline]
pub unsafe fn get_unchecked<I>(self, index: I) -> *const I::Output
pub const unsafe fn get_unchecked<I>(self, index: I) -> *const I::Output
where
I: SliceIndex<[T]>,
I: ~const SliceIndex<[T]>,
{
// SAFETY: the caller ensures that `self` is dereferenceable and `index` in-bounds.
unsafe { index.get_unchecked(self) }

View file

@ -1881,10 +1881,11 @@ impl<T> *mut [T] {
/// }
/// ```
#[unstable(feature = "slice_ptr_get", issue = "74265")]
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
#[inline(always)]
pub unsafe fn get_unchecked_mut<I>(self, index: I) -> *mut I::Output
pub const unsafe fn get_unchecked_mut<I>(self, index: I) -> *mut I::Output
where
I: SliceIndex<[T]>,
I: ~const SliceIndex<[T]>,
{
// SAFETY: the caller ensures that `self` is dereferenceable and `index` in-bounds.
unsafe { index.get_unchecked_mut(self) }

View file

@ -1597,10 +1597,11 @@ impl<T> NonNull<[T]> {
/// }
/// ```
#[unstable(feature = "slice_ptr_get", issue = "74265")]
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
#[inline]
pub unsafe fn get_unchecked_mut<I>(self, index: I) -> NonNull<I::Output>
pub const unsafe fn get_unchecked_mut<I>(self, index: I) -> NonNull<I::Output>
where
I: SliceIndex<[T]>,
I: ~const SliceIndex<[T]>,
{
// SAFETY: the caller ensures that `self` is dereferenceable and `index` in-bounds.
// As a consequence, the resulting pointer cannot be null.

View file

@ -186,14 +186,17 @@ impl<T> IntoBounds<T> for Range<T> {
}
#[unstable(feature = "new_range_api", issue = "125687")]
impl<T> From<Range<T>> for legacy::Range<T> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
impl<T> const From<Range<T>> for legacy::Range<T> {
#[inline]
fn from(value: Range<T>) -> Self {
Self { start: value.start, end: value.end }
}
}
#[unstable(feature = "new_range_api", issue = "125687")]
impl<T> From<legacy::Range<T>> for Range<T> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
impl<T> const From<legacy::Range<T>> for Range<T> {
#[inline]
fn from(value: legacy::Range<T>) -> Self {
Self { start: value.start, end: value.end }
@ -362,7 +365,8 @@ impl<T> IntoBounds<T> for RangeInclusive<T> {
}
#[unstable(feature = "new_range_api", issue = "125687")]
impl<T> From<RangeInclusive<T>> for legacy::RangeInclusive<T> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
impl<T> const From<RangeInclusive<T>> for legacy::RangeInclusive<T> {
#[inline]
fn from(value: RangeInclusive<T>) -> Self {
Self::new(value.start, value.end)
@ -506,14 +510,16 @@ impl<T> IntoBounds<T> for RangeFrom<T> {
}
#[unstable(feature = "new_range_api", issue = "125687")]
impl<T> From<RangeFrom<T>> for legacy::RangeFrom<T> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
impl<T> const From<RangeFrom<T>> for legacy::RangeFrom<T> {
#[inline]
fn from(value: RangeFrom<T>) -> Self {
Self { start: value.start }
}
}
#[unstable(feature = "new_range_api", issue = "125687")]
impl<T> From<legacy::RangeFrom<T>> for RangeFrom<T> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
impl<T> const From<legacy::RangeFrom<T>> for RangeFrom<T> {
#[inline]
fn from(value: legacy::RangeFrom<T>) -> Self {
Self { start: value.start }

View file

@ -6,9 +6,10 @@ use crate::ub_checks::assert_unsafe_precondition;
use crate::{ops, range};
#[stable(feature = "rust1", since = "1.0.0")]
impl<T, I> ops::Index<I> for [T]
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
impl<T, I> const ops::Index<I> for [T]
where
I: SliceIndex<[T]>,
I: ~const SliceIndex<[T]>,
{
type Output = I::Output;
@ -19,9 +20,10 @@ where
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T, I> ops::IndexMut<I> for [T]
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
impl<T, I> const ops::IndexMut<I> for [T]
where
I: SliceIndex<[T]>,
I: ~const SliceIndex<[T]>,
{
#[inline(always)]
fn index_mut(&mut self, index: I) -> &mut I::Output {
@ -158,6 +160,8 @@ mod private_slice_index {
message = "the type `{T}` cannot be indexed by `{Self}`",
label = "slice indices are of type `usize` or ranges of `usize`"
)]
#[const_trait]
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
pub unsafe trait SliceIndex<T: ?Sized>: private_slice_index::Sealed {
/// The output type returned by methods.
#[stable(feature = "slice_get_slice", since = "1.28.0")]
@ -208,7 +212,8 @@ pub unsafe trait SliceIndex<T: ?Sized>: private_slice_index::Sealed {
/// The methods `index` and `index_mut` panic if the index is out of bounds.
#[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
unsafe impl<T> SliceIndex<[T]> for usize {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl<T> const SliceIndex<[T]> for usize {
type Output = T;
#[inline]
@ -278,7 +283,8 @@ unsafe impl<T> SliceIndex<[T]> for usize {
/// Because `IndexRange` guarantees `start <= end`, fewer checks are needed here
/// than there are for a general `Range<usize>` (which might be `100..3`).
unsafe impl<T> SliceIndex<[T]> for ops::IndexRange {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl<T> const SliceIndex<[T]> for ops::IndexRange {
type Output = [T];
#[inline]
@ -354,7 +360,8 @@ unsafe impl<T> SliceIndex<[T]> for ops::IndexRange {
/// - the start of the range is greater than the end of the range or
/// - the end of the range is out of bounds.
#[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
unsafe impl<T> SliceIndex<[T]> for ops::Range<usize> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl<T> const SliceIndex<[T]> for ops::Range<usize> {
type Output = [T];
#[inline]
@ -453,7 +460,8 @@ unsafe impl<T> SliceIndex<[T]> for ops::Range<usize> {
}
#[unstable(feature = "new_range_api", issue = "125687")]
unsafe impl<T> SliceIndex<[T]> for range::Range<usize> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl<T> const SliceIndex<[T]> for range::Range<usize> {
type Output = [T];
#[inline]
@ -491,7 +499,8 @@ unsafe impl<T> SliceIndex<[T]> for range::Range<usize> {
/// The methods `index` and `index_mut` panic if the end of the range is out of bounds.
#[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
unsafe impl<T> SliceIndex<[T]> for ops::RangeTo<usize> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl<T> const SliceIndex<[T]> for ops::RangeTo<usize> {
type Output = [T];
#[inline]
@ -529,7 +538,8 @@ unsafe impl<T> SliceIndex<[T]> for ops::RangeTo<usize> {
/// The methods `index` and `index_mut` panic if the start of the range is out of bounds.
#[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
unsafe impl<T> SliceIndex<[T]> for ops::RangeFrom<usize> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl<T> const SliceIndex<[T]> for ops::RangeFrom<usize> {
type Output = [T];
#[inline]
@ -574,7 +584,8 @@ unsafe impl<T> SliceIndex<[T]> for ops::RangeFrom<usize> {
}
#[unstable(feature = "new_range_api", issue = "125687")]
unsafe impl<T> SliceIndex<[T]> for range::RangeFrom<usize> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl<T> const SliceIndex<[T]> for range::RangeFrom<usize> {
type Output = [T];
#[inline]
@ -611,7 +622,8 @@ unsafe impl<T> SliceIndex<[T]> for range::RangeFrom<usize> {
}
#[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
unsafe impl<T> SliceIndex<[T]> for ops::RangeFull {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl<T> const SliceIndex<[T]> for ops::RangeFull {
type Output = [T];
#[inline]
@ -650,7 +662,8 @@ unsafe impl<T> SliceIndex<[T]> for ops::RangeFull {
/// - the start of the range is greater than the end of the range or
/// - the end of the range is out of bounds.
#[stable(feature = "inclusive_range", since = "1.26.0")]
unsafe impl<T> SliceIndex<[T]> for ops::RangeInclusive<usize> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl<T> const SliceIndex<[T]> for ops::RangeInclusive<usize> {
type Output = [T];
#[inline]
@ -693,7 +706,8 @@ unsafe impl<T> SliceIndex<[T]> for ops::RangeInclusive<usize> {
}
#[unstable(feature = "new_range_api", issue = "125687")]
unsafe impl<T> SliceIndex<[T]> for range::RangeInclusive<usize> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl<T> const SliceIndex<[T]> for range::RangeInclusive<usize> {
type Output = [T];
#[inline]
@ -731,7 +745,8 @@ unsafe impl<T> SliceIndex<[T]> for range::RangeInclusive<usize> {
/// The methods `index` and `index_mut` panic if the end of the range is out of bounds.
#[stable(feature = "inclusive_range", since = "1.26.0")]
unsafe impl<T> SliceIndex<[T]> for ops::RangeToInclusive<usize> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl<T> const SliceIndex<[T]> for ops::RangeToInclusive<usize> {
type Output = [T];
#[inline]

View file

@ -568,9 +568,10 @@ impl<T> [T] {
#[rustc_no_implicit_autorefs]
#[inline]
#[must_use]
pub fn get<I>(&self, index: I) -> Option<&I::Output>
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
pub const fn get<I>(&self, index: I) -> Option<&I::Output>
where
I: SliceIndex<Self>,
I: ~const SliceIndex<Self>,
{
index.get(self)
}
@ -594,9 +595,10 @@ impl<T> [T] {
#[rustc_no_implicit_autorefs]
#[inline]
#[must_use]
pub fn get_mut<I>(&mut self, index: I) -> Option<&mut I::Output>
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
pub const fn get_mut<I>(&mut self, index: I) -> Option<&mut I::Output>
where
I: SliceIndex<Self>,
I: ~const SliceIndex<Self>,
{
index.get_mut(self)
}
@ -633,9 +635,10 @@ impl<T> [T] {
#[inline]
#[must_use]
#[track_caller]
pub unsafe fn get_unchecked<I>(&self, index: I) -> &I::Output
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
pub const unsafe fn get_unchecked<I>(&self, index: I) -> &I::Output
where
I: SliceIndex<Self>,
I: ~const SliceIndex<Self>,
{
// SAFETY: the caller must uphold most of the safety requirements for `get_unchecked`;
// the slice is dereferenceable because `self` is a safe reference.
@ -677,9 +680,10 @@ impl<T> [T] {
#[inline]
#[must_use]
#[track_caller]
pub unsafe fn get_unchecked_mut<I>(&mut self, index: I) -> &mut I::Output
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
pub const unsafe fn get_unchecked_mut<I>(&mut self, index: I) -> &mut I::Output
where
I: SliceIndex<Self>,
I: ~const SliceIndex<Self>,
{
// SAFETY: the caller must uphold the safety requirements for `get_unchecked_mut`;
// the slice is dereferenceable because `self` is a safe reference.

View file

@ -589,8 +589,9 @@ impl str {
/// assert!(v.get(..42).is_none());
/// ```
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
#[inline]
pub fn get<I: SliceIndex<str>>(&self, i: I) -> Option<&I::Output> {
pub const fn get<I: ~const SliceIndex<str>>(&self, i: I) -> Option<&I::Output> {
i.get(self)
}
@ -621,8 +622,9 @@ impl str {
/// assert_eq!("HEllo", v);
/// ```
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
#[inline]
pub fn get_mut<I: SliceIndex<str>>(&mut self, i: I) -> Option<&mut I::Output> {
pub const fn get_mut<I: ~const SliceIndex<str>>(&mut self, i: I) -> Option<&mut I::Output> {
i.get_mut(self)
}
@ -952,6 +954,7 @@ impl str {
///
/// The caller must ensure that `mid` is a valid byte offset from the start
/// of the string and falls on the boundary of a UTF-8 code point.
#[inline]
const unsafe fn split_at_unchecked(&self, mid: usize) -> (&str, &str) {
let len = self.len();
let ptr = self.as_ptr();

View file

@ -49,9 +49,10 @@ impl PartialOrd for str {
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<I> ops::Index<I> for str
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
impl<I> const ops::Index<I> for str
where
I: SliceIndex<str>,
I: ~const SliceIndex<str>,
{
type Output = I::Output;
@ -62,9 +63,10 @@ where
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<I> ops::IndexMut<I> for str
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
impl<I> const ops::IndexMut<I> for str
where
I: SliceIndex<str>,
I: ~const SliceIndex<str>,
{
#[inline]
fn index_mut(&mut self, index: I) -> &mut I::Output {
@ -92,7 +94,8 @@ const fn str_index_overflow_fail() -> ! {
///
/// Equivalent to `&self[0 .. len]` or `&mut self[0 .. len]`.
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
unsafe impl SliceIndex<str> for ops::RangeFull {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl const SliceIndex<str> for ops::RangeFull {
type Output = str;
#[inline]
fn get(self, slice: &str) -> Option<&Self::Output> {
@ -156,7 +159,8 @@ unsafe impl SliceIndex<str> for ops::RangeFull {
/// // &s[3 .. 100];
/// ```
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
unsafe impl SliceIndex<str> for ops::Range<usize> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl const SliceIndex<str> for ops::Range<usize> {
type Output = str;
#[inline]
fn get(self, slice: &str) -> Option<&Self::Output> {
@ -260,7 +264,8 @@ unsafe impl SliceIndex<str> for ops::Range<usize> {
}
#[unstable(feature = "new_range_api", issue = "125687")]
unsafe impl SliceIndex<str> for range::Range<usize> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl const SliceIndex<str> for range::Range<usize> {
type Output = str;
#[inline]
fn get(self, slice: &str) -> Option<&Self::Output> {
@ -431,7 +436,8 @@ unsafe impl SliceIndex<str> for (ops::Bound<usize>, ops::Bound<usize>) {
/// Panics if `end` does not point to the starting byte offset of a
/// character (as defined by `is_char_boundary`), or if `end > len`.
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
unsafe impl SliceIndex<str> for ops::RangeTo<usize> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl const SliceIndex<str> for ops::RangeTo<usize> {
type Output = str;
#[inline]
fn get(self, slice: &str) -> Option<&Self::Output> {
@ -499,7 +505,8 @@ unsafe impl SliceIndex<str> for ops::RangeTo<usize> {
/// Panics if `begin` does not point to the starting byte offset of
/// a character (as defined by `is_char_boundary`), or if `begin > len`.
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
unsafe impl SliceIndex<str> for ops::RangeFrom<usize> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl const SliceIndex<str> for ops::RangeFrom<usize> {
type Output = str;
#[inline]
fn get(self, slice: &str) -> Option<&Self::Output> {
@ -554,7 +561,8 @@ unsafe impl SliceIndex<str> for ops::RangeFrom<usize> {
}
#[unstable(feature = "new_range_api", issue = "125687")]
unsafe impl SliceIndex<str> for range::RangeFrom<usize> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl const SliceIndex<str> for range::RangeFrom<usize> {
type Output = str;
#[inline]
fn get(self, slice: &str) -> Option<&Self::Output> {
@ -625,7 +633,8 @@ unsafe impl SliceIndex<str> for range::RangeFrom<usize> {
/// to the ending byte offset of a character (`end + 1` is either a starting
/// byte offset or equal to `len`), if `begin > end`, or if `end >= len`.
#[stable(feature = "inclusive_range", since = "1.26.0")]
unsafe impl SliceIndex<str> for ops::RangeInclusive<usize> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl const SliceIndex<str> for ops::RangeInclusive<usize> {
type Output = str;
#[inline]
fn get(self, slice: &str) -> Option<&Self::Output> {
@ -662,7 +671,8 @@ unsafe impl SliceIndex<str> for ops::RangeInclusive<usize> {
}
#[unstable(feature = "new_range_api", issue = "125687")]
unsafe impl SliceIndex<str> for range::RangeInclusive<usize> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl const SliceIndex<str> for range::RangeInclusive<usize> {
type Output = str;
#[inline]
fn get(self, slice: &str) -> Option<&Self::Output> {
@ -713,7 +723,8 @@ unsafe impl SliceIndex<str> for range::RangeInclusive<usize> {
/// (`end + 1` is either a starting byte offset as defined by
/// `is_char_boundary`, or equal to `len`), or if `end >= len`.
#[stable(feature = "inclusive_range", since = "1.26.0")]
unsafe impl SliceIndex<str> for ops::RangeToInclusive<usize> {
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl const SliceIndex<str> for ops::RangeToInclusive<usize> {
type Output = str;
#[inline]
fn get(self, slice: &str) -> Option<&Self::Output> {

View file

@ -1,9 +1,7 @@
// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy
#![cfg(target_has_reliable_f128)]
use core::ops::{Add, Div, Mul, Sub};
use std::f128::consts;
use std::num::FpCategory as Fp;
use super::{assert_approx_eq, assert_biteq};
@ -38,160 +36,9 @@ const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa;
/// Second pattern over the mantissa
const NAN_MASK2: u128 = 0x00005555555555555555555555555555;
#[test]
fn test_num_f128() {
// FIXME(f16_f128): replace with a `test_num` call once the required `fmodl`/`fmodf128`
// function is available on all platforms.
let ten = 10f128;
let two = 2f128;
assert_biteq!(ten.add(two), ten + two);
assert_biteq!(ten.sub(two), ten - two);
assert_biteq!(ten.mul(two), ten * two);
assert_biteq!(ten.div(two), ten / two);
#[cfg(any(miri, target_has_reliable_f128_math))]
assert_biteq!(core::ops::Rem::rem(ten, two), ten % two);
}
// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support
// the intrinsics.
#[test]
fn test_infinity() {
let inf: f128 = f128::INFINITY;
assert!(inf.is_infinite());
assert!(!inf.is_finite());
assert!(inf.is_sign_positive());
assert!(!inf.is_sign_negative());
assert!(!inf.is_nan());
assert!(!inf.is_normal());
assert_eq!(Fp::Infinite, inf.classify());
}
#[test]
fn test_neg_infinity() {
let neg_inf: f128 = f128::NEG_INFINITY;
assert!(neg_inf.is_infinite());
assert!(!neg_inf.is_finite());
assert!(!neg_inf.is_sign_positive());
assert!(neg_inf.is_sign_negative());
assert!(!neg_inf.is_nan());
assert!(!neg_inf.is_normal());
assert_eq!(Fp::Infinite, neg_inf.classify());
}
#[test]
fn test_zero() {
let zero: f128 = 0.0f128;
assert_biteq!(0.0, zero);
assert!(!zero.is_infinite());
assert!(zero.is_finite());
assert!(zero.is_sign_positive());
assert!(!zero.is_sign_negative());
assert!(!zero.is_nan());
assert!(!zero.is_normal());
assert_eq!(Fp::Zero, zero.classify());
}
#[test]
fn test_neg_zero() {
let neg_zero: f128 = -0.0;
assert_eq!(0.0, neg_zero);
assert_biteq!(-0.0, neg_zero);
assert!(!neg_zero.is_infinite());
assert!(neg_zero.is_finite());
assert!(!neg_zero.is_sign_positive());
assert!(neg_zero.is_sign_negative());
assert!(!neg_zero.is_nan());
assert!(!neg_zero.is_normal());
assert_eq!(Fp::Zero, neg_zero.classify());
}
#[test]
fn test_one() {
let one: f128 = 1.0f128;
assert_biteq!(1.0, one);
assert!(!one.is_infinite());
assert!(one.is_finite());
assert!(one.is_sign_positive());
assert!(!one.is_sign_negative());
assert!(!one.is_nan());
assert!(one.is_normal());
assert_eq!(Fp::Normal, one.classify());
}
#[test]
fn test_is_nan() {
let nan: f128 = f128::NAN;
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
assert!(nan.is_nan());
assert!(!0.0f128.is_nan());
assert!(!5.3f128.is_nan());
assert!(!(-10.732f128).is_nan());
assert!(!inf.is_nan());
assert!(!neg_inf.is_nan());
}
#[test]
fn test_is_infinite() {
let nan: f128 = f128::NAN;
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
assert!(!nan.is_infinite());
assert!(inf.is_infinite());
assert!(neg_inf.is_infinite());
assert!(!0.0f128.is_infinite());
assert!(!42.8f128.is_infinite());
assert!(!(-109.2f128).is_infinite());
}
#[test]
fn test_is_finite() {
let nan: f128 = f128::NAN;
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
assert!(!nan.is_finite());
assert!(!inf.is_finite());
assert!(!neg_inf.is_finite());
assert!(0.0f128.is_finite());
assert!(42.8f128.is_finite());
assert!((-109.2f128).is_finite());
}
#[test]
fn test_is_normal() {
let nan: f128 = f128::NAN;
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
let zero: f128 = 0.0f128;
let neg_zero: f128 = -0.0;
assert!(!nan.is_normal());
assert!(!inf.is_normal());
assert!(!neg_inf.is_normal());
assert!(!zero.is_normal());
assert!(!neg_zero.is_normal());
assert!(1f128.is_normal());
assert!(1e-4931f128.is_normal());
assert!(!1e-4932f128.is_normal());
}
#[test]
fn test_classify() {
let nan: f128 = f128::NAN;
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
let zero: f128 = 0.0f128;
let neg_zero: f128 = -0.0;
assert_eq!(nan.classify(), Fp::Nan);
assert_eq!(inf.classify(), Fp::Infinite);
assert_eq!(neg_inf.classify(), Fp::Infinite);
assert_eq!(zero.classify(), Fp::Zero);
assert_eq!(neg_zero.classify(), Fp::Zero);
assert_eq!(1f128.classify(), Fp::Normal);
assert_eq!(1e-4931f128.classify(), Fp::Normal);
assert_eq!(1e-4932f128.classify(), Fp::Subnormal);
}
#[test]
#[cfg(any(miri, target_has_reliable_f128_math))]
fn test_abs() {

View file

@ -2,7 +2,6 @@
#![cfg(target_has_reliable_f16)]
use std::f16::consts;
use std::num::FpCategory as Fp;
use super::{assert_approx_eq, assert_biteq};
@ -43,151 +42,9 @@ const NAN_MASK1: u16 = 0x02aa;
/// Second pattern over the mantissa
const NAN_MASK2: u16 = 0x0155;
#[test]
fn test_num_f16() {
super::test_num(10f16, 2f16);
}
// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support
// the intrinsics.
#[test]
fn test_infinity() {
let inf: f16 = f16::INFINITY;
assert!(inf.is_infinite());
assert!(!inf.is_finite());
assert!(inf.is_sign_positive());
assert!(!inf.is_sign_negative());
assert!(!inf.is_nan());
assert!(!inf.is_normal());
assert_eq!(Fp::Infinite, inf.classify());
}
#[test]
fn test_neg_infinity() {
let neg_inf: f16 = f16::NEG_INFINITY;
assert!(neg_inf.is_infinite());
assert!(!neg_inf.is_finite());
assert!(!neg_inf.is_sign_positive());
assert!(neg_inf.is_sign_negative());
assert!(!neg_inf.is_nan());
assert!(!neg_inf.is_normal());
assert_eq!(Fp::Infinite, neg_inf.classify());
}
#[test]
fn test_zero() {
let zero: f16 = 0.0f16;
assert_biteq!(0.0, zero);
assert!(!zero.is_infinite());
assert!(zero.is_finite());
assert!(zero.is_sign_positive());
assert!(!zero.is_sign_negative());
assert!(!zero.is_nan());
assert!(!zero.is_normal());
assert_eq!(Fp::Zero, zero.classify());
}
#[test]
fn test_neg_zero() {
let neg_zero: f16 = -0.0;
assert_eq!(0.0, neg_zero);
assert_biteq!(-0.0, neg_zero);
assert!(!neg_zero.is_infinite());
assert!(neg_zero.is_finite());
assert!(!neg_zero.is_sign_positive());
assert!(neg_zero.is_sign_negative());
assert!(!neg_zero.is_nan());
assert!(!neg_zero.is_normal());
assert_eq!(Fp::Zero, neg_zero.classify());
}
#[test]
fn test_one() {
let one: f16 = 1.0f16;
assert_biteq!(1.0, one);
assert!(!one.is_infinite());
assert!(one.is_finite());
assert!(one.is_sign_positive());
assert!(!one.is_sign_negative());
assert!(!one.is_nan());
assert!(one.is_normal());
assert_eq!(Fp::Normal, one.classify());
}
#[test]
fn test_is_nan() {
let nan: f16 = f16::NAN;
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
assert!(nan.is_nan());
assert!(!0.0f16.is_nan());
assert!(!5.3f16.is_nan());
assert!(!(-10.732f16).is_nan());
assert!(!inf.is_nan());
assert!(!neg_inf.is_nan());
}
#[test]
fn test_is_infinite() {
let nan: f16 = f16::NAN;
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
assert!(!nan.is_infinite());
assert!(inf.is_infinite());
assert!(neg_inf.is_infinite());
assert!(!0.0f16.is_infinite());
assert!(!42.8f16.is_infinite());
assert!(!(-109.2f16).is_infinite());
}
#[test]
fn test_is_finite() {
let nan: f16 = f16::NAN;
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
assert!(!nan.is_finite());
assert!(!inf.is_finite());
assert!(!neg_inf.is_finite());
assert!(0.0f16.is_finite());
assert!(42.8f16.is_finite());
assert!((-109.2f16).is_finite());
}
#[test]
fn test_is_normal() {
let nan: f16 = f16::NAN;
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
let zero: f16 = 0.0f16;
let neg_zero: f16 = -0.0;
assert!(!nan.is_normal());
assert!(!inf.is_normal());
assert!(!neg_inf.is_normal());
assert!(!zero.is_normal());
assert!(!neg_zero.is_normal());
assert!(1f16.is_normal());
assert!(1e-4f16.is_normal());
assert!(!1e-5f16.is_normal());
}
#[test]
fn test_classify() {
let nan: f16 = f16::NAN;
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
let zero: f16 = 0.0f16;
let neg_zero: f16 = -0.0;
assert_eq!(nan.classify(), Fp::Nan);
assert_eq!(inf.classify(), Fp::Infinite);
assert_eq!(neg_inf.classify(), Fp::Infinite);
assert_eq!(zero.classify(), Fp::Zero);
assert_eq!(neg_zero.classify(), Fp::Zero);
assert_eq!(1f16.classify(), Fp::Normal);
assert_eq!(1e-4f16.classify(), Fp::Normal);
assert_eq!(1e-5f16.classify(), Fp::Subnormal);
}
#[test]
#[cfg(any(miri, target_has_reliable_f16_math))]
fn test_abs() {

View file

@ -1,6 +1,5 @@
use core::f32;
use core::f32::consts;
use core::num::FpCategory as Fp;
use super::{assert_approx_eq, assert_biteq};
@ -30,148 +29,6 @@ const NAN_MASK2: u32 = 0x0055_5555;
/// They serve as a way to get an idea of the real precision of floating point operations on different platforms.
const APPROX_DELTA: f32 = if cfg!(miri) { 1e-4 } else { 1e-6 };
#[test]
fn test_num_f32() {
super::test_num(10f32, 2f32);
}
#[test]
fn test_infinity() {
let inf: f32 = f32::INFINITY;
assert!(inf.is_infinite());
assert!(!inf.is_finite());
assert!(inf.is_sign_positive());
assert!(!inf.is_sign_negative());
assert!(!inf.is_nan());
assert!(!inf.is_normal());
assert_eq!(Fp::Infinite, inf.classify());
}
#[test]
fn test_neg_infinity() {
let neg_inf: f32 = f32::NEG_INFINITY;
assert!(neg_inf.is_infinite());
assert!(!neg_inf.is_finite());
assert!(!neg_inf.is_sign_positive());
assert!(neg_inf.is_sign_negative());
assert!(!neg_inf.is_nan());
assert!(!neg_inf.is_normal());
assert_eq!(Fp::Infinite, neg_inf.classify());
}
#[test]
fn test_zero() {
let zero: f32 = 0.0f32;
assert_biteq!(0.0, zero);
assert!(!zero.is_infinite());
assert!(zero.is_finite());
assert!(zero.is_sign_positive());
assert!(!zero.is_sign_negative());
assert!(!zero.is_nan());
assert!(!zero.is_normal());
assert_eq!(Fp::Zero, zero.classify());
}
#[test]
fn test_neg_zero() {
let neg_zero: f32 = -0.0;
assert_eq!(0.0, neg_zero);
assert_biteq!(-0.0, neg_zero);
assert!(!neg_zero.is_infinite());
assert!(neg_zero.is_finite());
assert!(!neg_zero.is_sign_positive());
assert!(neg_zero.is_sign_negative());
assert!(!neg_zero.is_nan());
assert!(!neg_zero.is_normal());
assert_eq!(Fp::Zero, neg_zero.classify());
}
#[test]
fn test_one() {
let one: f32 = 1.0f32;
assert_biteq!(1.0, one);
assert!(!one.is_infinite());
assert!(one.is_finite());
assert!(one.is_sign_positive());
assert!(!one.is_sign_negative());
assert!(!one.is_nan());
assert!(one.is_normal());
assert_eq!(Fp::Normal, one.classify());
}
#[test]
fn test_is_nan() {
let nan: f32 = f32::NAN;
let inf: f32 = f32::INFINITY;
let neg_inf: f32 = f32::NEG_INFINITY;
assert!(nan.is_nan());
assert!(!0.0f32.is_nan());
assert!(!5.3f32.is_nan());
assert!(!(-10.732f32).is_nan());
assert!(!inf.is_nan());
assert!(!neg_inf.is_nan());
}
#[test]
fn test_is_infinite() {
let nan: f32 = f32::NAN;
let inf: f32 = f32::INFINITY;
let neg_inf: f32 = f32::NEG_INFINITY;
assert!(!nan.is_infinite());
assert!(inf.is_infinite());
assert!(neg_inf.is_infinite());
assert!(!0.0f32.is_infinite());
assert!(!42.8f32.is_infinite());
assert!(!(-109.2f32).is_infinite());
}
#[test]
fn test_is_finite() {
let nan: f32 = f32::NAN;
let inf: f32 = f32::INFINITY;
let neg_inf: f32 = f32::NEG_INFINITY;
assert!(!nan.is_finite());
assert!(!inf.is_finite());
assert!(!neg_inf.is_finite());
assert!(0.0f32.is_finite());
assert!(42.8f32.is_finite());
assert!((-109.2f32).is_finite());
}
#[test]
fn test_is_normal() {
let nan: f32 = f32::NAN;
let inf: f32 = f32::INFINITY;
let neg_inf: f32 = f32::NEG_INFINITY;
let zero: f32 = 0.0f32;
let neg_zero: f32 = -0.0;
assert!(!nan.is_normal());
assert!(!inf.is_normal());
assert!(!neg_inf.is_normal());
assert!(!zero.is_normal());
assert!(!neg_zero.is_normal());
assert!(1f32.is_normal());
assert!(1e-37f32.is_normal());
assert!(!1e-38f32.is_normal());
}
#[test]
fn test_classify() {
let nan: f32 = f32::NAN;
let inf: f32 = f32::INFINITY;
let neg_inf: f32 = f32::NEG_INFINITY;
let zero: f32 = 0.0f32;
let neg_zero: f32 = -0.0;
assert_eq!(nan.classify(), Fp::Nan);
assert_eq!(inf.classify(), Fp::Infinite);
assert_eq!(neg_inf.classify(), Fp::Infinite);
assert_eq!(zero.classify(), Fp::Zero);
assert_eq!(neg_zero.classify(), Fp::Zero);
assert_eq!(1f32.classify(), Fp::Normal);
assert_eq!(1e-37f32.classify(), Fp::Normal);
assert_eq!(1e-38f32.classify(), Fp::Subnormal);
}
#[test]
fn test_abs() {
assert_biteq!(f32::INFINITY.abs(), f32::INFINITY);

View file

@ -1,6 +1,5 @@
use core::f64;
use core::f64::consts;
use core::num::FpCategory as Fp;
use super::{assert_approx_eq, assert_biteq};
@ -25,147 +24,6 @@ const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa;
/// Second pattern over the mantissa
const NAN_MASK2: u64 = 0x0005_5555_5555_5555;
#[test]
fn test_num_f64() {
super::test_num(10f64, 2f64);
}
#[test]
fn test_infinity() {
let inf: f64 = f64::INFINITY;
assert!(inf.is_infinite());
assert!(!inf.is_finite());
assert!(inf.is_sign_positive());
assert!(!inf.is_sign_negative());
assert!(!inf.is_nan());
assert!(!inf.is_normal());
assert_eq!(Fp::Infinite, inf.classify());
}
#[test]
fn test_neg_infinity() {
let neg_inf: f64 = f64::NEG_INFINITY;
assert!(neg_inf.is_infinite());
assert!(!neg_inf.is_finite());
assert!(!neg_inf.is_sign_positive());
assert!(neg_inf.is_sign_negative());
assert!(!neg_inf.is_nan());
assert!(!neg_inf.is_normal());
assert_eq!(Fp::Infinite, neg_inf.classify());
}
#[test]
fn test_zero() {
let zero: f64 = 0.0f64;
assert_biteq!(0.0, zero);
assert!(!zero.is_infinite());
assert!(zero.is_finite());
assert!(zero.is_sign_positive());
assert!(!zero.is_sign_negative());
assert!(!zero.is_nan());
assert!(!zero.is_normal());
assert_eq!(Fp::Zero, zero.classify());
}
#[test]
fn test_neg_zero() {
let neg_zero: f64 = -0.0;
assert_eq!(0.0, neg_zero);
assert_biteq!(-0.0, neg_zero);
assert!(!neg_zero.is_infinite());
assert!(neg_zero.is_finite());
assert!(!neg_zero.is_sign_positive());
assert!(neg_zero.is_sign_negative());
assert!(!neg_zero.is_nan());
assert!(!neg_zero.is_normal());
assert_eq!(Fp::Zero, neg_zero.classify());
}
#[test]
fn test_one() {
let one: f64 = 1.0f64;
assert_biteq!(1.0, one);
assert!(!one.is_infinite());
assert!(one.is_finite());
assert!(one.is_sign_positive());
assert!(!one.is_sign_negative());
assert!(!one.is_nan());
assert!(one.is_normal());
assert_eq!(Fp::Normal, one.classify());
}
#[test]
fn test_is_nan() {
let nan: f64 = f64::NAN;
let inf: f64 = f64::INFINITY;
let neg_inf: f64 = f64::NEG_INFINITY;
assert!(nan.is_nan());
assert!(!0.0f64.is_nan());
assert!(!5.3f64.is_nan());
assert!(!(-10.732f64).is_nan());
assert!(!inf.is_nan());
assert!(!neg_inf.is_nan());
}
#[test]
fn test_is_infinite() {
let nan: f64 = f64::NAN;
let inf: f64 = f64::INFINITY;
let neg_inf: f64 = f64::NEG_INFINITY;
assert!(!nan.is_infinite());
assert!(inf.is_infinite());
assert!(neg_inf.is_infinite());
assert!(!0.0f64.is_infinite());
assert!(!42.8f64.is_infinite());
assert!(!(-109.2f64).is_infinite());
}
#[test]
fn test_is_finite() {
let nan: f64 = f64::NAN;
let inf: f64 = f64::INFINITY;
let neg_inf: f64 = f64::NEG_INFINITY;
assert!(!nan.is_finite());
assert!(!inf.is_finite());
assert!(!neg_inf.is_finite());
assert!(0.0f64.is_finite());
assert!(42.8f64.is_finite());
assert!((-109.2f64).is_finite());
}
#[test]
fn test_is_normal() {
let nan: f64 = f64::NAN;
let inf: f64 = f64::INFINITY;
let neg_inf: f64 = f64::NEG_INFINITY;
let zero: f64 = 0.0f64;
let neg_zero: f64 = -0.0;
assert!(!nan.is_normal());
assert!(!inf.is_normal());
assert!(!neg_inf.is_normal());
assert!(!zero.is_normal());
assert!(!neg_zero.is_normal());
assert!(1f64.is_normal());
assert!(1e-307f64.is_normal());
assert!(!1e-308f64.is_normal());
}
#[test]
fn test_classify() {
let nan: f64 = f64::NAN;
let inf: f64 = f64::INFINITY;
let neg_inf: f64 = f64::NEG_INFINITY;
let zero: f64 = 0.0f64;
let neg_zero: f64 = -0.0;
assert_eq!(nan.classify(), Fp::Nan);
assert_eq!(inf.classify(), Fp::Infinite);
assert_eq!(neg_inf.classify(), Fp::Infinite);
assert_eq!(zero.classify(), Fp::Zero);
assert_eq!(neg_zero.classify(), Fp::Zero);
assert_eq!(1e-307f64.classify(), Fp::Normal);
assert_eq!(1e-308f64.classify(), Fp::Subnormal);
}
#[test]
fn test_abs() {
assert_biteq!(f64::INFINITY.abs(), f64::INFINITY);

View file

@ -1,28 +1,40 @@
use std::fmt;
use std::num::FpCategory as Fp;
use std::ops::{Add, Div, Mul, Rem, Sub};
/// Set the default tolerance for float comparison based on the type.
trait Approx {
const LIM: Self;
trait TestableFloat {
/// Set the default tolerance for float comparison based on the type.
const APPROX: Self;
const MIN_POSITIVE_NORMAL: Self;
const MAX_SUBNORMAL: Self;
}
impl Approx for f16 {
const LIM: Self = 1e-3;
impl TestableFloat for f16 {
const APPROX: Self = 1e-3;
const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE;
const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down();
}
impl Approx for f32 {
const LIM: Self = 1e-6;
impl TestableFloat for f32 {
const APPROX: Self = 1e-6;
const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE;
const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down();
}
impl Approx for f64 {
const LIM: Self = 1e-6;
impl TestableFloat for f64 {
const APPROX: Self = 1e-6;
const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE;
const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down();
}
impl Approx for f128 {
const LIM: Self = 1e-9;
impl TestableFloat for f128 {
const APPROX: Self = 1e-9;
const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE;
const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down();
}
/// Determine the tolerance for values of the argument type.
const fn lim_for_ty<T: Approx + Copy>(_x: T) -> T {
T::LIM
const fn lim_for_ty<T: TestableFloat + Copy>(_x: T) -> T {
T::APPROX
}
// We have runtime ("rt") and const versions of these macros.
@ -187,9 +199,11 @@ macro_rules! float_test {
$( $( #[$const_meta] )+ )?
mod const_ {
#[allow(unused)]
use super::Approx;
use super::TestableFloat;
#[allow(unused)]
use std::num::FpCategory as Fp;
#[allow(unused)]
use std::ops::{Add, Div, Mul, Rem, Sub};
// Shadow the runtime versions of the macro with const-compatible versions.
#[allow(unused)]
use $crate::floats::{
@ -229,30 +243,42 @@ macro_rules! float_test {
};
}
/// Helper function for testing numeric operations
pub fn test_num<T>(ten: T, two: T)
where
T: PartialEq
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>
+ Rem<Output = T>
+ fmt::Debug
+ Copy,
{
assert_eq!(ten.add(two), ten + two);
assert_eq!(ten.sub(two), ten - two);
assert_eq!(ten.mul(two), ten * two);
assert_eq!(ten.div(two), ten / two);
assert_eq!(ten.rem(two), ten % two);
}
mod f128;
mod f16;
mod f32;
mod f64;
float_test! {
name: num,
attrs: {
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let two: Float = 2.0;
let ten: Float = 10.0;
assert_biteq!(ten.add(two), ten + two);
assert_biteq!(ten.sub(two), ten - two);
assert_biteq!(ten.mul(two), ten * two);
assert_biteq!(ten.div(two), ten / two);
}
}
// FIXME(f16_f128): merge into `num` once the required `fmodl`/`fmodf128` function is available on
// all platforms.
float_test! {
name: num_rem,
attrs: {
f16: #[cfg(any(miri, target_has_reliable_f16_math))],
f128: #[cfg(any(miri, target_has_reliable_f128_math))],
},
test<Float> {
let two: Float = 2.0;
let ten: Float = 10.0;
assert_biteq!(ten.rem(two), ten % two);
}
}
float_test! {
name: nan,
attrs: {
@ -273,6 +299,213 @@ float_test! {
}
}
float_test! {
name: infinity,
attrs: {
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let inf: Float = Float::INFINITY;
assert!(inf.is_infinite());
assert!(!inf.is_finite());
assert!(inf.is_sign_positive());
assert!(!inf.is_sign_negative());
assert!(!inf.is_nan());
assert!(!inf.is_normal());
assert!(matches!(inf.classify(), Fp::Infinite));
}
}
float_test! {
name: neg_infinity,
attrs: {
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let neg_inf: Float = Float::NEG_INFINITY;
assert!(neg_inf.is_infinite());
assert!(!neg_inf.is_finite());
assert!(!neg_inf.is_sign_positive());
assert!(neg_inf.is_sign_negative());
assert!(!neg_inf.is_nan());
assert!(!neg_inf.is_normal());
assert!(matches!(neg_inf.classify(), Fp::Infinite));
}
}
float_test! {
name: zero,
attrs: {
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let zero: Float = 0.0;
assert_biteq!(0.0, zero);
assert!(!zero.is_infinite());
assert!(zero.is_finite());
assert!(zero.is_sign_positive());
assert!(!zero.is_sign_negative());
assert!(!zero.is_nan());
assert!(!zero.is_normal());
assert!(matches!(zero.classify(), Fp::Zero));
}
}
float_test! {
name: neg_zero,
attrs: {
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let neg_zero: Float = -0.0;
assert!(0.0 == neg_zero);
assert_biteq!(-0.0, neg_zero);
assert!(!neg_zero.is_infinite());
assert!(neg_zero.is_finite());
assert!(!neg_zero.is_sign_positive());
assert!(neg_zero.is_sign_negative());
assert!(!neg_zero.is_nan());
assert!(!neg_zero.is_normal());
assert!(matches!(neg_zero.classify(), Fp::Zero));
}
}
float_test! {
name: one,
attrs: {
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let one: Float = 1.0;
assert_biteq!(1.0, one);
assert!(!one.is_infinite());
assert!(one.is_finite());
assert!(one.is_sign_positive());
assert!(!one.is_sign_negative());
assert!(!one.is_nan());
assert!(one.is_normal());
assert!(matches!(one.classify(), Fp::Normal));
}
}
float_test! {
name: is_nan,
attrs: {
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let nan: Float = Float::NAN;
let inf: Float = Float::INFINITY;
let neg_inf: Float = Float::NEG_INFINITY;
let zero: Float = 0.0;
let pos: Float = 5.3;
let neg: Float = -10.732;
assert!(nan.is_nan());
assert!(!zero.is_nan());
assert!(!pos.is_nan());
assert!(!neg.is_nan());
assert!(!inf.is_nan());
assert!(!neg_inf.is_nan());
}
}
float_test! {
name: is_infinite,
attrs: {
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let nan: Float = Float::NAN;
let inf: Float = Float::INFINITY;
let neg_inf: Float = Float::NEG_INFINITY;
let zero: Float = 0.0;
let pos: Float = 42.8;
let neg: Float = -109.2;
assert!(!nan.is_infinite());
assert!(inf.is_infinite());
assert!(neg_inf.is_infinite());
assert!(!zero.is_infinite());
assert!(!pos.is_infinite());
assert!(!neg.is_infinite());
}
}
float_test! {
name: is_finite,
attrs: {
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let nan: Float = Float::NAN;
let inf: Float = Float::INFINITY;
let neg_inf: Float = Float::NEG_INFINITY;
let zero: Float = 0.0;
let pos: Float = 42.8;
let neg: Float = -109.2;
assert!(!nan.is_finite());
assert!(!inf.is_finite());
assert!(!neg_inf.is_finite());
assert!(zero.is_finite());
assert!(pos.is_finite());
assert!(neg.is_finite());
}
}
float_test! {
name: is_normal,
attrs: {
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let nan: Float = Float::NAN;
let inf: Float = Float::INFINITY;
let neg_inf: Float = Float::NEG_INFINITY;
let zero: Float = 0.0;
let neg_zero: Float = -0.0;
let one : Float = 1.0;
assert!(!nan.is_normal());
assert!(!inf.is_normal());
assert!(!neg_inf.is_normal());
assert!(!zero.is_normal());
assert!(!neg_zero.is_normal());
assert!(one.is_normal());
assert!(Float::MIN_POSITIVE_NORMAL.is_normal());
assert!(!Float::MAX_SUBNORMAL.is_normal());
}
}
float_test! {
name: classify,
attrs: {
f16: #[cfg(any(miri, target_has_reliable_f16))],
},
test<Float> {
let nan: Float = Float::NAN;
let inf: Float = Float::INFINITY;
let neg_inf: Float = Float::NEG_INFINITY;
let zero: Float = 0.0;
let neg_zero: Float = -0.0;
let one: Float = 1.0;
assert!(matches!(nan.classify(), Fp::Nan));
assert!(matches!(inf.classify(), Fp::Infinite));
assert!(matches!(neg_inf.classify(), Fp::Infinite));
assert!(matches!(zero.classify(), Fp::Zero));
assert!(matches!(neg_zero.classify(), Fp::Zero));
assert!(matches!(one.classify(), Fp::Normal));
assert!(matches!(Float::MIN_POSITIVE_NORMAL.classify(), Fp::Normal));
assert!(matches!(Float::MAX_SUBNORMAL.classify(), Fp::Subnormal));
}
}
float_test! {
name: min,
attrs: {

View file

@ -20,6 +20,7 @@
#![feature(const_destruct)]
#![feature(const_eval_select)]
#![feature(const_float_round_methods)]
#![feature(const_ops)]
#![feature(const_ref_cell)]
#![feature(const_trait_impl)]
#![feature(core_float_math)]

View file

@ -92,6 +92,9 @@ backtrace = [
'object/rustc-dep-of-std',
'miniz_oxide/rustc-dep-of-std',
]
# Disable symbolization in backtraces. For use with -Zbuild-std.
# FIXME: Ideally this should be an additive backtrace-symbolization feature
backtrace-trace-only = []
panic-unwind = ["dep:panic_unwind"]
compiler-builtins-c = ["alloc/compiler-builtins-c"]

View file

@ -68,61 +68,67 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::
return false;
}
let mut hit = false;
backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
hit = true;
if cfg!(feature = "backtrace-trace-only") {
const HEX_WIDTH: usize = 2 + 2 * size_of::<usize>();
let frame_ip = frame.ip();
res = writeln!(bt_fmt.formatter(), "{idx:4}: {frame_ip:HEX_WIDTH$?}");
} else {
let mut hit = false;
backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
hit = true;
// `__rust_end_short_backtrace` means we are done hiding symbols
// for now. Print until we see `__rust_begin_short_backtrace`.
if print_fmt == PrintFmt::Short {
if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
if sym.contains("__rust_end_short_backtrace") {
print = true;
return;
}
if print && sym.contains("__rust_begin_short_backtrace") {
print = false;
return;
}
if !print {
omitted_count += 1;
// `__rust_end_short_backtrace` means we are done hiding symbols
// for now. Print until we see `__rust_begin_short_backtrace`.
if print_fmt == PrintFmt::Short {
if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
if sym.contains("__rust_end_short_backtrace") {
print = true;
return;
}
if print && sym.contains("__rust_begin_short_backtrace") {
print = false;
return;
}
if !print {
omitted_count += 1;
}
}
}
}
if print {
if omitted_count > 0 {
debug_assert!(print_fmt == PrintFmt::Short);
// only print the message between the middle of frames
if !first_omit {
let _ = writeln!(
bt_fmt.formatter(),
" [... omitted {} frame{} ...]",
omitted_count,
if omitted_count > 1 { "s" } else { "" }
);
if print {
if omitted_count > 0 {
debug_assert!(print_fmt == PrintFmt::Short);
// only print the message between the middle of frames
if !first_omit {
let _ = writeln!(
bt_fmt.formatter(),
" [... omitted {} frame{} ...]",
omitted_count,
if omitted_count > 1 { "s" } else { "" }
);
}
first_omit = false;
omitted_count = 0;
}
first_omit = false;
omitted_count = 0;
res = bt_fmt.frame().symbol(frame, symbol);
}
res = bt_fmt.frame().symbol(frame, symbol);
});
#[cfg(target_os = "nto")]
if libc::__my_thread_exit as *mut libc::c_void == frame.ip() {
if !hit && print {
use crate::backtrace_rs::SymbolName;
res = bt_fmt.frame().print_raw(
frame.ip(),
Some(SymbolName::new("__my_thread_exit".as_bytes())),
None,
None,
);
}
return false;
}
});
#[cfg(target_os = "nto")]
if libc::__my_thread_exit as *mut libc::c_void == frame.ip() {
if !hit && print {
use crate::backtrace_rs::SymbolName;
res = bt_fmt.frame().print_raw(
frame.ip(),
Some(SymbolName::new("__my_thread_exit".as_bytes())),
None,
None,
);
res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
}
return false;
}
if !hit && print {
res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
}
idx += 1;

View file

@ -469,6 +469,29 @@ impl<T: 'static> LocalKey<Cell<T>> {
pub fn replace(&'static self, value: T) -> T {
self.with(|cell| cell.replace(value))
}
/// Updates the contained value using a function.
///
/// # Examples
///
/// ```
/// #![feature(local_key_cell_update)]
/// use std::cell::Cell;
///
/// thread_local! {
/// static X: Cell<i32> = const { Cell::new(5) };
/// }
///
/// X.update(|x| x + 1);
/// assert_eq!(X.get(), 6);
/// ```
#[unstable(feature = "local_key_cell_update", issue = "143989")]
pub fn update(&'static self, f: impl FnOnce(T) -> T)
where
T: Copy,
{
self.with(|cell| cell.update(f))
}
}
impl<T: 'static> LocalKey<RefCell<T>> {

View file

@ -20,6 +20,7 @@ test = { path = "../test", public = true }
[features]
default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"]
backtrace = ["std/backtrace"]
backtrace-trace-only = ["std/backtrace-trace-only"]
compiler-builtins-c = ["std/compiler-builtins-c"]
compiler-builtins-mem = ["std/compiler-builtins-mem"]
compiler-builtins-no-asm = ["std/compiler-builtins-no-asm"]

View file

@ -11,7 +11,6 @@ pub(crate) mod llvm;
pub(crate) mod perf;
pub(crate) mod run;
pub(crate) mod setup;
pub(crate) mod suggest;
pub(crate) mod synthetic_targets;
pub(crate) mod test;
pub(crate) mod tool;

View file

@ -1,68 +0,0 @@
//! Attempt to magically identify good tests to run
use std::path::PathBuf;
use std::str::FromStr;
use clap::Parser;
use crate::core::build_steps::tool::Tool;
use crate::core::builder::Builder;
/// Suggests a list of possible `x.py` commands to run based on modified files in branch.
pub fn suggest(builder: &Builder<'_>, run: bool) {
let git_config = builder.config.git_config();
let suggestions = builder
.tool_cmd(Tool::SuggestTests)
.env("SUGGEST_TESTS_NIGHTLY_BRANCH", git_config.nightly_branch)
.env("SUGGEST_TESTS_MERGE_COMMIT_EMAIL", git_config.git_merge_commit_email)
.run_capture_stdout(builder)
.stdout();
let suggestions = suggestions
.lines()
.map(|line| {
let mut sections = line.split_ascii_whitespace();
// this code expects one suggestion per line in the following format:
// <x_subcommand> {some number of flags} [optional stage number]
let cmd = sections.next().unwrap();
let stage = sections.next_back().and_then(|s| str::parse(s).ok());
let paths: Vec<PathBuf> = sections.map(|p| PathBuf::from_str(p).unwrap()).collect();
(cmd, stage, paths)
})
.collect::<Vec<_>>();
if !suggestions.is_empty() {
println!("==== SUGGESTIONS ====");
for sug in &suggestions {
print!("x {} ", sug.0);
if let Some(stage) = sug.1 {
print!("--stage {stage} ");
}
for path in &sug.2 {
print!("{} ", path.display());
}
println!();
}
println!("=====================");
} else {
println!("No suggestions found!");
return;
}
if run {
for sug in suggestions {
let mut build: crate::Build = builder.build.clone();
build.config.paths = sug.2;
build.config.cmd = crate::core::config::flags::Flags::parse_from(["x.py", sug.0]).cmd;
if let Some(stage) = sug.1 {
build.config.stage = stage;
}
build.build();
}
} else {
println!("HELP: consider using the `--run` flag to automatically run suggested tests");
}
}

View file

@ -47,12 +47,11 @@ impl Step for CrateBootstrap {
const DEFAULT: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
// This step is responsible for several different tool paths. By default
// it will test all of them, but requesting specific tools on the
// command-line (e.g. `./x test suggest-tests`) will test only the
// specified tools.
// This step is responsible for several different tool paths.
//
// By default, it will test all of them, but requesting specific tools on the command-line
// (e.g. `./x test src/tools/coverage-dump`) will test only the specified tools.
run.path("src/tools/jsondoclint")
.path("src/tools/suggest-tests")
.path("src/tools/replace-version-placeholder")
.path("src/tools/coverage-dump")
// We want `./x test tidy` to _run_ the tidy tool, not its tests.

View file

@ -517,7 +517,6 @@ bootstrap_tool!(
ReplaceVersionPlaceholder, "src/tools/replace-version-placeholder", "replace-version-placeholder";
CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
GenerateCopyright, "src/tools/generate-copyright", "generate-copyright";
SuggestTests, "src/tools/suggest-tests", "suggest-tests";
GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys";
// rustdoc-gui-test has a crate dependency on compiletest, so it needs the same unstable features.
RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = COMPILETEST_ALLOW_FEATURES;

View file

@ -113,7 +113,7 @@ impl Cargo {
match cmd_kind {
// No need to configure the target linker for these command types.
Kind::Clean | Kind::Check | Kind::Suggest | Kind::Format | Kind::Setup => {}
Kind::Clean | Kind::Check | Kind::Format | Kind::Setup => {}
_ => {
cargo.configure_linker(builder, mode);
}

View file

@ -845,7 +845,6 @@ pub enum Kind {
#[value(alias = "r")]
Run,
Setup,
Suggest,
Vendor,
Perf,
}
@ -869,7 +868,6 @@ impl Kind {
Kind::Install => "install",
Kind::Run => "run",
Kind::Setup => "setup",
Kind::Suggest => "suggest",
Kind::Vendor => "vendor",
Kind::Perf => "perf",
}
@ -881,7 +879,6 @@ impl Kind {
Kind::Bench => "Benchmarking",
Kind::Doc => "Documenting",
Kind::Run => "Running",
Kind::Suggest => "Suggesting",
Kind::Clippy => "Linting",
Kind::Perf => "Profiling & benchmarking",
_ => {
@ -1201,7 +1198,7 @@ impl<'a> Builder<'a> {
Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std),
Kind::Vendor => describe!(vendor::Vendor),
// special-cased in Build::build()
Kind::Format | Kind::Suggest | Kind::Perf => vec![],
Kind::Format | Kind::Perf => vec![],
Kind::MiriTest | Kind::MiriSetup => unreachable!(),
}
}
@ -1269,7 +1266,6 @@ impl<'a> Builder<'a> {
Subcommand::Run { .. } => (Kind::Run, &paths[..]),
Subcommand::Clean { .. } => (Kind::Clean, &paths[..]),
Subcommand::Format { .. } => (Kind::Format, &[][..]),
Subcommand::Suggest { .. } => (Kind::Suggest, &[][..]),
Subcommand::Setup { profile: ref path } => (
Kind::Setup,
path.as_ref().map_or([].as_slice(), |path| std::slice::from_ref(path)),

View file

@ -1050,7 +1050,6 @@ impl Config {
| Subcommand::Run { .. }
| Subcommand::Setup { .. }
| Subcommand::Format { .. }
| Subcommand::Suggest { .. }
| Subcommand::Vendor { .. } => flags_stage.unwrap_or(0),
};
@ -1098,7 +1097,6 @@ impl Config {
| Subcommand::Run { .. }
| Subcommand::Setup { .. }
| Subcommand::Format { .. }
| Subcommand::Suggest { .. }
| Subcommand::Vendor { .. }
| Subcommand::Perf { .. } => {}
}

View file

@ -481,13 +481,6 @@ Arguments:
#[arg(value_name = "<PROFILE>|hook|editor|link")]
profile: Option<PathBuf>,
},
/// Suggest a subset of tests to run, based on modified files
#[command(long_about = "\n")]
Suggest {
/// run suggested tests
#[arg(long)]
run: bool,
},
/// Vendor dependencies
Vendor {
/// Additional `Cargo.toml` to sync and vendor
@ -518,7 +511,6 @@ impl Subcommand {
Subcommand::Install => Kind::Install,
Subcommand::Run { .. } => Kind::Run,
Subcommand::Setup { .. } => Kind::Setup,
Subcommand::Suggest { .. } => Kind::Suggest,
Subcommand::Vendor { .. } => Kind::Vendor,
Subcommand::Perf { .. } => Kind::Perf,
}

View file

@ -216,7 +216,6 @@ than building it.
build.config.cmd,
Subcommand::Clean { .. }
| Subcommand::Check { .. }
| Subcommand::Suggest { .. }
| Subcommand::Format { .. }
| Subcommand::Setup { .. }
);

View file

@ -651,11 +651,9 @@ impl Build {
// Handle hard-coded subcommands.
{
#[cfg(feature = "tracing")]
let _hardcoded_span = span!(
tracing::Level::DEBUG,
"handling hardcoded subcommands (Format, Suggest, Perf)"
)
.entered();
let _hardcoded_span =
span!(tracing::Level::DEBUG, "handling hardcoded subcommands (Format, Perf)")
.entered();
match &self.config.cmd {
Subcommand::Format { check, all } => {
@ -666,9 +664,6 @@ impl Build {
&self.config.paths,
);
}
Subcommand::Suggest { run } => {
return core::build_steps::suggest::suggest(&builder::Builder::new(self), *run);
}
Subcommand::Perf(args) => {
return core::build_steps::perf::perf(&builder::Builder::new(self), args);
}

View file

@ -67,7 +67,6 @@ pub fn fill_compilers(build: &mut Build) {
// We don't need to check cross targets for these commands.
crate::Subcommand::Clean { .. }
| crate::Subcommand::Check { .. }
| crate::Subcommand::Suggest { .. }
| crate::Subcommand::Format { .. }
| crate::Subcommand::Setup { .. } => {
build.hosts.iter().cloned().chain(iter::once(build.host_target)).collect()
@ -221,10 +220,15 @@ fn default_compiler(
}
t if t.contains("-wasi") => {
let root = build
.wasi_sdk_path
.as_ref()
.expect("WASI_SDK_PATH mut be configured for a -wasi target");
let root = if let Some(path) = build.wasi_sdk_path.as_ref() {
path
} else {
if build.config.is_running_on_ci {
panic!("ERROR: WASI_SDK_PATH must be configured for a -wasi target on CI");
}
println!("WARNING: WASI_SDK_PATH not set, using default cc/cxx compiler");
return None;
};
let compiler = match compiler {
Language::C => format!("{t}-clang"),
Language::CPlusPlus => format!("{t}-clang++"),

Some files were not shown because too many files have changed in this diff Show more