Merge from rustc
This commit is contained in:
commit
f590fa9214
715 changed files with 16769 additions and 7776 deletions
14
.github/renovate.json5
vendored
Normal file
14
.github/renovate.json5
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
// Let Renovatebot keep an opened issue that tracks our dependencies
|
||||
"dependencyDashboard": true,
|
||||
// Disable "normal" package updates
|
||||
"enabledManagers": [],
|
||||
// Update lockfiles once per week
|
||||
"lockFileMaintenance": {
|
||||
"enabled": true,
|
||||
"schedule": [
|
||||
"before 5am on Tuesday"
|
||||
]
|
||||
}
|
||||
}
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -88,12 +88,12 @@ package.json
|
|||
tests/rustdoc-gui/src/**.lock
|
||||
|
||||
## direnv
|
||||
.envrc
|
||||
.direnv/
|
||||
/.envrc
|
||||
/.direnv/
|
||||
|
||||
## nix
|
||||
flake.nix
|
||||
/flake.nix
|
||||
flake.lock
|
||||
default.nix
|
||||
/default.nix
|
||||
|
||||
# Before adding new lines, see the comment at the top.
|
||||
|
|
|
|||
583
Cargo.lock
583
Cargo.lock
File diff suppressed because it is too large
Load diff
|
|
@ -21,7 +21,7 @@ standard library, and documentation.
|
|||
|
||||
## Why Rust?
|
||||
|
||||
- **Performance:** Fast and memory-efficient, suitable for critical services, embedded devices, and easily integrate with other languages.
|
||||
- **Performance:** Fast and memory-efficient, suitable for critical services, embedded devices, and easily integrated with other languages.
|
||||
|
||||
- **Reliability:** Our rich type system and ownership model ensure memory and thread safety, reducing bugs at compile-time.
|
||||
|
||||
|
|
|
|||
|
|
@ -3119,6 +3119,7 @@ pub struct FieldDef {
|
|||
pub ident: Option<Ident>,
|
||||
|
||||
pub ty: P<Ty>,
|
||||
pub default: Option<AnonConst>,
|
||||
pub is_placeholder: bool,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1120,13 +1120,14 @@ fn walk_poly_trait_ref<T: MutVisitor>(vis: &mut T, p: &mut PolyTraitRef) {
|
|||
}
|
||||
|
||||
pub fn walk_field_def<T: MutVisitor>(visitor: &mut T, fd: &mut FieldDef) {
|
||||
let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety } = fd;
|
||||
let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety, default } = fd;
|
||||
visitor.visit_id(id);
|
||||
visit_attrs(visitor, attrs);
|
||||
visitor.visit_vis(vis);
|
||||
visit_safety(visitor, safety);
|
||||
visit_opt(ident, |ident| visitor.visit_ident(ident));
|
||||
visitor.visit_ty(ty);
|
||||
visit_opt(default, |default| visitor.visit_anon_const(default));
|
||||
visitor.visit_span(span);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -975,11 +975,13 @@ pub fn walk_struct_def<'a, V: Visitor<'a>>(
|
|||
}
|
||||
|
||||
pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) -> V::Result {
|
||||
let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, safety: _ } = field;
|
||||
let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, safety: _, default } =
|
||||
field;
|
||||
walk_list!(visitor, visit_attribute, attrs);
|
||||
try_visit!(visitor.visit_vis(vis));
|
||||
visit_opt!(visitor, visit_ident, ident);
|
||||
try_visit!(visitor.visit_ty(ty));
|
||||
visit_opt!(visitor, visit_anon_const, &*default);
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,10 +45,6 @@ ast_lowering_bad_return_type_notation_output =
|
|||
|
||||
ast_lowering_bad_return_type_notation_position = return type notation not allowed in this position yet
|
||||
|
||||
ast_lowering_base_expression_double_dot =
|
||||
base expression required after `..`
|
||||
.suggestion = add a base expression here
|
||||
|
||||
ast_lowering_clobber_abi_not_supported =
|
||||
`clobber_abi` is not supported on this target
|
||||
|
||||
|
|
@ -57,6 +53,9 @@ ast_lowering_closure_cannot_be_static = closures cannot be static
|
|||
ast_lowering_coroutine_too_many_parameters =
|
||||
too many parameters for a coroutine (expected 0 or 1 parameters)
|
||||
|
||||
ast_lowering_default_field_in_tuple = default fields are not supported in tuple structs
|
||||
.label = default fields are only supported on structs
|
||||
|
||||
ast_lowering_does_not_support_modifiers =
|
||||
the `{$class_name}` register class does not support template modifiers
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,14 @@ pub(crate) struct InvalidAbi {
|
|||
pub suggestion: Option<InvalidAbiSuggestion>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_lowering_default_field_in_tuple)]
|
||||
pub(crate) struct TupleStructWithDefault {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
pub(crate) struct InvalidAbiReason(pub &'static str);
|
||||
|
||||
impl Subdiagnostic for InvalidAbiReason {
|
||||
|
|
@ -114,14 +122,6 @@ pub(crate) struct UnderscoreExprLhsAssign {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_lowering_base_expression_double_dot, code = E0797)]
|
||||
pub(crate) struct BaseExpressionDoubleDot {
|
||||
#[primary_span]
|
||||
#[suggestion(code = "/* expr */", applicability = "has-placeholders", style = "verbose")]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_lowering_await_only_in_async_fn_and_blocks, code = E0728)]
|
||||
pub(crate) struct AwaitOnlyInAsyncFnAndBlocks {
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ use thin_vec::{ThinVec, thin_vec};
|
|||
use visit::{Visitor, walk_expr};
|
||||
|
||||
use super::errors::{
|
||||
AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, BaseExpressionDoubleDot,
|
||||
ClosureCannotBeStatic, CoroutineTooManyParameters,
|
||||
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
|
||||
NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign,
|
||||
AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, ClosureCannotBeStatic,
|
||||
CoroutineTooManyParameters, FunctionalRecordUpdateDestructuringAssignment,
|
||||
InclusiveRangeWithNoEnd, MatchArmWithNoBody, NeverPatternWithBody, NeverPatternWithGuard,
|
||||
UnderscoreExprLhsAssign,
|
||||
};
|
||||
use super::{
|
||||
GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt,
|
||||
|
|
@ -357,12 +357,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
),
|
||||
ExprKind::Struct(se) => {
|
||||
let rest = match &se.rest {
|
||||
StructRest::Base(e) => Some(self.lower_expr(e)),
|
||||
StructRest::Rest(sp) => {
|
||||
let guar = self.dcx().emit_err(BaseExpressionDoubleDot { span: *sp });
|
||||
Some(&*self.arena.alloc(self.expr_err(*sp, guar)))
|
||||
}
|
||||
StructRest::None => None,
|
||||
StructRest::Base(e) => hir::StructTailExpr::Base(self.lower_expr(e)),
|
||||
StructRest::Rest(sp) => hir::StructTailExpr::DefaultFields(*sp),
|
||||
StructRest::None => hir::StructTailExpr::None,
|
||||
};
|
||||
hir::ExprKind::Struct(
|
||||
self.arena.alloc(self.lower_qpath(
|
||||
|
|
@ -1526,7 +1523,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
hir::ExprKind::Struct(
|
||||
self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span))),
|
||||
fields,
|
||||
None,
|
||||
hir::StructTailExpr::None,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@ use smallvec::{SmallVec, smallvec};
|
|||
use thin_vec::ThinVec;
|
||||
use tracing::instrument;
|
||||
|
||||
use super::errors::{InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound};
|
||||
use super::errors::{
|
||||
InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound,
|
||||
TupleStructWithDefault,
|
||||
};
|
||||
use super::{
|
||||
AstOwner, FnDeclKind, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode,
|
||||
ResolverAstLoweringExt,
|
||||
|
|
@ -690,13 +693,27 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
VariantData::Tuple(fields, id) => {
|
||||
let ctor_id = self.lower_node_id(*id);
|
||||
self.alias_attrs(ctor_id, parent_id);
|
||||
hir::VariantData::Tuple(
|
||||
self.arena.alloc_from_iter(
|
||||
fields.iter().enumerate().map(|f| self.lower_field_def(f)),
|
||||
),
|
||||
ctor_id,
|
||||
self.local_def_id(*id),
|
||||
)
|
||||
let fields = self
|
||||
.arena
|
||||
.alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f)));
|
||||
for field in &fields[..] {
|
||||
if let Some(default) = field.default {
|
||||
// Default values in tuple struct and tuple variants are not allowed by the
|
||||
// RFC due to concerns about the syntax, both in the item definition and the
|
||||
// expression. We could in the future allow `struct S(i32 = 0);` and force
|
||||
// users to construct the value with `let _ = S { .. };`.
|
||||
if self.tcx.features().default_field_values() {
|
||||
self.dcx().emit_err(TupleStructWithDefault { span: default.span });
|
||||
} else {
|
||||
let _ = self.dcx().span_delayed_bug(
|
||||
default.span,
|
||||
"expected `default values on `struct` fields aren't supported` \
|
||||
feature-gate error but none was produced",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::VariantData::Tuple(fields, ctor_id, self.local_def_id(*id))
|
||||
}
|
||||
VariantData::Unit(id) => {
|
||||
let ctor_id = self.lower_node_id(*id);
|
||||
|
|
@ -723,6 +740,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
None => Ident::new(sym::integer(index), self.lower_span(f.span)),
|
||||
},
|
||||
vis_span: self.lower_span(f.vis.span),
|
||||
default: f.default.as_ref().map(|v| self.lower_anon_const_to_anon_const(v)),
|
||||
ty,
|
||||
safety: self.lower_safety(f.safety, hir::Safety::Safe),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -557,6 +557,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
|||
gate_all!(explicit_tail_calls, "`become` expression is experimental");
|
||||
gate_all!(generic_const_items, "generic const items are experimental");
|
||||
gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards");
|
||||
gate_all!(default_field_values, "default values on fields are experimental");
|
||||
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
|
||||
gate_all!(postfix_match, "postfix match is experimental");
|
||||
gate_all!(mut_ref, "mutable by-reference bindings are experimental");
|
||||
|
|
|
|||
|
|
@ -1151,7 +1151,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
expr: &hir::Expr<'_>,
|
||||
) {
|
||||
let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
|
||||
let hir::ExprKind::Struct(struct_qpath, fields, Some(base)) = expr.kind else { return };
|
||||
let hir::ExprKind::Struct(struct_qpath, fields, hir::StructTailExpr::Base(base)) =
|
||||
expr.kind
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let hir::QPath::Resolved(_, path) = struct_qpath else { return };
|
||||
let hir::def::Res::Def(_, def_id) = path.res else { return };
|
||||
let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) else { return };
|
||||
|
|
@ -1239,7 +1243,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
expr: &'tcx hir::Expr<'tcx>,
|
||||
use_spans: Option<UseSpans<'tcx>>,
|
||||
) {
|
||||
if let hir::ExprKind::Struct(_, _, Some(_)) = expr.kind {
|
||||
if let hir::ExprKind::Struct(_, _, hir::StructTailExpr::Base(_)) = expr.kind {
|
||||
// We have `S { foo: val, ..base }`. In `check_aggregate_rvalue` we have a single
|
||||
// `Location` that covers both the `S { ... }` literal, all of its fields and the
|
||||
// `base`. If the move happens because of `S { foo: val, bar: base.bar }` the `expr`
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ use rustc_data_structures::graph::dominators::Dominators;
|
|||
use rustc_errors::Diag;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_index::bit_set::{BitSet, ChunkedBitSet};
|
||||
use rustc_index::bit_set::{BitSet, MixedBitSet};
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_infer::infer::{
|
||||
InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
|
||||
|
|
@ -1797,7 +1797,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
location: Location,
|
||||
desired_action: InitializationRequiringAction,
|
||||
place_span: (PlaceRef<'tcx>, Span),
|
||||
maybe_uninits: &ChunkedBitSet<MovePathIndex>,
|
||||
maybe_uninits: &MixedBitSet<MovePathIndex>,
|
||||
from: u64,
|
||||
to: u64,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ builtin_macros_non_exhaustive_default = default variant must be exhaustive
|
|||
|
||||
builtin_macros_non_generic_pointee = the `#[pointee]` attribute may only be used on generic parameters
|
||||
|
||||
builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants
|
||||
builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants{$post}
|
||||
.help = consider a manual implementation of `Default`
|
||||
|
||||
builtin_macros_only_one_argument = {$name} takes 1 argument
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ where
|
|||
let fields = fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, &(ident, span))| {
|
||||
.map(|(i, &(ident, span, _))| {
|
||||
let arg = getarg(cx, span, ident.name, i);
|
||||
cx.field_imm(span, ident, arg)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -54,26 +54,38 @@ pub(crate) fn expand_deriving_default(
|
|||
trait_def.expand(cx, mitem, item, push)
|
||||
}
|
||||
|
||||
fn default_call(cx: &ExtCtxt<'_>, span: Span) -> ast::ptr::P<ast::Expr> {
|
||||
// Note that `kw::Default` is "default" and `sym::Default` is "Default"!
|
||||
let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
|
||||
cx.expr_call_global(span, default_ident, ThinVec::new())
|
||||
}
|
||||
|
||||
fn default_struct_substructure(
|
||||
cx: &ExtCtxt<'_>,
|
||||
trait_span: Span,
|
||||
substr: &Substructure<'_>,
|
||||
summary: &StaticFields,
|
||||
) -> BlockOrExpr {
|
||||
// Note that `kw::Default` is "default" and `sym::Default` is "Default"!
|
||||
let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
|
||||
let default_call = |span| cx.expr_call_global(span, default_ident.clone(), ThinVec::new());
|
||||
|
||||
let expr = match summary {
|
||||
Unnamed(_, IsTuple::No) => cx.expr_ident(trait_span, substr.type_ident),
|
||||
Unnamed(fields, IsTuple::Yes) => {
|
||||
let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
|
||||
let exprs = fields.iter().map(|sp| default_call(cx, *sp)).collect();
|
||||
cx.expr_call_ident(trait_span, substr.type_ident, exprs)
|
||||
}
|
||||
Named(fields) => {
|
||||
let default_fields = fields
|
||||
.iter()
|
||||
.map(|&(ident, span)| cx.field_imm(span, ident, default_call(span)))
|
||||
.map(|(ident, span, default_val)| {
|
||||
let value = match default_val {
|
||||
// We use `Default::default()`.
|
||||
None => default_call(cx, *span),
|
||||
// We use the field default const expression.
|
||||
Some(val) => {
|
||||
cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone()))
|
||||
}
|
||||
};
|
||||
cx.field_imm(*span, *ident, value)
|
||||
})
|
||||
.collect();
|
||||
cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
|
||||
}
|
||||
|
|
@ -93,10 +105,38 @@ fn default_enum_substructure(
|
|||
} {
|
||||
Ok(default_variant) => {
|
||||
// We now know there is exactly one unit variant with exactly one `#[default]` attribute.
|
||||
cx.expr_path(cx.path(default_variant.span, vec![
|
||||
Ident::new(kw::SelfUpper, default_variant.span),
|
||||
default_variant.ident,
|
||||
]))
|
||||
match &default_variant.data {
|
||||
VariantData::Unit(_) => cx.expr_path(cx.path(default_variant.span, vec![
|
||||
Ident::new(kw::SelfUpper, default_variant.span),
|
||||
default_variant.ident,
|
||||
])),
|
||||
VariantData::Struct { fields, .. } => {
|
||||
// This only happens if `#![feature(default_field_values)]`. We have validated
|
||||
// all fields have default values in the definition.
|
||||
let default_fields = fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
cx.field_imm(field.span, field.ident.unwrap(), match &field.default {
|
||||
// We use `Default::default()`.
|
||||
None => default_call(cx, field.span),
|
||||
// We use the field default const expression.
|
||||
Some(val) => {
|
||||
cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone()))
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let path = cx.path(default_variant.span, vec![
|
||||
Ident::new(kw::SelfUpper, default_variant.span),
|
||||
default_variant.ident,
|
||||
]);
|
||||
cx.expr_struct(default_variant.span, path, default_fields)
|
||||
}
|
||||
// Logic error in `extract_default_variant`.
|
||||
VariantData::Tuple(..) => {
|
||||
cx.dcx().bug("encountered tuple variant annotated with `#[default]`")
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(guar) => DummyResult::raw_expr(trait_span, Some(guar)),
|
||||
};
|
||||
|
|
@ -156,8 +196,20 @@ fn extract_default_variant<'a>(
|
|||
}
|
||||
};
|
||||
|
||||
if !matches!(variant.data, VariantData::Unit(..)) {
|
||||
let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span });
|
||||
if cx.ecfg.features.default_field_values()
|
||||
&& let VariantData::Struct { fields, .. } = &variant.data
|
||||
&& fields.iter().all(|f| f.default.is_some())
|
||||
// Disallow `#[default] Variant {}`
|
||||
&& !fields.is_empty()
|
||||
{
|
||||
// Allowed
|
||||
} else if !matches!(variant.data, VariantData::Unit(..)) {
|
||||
let post = if cx.ecfg.features.default_field_values() {
|
||||
" or variants where every field has a default value"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span, post });
|
||||
return Err(guar);
|
||||
}
|
||||
|
||||
|
|
@ -216,7 +268,12 @@ struct DetectNonVariantDefaultAttr<'a, 'b> {
|
|||
impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
|
||||
fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
|
||||
if attr.has_name(kw::Default) {
|
||||
self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span });
|
||||
let post = if self.cx.ecfg.features.default_field_values() {
|
||||
" or variants where every field has a default value"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span, post });
|
||||
}
|
||||
|
||||
rustc_ast::visit::walk_attribute(self, attr);
|
||||
|
|
|
|||
|
|
@ -182,8 +182,8 @@ pub(crate) use StaticFields::*;
|
|||
pub(crate) use SubstructureFields::*;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::{
|
||||
self as ast, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics,
|
||||
Mutability, PatKind, VariantData,
|
||||
self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind,
|
||||
Generics, Mutability, PatKind, VariantData,
|
||||
};
|
||||
use rustc_attr as attr;
|
||||
use rustc_expand::base::{Annotatable, ExtCtxt};
|
||||
|
|
@ -296,7 +296,7 @@ pub(crate) enum StaticFields {
|
|||
/// Tuple and unit structs/enum variants like this.
|
||||
Unnamed(Vec<Span>, IsTuple),
|
||||
/// Normal structs/struct variants.
|
||||
Named(Vec<(Ident, Span)>),
|
||||
Named(Vec<(Ident, Span, Option<AnonConst>)>),
|
||||
}
|
||||
|
||||
/// A summary of the possible sets of fields.
|
||||
|
|
@ -1435,7 +1435,7 @@ impl<'a> TraitDef<'a> {
|
|||
for field in struct_def.fields() {
|
||||
let sp = field.span.with_ctxt(self.span.ctxt());
|
||||
match field.ident {
|
||||
Some(ident) => named_idents.push((ident, sp)),
|
||||
Some(ident) => named_idents.push((ident, sp, field.default.clone())),
|
||||
_ => just_spans.push(sp),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -424,6 +424,7 @@ pub(crate) struct MultipleDefaultsSugg {
|
|||
pub(crate) struct NonUnitDefault {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) post: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
|
|||
|
|
@ -55,26 +55,26 @@ impl<T: ?Sized> LegacyReceiver for &mut T {}
|
|||
impl<T: ?Sized> LegacyReceiver for Box<T> {}
|
||||
|
||||
#[lang = "copy"]
|
||||
pub unsafe trait Copy {}
|
||||
pub trait Copy {}
|
||||
|
||||
unsafe impl Copy for bool {}
|
||||
unsafe impl Copy for u8 {}
|
||||
unsafe impl Copy for u16 {}
|
||||
unsafe impl Copy for u32 {}
|
||||
unsafe impl Copy for u64 {}
|
||||
unsafe impl Copy for u128 {}
|
||||
unsafe impl Copy for usize {}
|
||||
unsafe impl Copy for i8 {}
|
||||
unsafe impl Copy for i16 {}
|
||||
unsafe impl Copy for i32 {}
|
||||
unsafe impl Copy for isize {}
|
||||
unsafe impl Copy for f32 {}
|
||||
unsafe impl Copy for f64 {}
|
||||
unsafe impl Copy for char {}
|
||||
unsafe impl<'a, T: ?Sized> Copy for &'a T {}
|
||||
unsafe impl<T: ?Sized> Copy for *const T {}
|
||||
unsafe impl<T: ?Sized> Copy for *mut T {}
|
||||
unsafe impl<T: Copy> Copy for Option<T> {}
|
||||
impl Copy for bool {}
|
||||
impl Copy for u8 {}
|
||||
impl Copy for u16 {}
|
||||
impl Copy for u32 {}
|
||||
impl Copy for u64 {}
|
||||
impl Copy for u128 {}
|
||||
impl Copy for usize {}
|
||||
impl Copy for i8 {}
|
||||
impl Copy for i16 {}
|
||||
impl Copy for i32 {}
|
||||
impl Copy for isize {}
|
||||
impl Copy for f32 {}
|
||||
impl Copy for f64 {}
|
||||
impl Copy for char {}
|
||||
impl<'a, T: ?Sized> Copy for &'a T {}
|
||||
impl<T: ?Sized> Copy for *const T {}
|
||||
impl<T: ?Sized> Copy for *mut T {}
|
||||
impl<T: Copy> Copy for Option<T> {}
|
||||
|
||||
#[lang = "sync"]
|
||||
pub unsafe trait Sync {}
|
||||
|
|
|
|||
|
|
@ -52,24 +52,24 @@ impl<T: ?Sized> LegacyReceiver for &mut T {}
|
|||
impl<T: ?Sized, A: Allocator> LegacyReceiver for Box<T, A> {}
|
||||
|
||||
#[lang = "copy"]
|
||||
pub unsafe trait Copy {}
|
||||
pub trait Copy {}
|
||||
|
||||
unsafe impl Copy for bool {}
|
||||
unsafe impl Copy for u8 {}
|
||||
unsafe impl Copy for u16 {}
|
||||
unsafe impl Copy for u32 {}
|
||||
unsafe impl Copy for u64 {}
|
||||
unsafe impl Copy for usize {}
|
||||
unsafe impl Copy for i8 {}
|
||||
unsafe impl Copy for i16 {}
|
||||
unsafe impl Copy for i32 {}
|
||||
unsafe impl Copy for isize {}
|
||||
unsafe impl Copy for f32 {}
|
||||
unsafe impl Copy for f64 {}
|
||||
unsafe impl Copy for char {}
|
||||
unsafe impl<'a, T: ?Sized> Copy for &'a T {}
|
||||
unsafe impl<T: ?Sized> Copy for *const T {}
|
||||
unsafe impl<T: ?Sized> Copy for *mut T {}
|
||||
impl Copy for bool {}
|
||||
impl Copy for u8 {}
|
||||
impl Copy for u16 {}
|
||||
impl Copy for u32 {}
|
||||
impl Copy for u64 {}
|
||||
impl Copy for usize {}
|
||||
impl Copy for i8 {}
|
||||
impl Copy for i16 {}
|
||||
impl Copy for i32 {}
|
||||
impl Copy for isize {}
|
||||
impl Copy for f32 {}
|
||||
impl Copy for f64 {}
|
||||
impl Copy for char {}
|
||||
impl<'a, T: ?Sized> Copy for &'a T {}
|
||||
impl<T: ?Sized> Copy for *const T {}
|
||||
impl<T: ?Sized> Copy for *mut T {}
|
||||
|
||||
#[lang = "sync"]
|
||||
pub unsafe trait Sync {}
|
||||
|
|
|
|||
|
|
@ -867,6 +867,13 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
|||
template_str.push_str("\n.popsection");
|
||||
self.context.add_top_level_asm(None, &template_str);
|
||||
}
|
||||
|
||||
fn mangled_name(&self, instance: Instance<'tcx>) -> String {
|
||||
// TODO(@Amanieu): Additional mangling is needed on
|
||||
// some targets to add a leading underscore (Mach-O)
|
||||
// or byte count suffixes (x86 Windows).
|
||||
self.tcx.symbol_name(instance).name.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn modifier_to_gcc(
|
||||
|
|
|
|||
|
|
@ -442,6 +442,14 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn mangled_name(&self, instance: Instance<'tcx>) -> String {
|
||||
let llval = self.get_fn(instance);
|
||||
llvm::build_string(|s| unsafe {
|
||||
llvm::LLVMRustGetMangledName(llval, s);
|
||||
})
|
||||
.expect("symbol is not valid UTF-8")
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn inline_asm_call<'ll>(
|
||||
|
|
@ -504,14 +512,13 @@ pub(crate) fn inline_asm_call<'ll>(
|
|||
let key = "srcloc";
|
||||
let kind = llvm::LLVMGetMDKindIDInContext(
|
||||
bx.llcx,
|
||||
key.as_ptr() as *const c_char,
|
||||
key.as_ptr().cast::<c_char>(),
|
||||
key.len() as c_uint,
|
||||
);
|
||||
|
||||
// srcloc contains one integer for each line of assembly code.
|
||||
// Unfortunately this isn't enough to encode a full span so instead
|
||||
// we just encode the start position of each line.
|
||||
// FIXME: Figure out a way to pass the entire line spans.
|
||||
// `srcloc` contains one 64-bit integer for each line of assembly code,
|
||||
// where the lower 32 bits hold the lo byte position and the upper 32 bits
|
||||
// hold the hi byte position.
|
||||
let mut srcloc = vec![];
|
||||
if dia == llvm::AsmDialect::Intel && line_spans.len() > 1 {
|
||||
// LLVM inserts an extra line to add the ".intel_syntax", so add
|
||||
|
|
@ -521,13 +528,13 @@ pub(crate) fn inline_asm_call<'ll>(
|
|||
// due to the asm template string coming from a macro. LLVM will
|
||||
// default to the first srcloc for lines that don't have an
|
||||
// associated srcloc.
|
||||
srcloc.push(llvm::LLVMValueAsMetadata(bx.const_i32(0)));
|
||||
srcloc.push(llvm::LLVMValueAsMetadata(bx.const_u64(0)));
|
||||
}
|
||||
srcloc.extend(
|
||||
line_spans
|
||||
.iter()
|
||||
.map(|span| llvm::LLVMValueAsMetadata(bx.const_i32(span.lo().to_u32() as i32))),
|
||||
);
|
||||
srcloc.extend(line_spans.iter().map(|span| {
|
||||
llvm::LLVMValueAsMetadata(bx.const_u64(
|
||||
u64::from(span.lo().to_u32()) | (u64::from(span.hi().to_u32()) << 32),
|
||||
))
|
||||
}));
|
||||
let md = llvm::LLVMMDNodeInContext2(bx.llcx, srcloc.as_ptr(), srcloc.len());
|
||||
let md = llvm::LLVMMetadataAsValue(&bx.llcx, md);
|
||||
llvm::LLVMSetMetadata(call, kind, md);
|
||||
|
|
|
|||
|
|
@ -395,17 +395,9 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
|
|||
to_add.push(MemoryEffects::None.create_attr(cx.llcx));
|
||||
}
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
|
||||
to_add.push(AttributeKind::Naked.create_attr(cx.llcx));
|
||||
// HACK(jubilee): "indirect branch tracking" works by attaching prologues to functions.
|
||||
// And it is a module-level attribute, so the alternative is pulling naked functions into
|
||||
// new LLVM modules. Otherwise LLVM's "naked" functions come with endbr prefixes per
|
||||
// https://github.com/rust-lang/rust/issues/98768
|
||||
to_add.push(AttributeKind::NoCfCheck.create_attr(cx.llcx));
|
||||
if llvm_util::get_version() < (19, 0, 0) {
|
||||
// Prior to LLVM 19, branch-target-enforcement was disabled by setting the attribute to
|
||||
// the string "false". Now it is disabled by absence of the attribute.
|
||||
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "branch-target-enforcement", "false"));
|
||||
}
|
||||
// do nothing; a naked function is converted into an extern function
|
||||
// and a global assembly block. LLVM's support for naked functions is
|
||||
// not used.
|
||||
} else {
|
||||
// Do not set sanitizer attributes for naked functions.
|
||||
to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize));
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ use rustc_session::Session;
|
|||
use rustc_session::config::{
|
||||
self, Lto, OutputType, Passes, RemapPathScopeComponents, SplitDwarfKind, SwitchWithOptPath,
|
||||
};
|
||||
use rustc_span::InnerSpan;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::{BytePos, InnerSpan, Pos, SpanData, SyntaxContext};
|
||||
use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel};
|
||||
use tracing::debug;
|
||||
|
||||
|
|
@ -415,21 +415,32 @@ fn report_inline_asm(
|
|||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
msg: String,
|
||||
level: llvm::DiagnosticLevel,
|
||||
mut cookie: u64,
|
||||
cookie: u64,
|
||||
source: Option<(String, Vec<InnerSpan>)>,
|
||||
) {
|
||||
// In LTO build we may get srcloc values from other crates which are invalid
|
||||
// since they use a different source map. To be safe we just suppress these
|
||||
// in LTO builds.
|
||||
if matches!(cgcx.lto, Lto::Fat | Lto::Thin) {
|
||||
cookie = 0;
|
||||
}
|
||||
let span = if cookie == 0 || matches!(cgcx.lto, Lto::Fat | Lto::Thin) {
|
||||
SpanData::default()
|
||||
} else {
|
||||
let lo = BytePos::from_u32(cookie as u32);
|
||||
let hi = BytePos::from_u32((cookie >> 32) as u32);
|
||||
SpanData {
|
||||
lo,
|
||||
// LLVM version < 19 silently truncates the cookie to 32 bits in some situations.
|
||||
hi: if hi.to_u32() != 0 { hi } else { lo },
|
||||
ctxt: SyntaxContext::root(),
|
||||
parent: None,
|
||||
}
|
||||
};
|
||||
let level = match level {
|
||||
llvm::DiagnosticLevel::Error => Level::Error,
|
||||
llvm::DiagnosticLevel::Warning => Level::Warning,
|
||||
llvm::DiagnosticLevel::Note | llvm::DiagnosticLevel::Remark => Level::Note,
|
||||
};
|
||||
cgcx.diag_emitter.inline_asm_error(cookie.try_into().unwrap(), msg, level, source);
|
||||
let msg = msg.strip_prefix("error: ").unwrap_or(&msg).to_string();
|
||||
cgcx.diag_emitter.inline_asm_error(span, msg, level, source);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void) {
|
||||
|
|
|
|||
|
|
@ -159,6 +159,16 @@ pub(crate) unsafe fn create_module<'ll>(
|
|||
// See https://github.com/llvm/llvm-project/pull/112084
|
||||
target_data_layout = target_data_layout.replace("-i128:128", "");
|
||||
}
|
||||
if sess.target.arch.starts_with("powerpc64") {
|
||||
// LLVM 20 updates the powerpc64 layout to correctly align 128 bit integers to 128 bit.
|
||||
// See https://github.com/llvm/llvm-project/pull/118004
|
||||
target_data_layout = target_data_layout.replace("-i128:128", "");
|
||||
}
|
||||
if sess.target.arch.starts_with("wasm32") || sess.target.arch.starts_with("wasm64") {
|
||||
// LLVM 20 updates the wasm(32|64) layout to correctly align 128 bit integers to 128 bit.
|
||||
// See https://github.com/llvm/llvm-project/pull/119204
|
||||
target_data_layout = target_data_layout.replace("-i128:128", "");
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the data-layout values hardcoded remain the defaults.
|
||||
|
|
|
|||
|
|
@ -152,6 +152,34 @@ impl CoverageSpan {
|
|||
}
|
||||
}
|
||||
|
||||
/// Holds tables of the various region types in one struct.
|
||||
///
|
||||
/// Don't pass this struct across FFI; pass the individual region tables as
|
||||
/// pointer/length pairs instead.
|
||||
///
|
||||
/// Each field name has a `_regions` suffix for improved readability after
|
||||
/// exhaustive destructing, which ensures that all region types are handled.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub(crate) struct Regions {
|
||||
pub(crate) code_regions: Vec<CodeRegion>,
|
||||
pub(crate) branch_regions: Vec<BranchRegion>,
|
||||
pub(crate) mcdc_branch_regions: Vec<MCDCBranchRegion>,
|
||||
pub(crate) mcdc_decision_regions: Vec<MCDCDecisionRegion>,
|
||||
}
|
||||
|
||||
impl Regions {
|
||||
/// Returns true if none of this structure's tables contain any regions.
|
||||
pub(crate) fn has_no_regions(&self) -> bool {
|
||||
let Self { code_regions, branch_regions, mcdc_branch_regions, mcdc_decision_regions } =
|
||||
self;
|
||||
|
||||
code_regions.is_empty()
|
||||
&& branch_regions.is_empty()
|
||||
&& mcdc_branch_regions.is_empty()
|
||||
&& mcdc_decision_regions.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
/// Must match the layout of `LLVMRustCoverageCodeRegion`.
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(C)]
|
||||
|
|
|
|||
|
|
@ -62,11 +62,10 @@ pub(crate) fn write_filenames_to_buffer<'a>(
|
|||
pub(crate) fn write_function_mappings_to_buffer(
|
||||
virtual_file_mapping: &[u32],
|
||||
expressions: &[ffi::CounterExpression],
|
||||
code_regions: &[ffi::CodeRegion],
|
||||
branch_regions: &[ffi::BranchRegion],
|
||||
mcdc_branch_regions: &[ffi::MCDCBranchRegion],
|
||||
mcdc_decision_regions: &[ffi::MCDCDecisionRegion],
|
||||
regions: &ffi::Regions,
|
||||
) -> Vec<u8> {
|
||||
let ffi::Regions { code_regions, branch_regions, mcdc_branch_regions, mcdc_decision_regions } =
|
||||
regions;
|
||||
llvm::build_byte_buffer(|buffer| unsafe {
|
||||
llvm::LLVMRustCoverageWriteFunctionMappingsToBuffer(
|
||||
virtual_file_mapping.as_ptr(),
|
||||
|
|
|
|||
|
|
@ -1,159 +1,38 @@
|
|||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_middle::mir::CoverageIdsInfo;
|
||||
use rustc_middle::mir::coverage::{
|
||||
CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping, MappingKind, Op,
|
||||
CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op,
|
||||
SourceRegion,
|
||||
};
|
||||
use rustc_middle::ty::Instance;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
|
||||
|
||||
/// Holds all of the coverage mapping data associated with a function instance,
|
||||
/// collected during traversal of `Coverage` statements in the function's MIR.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FunctionCoverageCollector<'tcx> {
|
||||
/// Coverage info that was attached to this function by the instrumentor.
|
||||
function_coverage_info: &'tcx FunctionCoverageInfo,
|
||||
ids_info: &'tcx CoverageIdsInfo,
|
||||
is_used: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> FunctionCoverageCollector<'tcx> {
|
||||
/// Creates a new set of coverage data for a used (called) function.
|
||||
pub(crate) fn new(
|
||||
instance: Instance<'tcx>,
|
||||
function_coverage_info: &'tcx FunctionCoverageInfo,
|
||||
ids_info: &'tcx CoverageIdsInfo,
|
||||
) -> Self {
|
||||
Self::create(instance, function_coverage_info, ids_info, true)
|
||||
}
|
||||
|
||||
/// Creates a new set of coverage data for an unused (never called) function.
|
||||
pub(crate) fn unused(
|
||||
instance: Instance<'tcx>,
|
||||
function_coverage_info: &'tcx FunctionCoverageInfo,
|
||||
ids_info: &'tcx CoverageIdsInfo,
|
||||
) -> Self {
|
||||
Self::create(instance, function_coverage_info, ids_info, false)
|
||||
}
|
||||
|
||||
fn create(
|
||||
instance: Instance<'tcx>,
|
||||
function_coverage_info: &'tcx FunctionCoverageInfo,
|
||||
ids_info: &'tcx CoverageIdsInfo,
|
||||
is_used: bool,
|
||||
) -> Self {
|
||||
let num_counters = function_coverage_info.num_counters;
|
||||
let num_expressions = function_coverage_info.expressions.len();
|
||||
debug!(
|
||||
"FunctionCoverage::create(instance={instance:?}) has \
|
||||
num_counters={num_counters}, num_expressions={num_expressions}, is_used={is_used}"
|
||||
);
|
||||
|
||||
Self { function_coverage_info, ids_info, is_used }
|
||||
}
|
||||
|
||||
/// Identify expressions that will always have a value of zero, and note
|
||||
/// their IDs in [`ZeroExpressions`]. Mappings that refer to a zero expression
|
||||
/// can instead become mappings to a constant zero value.
|
||||
///
|
||||
/// This method mainly exists to preserve the simplifications that were
|
||||
/// already being performed by the Rust-side expression renumbering, so that
|
||||
/// the resulting coverage mappings don't get worse.
|
||||
fn identify_zero_expressions(&self) -> ZeroExpressions {
|
||||
// The set of expressions that either were optimized out entirely, or
|
||||
// have zero as both of their operands, and will therefore always have
|
||||
// a value of zero. Other expressions that refer to these as operands
|
||||
// can have those operands replaced with `CovTerm::Zero`.
|
||||
let mut zero_expressions = ZeroExpressions::default();
|
||||
|
||||
// Simplify a copy of each expression based on lower-numbered expressions,
|
||||
// and then update the set of always-zero expressions if necessary.
|
||||
// (By construction, expressions can only refer to other expressions
|
||||
// that have lower IDs, so one pass is sufficient.)
|
||||
for (id, expression) in self.function_coverage_info.expressions.iter_enumerated() {
|
||||
if !self.is_used || !self.ids_info.expressions_seen.contains(id) {
|
||||
// If an expression was not seen, it must have been optimized away,
|
||||
// so any operand that refers to it can be replaced with zero.
|
||||
zero_expressions.insert(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We don't need to simplify the actual expression data in the
|
||||
// expressions list; we can just simplify a temporary copy and then
|
||||
// use that to update the set of always-zero expressions.
|
||||
let Expression { mut lhs, op, mut rhs } = *expression;
|
||||
|
||||
// If an expression has an operand that is also an expression, the
|
||||
// operand's ID must be strictly lower. This is what lets us find
|
||||
// all zero expressions in one pass.
|
||||
let assert_operand_expression_is_lower = |operand_id: ExpressionId| {
|
||||
assert!(
|
||||
operand_id < id,
|
||||
"Operand {operand_id:?} should be less than {id:?} in {expression:?}",
|
||||
)
|
||||
};
|
||||
|
||||
// If an operand refers to a counter or expression that is always
|
||||
// zero, then that operand can be replaced with `CovTerm::Zero`.
|
||||
let maybe_set_operand_to_zero = |operand: &mut CovTerm| {
|
||||
if let CovTerm::Expression(id) = *operand {
|
||||
assert_operand_expression_is_lower(id);
|
||||
}
|
||||
|
||||
if is_zero_term(&self.ids_info.counters_seen, &zero_expressions, *operand) {
|
||||
*operand = CovTerm::Zero;
|
||||
}
|
||||
};
|
||||
maybe_set_operand_to_zero(&mut lhs);
|
||||
maybe_set_operand_to_zero(&mut rhs);
|
||||
|
||||
// Coverage counter values cannot be negative, so if an expression
|
||||
// involves subtraction from zero, assume that its RHS must also be zero.
|
||||
// (Do this after simplifications that could set the LHS to zero.)
|
||||
if lhs == CovTerm::Zero && op == Op::Subtract {
|
||||
rhs = CovTerm::Zero;
|
||||
}
|
||||
|
||||
// After the above simplifications, if both operands are zero, then
|
||||
// we know that this expression is always zero too.
|
||||
if lhs == CovTerm::Zero && rhs == CovTerm::Zero {
|
||||
zero_expressions.insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
zero_expressions
|
||||
}
|
||||
|
||||
pub(crate) fn into_finished(self) -> FunctionCoverage<'tcx> {
|
||||
let zero_expressions = self.identify_zero_expressions();
|
||||
let FunctionCoverageCollector { function_coverage_info, ids_info, is_used, .. } = self;
|
||||
|
||||
FunctionCoverage { function_coverage_info, ids_info, is_used, zero_expressions }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct FunctionCoverage<'tcx> {
|
||||
pub(crate) function_coverage_info: &'tcx FunctionCoverageInfo,
|
||||
ids_info: &'tcx CoverageIdsInfo,
|
||||
is_used: bool,
|
||||
|
||||
zero_expressions: ZeroExpressions,
|
||||
/// If `None`, the corresponding function is unused.
|
||||
ids_info: Option<&'tcx CoverageIdsInfo>,
|
||||
}
|
||||
|
||||
impl<'tcx> FunctionCoverage<'tcx> {
|
||||
pub(crate) fn new_used(
|
||||
function_coverage_info: &'tcx FunctionCoverageInfo,
|
||||
ids_info: &'tcx CoverageIdsInfo,
|
||||
) -> Self {
|
||||
Self { function_coverage_info, ids_info: Some(ids_info) }
|
||||
}
|
||||
|
||||
pub(crate) fn new_unused(function_coverage_info: &'tcx FunctionCoverageInfo) -> Self {
|
||||
Self { function_coverage_info, ids_info: None }
|
||||
}
|
||||
|
||||
/// Returns true for a used (called) function, and false for an unused function.
|
||||
pub(crate) fn is_used(&self) -> bool {
|
||||
self.is_used
|
||||
self.ids_info.is_some()
|
||||
}
|
||||
|
||||
/// Return the source hash, generated from the HIR node structure, and used to indicate whether
|
||||
/// or not the source code structure changed between different compilations.
|
||||
pub(crate) fn source_hash(&self) -> u64 {
|
||||
if self.is_used { self.function_coverage_info.function_source_hash } else { 0 }
|
||||
if self.is_used() { self.function_coverage_info.function_source_hash } else { 0 }
|
||||
}
|
||||
|
||||
/// Convert this function's coverage expression data into a form that can be
|
||||
|
|
@ -196,37 +75,10 @@ impl<'tcx> FunctionCoverage<'tcx> {
|
|||
}
|
||||
|
||||
fn is_zero_term(&self, term: CovTerm) -> bool {
|
||||
!self.is_used || is_zero_term(&self.ids_info.counters_seen, &self.zero_expressions, term)
|
||||
}
|
||||
}
|
||||
|
||||
/// Set of expression IDs that are known to always evaluate to zero.
|
||||
/// Any mapping or expression operand that refers to these expressions can have
|
||||
/// that reference replaced with a constant zero value.
|
||||
#[derive(Default)]
|
||||
struct ZeroExpressions(FxIndexSet<ExpressionId>);
|
||||
|
||||
impl ZeroExpressions {
|
||||
fn insert(&mut self, id: ExpressionId) {
|
||||
self.0.insert(id);
|
||||
}
|
||||
|
||||
fn contains(&self, id: ExpressionId) -> bool {
|
||||
self.0.contains(&id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the given term is known to have a value of zero, taking
|
||||
/// into account knowledge of which counters are unused and which expressions
|
||||
/// are always zero.
|
||||
fn is_zero_term(
|
||||
counters_seen: &BitSet<CounterId>,
|
||||
zero_expressions: &ZeroExpressions,
|
||||
term: CovTerm,
|
||||
) -> bool {
|
||||
match term {
|
||||
CovTerm::Zero => true,
|
||||
CovTerm::Counter(id) => !counters_seen.contains(id),
|
||||
CovTerm::Expression(id) => zero_expressions.contains(id),
|
||||
match self.ids_info {
|
||||
Some(ids_info) => ids_info.is_zero_term(term),
|
||||
// This function is unused, so all coverage counters/expressions are zero.
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use std::ffi::CString;
|
||||
use std::iter;
|
||||
|
||||
use itertools::Itertools as _;
|
||||
|
|
@ -9,21 +8,22 @@ use rustc_codegen_ssa::traits::{
|
|||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::coverage::MappingKind;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_middle::{bug, mir};
|
||||
use rustc_session::RemapFileNameExt;
|
||||
use rustc_session::config::RemapPathScopeComponents;
|
||||
use rustc_span::def_id::DefIdSet;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_target::spec::HasTargetSpec;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::common::CodegenCx;
|
||||
use crate::coverageinfo::map_data::{FunctionCoverage, FunctionCoverageCollector};
|
||||
use crate::coverageinfo::{ffi, llvm_cov};
|
||||
use crate::coverageinfo::llvm_cov;
|
||||
use crate::coverageinfo::map_data::FunctionCoverage;
|
||||
use crate::coverageinfo::mapgen::covfun::prepare_covfun_record;
|
||||
use crate::llvm;
|
||||
|
||||
mod covfun;
|
||||
|
||||
/// Generates and exports the coverage map, which is embedded in special
|
||||
/// linker sections in the final binary.
|
||||
///
|
||||
|
|
@ -63,16 +63,11 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
|
|||
None => return,
|
||||
};
|
||||
if function_coverage_map.is_empty() {
|
||||
// This module has no functions with coverage instrumentation
|
||||
// This CGU has no functions with coverage instrumentation.
|
||||
return;
|
||||
}
|
||||
|
||||
let function_coverage_entries = function_coverage_map
|
||||
.into_iter()
|
||||
.map(|(instance, function_coverage)| (instance, function_coverage.into_finished()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let all_file_names = function_coverage_entries
|
||||
let all_file_names = function_coverage_map
|
||||
.iter()
|
||||
.map(|(_, fn_cov)| fn_cov.function_coverage_info.body_span)
|
||||
.map(|span| span_file_name(tcx, span));
|
||||
|
|
@ -85,47 +80,28 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
|
|||
let filenames_val = cx.const_bytes(&filenames_buffer);
|
||||
let filenames_ref = llvm_cov::hash_bytes(&filenames_buffer);
|
||||
|
||||
// Generate the coverage map header, which contains the filenames used by
|
||||
// this CGU's coverage mappings, and store it in a well-known global.
|
||||
generate_covmap_record(cx, covmap_version, filenames_size, filenames_val);
|
||||
|
||||
let mut unused_function_names = Vec::new();
|
||||
|
||||
// Encode coverage mappings and generate function records
|
||||
for (instance, function_coverage) in function_coverage_entries {
|
||||
debug!("Generate function coverage for {}, {:?}", cx.codegen_unit.name(), instance);
|
||||
let covfun_records = function_coverage_map
|
||||
.into_iter()
|
||||
.filter_map(|(instance, function_coverage)| {
|
||||
prepare_covfun_record(tcx, &global_file_table, instance, &function_coverage)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mangled_function_name = tcx.symbol_name(instance).name;
|
||||
let source_hash = function_coverage.source_hash();
|
||||
let is_used = function_coverage.is_used();
|
||||
// If there are no covfun records for this CGU, don't generate a covmap record.
|
||||
// Emitting a covmap record without any covfun records causes `llvm-cov` to
|
||||
// fail when generating coverage reports, and if there are no covfun records
|
||||
// then the covmap record isn't useful anyway.
|
||||
// This should prevent a repeat of <https://github.com/rust-lang/rust/issues/133606>.
|
||||
if covfun_records.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let coverage_mapping_buffer =
|
||||
encode_mappings_for_function(tcx, &global_file_table, &function_coverage);
|
||||
for covfun in &covfun_records {
|
||||
unused_function_names.extend(covfun.mangled_function_name_if_unused());
|
||||
|
||||
if coverage_mapping_buffer.is_empty() {
|
||||
if function_coverage.is_used() {
|
||||
bug!(
|
||||
"A used function should have had coverage mapping data but did not: {}",
|
||||
mangled_function_name
|
||||
);
|
||||
} else {
|
||||
debug!("unused function had no coverage mapping data: {}", mangled_function_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if !is_used {
|
||||
unused_function_names.push(mangled_function_name);
|
||||
}
|
||||
|
||||
generate_covfun_record(
|
||||
cx,
|
||||
mangled_function_name,
|
||||
source_hash,
|
||||
filenames_ref,
|
||||
coverage_mapping_buffer,
|
||||
is_used,
|
||||
);
|
||||
covfun::generate_covfun_record(cx, filenames_ref, covfun)
|
||||
}
|
||||
|
||||
// For unused functions, we need to take their mangled names and store them
|
||||
|
|
@ -146,6 +122,11 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
|
|||
llvm::set_linkage(array, llvm::Linkage::InternalLinkage);
|
||||
llvm::set_initializer(array, initializer);
|
||||
}
|
||||
|
||||
// Generate the coverage map header, which contains the filenames used by
|
||||
// this CGU's coverage mappings, and store it in a well-known global.
|
||||
// (This is skipped if we returned early due to having no covfun records.)
|
||||
generate_covmap_record(cx, covmap_version, filenames_size, filenames_val);
|
||||
}
|
||||
|
||||
/// Maps "global" (per-CGU) file ID numbers to their underlying filenames.
|
||||
|
|
@ -213,7 +194,7 @@ rustc_index::newtype_index! {
|
|||
|
||||
/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
|
||||
/// file IDs.
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, Default)]
|
||||
struct VirtualFileMapping {
|
||||
local_to_global: IndexVec<LocalFileId, GlobalFileId>,
|
||||
global_to_local: FxIndexMap<GlobalFileId, LocalFileId>,
|
||||
|
|
@ -227,10 +208,10 @@ impl VirtualFileMapping {
|
|||
.or_insert_with(|| self.local_to_global.push(global_file_id))
|
||||
}
|
||||
|
||||
fn into_vec(self) -> Vec<u32> {
|
||||
// This conversion should be optimized away to ~zero overhead.
|
||||
// In any case, it's probably not hot enough to worry about.
|
||||
self.local_to_global.into_iter().map(|global| global.as_u32()).collect()
|
||||
fn to_vec(&self) -> Vec<u32> {
|
||||
// This clone could be avoided by transmuting `&[GlobalFileId]` to `&[u32]`,
|
||||
// but it isn't hot or expensive enough to justify the extra unsafety.
|
||||
self.local_to_global.iter().map(|&global| GlobalFileId::as_u32(global)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -241,83 +222,6 @@ fn span_file_name(tcx: TyCtxt<'_>, span: Span) -> Symbol {
|
|||
Symbol::intern(&name)
|
||||
}
|
||||
|
||||
/// Using the expressions and counter regions collected for a single function,
|
||||
/// generate the variable-sized payload of its corresponding `__llvm_covfun`
|
||||
/// entry. The payload is returned as a vector of bytes.
|
||||
///
|
||||
/// Newly-encountered filenames will be added to the global file table.
|
||||
fn encode_mappings_for_function(
|
||||
tcx: TyCtxt<'_>,
|
||||
global_file_table: &GlobalFileTable,
|
||||
function_coverage: &FunctionCoverage<'_>,
|
||||
) -> Vec<u8> {
|
||||
let counter_regions = function_coverage.counter_regions();
|
||||
if counter_regions.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let expressions = function_coverage.counter_expressions().collect::<Vec<_>>();
|
||||
|
||||
let mut virtual_file_mapping = VirtualFileMapping::default();
|
||||
let mut code_regions = vec![];
|
||||
let mut branch_regions = vec![];
|
||||
let mut mcdc_branch_regions = vec![];
|
||||
let mut mcdc_decision_regions = vec![];
|
||||
|
||||
// Currently a function's mappings must all be in the same file as its body span.
|
||||
let file_name = span_file_name(tcx, function_coverage.function_coverage_info.body_span);
|
||||
|
||||
// Look up the global file ID for that filename.
|
||||
let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
|
||||
|
||||
// Associate that global file ID with a local file ID for this function.
|
||||
let local_file_id = virtual_file_mapping.local_id_for_global(global_file_id);
|
||||
debug!(" file id: {local_file_id:?} => {global_file_id:?} = '{file_name:?}'");
|
||||
|
||||
// For each counter/region pair in this function+file, convert it to a
|
||||
// form suitable for FFI.
|
||||
for (mapping_kind, region) in counter_regions {
|
||||
debug!("Adding counter {mapping_kind:?} to map for {region:?}");
|
||||
let span = ffi::CoverageSpan::from_source_region(local_file_id, region);
|
||||
match mapping_kind {
|
||||
MappingKind::Code(term) => {
|
||||
code_regions.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) });
|
||||
}
|
||||
MappingKind::Branch { true_term, false_term } => {
|
||||
branch_regions.push(ffi::BranchRegion {
|
||||
span,
|
||||
true_counter: ffi::Counter::from_term(true_term),
|
||||
false_counter: ffi::Counter::from_term(false_term),
|
||||
});
|
||||
}
|
||||
MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
|
||||
mcdc_branch_regions.push(ffi::MCDCBranchRegion {
|
||||
span,
|
||||
true_counter: ffi::Counter::from_term(true_term),
|
||||
false_counter: ffi::Counter::from_term(false_term),
|
||||
mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params),
|
||||
});
|
||||
}
|
||||
MappingKind::MCDCDecision(mcdc_decision_params) => {
|
||||
mcdc_decision_regions.push(ffi::MCDCDecisionRegion {
|
||||
span,
|
||||
mcdc_decision_params: ffi::mcdc::DecisionParameters::from(mcdc_decision_params),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Encode the function's coverage mappings into a buffer.
|
||||
llvm_cov::write_function_mappings_to_buffer(
|
||||
&virtual_file_mapping.into_vec(),
|
||||
&expressions,
|
||||
&code_regions,
|
||||
&branch_regions,
|
||||
&mcdc_branch_regions,
|
||||
&mcdc_decision_regions,
|
||||
)
|
||||
}
|
||||
|
||||
/// Generates the contents of the covmap record for this CGU, which mostly
|
||||
/// consists of a header and a list of filenames. The record is then stored
|
||||
/// as a global variable in the `__llvm_covmap` section.
|
||||
|
|
@ -355,61 +259,6 @@ fn generate_covmap_record<'ll>(
|
|||
cx.add_used_global(llglobal);
|
||||
}
|
||||
|
||||
/// Generates the contents of the covfun record for this function, which
|
||||
/// contains the function's coverage mapping data. The record is then stored
|
||||
/// as a global variable in the `__llvm_covfun` section.
|
||||
fn generate_covfun_record(
|
||||
cx: &CodegenCx<'_, '_>,
|
||||
mangled_function_name: &str,
|
||||
source_hash: u64,
|
||||
filenames_ref: u64,
|
||||
coverage_mapping_buffer: Vec<u8>,
|
||||
is_used: bool,
|
||||
) {
|
||||
// Concatenate the encoded coverage mappings
|
||||
let coverage_mapping_size = coverage_mapping_buffer.len();
|
||||
let coverage_mapping_val = cx.const_bytes(&coverage_mapping_buffer);
|
||||
|
||||
let func_name_hash = llvm_cov::hash_bytes(mangled_function_name.as_bytes());
|
||||
let func_name_hash_val = cx.const_u64(func_name_hash);
|
||||
let coverage_mapping_size_val = cx.const_u32(coverage_mapping_size as u32);
|
||||
let source_hash_val = cx.const_u64(source_hash);
|
||||
let filenames_ref_val = cx.const_u64(filenames_ref);
|
||||
let func_record_val = cx.const_struct(
|
||||
&[
|
||||
func_name_hash_val,
|
||||
coverage_mapping_size_val,
|
||||
source_hash_val,
|
||||
filenames_ref_val,
|
||||
coverage_mapping_val,
|
||||
],
|
||||
/*packed=*/ true,
|
||||
);
|
||||
|
||||
// Choose a variable name to hold this function's covfun data.
|
||||
// Functions that are used have a suffix ("u") to distinguish them from
|
||||
// unused copies of the same function (from different CGUs), so that if a
|
||||
// linker sees both it won't discard the used copy's data.
|
||||
let func_record_var_name =
|
||||
CString::new(format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" }))
|
||||
.unwrap();
|
||||
debug!("function record var name: {:?}", func_record_var_name);
|
||||
|
||||
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name);
|
||||
llvm::set_initializer(llglobal, func_record_val);
|
||||
llvm::set_global_constant(llglobal, true);
|
||||
llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage);
|
||||
llvm::set_visibility(llglobal, llvm::Visibility::Hidden);
|
||||
llvm::set_section(llglobal, cx.covfun_section_name());
|
||||
// LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
|
||||
// <https://llvm.org/docs/CoverageMappingFormat.html>
|
||||
llvm::set_alignment(llglobal, Align::EIGHT);
|
||||
if cx.target_spec().supports_comdat() {
|
||||
llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name);
|
||||
}
|
||||
cx.add_used_global(llglobal);
|
||||
}
|
||||
|
||||
/// Each CGU will normally only emit coverage metadata for the functions that it actually generates.
|
||||
/// But since we don't want unused functions to disappear from coverage reports, we also scan for
|
||||
/// functions that were instrumented but are not participating in codegen.
|
||||
|
|
@ -536,11 +385,6 @@ fn add_unused_function_coverage<'tcx>(
|
|||
);
|
||||
|
||||
// An unused function's mappings will all be rewritten to map to zero.
|
||||
let function_coverage = FunctionCoverageCollector::unused(
|
||||
instance,
|
||||
function_coverage_info,
|
||||
tcx.coverage_ids_info(instance.def),
|
||||
);
|
||||
|
||||
let function_coverage = FunctionCoverage::new_unused(function_coverage_info);
|
||||
cx.coverage_cx().function_coverage_map.borrow_mut().insert(instance, function_coverage);
|
||||
}
|
||||
|
|
|
|||
200
compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs
Normal file
200
compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
//! For each function that was instrumented for coverage, we need to embed its
|
||||
//! corresponding coverage mapping metadata inside the `__llvm_covfun`[^win]
|
||||
//! linker section of the final binary.
|
||||
//!
|
||||
//! [^win]: On Windows the section name is `.lcovfun`.
|
||||
|
||||
use std::ffi::CString;
|
||||
|
||||
use rustc_abi::Align;
|
||||
use rustc_codegen_ssa::traits::{
|
||||
BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
|
||||
};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::coverage::MappingKind;
|
||||
use rustc_middle::ty::{Instance, TyCtxt};
|
||||
use rustc_target::spec::HasTargetSpec;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::common::CodegenCx;
|
||||
use crate::coverageinfo::map_data::FunctionCoverage;
|
||||
use crate::coverageinfo::mapgen::{GlobalFileTable, VirtualFileMapping, span_file_name};
|
||||
use crate::coverageinfo::{ffi, llvm_cov};
|
||||
use crate::llvm;
|
||||
|
||||
/// Intermediate coverage metadata for a single function, used to help build
|
||||
/// the final record that will be embedded in the `__llvm_covfun` section.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CovfunRecord<'tcx> {
|
||||
mangled_function_name: &'tcx str,
|
||||
source_hash: u64,
|
||||
is_used: bool,
|
||||
|
||||
virtual_file_mapping: VirtualFileMapping,
|
||||
expressions: Vec<ffi::CounterExpression>,
|
||||
regions: ffi::Regions,
|
||||
}
|
||||
|
||||
impl<'tcx> CovfunRecord<'tcx> {
|
||||
/// FIXME(Zalathar): Make this the responsibility of the code that determines
|
||||
/// which functions are unused.
|
||||
pub(crate) fn mangled_function_name_if_unused(&self) -> Option<&'tcx str> {
|
||||
(!self.is_used).then_some(self.mangled_function_name)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn prepare_covfun_record<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
global_file_table: &GlobalFileTable,
|
||||
instance: Instance<'tcx>,
|
||||
function_coverage: &FunctionCoverage<'tcx>,
|
||||
) -> Option<CovfunRecord<'tcx>> {
|
||||
let mut covfun = CovfunRecord {
|
||||
mangled_function_name: tcx.symbol_name(instance).name,
|
||||
source_hash: function_coverage.source_hash(),
|
||||
is_used: function_coverage.is_used(),
|
||||
virtual_file_mapping: VirtualFileMapping::default(),
|
||||
expressions: function_coverage.counter_expressions().collect::<Vec<_>>(),
|
||||
regions: ffi::Regions::default(),
|
||||
};
|
||||
|
||||
fill_region_tables(tcx, global_file_table, function_coverage, &mut covfun);
|
||||
|
||||
if covfun.regions.has_no_regions() {
|
||||
if covfun.is_used {
|
||||
bug!("a used function should have had coverage mapping data but did not: {covfun:?}");
|
||||
} else {
|
||||
debug!(?covfun, "unused function had no coverage mapping data");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some(covfun)
|
||||
}
|
||||
|
||||
/// Populates the mapping region tables in the current function's covfun record.
|
||||
fn fill_region_tables<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
global_file_table: &GlobalFileTable,
|
||||
function_coverage: &FunctionCoverage<'tcx>,
|
||||
covfun: &mut CovfunRecord<'tcx>,
|
||||
) {
|
||||
let counter_regions = function_coverage.counter_regions();
|
||||
if counter_regions.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Currently a function's mappings must all be in the same file as its body span.
|
||||
let file_name = span_file_name(tcx, function_coverage.function_coverage_info.body_span);
|
||||
|
||||
// Look up the global file ID for that filename.
|
||||
let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
|
||||
|
||||
// Associate that global file ID with a local file ID for this function.
|
||||
let local_file_id = covfun.virtual_file_mapping.local_id_for_global(global_file_id);
|
||||
debug!(" file id: {local_file_id:?} => {global_file_id:?} = '{file_name:?}'");
|
||||
|
||||
let ffi::Regions { code_regions, branch_regions, mcdc_branch_regions, mcdc_decision_regions } =
|
||||
&mut covfun.regions;
|
||||
|
||||
// For each counter/region pair in this function+file, convert it to a
|
||||
// form suitable for FFI.
|
||||
for (mapping_kind, region) in counter_regions {
|
||||
debug!("Adding counter {mapping_kind:?} to map for {region:?}");
|
||||
let span = ffi::CoverageSpan::from_source_region(local_file_id, region);
|
||||
match mapping_kind {
|
||||
MappingKind::Code(term) => {
|
||||
code_regions.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) });
|
||||
}
|
||||
MappingKind::Branch { true_term, false_term } => {
|
||||
branch_regions.push(ffi::BranchRegion {
|
||||
span,
|
||||
true_counter: ffi::Counter::from_term(true_term),
|
||||
false_counter: ffi::Counter::from_term(false_term),
|
||||
});
|
||||
}
|
||||
MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
|
||||
mcdc_branch_regions.push(ffi::MCDCBranchRegion {
|
||||
span,
|
||||
true_counter: ffi::Counter::from_term(true_term),
|
||||
false_counter: ffi::Counter::from_term(false_term),
|
||||
mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params),
|
||||
});
|
||||
}
|
||||
MappingKind::MCDCDecision(mcdc_decision_params) => {
|
||||
mcdc_decision_regions.push(ffi::MCDCDecisionRegion {
|
||||
span,
|
||||
mcdc_decision_params: ffi::mcdc::DecisionParameters::from(mcdc_decision_params),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the contents of the covfun record for this function, which
|
||||
/// contains the function's coverage mapping data. The record is then stored
|
||||
/// as a global variable in the `__llvm_covfun` section.
|
||||
pub(crate) fn generate_covfun_record<'tcx>(
|
||||
cx: &CodegenCx<'_, 'tcx>,
|
||||
filenames_ref: u64,
|
||||
covfun: &CovfunRecord<'tcx>,
|
||||
) {
|
||||
let &CovfunRecord {
|
||||
mangled_function_name,
|
||||
source_hash,
|
||||
is_used,
|
||||
ref virtual_file_mapping,
|
||||
ref expressions,
|
||||
ref regions,
|
||||
} = covfun;
|
||||
|
||||
// Encode the function's coverage mappings into a buffer.
|
||||
let coverage_mapping_buffer = llvm_cov::write_function_mappings_to_buffer(
|
||||
&virtual_file_mapping.to_vec(),
|
||||
expressions,
|
||||
regions,
|
||||
);
|
||||
|
||||
// Concatenate the encoded coverage mappings
|
||||
let coverage_mapping_size = coverage_mapping_buffer.len();
|
||||
let coverage_mapping_val = cx.const_bytes(&coverage_mapping_buffer);
|
||||
|
||||
let func_name_hash = llvm_cov::hash_bytes(mangled_function_name.as_bytes());
|
||||
let func_name_hash_val = cx.const_u64(func_name_hash);
|
||||
let coverage_mapping_size_val = cx.const_u32(coverage_mapping_size as u32);
|
||||
let source_hash_val = cx.const_u64(source_hash);
|
||||
let filenames_ref_val = cx.const_u64(filenames_ref);
|
||||
let func_record_val = cx.const_struct(
|
||||
&[
|
||||
func_name_hash_val,
|
||||
coverage_mapping_size_val,
|
||||
source_hash_val,
|
||||
filenames_ref_val,
|
||||
coverage_mapping_val,
|
||||
],
|
||||
/*packed=*/ true,
|
||||
);
|
||||
|
||||
// Choose a variable name to hold this function's covfun data.
|
||||
// Functions that are used have a suffix ("u") to distinguish them from
|
||||
// unused copies of the same function (from different CGUs), so that if a
|
||||
// linker sees both it won't discard the used copy's data.
|
||||
let func_record_var_name =
|
||||
CString::new(format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" }))
|
||||
.unwrap();
|
||||
debug!("function record var name: {:?}", func_record_var_name);
|
||||
|
||||
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name);
|
||||
llvm::set_initializer(llglobal, func_record_val);
|
||||
llvm::set_global_constant(llglobal, true);
|
||||
llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage);
|
||||
llvm::set_visibility(llglobal, llvm::Visibility::Hidden);
|
||||
llvm::set_section(llglobal, cx.covfun_section_name());
|
||||
// LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
|
||||
// <https://llvm.org/docs/CoverageMappingFormat.html>
|
||||
llvm::set_alignment(llglobal, Align::EIGHT);
|
||||
if cx.target_spec().supports_comdat() {
|
||||
llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name);
|
||||
}
|
||||
cx.add_used_global(llglobal);
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ use tracing::{debug, instrument};
|
|||
|
||||
use crate::builder::Builder;
|
||||
use crate::common::CodegenCx;
|
||||
use crate::coverageinfo::map_data::FunctionCoverageCollector;
|
||||
use crate::coverageinfo::map_data::FunctionCoverage;
|
||||
use crate::llvm;
|
||||
|
||||
pub(crate) mod ffi;
|
||||
|
|
@ -24,8 +24,7 @@ mod mapgen;
|
|||
/// Extra per-CGU context/state needed for coverage instrumentation.
|
||||
pub(crate) struct CguCoverageContext<'ll, 'tcx> {
|
||||
/// Coverage data for each instrumented function identified by DefId.
|
||||
pub(crate) function_coverage_map:
|
||||
RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>>>,
|
||||
pub(crate) function_coverage_map: RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverage<'tcx>>>,
|
||||
pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
|
||||
pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, Vec<&'ll llvm::Value>>>,
|
||||
|
||||
|
|
@ -42,9 +41,7 @@ impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn take_function_coverage_map(
|
||||
&self,
|
||||
) -> FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>> {
|
||||
fn take_function_coverage_map(&self) -> FxIndexMap<Instance<'tcx>, FunctionCoverage<'tcx>> {
|
||||
self.function_coverage_map.replace(FxIndexMap::default())
|
||||
}
|
||||
|
||||
|
|
@ -161,8 +158,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
|||
// This includes functions that were not partitioned into this CGU,
|
||||
// but were MIR-inlined into one of this CGU's functions.
|
||||
coverage_cx.function_coverage_map.borrow_mut().entry(instance).or_insert_with(|| {
|
||||
FunctionCoverageCollector::new(
|
||||
instance,
|
||||
FunctionCoverage::new_used(
|
||||
function_coverage_info,
|
||||
bx.tcx.coverage_ids_info(instance.def),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ impl InlineAsmDiagnostic {
|
|||
unsafe { SrcMgrDiagnostic::unpack(super::LLVMRustGetSMDiagnostic(di, &mut cookie)) };
|
||||
InlineAsmDiagnostic {
|
||||
level: smdiag.level,
|
||||
cookie: cookie.into(),
|
||||
cookie,
|
||||
message: smdiag.message,
|
||||
source: smdiag.source,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2317,7 +2317,7 @@ unsafe extern "C" {
|
|||
|
||||
pub fn LLVMRustGetSMDiagnostic<'a>(
|
||||
DI: &'a DiagnosticInfo,
|
||||
cookie_out: &mut c_uint,
|
||||
cookie_out: &mut u64,
|
||||
) -> &'a SMDiagnostic;
|
||||
|
||||
pub fn LLVMRustUnpackSMDiagnostic(
|
||||
|
|
|
|||
|
|
@ -230,6 +230,8 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
|
|||
"aarch64"
|
||||
} else if sess.target.arch == "sparc64" {
|
||||
"sparc"
|
||||
} else if sess.target.arch == "powerpc64" {
|
||||
"powerpc"
|
||||
} else {
|
||||
&*sess.target.arch
|
||||
};
|
||||
|
|
@ -289,6 +291,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
|
|||
// https://github.com/llvm/llvm-project/blob/llvmorg-18.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L26
|
||||
("sparc", "v8plus") if get_version().0 == 19 => Some(LLVMFeature::new("v9")),
|
||||
("sparc", "v8plus") if get_version().0 < 19 => None,
|
||||
("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")),
|
||||
(_, s) => Some(LLVMFeature::new(s)),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ regex = "1.4"
|
|||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_arena = { path = "../rustc_arena" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
|
|
@ -42,7 +43,7 @@ tempfile = "3.2"
|
|||
thin-vec = "0.2.12"
|
||||
thorin-dwp = "0.8"
|
||||
tracing = "0.1"
|
||||
wasm-encoder = "0.216.0"
|
||||
wasm-encoder = "0.219"
|
||||
# tidy-alphabetical-end
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
|
|
|
|||
|
|
@ -201,6 +201,11 @@ codegen_ssa_missing_memory_ordering = Atomic intrinsic missing memory ordering
|
|||
codegen_ssa_missing_query_depgraph =
|
||||
found CGU-reuse attribute but `-Zquery-dep-graph` was not specified
|
||||
|
||||
codegen_ssa_mixed_export_name_and_no_mangle = `{$no_mangle_attr}` attribute may not be used in combination with `#[export_name]`
|
||||
.label = `{$no_mangle_attr}` is ignored
|
||||
.note = `#[export_name]` takes precedence
|
||||
.suggestion = remove the `{$no_mangle_attr}` attribute
|
||||
|
||||
codegen_ssa_msvc_missing_linker = the msvc targets depend on the msvc linker but `link.exe` was not found
|
||||
|
||||
codegen_ssa_multiple_external_func_decl = multiple declarations of external function `{$function}` from library `{$library_name}` have different calling conventions
|
||||
|
|
|
|||
|
|
@ -992,12 +992,12 @@ fn link_natively(
|
|||
let mut output = prog.stderr.clone();
|
||||
output.extend_from_slice(&prog.stdout);
|
||||
let escaped_output = escape_linker_output(&output, flavor);
|
||||
// FIXME: Add UI tests for this error.
|
||||
let err = errors::LinkingFailed {
|
||||
linker_path: &linker_path,
|
||||
exit_status: prog.status,
|
||||
command: &cmd,
|
||||
command: cmd,
|
||||
escaped_output,
|
||||
verbose: sess.opts.verbose,
|
||||
};
|
||||
sess.dcx().emit_err(err);
|
||||
// If MSVC's `link.exe` was expected but the return code
|
||||
|
|
@ -2745,6 +2745,15 @@ fn add_upstream_rust_crates(
|
|||
.find(|(ty, _)| *ty == crate_type)
|
||||
.expect("failed to find crate type in dependency format list");
|
||||
|
||||
if sess.target.is_like_aix {
|
||||
// Unlike ELF linkers, AIX doesn't feature `DT_SONAME` to override
|
||||
// the dependency name when outputing a shared library. Thus, `ld` will
|
||||
// use the full path to shared libraries as the dependency if passed it
|
||||
// by default unless `noipath` is passed.
|
||||
// https://www.ibm.com/docs/en/aix/7.3?topic=l-ld-command.
|
||||
cmd.link_or_cc_arg("-bnoipath");
|
||||
}
|
||||
|
||||
for &cnum in &codegen_results.crate_info.used_crates {
|
||||
// We may not pass all crates through to the linker. Some crates may appear statically in
|
||||
// an existing dylib, meaning we'll pick up all the symbols from the dylib.
|
||||
|
|
|
|||
|
|
@ -1694,6 +1694,8 @@ impl<'a> Linker for AixLinker<'a> {
|
|||
|
||||
fn pgo_gen(&mut self) {
|
||||
self.link_arg("-bdbg:namedsects:ss");
|
||||
self.link_arg("-u");
|
||||
self.link_arg("__llvm_profile_runtime");
|
||||
}
|
||||
|
||||
fn control_flow_guard(&mut self) {}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ use rustc_session::config::{
|
|||
};
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::{BytePos, FileName, InnerSpan, Pos, Span};
|
||||
use rustc_span::{FileName, InnerSpan, Span, SpanData};
|
||||
use rustc_target::spec::{MergeFunctions, SanitizerSet};
|
||||
use tracing::debug;
|
||||
|
||||
|
|
@ -1837,7 +1837,7 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
|
|||
|
||||
enum SharedEmitterMessage {
|
||||
Diagnostic(Diagnostic),
|
||||
InlineAsmError(u32, String, Level, Option<(String, Vec<InnerSpan>)>),
|
||||
InlineAsmError(SpanData, String, Level, Option<(String, Vec<InnerSpan>)>),
|
||||
Fatal(String),
|
||||
}
|
||||
|
||||
|
|
@ -1859,12 +1859,12 @@ impl SharedEmitter {
|
|||
|
||||
pub fn inline_asm_error(
|
||||
&self,
|
||||
cookie: u32,
|
||||
span: SpanData,
|
||||
msg: String,
|
||||
level: Level,
|
||||
source: Option<(String, Vec<InnerSpan>)>,
|
||||
) {
|
||||
drop(self.sender.send(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source)));
|
||||
drop(self.sender.send(SharedEmitterMessage::InlineAsmError(span, msg, level, source)));
|
||||
}
|
||||
|
||||
fn fatal(&self, msg: &str) {
|
||||
|
|
@ -1953,17 +1953,12 @@ impl SharedEmitterMain {
|
|||
dcx.emit_diagnostic(d);
|
||||
sess.dcx().abort_if_errors();
|
||||
}
|
||||
Ok(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source)) => {
|
||||
Ok(SharedEmitterMessage::InlineAsmError(span, msg, level, source)) => {
|
||||
assert_matches!(level, Level::Error | Level::Warning | Level::Note);
|
||||
let msg = msg.strip_prefix("error: ").unwrap_or(&msg).to_string();
|
||||
let mut err = Diag::<()>::new(sess.dcx(), level, msg);
|
||||
|
||||
// If the cookie is 0 then we don't have span information.
|
||||
if cookie != 0 {
|
||||
let pos = BytePos::from_u32(cookie);
|
||||
let span = Span::with_root_ctxt(pos, pos);
|
||||
err.span(span);
|
||||
};
|
||||
if !span.is_dummy() {
|
||||
err.span(span.span());
|
||||
}
|
||||
|
||||
// Point to the generated assembly if it is available.
|
||||
if let Some((buffer, spans)) = source {
|
||||
|
|
|
|||
|
|
@ -3,11 +3,10 @@ use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr, list_contains_nam
|
|||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
|
||||
use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
|
||||
use rustc_hir::{LangItem, lang_items};
|
||||
use rustc_hir::{self as hir, HirId, LangItem, lang_items};
|
||||
use rustc_middle::middle::codegen_fn_attrs::{
|
||||
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
|
||||
};
|
||||
|
|
@ -78,6 +77,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
let mut inline_span = None;
|
||||
let mut link_ordinal_span = None;
|
||||
let mut no_sanitize_span = None;
|
||||
let mut mixed_export_name_no_mangle_lint_state = MixedExportNameAndNoMangleState::default();
|
||||
|
||||
for attr in attrs.iter() {
|
||||
// In some cases, attribute are only valid on functions, but it's the `check_attr`
|
||||
|
|
@ -116,7 +116,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
|
||||
sym::no_mangle => {
|
||||
if tcx.opt_item_name(did.to_def_id()).is_some() {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
|
||||
mixed_export_name_no_mangle_lint_state.track_no_mangle(
|
||||
attr.span,
|
||||
tcx.local_def_id_to_hir_id(did),
|
||||
attr,
|
||||
);
|
||||
} else {
|
||||
tcx.dcx()
|
||||
.struct_span_err(
|
||||
|
|
@ -240,6 +245,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
.emit();
|
||||
}
|
||||
codegen_fn_attrs.export_name = Some(s);
|
||||
mixed_export_name_no_mangle_lint_state.track_export_name(attr.span);
|
||||
}
|
||||
}
|
||||
sym::target_feature => {
|
||||
|
|
@ -513,6 +519,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
}
|
||||
|
||||
mixed_export_name_no_mangle_lint_state.lint_if_mixed(tcx);
|
||||
|
||||
codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
|
||||
if !attr.has_name(sym::inline) {
|
||||
return ia;
|
||||
|
|
@ -542,6 +550,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
});
|
||||
|
||||
// naked function MUST NOT be inlined! This attribute is required for the rust compiler itself,
|
||||
// but not for the code generation backend because at that point the naked function will just be
|
||||
// a declaration, with a definition provided in global assembly.
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
|
||||
codegen_fn_attrs.inline = InlineAttr::Never;
|
||||
}
|
||||
|
||||
codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| {
|
||||
if !attr.has_name(sym::optimize) {
|
||||
return ia;
|
||||
|
|
@ -626,10 +641,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
}
|
||||
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
|
||||
codegen_fn_attrs.inline = InlineAttr::Never;
|
||||
}
|
||||
|
||||
// Weak lang items have the same semantics as "std internal" symbols in the
|
||||
// sense that they're preserved through all our LTO passes and only
|
||||
// strippable by the linker.
|
||||
|
|
@ -779,6 +790,49 @@ fn check_link_name_xor_ordinal(
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct MixedExportNameAndNoMangleState<'a> {
|
||||
export_name: Option<Span>,
|
||||
hir_id: Option<HirId>,
|
||||
no_mangle: Option<Span>,
|
||||
no_mangle_attr: Option<&'a ast::Attribute>,
|
||||
}
|
||||
|
||||
impl<'a> MixedExportNameAndNoMangleState<'a> {
|
||||
fn track_export_name(&mut self, span: Span) {
|
||||
self.export_name = Some(span);
|
||||
}
|
||||
|
||||
fn track_no_mangle(&mut self, span: Span, hir_id: HirId, attr_name: &'a ast::Attribute) {
|
||||
self.no_mangle = Some(span);
|
||||
self.hir_id = Some(hir_id);
|
||||
self.no_mangle_attr = Some(attr_name);
|
||||
}
|
||||
|
||||
/// Emit diagnostics if the lint condition is met.
|
||||
fn lint_if_mixed(self, tcx: TyCtxt<'_>) {
|
||||
if let Self {
|
||||
export_name: Some(export_name),
|
||||
no_mangle: Some(no_mangle),
|
||||
hir_id: Some(hir_id),
|
||||
no_mangle_attr: Some(no_mangle_attr),
|
||||
} = self
|
||||
{
|
||||
tcx.emit_node_span_lint(
|
||||
lint::builtin::UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
no_mangle,
|
||||
errors::MixedExportNameAndNoMangle {
|
||||
no_mangle,
|
||||
no_mangle_attr: rustc_ast_pretty::pprust::attribute_to_string(no_mangle_attr),
|
||||
export_name,
|
||||
removal_span: no_mangle,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn provide(providers: &mut Providers) {
|
||||
*providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//! Errors emitted by codegen_ssa
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::OsString;
|
||||
use std::io::Error;
|
||||
use std::num::ParseIntError;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
@ -10,7 +11,7 @@ use rustc_errors::codes::*;
|
|||
use rustc_errors::{
|
||||
Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level,
|
||||
};
|
||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::layout::LayoutError;
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
|
@ -345,21 +346,82 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for ThorinErrorWrapper {
|
|||
}
|
||||
|
||||
pub(crate) struct LinkingFailed<'a> {
|
||||
pub linker_path: &'a PathBuf,
|
||||
pub linker_path: &'a Path,
|
||||
pub exit_status: ExitStatus,
|
||||
pub command: &'a Command,
|
||||
pub command: Command,
|
||||
pub escaped_output: String,
|
||||
pub verbose: bool,
|
||||
}
|
||||
|
||||
impl<G: EmissionGuarantee> Diagnostic<'_, G> for LinkingFailed<'_> {
|
||||
fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
|
||||
fn into_diag(mut self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
|
||||
let mut diag = Diag::new(dcx, level, fluent::codegen_ssa_linking_failed);
|
||||
diag.arg("linker_path", format!("{}", self.linker_path.display()));
|
||||
diag.arg("exit_status", format!("{}", self.exit_status));
|
||||
|
||||
let contains_undefined_ref = self.escaped_output.contains("undefined reference to");
|
||||
|
||||
diag.note(format!("{:?}", self.command)).note(self.escaped_output);
|
||||
if self.verbose {
|
||||
diag.note(format!("{:?}", self.command));
|
||||
} else {
|
||||
enum ArgGroup {
|
||||
Regular(OsString),
|
||||
Objects(usize),
|
||||
Rlibs(PathBuf, Vec<OsString>),
|
||||
}
|
||||
|
||||
// Omit rust object files and fold rlibs in the error by default to make linker errors a
|
||||
// bit less verbose.
|
||||
let orig_args = self.command.take_args();
|
||||
let mut args: Vec<ArgGroup> = vec![];
|
||||
for arg in orig_args {
|
||||
if arg.as_encoded_bytes().ends_with(b".rcgu.o") {
|
||||
if let Some(ArgGroup::Objects(n)) = args.last_mut() {
|
||||
*n += 1;
|
||||
} else {
|
||||
args.push(ArgGroup::Objects(1));
|
||||
}
|
||||
} else if arg.as_encoded_bytes().ends_with(b".rlib") {
|
||||
let rlib_path = Path::new(&arg);
|
||||
let dir = rlib_path.parent().unwrap();
|
||||
let filename = rlib_path.file_name().unwrap().to_owned();
|
||||
if let Some(ArgGroup::Rlibs(parent, rlibs)) = args.last_mut() {
|
||||
if parent == dir {
|
||||
rlibs.push(filename);
|
||||
} else {
|
||||
args.push(ArgGroup::Rlibs(dir.to_owned(), vec![filename]));
|
||||
}
|
||||
} else {
|
||||
args.push(ArgGroup::Rlibs(dir.to_owned(), vec![filename]));
|
||||
}
|
||||
} else {
|
||||
args.push(ArgGroup::Regular(arg));
|
||||
}
|
||||
}
|
||||
self.command.args(args.into_iter().map(|arg_group| match arg_group {
|
||||
ArgGroup::Regular(arg) => arg,
|
||||
ArgGroup::Objects(n) => OsString::from(format!("<{n} object files omitted>")),
|
||||
ArgGroup::Rlibs(dir, rlibs) => {
|
||||
let mut arg = dir.into_os_string();
|
||||
arg.push("/{");
|
||||
let mut first = true;
|
||||
for rlib in rlibs {
|
||||
if !first {
|
||||
arg.push(",");
|
||||
}
|
||||
first = false;
|
||||
arg.push(rlib);
|
||||
}
|
||||
arg.push("}");
|
||||
arg
|
||||
}
|
||||
}));
|
||||
|
||||
diag.note(format!("{:?}", self.command));
|
||||
diag.note("some arguments are omitted. use `--verbose` to show all linker arguments");
|
||||
}
|
||||
|
||||
diag.note(self.escaped_output);
|
||||
|
||||
// Trying to match an error from OS linkers
|
||||
// which by now we have no way to translate.
|
||||
|
|
@ -1114,3 +1176,15 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_
|
|||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_aix_strip_not_used)]
|
||||
pub(crate) struct AixStripNotUsed;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(codegen_ssa_mixed_export_name_and_no_mangle)]
|
||||
pub(crate) struct MixedExportNameAndNoMangle {
|
||||
#[label]
|
||||
pub no_mangle: Span,
|
||||
pub no_mangle_attr: String,
|
||||
#[note]
|
||||
pub export_name: Span,
|
||||
#[suggestion(style = "verbose", code = "", applicability = "machine-applicable")]
|
||||
pub removal_span: Span,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ mod coverageinfo;
|
|||
pub mod debuginfo;
|
||||
mod intrinsic;
|
||||
mod locals;
|
||||
mod naked_asm;
|
||||
pub mod operand;
|
||||
pub mod place;
|
||||
mod rvalue;
|
||||
|
|
@ -176,6 +177,11 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
|
||||
debug!("fn_abi: {:?}", fn_abi);
|
||||
|
||||
if cx.tcx().codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) {
|
||||
crate::mir::naked_asm::codegen_naked_asm::<Bx>(cx, &mir, instance);
|
||||
return;
|
||||
}
|
||||
|
||||
let debug_context = cx.create_function_debug_context(instance, fn_abi, llfn, mir);
|
||||
|
||||
let start_llbb = Bx::append_block(cx, llfn, "start");
|
||||
|
|
|
|||
266
compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
Normal file
266
compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
use rustc_attr::InstructionSetAttr;
|
||||
use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility};
|
||||
use rustc_middle::mir::{Body, InlineAsmOperand};
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf};
|
||||
use rustc_middle::ty::{Instance, TyCtxt};
|
||||
use rustc_middle::{bug, ty};
|
||||
use rustc_span::sym;
|
||||
|
||||
use crate::common;
|
||||
use crate::traits::{AsmCodegenMethods, BuilderMethods, GlobalAsmOperandRef, MiscCodegenMethods};
|
||||
|
||||
pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
cx: &'a Bx::CodegenCx,
|
||||
mir: &Body<'tcx>,
|
||||
instance: Instance<'tcx>,
|
||||
) {
|
||||
let rustc_middle::mir::TerminatorKind::InlineAsm {
|
||||
asm_macro: _,
|
||||
template,
|
||||
ref operands,
|
||||
options,
|
||||
line_spans,
|
||||
targets: _,
|
||||
unwind: _,
|
||||
} = mir.basic_blocks.iter().next().unwrap().terminator().kind
|
||||
else {
|
||||
bug!("#[naked] functions should always terminate with an asm! block")
|
||||
};
|
||||
|
||||
let operands: Vec<_> =
|
||||
operands.iter().map(|op| inline_to_global_operand::<Bx>(cx, instance, op)).collect();
|
||||
|
||||
let item_data = cx.codegen_unit().items().get(&MonoItem::Fn(instance)).unwrap();
|
||||
let name = cx.mangled_name(instance);
|
||||
let (begin, end) = prefix_and_suffix(cx.tcx(), instance, &name, item_data);
|
||||
|
||||
let mut template_vec = Vec::new();
|
||||
template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(begin.into()));
|
||||
template_vec.extend(template.iter().cloned());
|
||||
template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(end.into()));
|
||||
|
||||
cx.codegen_global_asm(&template_vec, &operands, options, line_spans);
|
||||
}
|
||||
|
||||
fn inline_to_global_operand<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
cx: &'a Bx::CodegenCx,
|
||||
instance: Instance<'tcx>,
|
||||
op: &InlineAsmOperand<'tcx>,
|
||||
) -> GlobalAsmOperandRef<'tcx> {
|
||||
match op {
|
||||
InlineAsmOperand::Const { value } => {
|
||||
let const_value = instance
|
||||
.instantiate_mir_and_normalize_erasing_regions(
|
||||
cx.tcx(),
|
||||
cx.typing_env(),
|
||||
ty::EarlyBinder::bind(value.const_),
|
||||
)
|
||||
.eval(cx.tcx(), cx.typing_env(), value.span)
|
||||
.expect("erroneous constant missed by mono item collection");
|
||||
|
||||
let mono_type = instance.instantiate_mir_and_normalize_erasing_regions(
|
||||
cx.tcx(),
|
||||
cx.typing_env(),
|
||||
ty::EarlyBinder::bind(value.ty()),
|
||||
);
|
||||
|
||||
let string = common::asm_const_to_str(
|
||||
cx.tcx(),
|
||||
value.span,
|
||||
const_value,
|
||||
cx.layout_of(mono_type),
|
||||
);
|
||||
|
||||
GlobalAsmOperandRef::Const { string }
|
||||
}
|
||||
InlineAsmOperand::SymFn { value } => {
|
||||
let mono_type = instance.instantiate_mir_and_normalize_erasing_regions(
|
||||
cx.tcx(),
|
||||
cx.typing_env(),
|
||||
ty::EarlyBinder::bind(value.ty()),
|
||||
);
|
||||
|
||||
let instance = match mono_type.kind() {
|
||||
&ty::FnDef(def_id, args) => Instance::new(def_id, args),
|
||||
_ => bug!("asm sym is not a function"),
|
||||
};
|
||||
|
||||
GlobalAsmOperandRef::SymFn { instance }
|
||||
}
|
||||
InlineAsmOperand::SymStatic { def_id } => {
|
||||
GlobalAsmOperandRef::SymStatic { def_id: *def_id }
|
||||
}
|
||||
InlineAsmOperand::In { .. }
|
||||
| InlineAsmOperand::Out { .. }
|
||||
| InlineAsmOperand::InOut { .. }
|
||||
| InlineAsmOperand::Label { .. } => {
|
||||
bug!("invalid operand type for naked_asm!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum AsmBinaryFormat {
|
||||
Elf,
|
||||
Macho,
|
||||
Coff,
|
||||
}
|
||||
|
||||
impl AsmBinaryFormat {
|
||||
fn from_target(target: &rustc_target::spec::Target) -> Self {
|
||||
if target.is_like_windows {
|
||||
Self::Coff
|
||||
} else if target.is_like_osx {
|
||||
Self::Macho
|
||||
} else {
|
||||
Self::Elf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn prefix_and_suffix<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
instance: Instance<'tcx>,
|
||||
asm_name: &str,
|
||||
item_data: &MonoItemData,
|
||||
) -> (String, String) {
|
||||
use std::fmt::Write;
|
||||
|
||||
let asm_binary_format = AsmBinaryFormat::from_target(&tcx.sess.target);
|
||||
|
||||
let is_arm = tcx.sess.target.arch == "arm";
|
||||
let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode);
|
||||
|
||||
let attrs = tcx.codegen_fn_attrs(instance.def_id());
|
||||
let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string());
|
||||
let align = attrs.alignment.map(|a| a.bytes()).unwrap_or(4);
|
||||
|
||||
// See https://sourceware.org/binutils/docs/as/ARM-Directives.html for info on these directives.
|
||||
// In particular, `.arm` can also be written `.code 32` and `.thumb` as `.code 16`.
|
||||
let (arch_prefix, arch_suffix) = if is_arm {
|
||||
(
|
||||
match attrs.instruction_set {
|
||||
None => match is_thumb {
|
||||
true => ".thumb\n.thumb_func",
|
||||
false => ".arm",
|
||||
},
|
||||
Some(InstructionSetAttr::ArmT32) => ".thumb\n.thumb_func",
|
||||
Some(InstructionSetAttr::ArmA32) => ".arm",
|
||||
},
|
||||
match is_thumb {
|
||||
true => ".thumb",
|
||||
false => ".arm",
|
||||
},
|
||||
)
|
||||
} else {
|
||||
("", "")
|
||||
};
|
||||
|
||||
let emit_fatal = |msg| tcx.dcx().span_fatal(tcx.def_span(instance.def_id()), msg);
|
||||
|
||||
// see https://godbolt.org/z/cPK4sxKor.
|
||||
let write_linkage = |w: &mut String| -> std::fmt::Result {
|
||||
match item_data.linkage {
|
||||
Linkage::External => {
|
||||
writeln!(w, ".globl {asm_name}")?;
|
||||
}
|
||||
Linkage::LinkOnceAny | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR => {
|
||||
match asm_binary_format {
|
||||
AsmBinaryFormat::Elf | AsmBinaryFormat::Coff => {
|
||||
writeln!(w, ".weak {asm_name}")?;
|
||||
}
|
||||
AsmBinaryFormat::Macho => {
|
||||
writeln!(w, ".globl {asm_name}")?;
|
||||
writeln!(w, ".weak_definition {asm_name}")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Linkage::Internal | Linkage::Private => {
|
||||
// write nothing
|
||||
}
|
||||
Linkage::Appending => emit_fatal("Only global variables can have appending linkage!"),
|
||||
Linkage::Common => emit_fatal("Functions may not have common linkage"),
|
||||
Linkage::AvailableExternally => {
|
||||
// this would make the function equal an extern definition
|
||||
emit_fatal("Functions may not have available_externally linkage")
|
||||
}
|
||||
Linkage::ExternalWeak => {
|
||||
// FIXME: actually this causes a SIGILL in LLVM
|
||||
emit_fatal("Functions may not have external weak linkage")
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let mut begin = String::new();
|
||||
let mut end = String::new();
|
||||
match asm_binary_format {
|
||||
AsmBinaryFormat::Elf => {
|
||||
let section = link_section.unwrap_or(format!(".text.{asm_name}"));
|
||||
|
||||
let progbits = match is_arm {
|
||||
true => "%progbits",
|
||||
false => "@progbits",
|
||||
};
|
||||
|
||||
let function = match is_arm {
|
||||
true => "%function",
|
||||
false => "@function",
|
||||
};
|
||||
|
||||
writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap();
|
||||
writeln!(begin, ".balign {align}").unwrap();
|
||||
write_linkage(&mut begin).unwrap();
|
||||
if let Visibility::Hidden = item_data.visibility {
|
||||
writeln!(begin, ".hidden {asm_name}").unwrap();
|
||||
}
|
||||
writeln!(begin, ".type {asm_name}, {function}").unwrap();
|
||||
if !arch_prefix.is_empty() {
|
||||
writeln!(begin, "{}", arch_prefix).unwrap();
|
||||
}
|
||||
writeln!(begin, "{asm_name}:").unwrap();
|
||||
|
||||
writeln!(end).unwrap();
|
||||
writeln!(end, ".size {asm_name}, . - {asm_name}").unwrap();
|
||||
writeln!(end, ".popsection").unwrap();
|
||||
if !arch_suffix.is_empty() {
|
||||
writeln!(end, "{}", arch_suffix).unwrap();
|
||||
}
|
||||
}
|
||||
AsmBinaryFormat::Macho => {
|
||||
let section = link_section.unwrap_or("__TEXT,__text".to_string());
|
||||
writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap();
|
||||
writeln!(begin, ".balign {align}").unwrap();
|
||||
write_linkage(&mut begin).unwrap();
|
||||
if let Visibility::Hidden = item_data.visibility {
|
||||
writeln!(begin, ".private_extern {asm_name}").unwrap();
|
||||
}
|
||||
writeln!(begin, "{asm_name}:").unwrap();
|
||||
|
||||
writeln!(end).unwrap();
|
||||
writeln!(end, ".popsection").unwrap();
|
||||
if !arch_suffix.is_empty() {
|
||||
writeln!(end, "{}", arch_suffix).unwrap();
|
||||
}
|
||||
}
|
||||
AsmBinaryFormat::Coff => {
|
||||
let section = link_section.unwrap_or(format!(".text.{asm_name}"));
|
||||
writeln!(begin, ".pushsection {},\"xr\"", section).unwrap();
|
||||
writeln!(begin, ".balign {align}").unwrap();
|
||||
write_linkage(&mut begin).unwrap();
|
||||
writeln!(begin, ".def {asm_name}").unwrap();
|
||||
writeln!(begin, ".scl 2").unwrap();
|
||||
writeln!(begin, ".type 32").unwrap();
|
||||
writeln!(begin, ".endef {asm_name}").unwrap();
|
||||
writeln!(begin, "{asm_name}:").unwrap();
|
||||
|
||||
writeln!(end).unwrap();
|
||||
writeln!(end, ".popsection").unwrap();
|
||||
if !arch_suffix.is_empty() {
|
||||
writeln!(end, "{}", arch_suffix).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(begin, end)
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
use rustc_hir as hir;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility};
|
||||
use rustc_middle::ty::Instance;
|
||||
|
|
@ -135,7 +136,13 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
|
|||
cx.predefine_static(def_id, linkage, visibility, symbol_name);
|
||||
}
|
||||
MonoItem::Fn(instance) => {
|
||||
cx.predefine_fn(instance, linkage, visibility, symbol_name);
|
||||
let attrs = cx.tcx().codegen_fn_attrs(instance.def_id());
|
||||
|
||||
if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
|
||||
// do not define this function; it will become a global assembly block
|
||||
} else {
|
||||
cx.predefine_fn(instance, linkage, visibility, symbol_name);
|
||||
};
|
||||
}
|
||||
MonoItem::GlobalAsm(..) => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,4 +68,11 @@ pub trait AsmCodegenMethods<'tcx> {
|
|||
options: InlineAsmOptions,
|
||||
line_spans: &[Span],
|
||||
);
|
||||
|
||||
/// The mangled name of this instance
|
||||
///
|
||||
/// Additional mangling is used on
|
||||
/// some targets to add a leading underscore (Mach-O)
|
||||
/// or byte count suffixes (x86 Windows).
|
||||
fn mangled_name(&self, instance: Instance<'tcx>) -> String;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ where
|
|||
let reported = if allowed_in_infallible {
|
||||
ReportedErrorInfo::allowed_in_infallible(g)
|
||||
} else {
|
||||
ReportedErrorInfo::from(g)
|
||||
ReportedErrorInfo::const_eval_error(g)
|
||||
};
|
||||
ErrorHandled::Reported(reported, span)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ use std::sync::atomic::Ordering::Relaxed;
|
|||
use either::{Left, Right};
|
||||
use rustc_abi::{self as abi, BackendRepr};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo};
|
||||
use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo, ReportedErrorInfo};
|
||||
use rustc_middle::mir::{self, ConstAlloc, ConstValue};
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::{bug, throw_inval};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
|
@ -93,18 +93,18 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
|
|||
match intern_result {
|
||||
Ok(()) => {}
|
||||
Err(InternResult::FoundDanglingPointer) => {
|
||||
return Err(ecx
|
||||
.tcx
|
||||
.dcx()
|
||||
.emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind }))
|
||||
.into();
|
||||
throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
|
||||
ecx.tcx
|
||||
.dcx()
|
||||
.emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind }),
|
||||
)));
|
||||
}
|
||||
Err(InternResult::FoundBadMutablePointer) => {
|
||||
return Err(ecx
|
||||
.tcx
|
||||
.dcx()
|
||||
.emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }))
|
||||
.into();
|
||||
throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(
|
||||
ecx.tcx
|
||||
.dcx()
|
||||
.emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry};
|
|||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem};
|
||||
use rustc_middle::mir::AssertMessage;
|
||||
use rustc_middle::mir::interpret::ReportedErrorInfo;
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
|
|
@ -563,7 +564,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
|||
.tcx
|
||||
.dcx()
|
||||
.span_delayed_bug(span, "The deny lint should have already errored");
|
||||
throw_inval!(AlreadyReported(guard.into()));
|
||||
throw_inval!(AlreadyReported(ReportedErrorInfo::allowed_in_infallible(guard)));
|
||||
}
|
||||
} else if new_steps > start && new_steps.is_power_of_two() {
|
||||
// Only report after a certain number of terminators have been evaluated and the
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use rustc_abi::{BackendRepr, VariantIdx};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
|
||||
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ReportedErrorInfo};
|
||||
use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
|
||||
use rustc_middle::{bug, mir};
|
||||
|
|
@ -261,7 +261,7 @@ pub(crate) fn eval_to_valtree<'tcx>(
|
|||
ValTreeCreationError::NodesOverflow => {
|
||||
let handled =
|
||||
tcx.dcx().emit_err(MaxNumNodesInConstErr { span, global_const_id });
|
||||
Err(handled.into())
|
||||
Err(ReportedErrorInfo::allowed_in_infallible(handled).into())
|
||||
}
|
||||
ValTreeCreationError::NonSupportedType(ty) => Ok(Err(ty)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
};
|
||||
// do not continue if typeck errors occurred (can only occur in local crate)
|
||||
if let Some(err) = body.tainted_by_errors {
|
||||
throw_inval!(AlreadyReported(ReportedErrorInfo::from(err)));
|
||||
throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error(err)));
|
||||
}
|
||||
interp_ok(body)
|
||||
}
|
||||
|
|
@ -317,7 +317,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
Ok(None) => throw_inval!(TooGeneric),
|
||||
|
||||
// FIXME(eddyb) this could be a bit more specific than `AlreadyReported`.
|
||||
Err(error_reported) => throw_inval!(AlreadyReported(error_reported.into())),
|
||||
Err(error_guaranteed) => throw_inval!(AlreadyReported(
|
||||
ReportedErrorInfo::non_const_eval_error(error_guaranteed)
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@
|
|||
use std::assert_matches::assert_matches;
|
||||
|
||||
use either::{Either, Left, Right};
|
||||
use rustc_abi::{Align, BackendRepr, HasDataLayout, Size};
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_abi::{BackendRepr, HasDataLayout, Size};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::{bug, mir, span_bug};
|
||||
|
|
@ -1018,40 +1017,31 @@ where
|
|||
self.allocate_dyn(layout, kind, MemPlaceMeta::None)
|
||||
}
|
||||
|
||||
/// Allocates a sequence of bytes in the interpreter's memory.
|
||||
/// For immutable allocations, uses deduplication to reuse existing memory.
|
||||
/// For mutable allocations, creates a new unique allocation.
|
||||
pub fn allocate_bytes(
|
||||
/// Allocates a sequence of bytes in the interpreter's memory with alignment 1.
|
||||
/// This is allocated in immutable global memory and deduplicated.
|
||||
pub fn allocate_bytes_dedup(
|
||||
&mut self,
|
||||
bytes: &[u8],
|
||||
align: Align,
|
||||
kind: MemoryKind<M::MemoryKind>,
|
||||
mutbl: Mutability,
|
||||
) -> InterpResult<'tcx, Pointer<M::Provenance>> {
|
||||
// Use cache for immutable strings.
|
||||
if mutbl.is_not() {
|
||||
// Use dedup'd allocation function.
|
||||
let salt = M::get_global_alloc_salt(self, None);
|
||||
let id = self.tcx.allocate_bytes_dedup(bytes, salt);
|
||||
let salt = M::get_global_alloc_salt(self, None);
|
||||
let id = self.tcx.allocate_bytes_dedup(bytes, salt);
|
||||
|
||||
// Turn untagged "global" pointers (obtained via `tcx`) into the machine pointer to the allocation.
|
||||
M::adjust_alloc_root_pointer(&self, Pointer::from(id), Some(kind))
|
||||
} else {
|
||||
// Allocate new memory for mutable data.
|
||||
self.allocate_bytes_ptr(bytes, align, kind, mutbl)
|
||||
}
|
||||
// Turn untagged "global" pointers (obtained via `tcx`) into the machine pointer to the allocation.
|
||||
M::adjust_alloc_root_pointer(
|
||||
&self,
|
||||
Pointer::from(id),
|
||||
M::GLOBAL_KIND.map(MemoryKind::Machine),
|
||||
)
|
||||
}
|
||||
|
||||
/// Allocates a string in the interpreter's memory with metadata for length.
|
||||
/// Uses `allocate_bytes` internally but adds string-specific metadata handling.
|
||||
pub fn allocate_str(
|
||||
/// Allocates a string in the interpreter's memory, returning it as a (wide) place.
|
||||
/// This is allocated in immutable global memory and deduplicated.
|
||||
pub fn allocate_str_dedup(
|
||||
&mut self,
|
||||
str: &str,
|
||||
kind: MemoryKind<M::MemoryKind>,
|
||||
mutbl: Mutability,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
|
||||
let bytes = str.as_bytes();
|
||||
let ptr = self.allocate_bytes(bytes, Align::ONE, kind, mutbl)?;
|
||||
let ptr = self.allocate_bytes_dedup(bytes)?;
|
||||
|
||||
// Create length metadata for the string.
|
||||
let meta = Scalar::from_target_usize(u64::try_from(bytes.len()).unwrap(), self);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use rustc_hir::LangItem;
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::{self, Mutability};
|
||||
use rustc_middle::ty::{self};
|
||||
use rustc_middle::{bug, mir};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use tracing::trace;
|
||||
|
|
@ -20,12 +20,9 @@ fn alloc_caller_location<'tcx>(
|
|||
// This can fail if rustc runs out of memory right here. Trying to emit an error would be
|
||||
// pointless, since that would require allocating more memory than these short strings.
|
||||
let file = if loc_details.file {
|
||||
ecx.allocate_str(filename.as_str(), MemoryKind::CallerLocation, Mutability::Not).unwrap()
|
||||
ecx.allocate_str_dedup(filename.as_str()).unwrap()
|
||||
} else {
|
||||
// FIXME: This creates a new allocation each time. It might be preferable to
|
||||
// perform this allocation only once, and re-use the `MPlaceTy`.
|
||||
// See https://github.com/rust-lang/rust/pull/89920#discussion_r730012398
|
||||
ecx.allocate_str("<redacted>", MemoryKind::CallerLocation, Mutability::Not).unwrap()
|
||||
ecx.allocate_str_dedup("<redacted>").unwrap()
|
||||
};
|
||||
let file = file.map_provenance(CtfeProvenance::as_immutable);
|
||||
let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) };
|
||||
|
|
|
|||
|
|
@ -1291,7 +1291,7 @@ impl<'a> DiagCtxtHandle<'a> {
|
|||
Diag::<ErrorGuaranteed>::new(self, DelayedBug, msg.into()).emit()
|
||||
}
|
||||
|
||||
/// Ensures that an error is printed. See `Level::DelayedBug`.
|
||||
/// Ensures that an error is printed. See [`Level::DelayedBug`].
|
||||
///
|
||||
/// Note: this function used to be called `delay_span_bug`. It was renamed
|
||||
/// to match similar functions like `span_err`, `span_warn`, etc.
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@ pub(crate) fn placeholder(
|
|||
vis,
|
||||
is_placeholder: true,
|
||||
safety: Safety::Default,
|
||||
default: None,
|
||||
}]),
|
||||
AstFragmentKind::Variants => AstFragment::Variants(smallvec![ast::Variant {
|
||||
attrs: Default::default(),
|
||||
|
|
|
|||
|
|
@ -455,6 +455,9 @@ declare_features! (
|
|||
(unstable, custom_test_frameworks, "1.30.0", Some(50297)),
|
||||
/// Allows declarative macros 2.0 (`macro`).
|
||||
(unstable, decl_macro, "1.17.0", Some(39412)),
|
||||
/// Allows the use of default values on struct definitions and the construction of struct
|
||||
/// literals with the functional update syntax without a base.
|
||||
(unstable, default_field_values, "CURRENT_RUSTC_VERSION", Some(132162)),
|
||||
/// Allows using `#[deprecated_safe]` to deprecate the safeness of a function or trait
|
||||
(unstable, deprecated_safe, "1.61.0", Some(94978)),
|
||||
/// Allows having using `suggestion` in the `#[deprecated]` attribute.
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ use rustc_macros::{Decodable, Encodable, HashStable_Generic};
|
|||
use rustc_span::Symbol;
|
||||
use rustc_span::def_id::{DefId, LocalDefId};
|
||||
use rustc_span::hygiene::MacroKind;
|
||||
use rustc_span::symbol::kw;
|
||||
|
||||
use crate::definitions::DefPathData;
|
||||
use crate::hir;
|
||||
|
|
@ -256,7 +255,6 @@ impl DefKind {
|
|||
|
||||
pub fn def_path_data(self, name: Symbol) -> DefPathData {
|
||||
match self {
|
||||
DefKind::Struct | DefKind::Union if name == kw::Empty => DefPathData::AnonAdt,
|
||||
DefKind::Mod
|
||||
| DefKind::Struct
|
||||
| DefKind::Union
|
||||
|
|
|
|||
|
|
@ -289,8 +289,6 @@ pub enum DefPathData {
|
|||
/// An existential `impl Trait` type node.
|
||||
/// Argument position `impl Trait` have a `TypeNs` with their pretty-printed name.
|
||||
OpaqueTy,
|
||||
/// An anonymous struct or union type i.e. `struct { foo: Type }` or `union { bar: Type }`
|
||||
AnonAdt,
|
||||
}
|
||||
|
||||
impl Definitions {
|
||||
|
|
@ -415,7 +413,7 @@ impl DefPathData {
|
|||
TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => Some(name),
|
||||
|
||||
Impl | ForeignMod | CrateRoot | Use | GlobalAsm | Closure | Ctor | AnonConst
|
||||
| OpaqueTy | AnonAdt => None,
|
||||
| OpaqueTy => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -438,7 +436,6 @@ impl DefPathData {
|
|||
Ctor => DefPathDataName::Anon { namespace: sym::constructor },
|
||||
AnonConst => DefPathDataName::Anon { namespace: sym::constant },
|
||||
OpaqueTy => DefPathDataName::Anon { namespace: sym::opaque },
|
||||
AnonAdt => DefPathDataName::Anon { namespace: sym::anon_adt },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1857,7 +1857,12 @@ impl Expr<'_> {
|
|||
base.can_have_side_effects()
|
||||
}
|
||||
ExprKind::Struct(_, fields, init) => {
|
||||
fields.iter().map(|field| field.expr).chain(init).any(|e| e.can_have_side_effects())
|
||||
let init_side_effects = match init {
|
||||
StructTailExpr::Base(init) => init.can_have_side_effects(),
|
||||
StructTailExpr::DefaultFields(_) | StructTailExpr::None => false,
|
||||
};
|
||||
fields.iter().map(|field| field.expr).any(|e| e.can_have_side_effects())
|
||||
|| init_side_effects
|
||||
}
|
||||
|
||||
ExprKind::Array(args)
|
||||
|
|
@ -1926,20 +1931,52 @@ impl Expr<'_> {
|
|||
ExprKind::Path(QPath::Resolved(None, path2)),
|
||||
) => path1.res == path2.res,
|
||||
(
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val1], None),
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val2], None),
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::RangeTo, _),
|
||||
[val1],
|
||||
StructTailExpr::None,
|
||||
),
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::RangeTo, _),
|
||||
[val2],
|
||||
StructTailExpr::None,
|
||||
),
|
||||
)
|
||||
| (
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val1], None),
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val2], None),
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::RangeToInclusive, _),
|
||||
[val1],
|
||||
StructTailExpr::None,
|
||||
),
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::RangeToInclusive, _),
|
||||
[val2],
|
||||
StructTailExpr::None,
|
||||
),
|
||||
)
|
||||
| (
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val1], None),
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val2], None),
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::RangeFrom, _),
|
||||
[val1],
|
||||
StructTailExpr::None,
|
||||
),
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::RangeFrom, _),
|
||||
[val2],
|
||||
StructTailExpr::None,
|
||||
),
|
||||
) => val1.expr.equivalent_for_indexing(val2.expr),
|
||||
(
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val1, val3], None),
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val2, val4], None),
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::Range, _),
|
||||
[val1, val3],
|
||||
StructTailExpr::None,
|
||||
),
|
||||
ExprKind::Struct(
|
||||
QPath::LangItem(LangItem::Range, _),
|
||||
[val2, val4],
|
||||
StructTailExpr::None,
|
||||
),
|
||||
) => {
|
||||
val1.expr.equivalent_for_indexing(val2.expr)
|
||||
&& val3.expr.equivalent_for_indexing(val4.expr)
|
||||
|
|
@ -2096,7 +2133,7 @@ pub enum ExprKind<'hir> {
|
|||
///
|
||||
/// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. base}`,
|
||||
/// where `base` is the `Option<Expr>`.
|
||||
Struct(&'hir QPath<'hir>, &'hir [ExprField<'hir>], Option<&'hir Expr<'hir>>),
|
||||
Struct(&'hir QPath<'hir>, &'hir [ExprField<'hir>], StructTailExpr<'hir>),
|
||||
|
||||
/// An array literal constructed from one repeated element.
|
||||
///
|
||||
|
|
@ -2111,6 +2148,19 @@ pub enum ExprKind<'hir> {
|
|||
Err(rustc_span::ErrorGuaranteed),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, HashStable_Generic)]
|
||||
pub enum StructTailExpr<'hir> {
|
||||
/// A struct expression where all the fields are explicitly enumerated: `Foo { a, b }`.
|
||||
None,
|
||||
/// A struct expression with a "base", an expression of the same type as the outer struct that
|
||||
/// will be used to populate any fields not explicitly mentioned: `Foo { ..base }`
|
||||
Base(&'hir Expr<'hir>),
|
||||
/// A struct expression with a `..` tail but no "base" expression. The values from the struct
|
||||
/// fields' default values will be used to populate any fields not explicitly mentioned:
|
||||
/// `Foo { .. }`.
|
||||
DefaultFields(Span),
|
||||
}
|
||||
|
||||
/// Represents an optionally `Self`-qualified value/type path or associated extension.
|
||||
///
|
||||
/// To resolve the path to a `DefId`, call [`qpath_res`].
|
||||
|
|
@ -2832,8 +2882,6 @@ pub enum TyKind<'hir> {
|
|||
Never,
|
||||
/// A tuple (`(A, B, C, D, ...)`).
|
||||
Tup(&'hir [Ty<'hir>]),
|
||||
/// An anonymous struct or union type i.e. `struct { foo: Type }` or `union { foo: Type }`
|
||||
AnonAdt(ItemId),
|
||||
/// A path to a type definition (`module::module::...::Type`), or an
|
||||
/// associated type (e.g., `<Vec<T> as Trait>::Type` or `<T>::Target`).
|
||||
///
|
||||
|
|
@ -3172,6 +3220,7 @@ pub struct FieldDef<'hir> {
|
|||
pub def_id: LocalDefId,
|
||||
pub ty: &'hir Ty<'hir>,
|
||||
pub safety: Safety,
|
||||
pub default: Option<&'hir AnonConst>,
|
||||
}
|
||||
|
||||
impl FieldDef<'_> {
|
||||
|
|
|
|||
|
|
@ -748,7 +748,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
|
|||
ExprKind::Struct(ref qpath, fields, ref optional_base) => {
|
||||
try_visit!(visitor.visit_qpath(qpath, expression.hir_id, expression.span));
|
||||
walk_list!(visitor, visit_expr_field, fields);
|
||||
visit_opt!(visitor, visit_expr, optional_base);
|
||||
match optional_base {
|
||||
StructTailExpr::Base(base) => try_visit!(visitor.visit_expr(base)),
|
||||
StructTailExpr::None | StructTailExpr::DefaultFields(_) => {}
|
||||
}
|
||||
}
|
||||
ExprKind::Tup(subexpressions) => {
|
||||
walk_list!(visitor, visit_expr, subexpressions);
|
||||
|
|
@ -901,9 +904,6 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
|
|||
}
|
||||
TyKind::Typeof(ref expression) => try_visit!(visitor.visit_anon_const(expression)),
|
||||
TyKind::Infer | TyKind::InferDelegation(..) | TyKind::Err(_) => {}
|
||||
TyKind::AnonAdt(item_id) => {
|
||||
try_visit!(visitor.visit_nested_item(item_id));
|
||||
}
|
||||
TyKind::Pat(ty, pat) => {
|
||||
try_visit!(visitor.visit_ty(ty));
|
||||
try_visit!(visitor.visit_pattern_type_pattern(pat));
|
||||
|
|
@ -1190,10 +1190,14 @@ pub fn walk_struct_def<'v, V: Visitor<'v>>(
|
|||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_field_def<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v FieldDef<'v>) -> V::Result {
|
||||
try_visit!(visitor.visit_id(field.hir_id));
|
||||
try_visit!(visitor.visit_ident(field.ident));
|
||||
visitor.visit_ty(field.ty)
|
||||
pub fn walk_field_def<'v, V: Visitor<'v>>(
|
||||
visitor: &mut V,
|
||||
FieldDef { hir_id, ident, ty, default, span: _, vis_span: _, def_id: _, safety: _ }: &'v FieldDef<'v>,
|
||||
) -> V::Result {
|
||||
try_visit!(visitor.visit_id(*hir_id));
|
||||
try_visit!(visitor.visit_ident(*ident));
|
||||
visit_opt!(visitor, visit_anon_const, default);
|
||||
visitor.visit_ty(*ty)
|
||||
}
|
||||
|
||||
pub fn walk_enum_def<'v, V: Visitor<'v>>(
|
||||
|
|
|
|||
|
|
@ -96,12 +96,14 @@ hir_analysis_coercion_between_struct_same_note = expected coercion between the s
|
|||
|
||||
hir_analysis_coercion_between_struct_single_note = expected a single field to be coerced, none found
|
||||
|
||||
hir_analysis_const_bound_for_non_const_trait =
|
||||
`{$modifier}` can only be applied to `#[const_trait]` traits
|
||||
hir_analysis_const_bound_for_non_const_trait = `{$modifier}` can only be applied to `#[const_trait]` traits
|
||||
.label = can't be applied to `{$trait_name}`
|
||||
.note = `{$trait_name}` can't be used with `{$modifier}` because it isn't annotated with `#[const_trait]`
|
||||
.suggestion = {$suggestion_pre}mark `{$trait_name}` as `#[const_trait]` to allow it to have `const` implementations
|
||||
|
||||
hir_analysis_const_impl_for_non_const_trait =
|
||||
const `impl` for trait `{$trait_name}` which is not marked with `#[const_trait]`
|
||||
.suggestion = mark `{$trait_name}` as const
|
||||
hir_analysis_const_impl_for_non_const_trait = const `impl` for trait `{$trait_name}` which is not marked with `#[const_trait]`
|
||||
.label = this trait is not `const`
|
||||
.suggestion = {$suggestion_pre}mark `{$trait_name}` as `#[const_trait]` to allow it to have `const` implementations
|
||||
.note = marking a trait with `#[const_trait]` ensures all default method bodies are `const`
|
||||
.adding = adding a non-const method body in the future would be a breaking change
|
||||
|
||||
|
|
|
|||
|
|
@ -1104,6 +1104,25 @@ fn check_type_defn<'tcx>(
|
|||
for variant in variants.iter() {
|
||||
// All field types must be well-formed.
|
||||
for field in &variant.fields {
|
||||
if let Some(def_id) = field.value
|
||||
&& let Some(_ty) = tcx.type_of(def_id).no_bound_vars()
|
||||
{
|
||||
// FIXME(generic_const_exprs, default_field_values): this is a hack and needs to
|
||||
// be refactored to check the instantiate-ability of the code better.
|
||||
if let Some(def_id) = def_id.as_local()
|
||||
&& let hir::Node::AnonConst(anon) = tcx.hir_node_by_def_id(def_id)
|
||||
&& let expr = &tcx.hir().body(anon.body).value
|
||||
&& let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
|
||||
&& let Res::Def(DefKind::ConstParam, _def_id) = path.res
|
||||
{
|
||||
// Do not evaluate bare `const` params, as those would ICE and are only
|
||||
// usable if `#![feature(generic_const_exprs)]` is enabled.
|
||||
} else {
|
||||
// Evaluate the constant proactively, to emit an error if the constant has
|
||||
// an unconditional error. We only do so if the const has no type params.
|
||||
let _ = tcx.const_eval_poly(def_id.into());
|
||||
}
|
||||
}
|
||||
let field_id = field.did.expect_local();
|
||||
let hir::FieldDef { ty: hir_ty, .. } =
|
||||
tcx.hir_node_by_def_id(field_id).expect_field();
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran
|
|||
}
|
||||
|
||||
let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did);
|
||||
match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) {
|
||||
match type_allowed_to_implement_copy(tcx, param_env, self_type, cause, impl_header.safety) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(CopyImplementationError::InfringingFields(fields)) => {
|
||||
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
|
||||
|
|
@ -123,6 +123,12 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran
|
|||
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
|
||||
Err(tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span }))
|
||||
}
|
||||
Err(CopyImplementationError::HasUnsafeFields) => {
|
||||
let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
|
||||
Err(tcx
|
||||
.dcx()
|
||||
.span_delayed_bug(span, format!("cannot implement `Copy` for `{}`", self_type)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::struct_span_code_err;
|
||||
use rustc_hir::Safety;
|
||||
use rustc_hir::{LangItem, Safety};
|
||||
use rustc_middle::ty::ImplPolarity::*;
|
||||
use rustc_middle::ty::print::PrintTraitRefExt as _;
|
||||
use rustc_middle::ty::{ImplTraitHeader, TraitDef, TyCtxt};
|
||||
|
|
@ -20,7 +20,19 @@ pub(super) fn check_item(
|
|||
tcx.generics_of(def_id).own_params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle");
|
||||
let trait_ref = trait_header.trait_ref.instantiate_identity();
|
||||
|
||||
match (trait_def.safety, unsafe_attr, trait_header.safety, trait_header.polarity) {
|
||||
let is_copy = tcx.is_lang_item(trait_def.def_id, LangItem::Copy);
|
||||
let trait_def_safety = if is_copy {
|
||||
// If `Self` has unsafe fields, `Copy` is unsafe to implement.
|
||||
if trait_header.trait_ref.skip_binder().self_ty().has_unsafe_fields() {
|
||||
rustc_hir::Safety::Unsafe
|
||||
} else {
|
||||
rustc_hir::Safety::Safe
|
||||
}
|
||||
} else {
|
||||
trait_def.safety
|
||||
};
|
||||
|
||||
match (trait_def_safety, unsafe_attr, trait_header.safety, trait_header.polarity) {
|
||||
(Safety::Safe, None, Safety::Unsafe, Positive | Reservation) => {
|
||||
let span = tcx.def_span(def_id);
|
||||
return Err(struct_span_code_err!(
|
||||
|
|
@ -48,12 +60,22 @@ pub(super) fn check_item(
|
|||
"the trait `{}` requires an `unsafe impl` declaration",
|
||||
trait_ref.print_trait_sugared()
|
||||
)
|
||||
.with_note(format!(
|
||||
"the trait `{}` enforces invariants that the compiler can't check. \
|
||||
Review the trait documentation and make sure this implementation \
|
||||
upholds those invariants before adding the `unsafe` keyword",
|
||||
trait_ref.print_trait_sugared()
|
||||
))
|
||||
.with_note(if is_copy {
|
||||
format!(
|
||||
"the trait `{}` cannot be safely implemented for `{}` \
|
||||
because it has unsafe fields. Review the invariants \
|
||||
of those fields before adding an `unsafe impl`",
|
||||
trait_ref.print_trait_sugared(),
|
||||
trait_ref.self_ty(),
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"the trait `{}` enforces invariants that the compiler can't check. \
|
||||
Review the trait documentation and make sure this implementation \
|
||||
upholds those invariants before adding the `unsafe` keyword",
|
||||
trait_ref.print_trait_sugared()
|
||||
)
|
||||
})
|
||||
.with_span_suggestion_verbose(
|
||||
span.shrink_to_lo(),
|
||||
"add `unsafe` to this trait implementation",
|
||||
|
|
|
|||
|
|
@ -1021,12 +1021,12 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn lower_variant(
|
||||
tcx: TyCtxt<'_>,
|
||||
fn lower_variant<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
variant_did: Option<LocalDefId>,
|
||||
ident: Ident,
|
||||
discr: ty::VariantDiscr,
|
||||
def: &hir::VariantData<'_>,
|
||||
def: &hir::VariantData<'tcx>,
|
||||
adt_kind: ty::AdtKind,
|
||||
parent_did: LocalDefId,
|
||||
) -> ty::VariantDef {
|
||||
|
|
@ -1042,6 +1042,7 @@ fn lower_variant(
|
|||
name: f.ident.name,
|
||||
vis: tcx.visibility(f.def_id),
|
||||
safety: f.safety,
|
||||
value: f.default.map(|v| v.def_id.to_def_id()),
|
||||
})
|
||||
.collect();
|
||||
let recovered = match def {
|
||||
|
|
@ -1637,11 +1638,23 @@ fn check_impl_constness(
|
|||
}
|
||||
|
||||
let trait_name = tcx.item_name(trait_def_id).to_string();
|
||||
let (local_trait_span, suggestion_pre) =
|
||||
match (trait_def_id.is_local(), tcx.sess.is_nightly_build()) {
|
||||
(true, true) => (
|
||||
Some(tcx.def_span(trait_def_id).shrink_to_lo()),
|
||||
if tcx.features().const_trait_impl() {
|
||||
""
|
||||
} else {
|
||||
"enable `#![feature(const_trait_impl)]` in your crate and "
|
||||
},
|
||||
),
|
||||
(false, _) | (_, false) => (None, ""),
|
||||
};
|
||||
Some(tcx.dcx().emit_err(errors::ConstImplForNonConstTrait {
|
||||
trait_ref_span: hir_trait_ref.path.span,
|
||||
trait_name,
|
||||
local_trait_span:
|
||||
trait_def_id.as_local().map(|_| tcx.def_span(trait_def_id).shrink_to_lo()),
|
||||
local_trait_span,
|
||||
suggestion_pre,
|
||||
marking: (),
|
||||
adding: (),
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -711,12 +711,19 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>(
|
|||
`{filter:?}` implied bounds: {clause:?}"
|
||||
);
|
||||
}
|
||||
ty::ClauseKind::HostEffect(host_effect_predicate) => {
|
||||
assert_eq!(
|
||||
host_effect_predicate.self_ty(),
|
||||
ty,
|
||||
"expected `Self` predicate when computing \
|
||||
`{filter:?}` implied bounds: {clause:?}"
|
||||
);
|
||||
}
|
||||
|
||||
ty::ClauseKind::RegionOutlives(_)
|
||||
| ty::ClauseKind::ConstArgHasType(_, _)
|
||||
| ty::ClauseKind::WellFormed(_)
|
||||
| ty::ClauseKind::ConstEvaluatable(_)
|
||||
| ty::ClauseKind::HostEffect(..) => {
|
||||
| ty::ClauseKind::ConstEvaluatable(_) => {
|
||||
bug!(
|
||||
"unexpected non-`Self` predicate when computing \
|
||||
`{filter:?}` implied bounds: {clause:?}"
|
||||
|
|
|
|||
|
|
@ -125,6 +125,12 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
|
|||
return ty;
|
||||
}
|
||||
|
||||
Node::Field(&hir::FieldDef { default: Some(c), def_id: field_def_id, .. })
|
||||
if c.hir_id == hir_id =>
|
||||
{
|
||||
tcx.type_of(field_def_id).instantiate_identity()
|
||||
}
|
||||
|
||||
_ => Ty::new_error_with_message(
|
||||
tcx,
|
||||
span,
|
||||
|
|
|
|||
|
|
@ -530,10 +530,16 @@ pub(crate) struct GenericArgsOnOverriddenImpl {
|
|||
#[diag(hir_analysis_const_impl_for_non_const_trait)]
|
||||
pub(crate) struct ConstImplForNonConstTrait {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub trait_ref_span: Span,
|
||||
pub trait_name: String,
|
||||
#[suggestion(applicability = "machine-applicable", code = "#[const_trait]")]
|
||||
#[suggestion(
|
||||
applicability = "machine-applicable",
|
||||
code = "#[const_trait] ",
|
||||
style = "verbose"
|
||||
)]
|
||||
pub local_trait_span: Option<Span>,
|
||||
pub suggestion_pre: &'static str,
|
||||
#[note]
|
||||
pub marking: (),
|
||||
#[note(hir_analysis_adding)]
|
||||
|
|
@ -544,8 +550,19 @@ pub(crate) struct ConstImplForNonConstTrait {
|
|||
#[diag(hir_analysis_const_bound_for_non_const_trait)]
|
||||
pub(crate) struct ConstBoundForNonConstTrait {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
pub modifier: &'static str,
|
||||
#[note]
|
||||
pub def_span: Option<Span>,
|
||||
pub suggestion_pre: &'static str,
|
||||
#[suggestion(
|
||||
applicability = "machine-applicable",
|
||||
code = "#[const_trait] ",
|
||||
style = "verbose"
|
||||
)]
|
||||
pub suggestion: Option<Span>,
|
||||
pub trait_name: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
// corresponding `Projection` clause
|
||||
for def_ids in associated_types.values_mut() {
|
||||
for (projection_bound, span) in &projection_bounds {
|
||||
let def_id = projection_bound.projection_def_id();
|
||||
let def_id = projection_bound.item_def_id();
|
||||
def_ids.swap_remove(&def_id);
|
||||
if tcx.generics_require_sized_self(def_id) {
|
||||
tcx.emit_node_span_lint(
|
||||
|
|
@ -413,7 +413,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
late_bound_in_projection_term,
|
||||
late_bound_in_term,
|
||||
|br_name| {
|
||||
let item_name = tcx.item_name(pred.projection_def_id());
|
||||
let item_name = tcx.item_name(pred.item_def_id());
|
||||
struct_span_code_err!(
|
||||
self.dcx(),
|
||||
span,
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ use rustc_span::{DUMMY_SP, Span};
|
|||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::wf::object_region_bounds;
|
||||
use rustc_trait_selection::traits::{self, ObligationCtxt};
|
||||
use tracing::{debug, debug_span, instrument};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use crate::bounds::Bounds;
|
||||
use crate::check::check_abi_fn_ptr;
|
||||
|
|
@ -737,9 +737,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
if let hir::BoundConstness::Always(span) | hir::BoundConstness::Maybe(span) = constness
|
||||
&& !self.tcx().is_const_trait(trait_def_id)
|
||||
{
|
||||
let (def_span, suggestion, suggestion_pre) =
|
||||
match (trait_def_id.is_local(), self.tcx().sess.is_nightly_build()) {
|
||||
(true, true) => (
|
||||
None,
|
||||
Some(tcx.def_span(trait_def_id).shrink_to_lo()),
|
||||
if self.tcx().features().const_trait_impl() {
|
||||
""
|
||||
} else {
|
||||
"enable `#![feature(const_trait_impl)]` in your crate and "
|
||||
},
|
||||
),
|
||||
(false, _) | (_, false) => (Some(tcx.def_span(trait_def_id)), None, ""),
|
||||
};
|
||||
self.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait {
|
||||
span,
|
||||
modifier: constness.as_str(),
|
||||
def_span,
|
||||
trait_name: self.tcx().def_path_str(trait_def_id),
|
||||
suggestion_pre,
|
||||
suggestion,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -2287,19 +2304,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
hir::TyKind::Tup(fields) => {
|
||||
Ty::new_tup_from_iter(tcx, fields.iter().map(|t| self.lower_ty(t)))
|
||||
}
|
||||
hir::TyKind::AnonAdt(item_id) => {
|
||||
let _guard = debug_span!("AnonAdt");
|
||||
|
||||
let did = item_id.owner_id.def_id;
|
||||
let adt_def = tcx.adt_def(did);
|
||||
|
||||
let args = ty::GenericArgs::for_item(tcx, did.to_def_id(), |param, _| {
|
||||
tcx.mk_param_from_def(param)
|
||||
});
|
||||
debug!(?args);
|
||||
|
||||
Ty::new_adt(tcx, adt_def, tcx.mk_args(args))
|
||||
}
|
||||
hir::TyKind::BareFn(bf) => {
|
||||
require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, hir_ty.span);
|
||||
|
||||
|
|
|
|||
|
|
@ -330,7 +330,6 @@ impl<'a> State<'a> {
|
|||
hir::TyKind::Infer | hir::TyKind::InferDelegation(..) => {
|
||||
self.word("_");
|
||||
}
|
||||
hir::TyKind::AnonAdt(..) => self.word("/* anonymous adt */"),
|
||||
hir::TyKind::Pat(ty, pat) => {
|
||||
self.print_type(ty);
|
||||
self.word(" is ");
|
||||
|
|
@ -1080,22 +1079,36 @@ impl<'a> State<'a> {
|
|||
&mut self,
|
||||
qpath: &hir::QPath<'_>,
|
||||
fields: &[hir::ExprField<'_>],
|
||||
wth: Option<&hir::Expr<'_>>,
|
||||
wth: hir::StructTailExpr<'_>,
|
||||
) {
|
||||
self.print_qpath(qpath, true);
|
||||
self.word("{");
|
||||
self.commasep_cmnt(Consistent, fields, |s, field| s.print_expr_field(field), |f| f.span);
|
||||
if let Some(expr) = wth {
|
||||
self.ibox(INDENT_UNIT);
|
||||
if !fields.is_empty() {
|
||||
self.word(",");
|
||||
self.space();
|
||||
match wth {
|
||||
hir::StructTailExpr::Base(expr) => {
|
||||
self.ibox(INDENT_UNIT);
|
||||
if !fields.is_empty() {
|
||||
self.word(",");
|
||||
self.space();
|
||||
}
|
||||
self.word("..");
|
||||
self.print_expr(expr);
|
||||
self.end();
|
||||
}
|
||||
hir::StructTailExpr::DefaultFields(_) => {
|
||||
self.ibox(INDENT_UNIT);
|
||||
if !fields.is_empty() {
|
||||
self.word(",");
|
||||
self.space();
|
||||
}
|
||||
self.word("..");
|
||||
self.end();
|
||||
}
|
||||
hir::StructTailExpr::None => {
|
||||
if !fields.is_empty() {
|
||||
self.word(",");
|
||||
}
|
||||
}
|
||||
self.word("..");
|
||||
self.print_expr(expr);
|
||||
self.end();
|
||||
} else if !fields.is_empty() {
|
||||
self.word(",");
|
||||
}
|
||||
|
||||
self.word("}");
|
||||
|
|
|
|||
|
|
@ -10,6 +10,12 @@ hir_typeck_address_of_temporary_taken = cannot take address of a temporary
|
|||
hir_typeck_arg_mismatch_indeterminate = argument type mismatch was detected, but rustc had trouble determining where
|
||||
.note = we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new
|
||||
|
||||
hir_typeck_base_expression_double_dot = base expression required after `..`
|
||||
hir_typeck_base_expression_double_dot_add_expr = add a base expression here
|
||||
hir_typeck_base_expression_double_dot_enable_default_field_values =
|
||||
add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields
|
||||
hir_typeck_base_expression_double_dot_remove = remove the `..` as all the fields are already present
|
||||
|
||||
hir_typeck_candidate_trait_note = `{$trait_name}` defines an item `{$item_name}`{$action_or_ty ->
|
||||
[NONE] {""}
|
||||
[implement] , perhaps you need to implement it
|
||||
|
|
|
|||
|
|
@ -454,20 +454,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
closure_kind: hir::ClosureKind,
|
||||
projection: ty::PolyProjectionPredicate<'tcx>,
|
||||
) -> Option<ExpectedSig<'tcx>> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let trait_def_id = projection.trait_def_id(tcx);
|
||||
let def_id = projection.item_def_id();
|
||||
|
||||
// For now, we only do signature deduction based off of the `Fn` and `AsyncFn` traits,
|
||||
// for closures and async closures, respectively.
|
||||
match closure_kind {
|
||||
hir::ClosureKind::Closure
|
||||
if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() =>
|
||||
{
|
||||
hir::ClosureKind::Closure if self.tcx.is_lang_item(def_id, LangItem::FnOnceOutput) => {
|
||||
self.extract_sig_from_projection(cause_span, projection)
|
||||
}
|
||||
hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async)
|
||||
if self.tcx.async_fn_trait_kind_from_def_id(trait_def_id).is_some() =>
|
||||
if self.tcx.is_lang_item(def_id, LangItem::AsyncFnOnceOutput) =>
|
||||
{
|
||||
self.extract_sig_from_projection(cause_span, projection)
|
||||
}
|
||||
|
|
@ -475,7 +471,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// `F: FnOnce() -> Fut, Fut: Future<Output = T>` style bound. Let's still
|
||||
// guide inference here, since it's beneficial for the user.
|
||||
hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async)
|
||||
if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() =>
|
||||
if self.tcx.is_lang_item(def_id, LangItem::FnOnceOutput) =>
|
||||
{
|
||||
self.extract_sig_from_projection_and_future_bound(cause_span, projection)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,47 @@ use rustc_span::{Span, Symbol};
|
|||
|
||||
use crate::fluent_generated as fluent;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_typeck_base_expression_double_dot, code = E0797)]
|
||||
pub(crate) struct BaseExpressionDoubleDot {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[subdiagnostic]
|
||||
pub default_field_values: Option<BaseExpressionDoubleDotEnableDefaultFieldValues>,
|
||||
#[subdiagnostic]
|
||||
pub add_expr: Option<BaseExpressionDoubleDotAddExpr>,
|
||||
#[subdiagnostic]
|
||||
pub remove_dots: Option<BaseExpressionDoubleDotRemove>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[suggestion(
|
||||
hir_typeck_base_expression_double_dot_remove,
|
||||
code = "",
|
||||
applicability = "machine-applicable",
|
||||
style = "verbose"
|
||||
)]
|
||||
pub(crate) struct BaseExpressionDoubleDotRemove {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[suggestion(
|
||||
hir_typeck_base_expression_double_dot_add_expr,
|
||||
code = "/* expr */",
|
||||
applicability = "has-placeholders",
|
||||
style = "verbose"
|
||||
)]
|
||||
pub(crate) struct BaseExpressionDoubleDotAddExpr {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[help(hir_typeck_base_expression_double_dot_enable_default_field_values)]
|
||||
pub(crate) struct BaseExpressionDoubleDotEnableDefaultFieldValues;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_typeck_field_multiply_specified_in_initializer, code = E0062)]
|
||||
pub(crate) struct FieldMultiplySpecifiedInInitializer {
|
||||
|
|
|
|||
|
|
@ -44,10 +44,11 @@ use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectatio
|
|||
use crate::TupleArgumentsFlag::DontTupleArguments;
|
||||
use crate::coercion::{CoerceMany, DynamicCoerceMany};
|
||||
use crate::errors::{
|
||||
AddressOfTemporaryTaken, FieldMultiplySpecifiedInInitializer,
|
||||
FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, ReturnLikeStatementKind,
|
||||
ReturnStmtOutsideOfFnBody, StructExprNonExhaustive, TypeMismatchFruTypo,
|
||||
YieldExprOutsideOfCoroutine,
|
||||
AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr,
|
||||
BaseExpressionDoubleDotEnableDefaultFieldValues, BaseExpressionDoubleDotRemove,
|
||||
FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition,
|
||||
ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive,
|
||||
TypeMismatchFruTypo, YieldExprOutsideOfCoroutine,
|
||||
};
|
||||
use crate::{
|
||||
BreakableCtxt, CoroutineTypes, Diverges, FnCtxt, Needs, cast, fatally_break_rust,
|
||||
|
|
@ -402,6 +403,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
})
|
||||
| hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => true,
|
||||
|
||||
hir::Node::Pat(_) => {
|
||||
self.dcx().span_delayed_bug(expr.span, "place expr not allowed in pattern");
|
||||
true
|
||||
}
|
||||
|
||||
// These nodes do not have direct sub-exprs.
|
||||
hir::Node::Param(_)
|
||||
| hir::Node::Item(_)
|
||||
|
|
@ -414,7 +420,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
| hir::Node::Ty(_)
|
||||
| hir::Node::AssocItemConstraint(_)
|
||||
| hir::Node::TraitRef(_)
|
||||
| hir::Node::Pat(_)
|
||||
| hir::Node::PatField(_)
|
||||
| hir::Node::LetStmt(_)
|
||||
| hir::Node::Synthetic
|
||||
|
|
@ -723,7 +728,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
self.suggest_assoc_method_call(segs);
|
||||
let e =
|
||||
self.dcx().span_delayed_bug(qpath.span(), "`Res::Err` but no error emitted");
|
||||
self.set_tainted_by_errors(e);
|
||||
Ty::new_error(tcx, e)
|
||||
}
|
||||
Res::Def(DefKind::Variant, _) => {
|
||||
|
|
@ -1855,11 +1859,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
fn check_expr_struct(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
expr: &hir::Expr<'tcx>,
|
||||
expected: Expectation<'tcx>,
|
||||
qpath: &QPath<'tcx>,
|
||||
qpath: &'tcx QPath<'tcx>,
|
||||
fields: &'tcx [hir::ExprField<'tcx>],
|
||||
base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
|
||||
base_expr: &'tcx hir::StructTailExpr<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
// Find the relevant variant
|
||||
let (variant, adt_ty) = match self.check_struct_path(qpath, expr.hir_id) {
|
||||
|
|
@ -1899,7 +1903,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
span: Span,
|
||||
variant: &'tcx ty::VariantDef,
|
||||
hir_fields: &'tcx [hir::ExprField<'tcx>],
|
||||
base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
|
||||
base_expr: &'tcx hir::StructTailExpr<'tcx>,
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
|
||||
|
|
@ -2023,13 +2027,90 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// the fields with the base_expr. This could cause us to hit errors later
|
||||
// when certain fields are assumed to exist that in fact do not.
|
||||
if error_happened {
|
||||
if let Some(base_expr) = base_expr {
|
||||
if let hir::StructTailExpr::Base(base_expr) = base_expr {
|
||||
self.check_expr(base_expr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(base_expr) = base_expr {
|
||||
if let hir::StructTailExpr::DefaultFields(span) = *base_expr {
|
||||
let mut missing_mandatory_fields = Vec::new();
|
||||
let mut missing_optional_fields = Vec::new();
|
||||
for f in &variant.fields {
|
||||
let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id);
|
||||
if let Some(_) = remaining_fields.remove(&ident) {
|
||||
if f.value.is_none() {
|
||||
missing_mandatory_fields.push(ident);
|
||||
} else {
|
||||
missing_optional_fields.push(ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !self.tcx.features().default_field_values() {
|
||||
self.dcx().emit_err(BaseExpressionDoubleDot {
|
||||
span: span.shrink_to_hi(),
|
||||
// We only mention enabling the feature if this is a nightly rustc *and* the
|
||||
// expression would make sense with the feature enabled.
|
||||
default_field_values: if self.tcx.sess.is_nightly_build()
|
||||
&& missing_mandatory_fields.is_empty()
|
||||
&& !missing_optional_fields.is_empty()
|
||||
{
|
||||
Some(BaseExpressionDoubleDotEnableDefaultFieldValues)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
add_expr: if !missing_mandatory_fields.is_empty()
|
||||
|| !missing_optional_fields.is_empty()
|
||||
{
|
||||
Some(BaseExpressionDoubleDotAddExpr { span: span.shrink_to_hi() })
|
||||
} else {
|
||||
None
|
||||
},
|
||||
remove_dots: if missing_mandatory_fields.is_empty()
|
||||
&& missing_optional_fields.is_empty()
|
||||
{
|
||||
Some(BaseExpressionDoubleDotRemove { span })
|
||||
} else {
|
||||
None
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
if !missing_mandatory_fields.is_empty() {
|
||||
let s = pluralize!(missing_mandatory_fields.len());
|
||||
let fields: Vec<_> =
|
||||
missing_mandatory_fields.iter().map(|f| format!("`{f}`")).collect();
|
||||
let fields = match &fields[..] {
|
||||
[] => unreachable!(),
|
||||
[only] => only.to_string(),
|
||||
[start @ .., last] => format!("{} and {last}", start.join(", ")),
|
||||
};
|
||||
self.dcx()
|
||||
.struct_span_err(
|
||||
span.shrink_to_hi(),
|
||||
format!("missing mandatory field{s} {fields}"),
|
||||
)
|
||||
.emit();
|
||||
return;
|
||||
}
|
||||
let fru_tys = match adt_ty.kind() {
|
||||
ty::Adt(adt, args) if adt.is_struct() => variant
|
||||
.fields
|
||||
.iter()
|
||||
.map(|f| self.normalize(span, f.ty(self.tcx, args)))
|
||||
.collect(),
|
||||
ty::Adt(adt, args) if adt.is_enum() => variant
|
||||
.fields
|
||||
.iter()
|
||||
.map(|f| self.normalize(span, f.ty(self.tcx, args)))
|
||||
.collect(),
|
||||
_ => {
|
||||
self.dcx().emit_err(FunctionalRecordUpdateOnNonStruct { span });
|
||||
return;
|
||||
}
|
||||
};
|
||||
self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr.hir_id, fru_tys);
|
||||
} else if let hir::StructTailExpr::Base(base_expr) = base_expr {
|
||||
// FIXME: We are currently creating two branches here in order to maintain
|
||||
// consistency. But they should be merged as much as possible.
|
||||
let fru_tys = if self.tcx.features().type_changing_struct_update() {
|
||||
|
|
@ -2161,12 +2242,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
fn check_struct_fields_on_error(
|
||||
&self,
|
||||
fields: &'tcx [hir::ExprField<'tcx>],
|
||||
base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
|
||||
base_expr: &'tcx hir::StructTailExpr<'tcx>,
|
||||
) {
|
||||
for field in fields {
|
||||
self.check_expr(field.expr);
|
||||
}
|
||||
if let Some(base) = *base_expr {
|
||||
if let hir::StructTailExpr::Base(base) = *base_expr {
|
||||
self.check_expr(base);
|
||||
}
|
||||
}
|
||||
|
|
@ -2611,33 +2692,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
None
|
||||
}
|
||||
|
||||
// Check field access expressions
|
||||
/// Check field access expressions, this works for both structs and tuples.
|
||||
/// Returns the Ty of the field.
|
||||
///
|
||||
/// ```not_rust
|
||||
/// base.field
|
||||
/// ^^^^^^^^^^ expr
|
||||
/// ^^^^ base
|
||||
/// ^^^^^ field
|
||||
/// ```
|
||||
fn check_expr_field(
|
||||
&self,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
base: &'tcx hir::Expr<'tcx>,
|
||||
field: Ident,
|
||||
// The expected type hint of the field.
|
||||
expected: Expectation<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field);
|
||||
let base_ty = self.check_expr(base);
|
||||
let base_ty = self.structurally_resolve_type(base.span, base_ty);
|
||||
|
||||
// Whether we are trying to access a private field. Used for error reporting.
|
||||
let mut private_candidate = None;
|
||||
|
||||
// Field expressions automatically deref
|
||||
let mut autoderef = self.autoderef(expr.span, base_ty);
|
||||
while let Some((deref_base_ty, _)) = autoderef.next() {
|
||||
debug!("deref_base_ty: {:?}", deref_base_ty);
|
||||
match deref_base_ty.kind() {
|
||||
ty::Adt(base_def, args) if !base_def.is_enum() => {
|
||||
debug!("struct named {:?}", deref_base_ty);
|
||||
let body_hir_id = self.tcx.local_def_id_to_hir_id(self.body_id);
|
||||
let (ident, def_scope) =
|
||||
self.tcx.adjust_ident_and_get_scope(field, base_def.did(), body_hir_id);
|
||||
|
||||
// we don't care to report errors for a struct if the struct itself is tainted
|
||||
if let Err(guar) = base_def.non_enum_variant().has_errors() {
|
||||
return Ty::new_error(self.tcx(), guar);
|
||||
}
|
||||
|
||||
let fn_body_hir_id = self.tcx.local_def_id_to_hir_id(self.body_id);
|
||||
let (ident, def_scope) =
|
||||
self.tcx.adjust_ident_and_get_scope(field, base_def.did(), fn_body_hir_id);
|
||||
|
||||
if let Some((idx, field)) = self.find_adt_field(*base_def, ident) {
|
||||
self.write_field_index(expr.hir_id, idx);
|
||||
|
||||
|
|
@ -2671,6 +2765,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
// We failed to check the expression, report an error.
|
||||
|
||||
// Emits an error if we deref an infer variable, like calling `.field` on a base type of &_.
|
||||
self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));
|
||||
|
||||
if let Some((adjustments, did)) = private_candidate {
|
||||
|
|
@ -2695,6 +2792,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
expr.hir_id,
|
||||
expected.only_has_type(self),
|
||||
) {
|
||||
// If taking a method instead of calling it
|
||||
self.ban_take_value_of_method(expr, base_ty, field)
|
||||
} else if !base_ty.is_primitive_ty() {
|
||||
self.ban_nonexisting_field(field, base, expr, base_ty)
|
||||
|
|
|
|||
|
|
@ -686,7 +686,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
fn walk_struct_expr<'hir>(
|
||||
&self,
|
||||
fields: &[hir::ExprField<'_>],
|
||||
opt_with: &Option<&'hir hir::Expr<'_>>,
|
||||
opt_with: &hir::StructTailExpr<'hir>,
|
||||
) -> Result<(), Cx::Error> {
|
||||
// Consume the expressions supplying values for each field.
|
||||
for field in fields {
|
||||
|
|
@ -702,8 +702,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
}
|
||||
|
||||
let with_expr = match *opt_with {
|
||||
Some(w) => &*w,
|
||||
None => {
|
||||
hir::StructTailExpr::Base(w) => &*w,
|
||||
hir::StructTailExpr::DefaultFields(_) | hir::StructTailExpr::None => {
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -903,6 +903,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
let detect_dotdot = |err: &mut Diag<'_>, ty: Ty<'_>, expr: &hir::Expr<'_>| {
|
||||
if let ty::Adt(adt, _) = ty.kind()
|
||||
&& self.tcx().lang_items().get(hir::LangItem::RangeFull) == Some(adt.did())
|
||||
&& let hir::ExprKind::Struct(
|
||||
hir::QPath::LangItem(hir::LangItem::RangeFull, _),
|
||||
[],
|
||||
_,
|
||||
) = expr.kind
|
||||
{
|
||||
// We have `Foo(a, .., c)`, where the user might be trying to use the "rest" syntax
|
||||
// from default field values, which is not supported on tuples.
|
||||
let explanation = if self.tcx.features().default_field_values() {
|
||||
"this is only supported on non-tuple struct literals"
|
||||
} else if self.tcx.sess.is_nightly_build() {
|
||||
"this is only supported on non-tuple struct literals when \
|
||||
`#![feature(default_field_values)]` is enabled"
|
||||
} else {
|
||||
"this is not supported"
|
||||
};
|
||||
let msg = format!(
|
||||
"you might have meant to use `..` to skip providing a value for \
|
||||
expected fields, but {explanation}; it is instead interpreted as a \
|
||||
`std::ops::RangeFull` literal",
|
||||
);
|
||||
err.span_help(expr.span, msg);
|
||||
}
|
||||
};
|
||||
|
||||
let mut reported = None;
|
||||
errors.retain(|error| {
|
||||
let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) =
|
||||
|
|
@ -1009,6 +1037,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
tuple_arguments,
|
||||
);
|
||||
suggest_confusable(&mut err);
|
||||
detect_dotdot(&mut err, provided_ty, provided_args[provided_idx]);
|
||||
return err.emit();
|
||||
}
|
||||
|
||||
|
|
@ -1133,6 +1162,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
None,
|
||||
None,
|
||||
);
|
||||
detect_dotdot(&mut err, provided_ty, provided_args[provided_idx]);
|
||||
}
|
||||
Error::Extra(arg_idx) => {
|
||||
let (provided_ty, provided_span) = provided_arg_tys[arg_idx];
|
||||
|
|
@ -1216,6 +1246,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
};
|
||||
prev_extra_idx = Some(arg_idx.index())
|
||||
}
|
||||
detect_dotdot(&mut err, provided_ty, provided_args[arg_idx]);
|
||||
}
|
||||
Error::Missing(expected_idx) => {
|
||||
// If there are multiple missing arguments adjacent to each other,
|
||||
|
|
|
|||
|
|
@ -296,6 +296,111 @@ impl<T: Idx> From<GrowableBitSet<T>> for BitSet<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for BitSet<T> {
|
||||
fn clone(&self) -> Self {
|
||||
BitSet { domain_size: self.domain_size, words: self.words.clone(), marker: PhantomData }
|
||||
}
|
||||
|
||||
fn clone_from(&mut self, from: &Self) {
|
||||
self.domain_size = from.domain_size;
|
||||
self.words.clone_from(&from.words);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Idx> fmt::Debug for BitSet<T> {
|
||||
fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
w.debug_list().entries(self.iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Idx> ToString for BitSet<T> {
|
||||
fn to_string(&self) -> String {
|
||||
let mut result = String::new();
|
||||
let mut sep = '[';
|
||||
|
||||
// Note: this is a little endian printout of bytes.
|
||||
|
||||
// i tracks how many bits we have printed so far.
|
||||
let mut i = 0;
|
||||
for word in &self.words {
|
||||
let mut word = *word;
|
||||
for _ in 0..WORD_BYTES {
|
||||
// for each byte in `word`:
|
||||
let remain = self.domain_size - i;
|
||||
// If less than a byte remains, then mask just that many bits.
|
||||
let mask = if remain <= 8 { (1 << remain) - 1 } else { 0xFF };
|
||||
assert!(mask <= 0xFF);
|
||||
let byte = word & mask;
|
||||
|
||||
result.push_str(&format!("{sep}{byte:02x}"));
|
||||
|
||||
if remain <= 8 {
|
||||
break;
|
||||
}
|
||||
word >>= 8;
|
||||
i += 8;
|
||||
sep = '-';
|
||||
}
|
||||
sep = '|';
|
||||
}
|
||||
result.push(']');
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BitIter<'a, T: Idx> {
|
||||
/// A copy of the current word, but with any already-visited bits cleared.
|
||||
/// (This lets us use `trailing_zeros()` to find the next set bit.) When it
|
||||
/// is reduced to 0, we move onto the next word.
|
||||
word: Word,
|
||||
|
||||
/// The offset (measured in bits) of the current word.
|
||||
offset: usize,
|
||||
|
||||
/// Underlying iterator over the words.
|
||||
iter: slice::Iter<'a, Word>,
|
||||
|
||||
marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: Idx> BitIter<'a, T> {
|
||||
#[inline]
|
||||
fn new(words: &'a [Word]) -> BitIter<'a, T> {
|
||||
// We initialize `word` and `offset` to degenerate values. On the first
|
||||
// call to `next()` we will fall through to getting the first word from
|
||||
// `iter`, which sets `word` to the first word (if there is one) and
|
||||
// `offset` to 0. Doing it this way saves us from having to maintain
|
||||
// additional state about whether we have started.
|
||||
BitIter {
|
||||
word: 0,
|
||||
offset: usize::MAX - (WORD_BITS - 1),
|
||||
iter: words.iter(),
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Idx> Iterator for BitIter<'a, T> {
|
||||
type Item = T;
|
||||
fn next(&mut self) -> Option<T> {
|
||||
loop {
|
||||
if self.word != 0 {
|
||||
// Get the position of the next set bit in the current word,
|
||||
// then clear the bit.
|
||||
let bit_pos = self.word.trailing_zeros() as usize;
|
||||
self.word ^= 1 << bit_pos;
|
||||
return Some(T::new(bit_pos + self.offset));
|
||||
}
|
||||
|
||||
// Move onto the next word. `wrapping_add()` is needed to handle
|
||||
// the degenerate initial value given to `offset` in `new()`.
|
||||
self.word = *self.iter.next()?;
|
||||
self.offset = self.offset.wrapping_add(WORD_BITS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A fixed-size bitset type with a partially dense, partially sparse
|
||||
/// representation. The bitset is broken into chunks, and chunks that are all
|
||||
/// zeros or all ones are represented and handled very efficiently.
|
||||
|
|
@ -305,6 +410,9 @@ impl<T: Idx> From<GrowableBitSet<T>> for BitSet<T> {
|
|||
/// some stretches with lots of 0s and 1s mixed in a way that causes trouble
|
||||
/// for `IntervalSet`.
|
||||
///
|
||||
/// Best used via `MixedBitSet`, rather than directly, because `MixedBitSet`
|
||||
/// has better performance for small bitsets.
|
||||
///
|
||||
/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also
|
||||
/// just be `usize`.
|
||||
///
|
||||
|
|
@ -958,117 +1066,12 @@ fn sequential_update<T: Idx>(
|
|||
it.fold(false, |changed, elem| self_update(elem) | changed)
|
||||
}
|
||||
|
||||
impl<T> Clone for BitSet<T> {
|
||||
fn clone(&self) -> Self {
|
||||
BitSet { domain_size: self.domain_size, words: self.words.clone(), marker: PhantomData }
|
||||
}
|
||||
|
||||
fn clone_from(&mut self, from: &Self) {
|
||||
self.domain_size = from.domain_size;
|
||||
self.words.clone_from(&from.words);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Idx> fmt::Debug for BitSet<T> {
|
||||
fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
w.debug_list().entries(self.iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Idx> fmt::Debug for ChunkedBitSet<T> {
|
||||
fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
w.debug_list().entries(self.iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Idx> ToString for BitSet<T> {
|
||||
fn to_string(&self) -> String {
|
||||
let mut result = String::new();
|
||||
let mut sep = '[';
|
||||
|
||||
// Note: this is a little endian printout of bytes.
|
||||
|
||||
// i tracks how many bits we have printed so far.
|
||||
let mut i = 0;
|
||||
for word in &self.words {
|
||||
let mut word = *word;
|
||||
for _ in 0..WORD_BYTES {
|
||||
// for each byte in `word`:
|
||||
let remain = self.domain_size - i;
|
||||
// If less than a byte remains, then mask just that many bits.
|
||||
let mask = if remain <= 8 { (1 << remain) - 1 } else { 0xFF };
|
||||
assert!(mask <= 0xFF);
|
||||
let byte = word & mask;
|
||||
|
||||
result.push_str(&format!("{sep}{byte:02x}"));
|
||||
|
||||
if remain <= 8 {
|
||||
break;
|
||||
}
|
||||
word >>= 8;
|
||||
i += 8;
|
||||
sep = '-';
|
||||
}
|
||||
sep = '|';
|
||||
}
|
||||
result.push(']');
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BitIter<'a, T: Idx> {
|
||||
/// A copy of the current word, but with any already-visited bits cleared.
|
||||
/// (This lets us use `trailing_zeros()` to find the next set bit.) When it
|
||||
/// is reduced to 0, we move onto the next word.
|
||||
word: Word,
|
||||
|
||||
/// The offset (measured in bits) of the current word.
|
||||
offset: usize,
|
||||
|
||||
/// Underlying iterator over the words.
|
||||
iter: slice::Iter<'a, Word>,
|
||||
|
||||
marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: Idx> BitIter<'a, T> {
|
||||
#[inline]
|
||||
fn new(words: &'a [Word]) -> BitIter<'a, T> {
|
||||
// We initialize `word` and `offset` to degenerate values. On the first
|
||||
// call to `next()` we will fall through to getting the first word from
|
||||
// `iter`, which sets `word` to the first word (if there is one) and
|
||||
// `offset` to 0. Doing it this way saves us from having to maintain
|
||||
// additional state about whether we have started.
|
||||
BitIter {
|
||||
word: 0,
|
||||
offset: usize::MAX - (WORD_BITS - 1),
|
||||
iter: words.iter(),
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Idx> Iterator for BitIter<'a, T> {
|
||||
type Item = T;
|
||||
fn next(&mut self) -> Option<T> {
|
||||
loop {
|
||||
if self.word != 0 {
|
||||
// Get the position of the next set bit in the current word,
|
||||
// then clear the bit.
|
||||
let bit_pos = self.word.trailing_zeros() as usize;
|
||||
self.word ^= 1 << bit_pos;
|
||||
return Some(T::new(bit_pos + self.offset));
|
||||
}
|
||||
|
||||
// Move onto the next word. `wrapping_add()` is needed to handle
|
||||
// the degenerate initial value given to `offset` in `new()`.
|
||||
self.word = *self.iter.next()?;
|
||||
self.offset = self.offset.wrapping_add(WORD_BITS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bitwise<Op>(out_vec: &mut [Word], in_vec: &[Word], op: Op) -> bool
|
||||
where
|
||||
|
|
@ -1106,6 +1109,158 @@ where
|
|||
false
|
||||
}
|
||||
|
||||
/// A bitset with a mixed representation, using `BitSet` for small and medium
|
||||
/// bitsets, and `ChunkedBitSet` for large bitsets, i.e. those with enough bits
|
||||
/// for at least two chunks. This is a good choice for many bitsets that can
|
||||
/// have large domain sizes (e.g. 5000+).
|
||||
///
|
||||
/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also
|
||||
/// just be `usize`.
|
||||
///
|
||||
/// All operations that involve an element will panic if the element is equal
|
||||
/// to or greater than the domain size. All operations that involve two bitsets
|
||||
/// will panic if the bitsets have differing domain sizes.
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum MixedBitSet<T> {
|
||||
Small(BitSet<T>),
|
||||
Large(ChunkedBitSet<T>),
|
||||
}
|
||||
|
||||
impl<T> MixedBitSet<T> {
|
||||
pub fn domain_size(&self) -> usize {
|
||||
match self {
|
||||
MixedBitSet::Small(set) => set.domain_size(),
|
||||
MixedBitSet::Large(set) => set.domain_size(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Idx> MixedBitSet<T> {
|
||||
#[inline]
|
||||
pub fn new_empty(domain_size: usize) -> MixedBitSet<T> {
|
||||
if domain_size <= CHUNK_BITS {
|
||||
MixedBitSet::Small(BitSet::new_empty(domain_size))
|
||||
} else {
|
||||
MixedBitSet::Large(ChunkedBitSet::new_empty(domain_size))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
MixedBitSet::Small(set) => set.is_empty(),
|
||||
MixedBitSet::Large(set) => set.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains(&self, elem: T) -> bool {
|
||||
match self {
|
||||
MixedBitSet::Small(set) => set.contains(elem),
|
||||
MixedBitSet::Large(set) => set.contains(elem),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn insert(&mut self, elem: T) -> bool {
|
||||
match self {
|
||||
MixedBitSet::Small(set) => set.insert(elem),
|
||||
MixedBitSet::Large(set) => set.insert(elem),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_all(&mut self) {
|
||||
match self {
|
||||
MixedBitSet::Small(set) => set.insert_all(),
|
||||
MixedBitSet::Large(set) => set.insert_all(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn remove(&mut self, elem: T) -> bool {
|
||||
match self {
|
||||
MixedBitSet::Small(set) => set.remove(elem),
|
||||
MixedBitSet::Large(set) => set.remove(elem),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> MixedBitIter<'_, T> {
|
||||
match self {
|
||||
MixedBitSet::Small(set) => MixedBitIter::Small(set.iter()),
|
||||
MixedBitSet::Large(set) => MixedBitIter::Large(set.iter()),
|
||||
}
|
||||
}
|
||||
|
||||
bit_relations_inherent_impls! {}
|
||||
}
|
||||
|
||||
impl<T> Clone for MixedBitSet<T> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
MixedBitSet::Small(set) => MixedBitSet::Small(set.clone()),
|
||||
MixedBitSet::Large(set) => MixedBitSet::Large(set.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
/// WARNING: this implementation of clone_from may panic if the two
|
||||
/// bitsets have different domain sizes. This constraint is not inherent to
|
||||
/// `clone_from`, but it works with the existing call sites and allows a
|
||||
/// faster implementation, which is important because this function is hot.
|
||||
fn clone_from(&mut self, from: &Self) {
|
||||
match (self, from) {
|
||||
(MixedBitSet::Small(set), MixedBitSet::Small(from)) => set.clone_from(from),
|
||||
(MixedBitSet::Large(set), MixedBitSet::Large(from)) => set.clone_from(from),
|
||||
_ => panic!("MixedBitSet size mismatch"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Idx> BitRelations<MixedBitSet<T>> for MixedBitSet<T> {
|
||||
fn union(&mut self, other: &MixedBitSet<T>) -> bool {
|
||||
match (self, other) {
|
||||
(MixedBitSet::Small(set), MixedBitSet::Small(other)) => set.union(other),
|
||||
(MixedBitSet::Large(set), MixedBitSet::Large(other)) => set.union(other),
|
||||
_ => panic!("MixedBitSet size mismatch"),
|
||||
}
|
||||
}
|
||||
|
||||
fn subtract(&mut self, other: &MixedBitSet<T>) -> bool {
|
||||
match (self, other) {
|
||||
(MixedBitSet::Small(set), MixedBitSet::Small(other)) => set.subtract(other),
|
||||
(MixedBitSet::Large(set), MixedBitSet::Large(other)) => set.subtract(other),
|
||||
_ => panic!("MixedBitSet size mismatch"),
|
||||
}
|
||||
}
|
||||
|
||||
fn intersect(&mut self, _other: &MixedBitSet<T>) -> bool {
|
||||
unimplemented!("implement if/when necessary");
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Idx> fmt::Debug for MixedBitSet<T> {
|
||||
fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
MixedBitSet::Small(set) => set.fmt(w),
|
||||
MixedBitSet::Large(set) => set.fmt(w),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum MixedBitIter<'a, T: Idx> {
|
||||
Small(BitIter<'a, T>),
|
||||
Large(ChunkedBitIter<'a, T>),
|
||||
}
|
||||
|
||||
impl<'a, T: Idx> Iterator for MixedBitIter<'a, T> {
|
||||
type Item = T;
|
||||
fn next(&mut self) -> Option<T> {
|
||||
match self {
|
||||
MixedBitIter::Small(iter) => iter.next(),
|
||||
MixedBitIter::Large(iter) => iter.next(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A resizable bitset type with a dense representation.
|
||||
///
|
||||
/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also
|
||||
|
|
@ -1374,7 +1529,7 @@ impl<R: Idx, C: Idx> fmt::Debug for BitMatrix<R, C> {
|
|||
/// sparse representation.
|
||||
///
|
||||
/// Initially, every row has no explicit representation. If any bit within a
|
||||
/// row is set, the entire row is instantiated as `Some(<ChunkedBitSet>)`.
|
||||
/// row is set, the entire row is instantiated as `Some(<BitSet>)`.
|
||||
/// Furthermore, any previously uninstantiated rows prior to it will be
|
||||
/// instantiated as `None`. Those prior rows may themselves become fully
|
||||
/// instantiated later on if any of their bits are set.
|
||||
|
|
@ -1388,7 +1543,7 @@ where
|
|||
C: Idx,
|
||||
{
|
||||
num_columns: usize,
|
||||
rows: IndexVec<R, Option<ChunkedBitSet<C>>>,
|
||||
rows: IndexVec<R, Option<BitSet<C>>>,
|
||||
}
|
||||
|
||||
impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
|
||||
|
|
@ -1397,10 +1552,10 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
|
|||
Self { num_columns, rows: IndexVec::new() }
|
||||
}
|
||||
|
||||
fn ensure_row(&mut self, row: R) -> &mut ChunkedBitSet<C> {
|
||||
// Instantiate any missing rows up to and including row `row` with an empty ChunkedBitSet.
|
||||
// Then replace row `row` with a full ChunkedBitSet if necessary.
|
||||
self.rows.get_or_insert_with(row, || ChunkedBitSet::new_empty(self.num_columns))
|
||||
fn ensure_row(&mut self, row: R) -> &mut BitSet<C> {
|
||||
// Instantiate any missing rows up to and including row `row` with an empty `BitSet`.
|
||||
// Then replace row `row` with a full `BitSet` if necessary.
|
||||
self.rows.get_or_insert_with(row, || BitSet::new_empty(self.num_columns))
|
||||
}
|
||||
|
||||
/// Sets the cell at `(row, column)` to true. Put another way, insert
|
||||
|
|
@ -1474,7 +1629,7 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
|
|||
self.row(row).into_iter().flat_map(|r| r.iter())
|
||||
}
|
||||
|
||||
pub fn row(&self, row: R) -> Option<&ChunkedBitSet<C>> {
|
||||
pub fn row(&self, row: R) -> Option<&BitSet<C>> {
|
||||
self.rows.get(row)?.as_ref()
|
||||
}
|
||||
|
||||
|
|
@ -1484,7 +1639,7 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
|
|||
/// Returns true if the row was changed.
|
||||
pub fn intersect_row<Set>(&mut self, row: R, set: &Set) -> bool
|
||||
where
|
||||
ChunkedBitSet<C>: BitRelations<Set>,
|
||||
BitSet<C>: BitRelations<Set>,
|
||||
{
|
||||
match self.rows.get_mut(row) {
|
||||
Some(Some(row)) => row.intersect(set),
|
||||
|
|
@ -1498,7 +1653,7 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
|
|||
/// Returns true if the row was changed.
|
||||
pub fn subtract_row<Set>(&mut self, row: R, set: &Set) -> bool
|
||||
where
|
||||
ChunkedBitSet<C>: BitRelations<Set>,
|
||||
BitSet<C>: BitRelations<Set>,
|
||||
{
|
||||
match self.rows.get_mut(row) {
|
||||
Some(Some(row)) => row.subtract(set),
|
||||
|
|
@ -1512,7 +1667,7 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
|
|||
/// Returns true if the row was changed.
|
||||
pub fn union_row<Set>(&mut self, row: R, set: &Set) -> bool
|
||||
where
|
||||
ChunkedBitSet<C>: BitRelations<Set>,
|
||||
BitSet<C>: BitRelations<Set>,
|
||||
{
|
||||
self.ensure_row(row).union(set)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -503,15 +503,15 @@ fn sparse_matrix_operations() {
|
|||
matrix.insert(2, 99);
|
||||
matrix.insert(4, 0);
|
||||
|
||||
let mut disjoint: ChunkedBitSet<usize> = ChunkedBitSet::new_empty(100);
|
||||
let mut disjoint: BitSet<usize> = BitSet::new_empty(100);
|
||||
disjoint.insert(33);
|
||||
|
||||
let mut superset = ChunkedBitSet::new_empty(100);
|
||||
let mut superset = BitSet::new_empty(100);
|
||||
superset.insert(22);
|
||||
superset.insert(75);
|
||||
superset.insert(33);
|
||||
|
||||
let mut subset = ChunkedBitSet::new_empty(100);
|
||||
let mut subset = BitSet::new_empty(100);
|
||||
subset.insert(22);
|
||||
|
||||
// SparseBitMatrix::remove
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ pub mod unescape;
|
|||
mod tests;
|
||||
|
||||
use unicode_properties::UnicodeEmoji;
|
||||
pub use unicode_xid::UNICODE_VERSION as UNICODE_XID_VERSION;
|
||||
|
||||
use self::LiteralKind::*;
|
||||
use self::TokenKind::*;
|
||||
|
|
|
|||
|
|
@ -359,6 +359,7 @@ lint_improper_ctypes_128bit = 128-bit integers don't currently have a known stab
|
|||
lint_improper_ctypes_array_help = consider passing a pointer to the array
|
||||
|
||||
lint_improper_ctypes_array_reason = passing raw arrays by value is not FFI-safe
|
||||
lint_improper_ctypes_box = box cannot be represented as a single pointer
|
||||
|
||||
lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead
|
||||
|
||||
|
|
@ -376,9 +377,7 @@ lint_improper_ctypes_enum_repr_help =
|
|||
lint_improper_ctypes_enum_repr_reason = enum has no representation hint
|
||||
lint_improper_ctypes_fnptr_help = consider using an `extern fn(...) -> ...` function pointer instead
|
||||
|
||||
lint_improper_ctypes_fnptr_indirect_reason = the function pointer to `{$ty}` is FFI-unsafe due to `{$inner_ty}`
|
||||
lint_improper_ctypes_fnptr_reason = this function pointer has Rust-specific calling convention
|
||||
|
||||
lint_improper_ctypes_non_exhaustive = this enum is non-exhaustive
|
||||
lint_improper_ctypes_non_exhaustive_variant = this enum has non-exhaustive variants
|
||||
|
||||
|
|
@ -389,11 +388,7 @@ lint_improper_ctypes_opaque = opaque types have no C equivalent
|
|||
lint_improper_ctypes_pat_help = consider using the base type instead
|
||||
|
||||
lint_improper_ctypes_pat_reason = pattern types have no C equivalent
|
||||
|
||||
lint_improper_ctypes_sized_ptr_to_unsafe_type =
|
||||
this reference (`{$ty}`) is ABI-compatible with a C pointer, but `{$inner_ty}` itself does not have a C layout
|
||||
|
||||
lint_improper_ctypes_slice_help = consider using a raw pointer to the slice's first element (and a length) instead
|
||||
lint_improper_ctypes_slice_help = consider using a raw pointer instead
|
||||
|
||||
lint_improper_ctypes_slice_reason = slices have no C equivalent
|
||||
lint_improper_ctypes_str_help = consider using `*const u8` and a length instead
|
||||
|
|
@ -419,10 +414,6 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re
|
|||
lint_improper_ctypes_union_layout_reason = this union has unspecified layout
|
||||
lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive
|
||||
|
||||
lint_improper_ctypes_unsized_box = this box for an unsized type contains metadata, which makes it incompatible with a C pointer
|
||||
lint_improper_ctypes_unsized_ptr = this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer
|
||||
lint_improper_ctypes_unsized_ref = this reference to an unsized type contains metadata, which makes it incompatible with a C pointer
|
||||
|
||||
lint_incomplete_include =
|
||||
include macro expected single expression in source
|
||||
|
||||
|
|
|
|||
|
|
@ -625,6 +625,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
|
|||
cx.param_env,
|
||||
ty,
|
||||
traits::ObligationCause::misc(item.span, item.owner_id.def_id),
|
||||
hir::Safety::Safe,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1851,44 +1851,13 @@ pub(crate) struct UnpredictableFunctionPointerComparisonsSuggestion<'a> {
|
|||
pub right: Span,
|
||||
}
|
||||
|
||||
pub(crate) struct ImproperCTypesLayer<'a> {
|
||||
pub ty: Ty<'a>,
|
||||
pub inner_ty: Option<Ty<'a>>,
|
||||
pub note: DiagMessage,
|
||||
pub span_note: Option<Span>,
|
||||
pub help: Option<DiagMessage>,
|
||||
}
|
||||
|
||||
impl<'a> Subdiagnostic for ImproperCTypesLayer<'a> {
|
||||
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
|
||||
self,
|
||||
diag: &mut Diag<'_, G>,
|
||||
f: &F,
|
||||
) {
|
||||
diag.arg("ty", self.ty);
|
||||
if let Some(ty) = self.inner_ty {
|
||||
diag.arg("inner_ty", ty);
|
||||
}
|
||||
|
||||
if let Some(help) = self.help {
|
||||
let msg = f(diag, help.into());
|
||||
diag.help(msg);
|
||||
}
|
||||
|
||||
let msg = f(diag, self.note.into());
|
||||
diag.note(msg);
|
||||
if let Some(note) = self.span_note {
|
||||
let msg = f(diag, fluent::lint_note.into());
|
||||
diag.span_note(note, msg);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ImproperCTypes<'a> {
|
||||
pub ty: Ty<'a>,
|
||||
pub desc: &'a str,
|
||||
pub label: Span,
|
||||
pub reasons: Vec<ImproperCTypesLayer<'a>>,
|
||||
pub help: Option<DiagMessage>,
|
||||
pub note: DiagMessage,
|
||||
pub span_note: Option<Span>,
|
||||
}
|
||||
|
||||
// Used because of the complexity of Option<DiagMessage>, DiagMessage, and Option<Span>
|
||||
|
|
@ -1898,8 +1867,12 @@ impl<'a> LintDiagnostic<'a, ()> for ImproperCTypes<'_> {
|
|||
diag.arg("ty", self.ty);
|
||||
diag.arg("desc", self.desc);
|
||||
diag.span_label(self.label, fluent::lint_label);
|
||||
for reason in self.reasons.into_iter() {
|
||||
diag.subdiagnostic(reason);
|
||||
if let Some(help) = self.help {
|
||||
diag.help(help);
|
||||
}
|
||||
diag.note(self.note);
|
||||
if let Some(note) = self.span_note {
|
||||
diag.span_note(note, fluent::lint_note);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ mod improper_ctypes;
|
|||
use crate::lints::{
|
||||
AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
|
||||
AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
|
||||
AtomicOrderingStore, ImproperCTypes, ImproperCTypesLayer, InvalidAtomicOrderingDiag,
|
||||
InvalidNanComparisons, InvalidNanComparisonsSuggestion,
|
||||
UnpredictableFunctionPointerComparisons, UnpredictableFunctionPointerComparisonsSuggestion,
|
||||
UnusedComparisons, VariantSizeDifferencesDiag,
|
||||
AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons,
|
||||
InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons,
|
||||
UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons,
|
||||
VariantSizeDifferencesDiag,
|
||||
};
|
||||
use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
|
||||
|
||||
|
|
@ -727,109 +727,7 @@ struct CTypesVisitorState<'tcx> {
|
|||
enum FfiResult<'tcx> {
|
||||
FfiSafe,
|
||||
FfiPhantom(Ty<'tcx>),
|
||||
FfiUnsafe {
|
||||
ty: Ty<'tcx>,
|
||||
reason: DiagMessage,
|
||||
help: Option<DiagMessage>,
|
||||
},
|
||||
FfiUnsafeWrapper {
|
||||
ty: Ty<'tcx>,
|
||||
reason: DiagMessage,
|
||||
help: Option<DiagMessage>,
|
||||
wrapped: Box<FfiResult<'tcx>>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Determine if a type is sized or not, and wether it affects references/pointers/boxes to it
|
||||
#[derive(Clone, Copy)]
|
||||
enum TypeSizedness {
|
||||
/// type of definite size (pointers are C-compatible)
|
||||
Definite,
|
||||
/// unsized type because it includes an opaque/foreign type (pointers are C-compatible)
|
||||
UnsizedWithExternType,
|
||||
/// unsized type for other reasons (slice, string, dyn Trait, closure, ...) (pointers are not C-compatible)
|
||||
UnsizedWithMetadata,
|
||||
}
|
||||
|
||||
/// Is this type unsized because it contains (or is) a foreign type?
|
||||
/// (Returns Err if the type happens to be sized after all)
|
||||
fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> TypeSizedness {
|
||||
let tcx = cx.tcx;
|
||||
|
||||
if ty.is_sized(tcx, cx.typing_env()) {
|
||||
TypeSizedness::Definite
|
||||
} else {
|
||||
match ty.kind() {
|
||||
ty::Slice(_) => TypeSizedness::UnsizedWithMetadata,
|
||||
ty::Str => TypeSizedness::UnsizedWithMetadata,
|
||||
ty::Dynamic(..) => TypeSizedness::UnsizedWithMetadata,
|
||||
ty::Foreign(..) => TypeSizedness::UnsizedWithExternType,
|
||||
// While opaque types are checked for earlier, if a projection in a struct field
|
||||
// normalizes to an opaque type, then it will reach this branch.
|
||||
ty::Alias(ty::Opaque, ..) => todo!("We... don't know enough about this type yet?"),
|
||||
ty::Adt(def, args) => {
|
||||
// for now assume: boxes and phantoms don't mess with this
|
||||
match def.adt_kind() {
|
||||
AdtKind::Union | AdtKind::Enum => {
|
||||
bug!("unions and enums are necessarily sized")
|
||||
}
|
||||
AdtKind::Struct => {
|
||||
if let Some(sym::cstring_type | sym::cstr_type) =
|
||||
tcx.get_diagnostic_name(def.did())
|
||||
{
|
||||
return TypeSizedness::UnsizedWithMetadata;
|
||||
}
|
||||
// FIXME: how do we deal with non-exhaustive unsized structs/unions?
|
||||
|
||||
if def.non_enum_variant().fields.is_empty() {
|
||||
bug!("an empty struct is necessarily sized");
|
||||
}
|
||||
|
||||
let variant = def.non_enum_variant();
|
||||
|
||||
// only the last field may be unsized
|
||||
let n_fields = variant.fields.len();
|
||||
let last_field = &variant.fields[(n_fields - 1).into()];
|
||||
let field_ty = last_field.ty(cx.tcx, args);
|
||||
let field_ty = cx
|
||||
.tcx
|
||||
.try_normalize_erasing_regions(cx.typing_env(), field_ty)
|
||||
.unwrap_or(field_ty);
|
||||
match get_type_sizedness(cx, field_ty) {
|
||||
s @ (TypeSizedness::UnsizedWithMetadata
|
||||
| TypeSizedness::UnsizedWithExternType) => s,
|
||||
TypeSizedness::Definite => {
|
||||
bug!("failed to find the reason why struct `{:?}` is unsized", ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ty::Tuple(tuple) => {
|
||||
// only the last field may be unsized
|
||||
let n_fields = tuple.len();
|
||||
let field_ty: Ty<'tcx> = tuple[n_fields - 1];
|
||||
//let field_ty = last_field.ty(cx.tcx, args);
|
||||
let field_ty = cx
|
||||
.tcx
|
||||
.try_normalize_erasing_regions(cx.typing_env(), field_ty)
|
||||
.unwrap_or(field_ty);
|
||||
match get_type_sizedness(cx, field_ty) {
|
||||
s @ (TypeSizedness::UnsizedWithMetadata
|
||||
| TypeSizedness::UnsizedWithExternType) => s,
|
||||
TypeSizedness::Definite => {
|
||||
bug!("failed to find the reason why tuple `{:?}` is unsized", ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
ty => {
|
||||
bug!(
|
||||
"we shouldn't be trying to determine if this is unsized for a reason or another: `{:?}`",
|
||||
ty
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option<DiagMessage> },
|
||||
}
|
||||
|
||||
pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
|
||||
|
|
@ -866,7 +764,7 @@ fn ty_is_known_nonnull<'tcx>(
|
|||
match ty.kind() {
|
||||
ty::FnPtr(..) => true,
|
||||
ty::Ref(..) => true,
|
||||
ty::Adt(def, _) if def.is_box() => true,
|
||||
ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true,
|
||||
ty::Adt(def, args) if def.repr().transparent() && !def.is_union() => {
|
||||
let marked_non_null = nonnull_optimization_guaranteed(tcx, *def);
|
||||
|
||||
|
|
@ -1035,13 +933,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
/// Check if the type is array and emit an unsafe type lint.
|
||||
fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
|
||||
if let ty::Array(..) = ty.kind() {
|
||||
self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer {
|
||||
self.emit_ffi_unsafe_type_lint(
|
||||
ty,
|
||||
note: fluent::lint_improper_ctypes_array_reason,
|
||||
help: Some(fluent::lint_improper_ctypes_array_help),
|
||||
inner_ty: None,
|
||||
span_note: None,
|
||||
}]);
|
||||
sp,
|
||||
fluent::lint_improper_ctypes_array_reason,
|
||||
Some(fluent::lint_improper_ctypes_array_help),
|
||||
);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
|
@ -1098,9 +995,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
all_phantom &= match self.check_field_type_for_ffi(acc, field, args) {
|
||||
FfiSafe => false,
|
||||
// `()` fields are FFI-safe!
|
||||
FfiUnsafe { ty, .. } | FfiUnsafeWrapper { ty, .. } if ty.is_unit() => false,
|
||||
FfiUnsafe { ty, .. } if ty.is_unit() => false,
|
||||
FfiPhantom(..) => true,
|
||||
r @ (FfiUnsafe { .. } | FfiUnsafeWrapper { .. }) => return r,
|
||||
r @ FfiUnsafe { .. } => return r,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1134,47 +1031,16 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
|
||||
match *ty.kind() {
|
||||
ty::Adt(def, args) => {
|
||||
if let Some(inner_ty) = ty.boxed_ty() {
|
||||
if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite =
|
||||
get_type_sizedness(self.cx, inner_ty)
|
||||
{
|
||||
// discussion on declaration vs definition:
|
||||
// see the `ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _)` arm
|
||||
// of this `match *ty.kind()` block
|
||||
if matches!(self.mode, CItemKind::Definition) {
|
||||
return FfiSafe;
|
||||
} else {
|
||||
let inner_res = self.check_type_for_ffi(acc, inner_ty);
|
||||
return match inner_res {
|
||||
FfiUnsafe { .. } | FfiUnsafeWrapper { .. } => FfiUnsafeWrapper {
|
||||
ty,
|
||||
reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type,
|
||||
wrapped: Box::new(inner_res),
|
||||
help: None,
|
||||
},
|
||||
_ => inner_res,
|
||||
};
|
||||
}
|
||||
if let Some(boxed) = ty.boxed_ty()
|
||||
&& matches!(self.mode, CItemKind::Definition)
|
||||
{
|
||||
if boxed.is_sized(tcx, self.cx.typing_env()) {
|
||||
return FfiSafe;
|
||||
} else {
|
||||
let help = match inner_ty.kind() {
|
||||
ty::Str => Some(fluent::lint_improper_ctypes_str_help),
|
||||
ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help),
|
||||
ty::Adt(def, _)
|
||||
if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union)
|
||||
&& matches!(
|
||||
tcx.get_diagnostic_name(def.did()),
|
||||
Some(sym::cstring_type | sym::cstr_type)
|
||||
)
|
||||
&& !acc.base_ty.is_mutable_ptr() =>
|
||||
{
|
||||
Some(fluent::lint_improper_ctypes_cstr_help)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
return FfiUnsafe {
|
||||
ty,
|
||||
reason: fluent::lint_improper_ctypes_unsized_box,
|
||||
help,
|
||||
reason: fluent::lint_improper_ctypes_box,
|
||||
help: None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1330,6 +1196,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
help: Some(fluent::lint_improper_ctypes_tuple_help),
|
||||
},
|
||||
|
||||
ty::RawPtr(ty, _) | ty::Ref(_, ty, _)
|
||||
if {
|
||||
matches!(self.mode, CItemKind::Definition)
|
||||
&& ty.is_sized(self.cx.tcx, self.cx.typing_env())
|
||||
} =>
|
||||
{
|
||||
FfiSafe
|
||||
}
|
||||
|
||||
ty::RawPtr(ty, _)
|
||||
if match ty.kind() {
|
||||
ty::Tuple(tuple) => tuple.is_empty(),
|
||||
|
|
@ -1339,70 +1214,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
FfiSafe
|
||||
}
|
||||
|
||||
ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _) => {
|
||||
if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite =
|
||||
get_type_sizedness(self.cx, inner_ty)
|
||||
{
|
||||
// there's a nuance on what this lint should do for
|
||||
// function definitions (`extern "C" fn fn_name(...) {...}`)
|
||||
// versus declarations (`unsafe extern "C" {fn fn_name(...);}`).
|
||||
// This is touched upon in https://github.com/rust-lang/rust/issues/66220
|
||||
// and https://github.com/rust-lang/rust/pull/72700
|
||||
//
|
||||
// The big question is: what does "ABI safety" mean? if you have something translated to a C pointer
|
||||
// (which has a stable layout) but points to FFI-unsafe type, is it safe?
|
||||
// On one hand, the function's ABI will match that of a similar C-declared function API,
|
||||
// on the other, dereferencing the pointer on the other side of the FFI boundary will be painful.
|
||||
// In this code, the opinion on is split between function declarations and function definitions,
|
||||
// with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type.
|
||||
// For declarations, we see this as unsafe, but for definitions, we see this as safe.
|
||||
//
|
||||
// For extern function declarations, the actual definition of the function is written somewhere else,
|
||||
// meaning the declaration is free to express this opaqueness with an extern type (opaque caller-side) or a std::ffi::c_void (opaque callee-side)
|
||||
// For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side,
|
||||
// and having the full type information is necessary to compile the function.
|
||||
if matches!(self.mode, CItemKind::Definition) {
|
||||
return FfiSafe;
|
||||
} else if matches!(ty.kind(), ty::RawPtr(..))
|
||||
&& matches!(inner_ty.kind(), ty::Tuple(tuple) if tuple.is_empty())
|
||||
{
|
||||
FfiSafe
|
||||
} else {
|
||||
let inner_res = self.check_type_for_ffi(acc, inner_ty);
|
||||
return match inner_res {
|
||||
FfiSafe => inner_res,
|
||||
_ => FfiUnsafeWrapper {
|
||||
ty,
|
||||
reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type,
|
||||
wrapped: Box::new(inner_res),
|
||||
help: None,
|
||||
},
|
||||
};
|
||||
}
|
||||
} else {
|
||||
let help = match inner_ty.kind() {
|
||||
ty::Str => Some(fluent::lint_improper_ctypes_str_help),
|
||||
ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help),
|
||||
ty::Adt(def, _)
|
||||
if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union)
|
||||
&& matches!(
|
||||
tcx.get_diagnostic_name(def.did()),
|
||||
Some(sym::cstring_type | sym::cstr_type)
|
||||
)
|
||||
&& !acc.base_ty.is_mutable_ptr() =>
|
||||
{
|
||||
Some(fluent::lint_improper_ctypes_cstr_help)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let reason = match ty.kind() {
|
||||
ty::RawPtr(..) => fluent::lint_improper_ctypes_unsized_ptr,
|
||||
ty::Ref(..) => fluent::lint_improper_ctypes_unsized_ref,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
FfiUnsafe { ty, reason, help }
|
||||
}
|
||||
}
|
||||
ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.check_type_for_ffi(acc, ty),
|
||||
|
||||
ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty),
|
||||
|
||||
|
|
@ -1420,14 +1232,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
for arg in sig.inputs() {
|
||||
match self.check_type_for_ffi(acc, *arg) {
|
||||
FfiSafe => {}
|
||||
r => {
|
||||
return FfiUnsafeWrapper {
|
||||
ty,
|
||||
reason: fluent::lint_improper_ctypes_fnptr_indirect_reason,
|
||||
help: None,
|
||||
wrapped: Box::new(r),
|
||||
};
|
||||
}
|
||||
r => return r,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1436,15 +1241,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
return FfiSafe;
|
||||
}
|
||||
|
||||
match self.check_type_for_ffi(acc, ret_ty) {
|
||||
r @ (FfiSafe | FfiPhantom(_)) => r,
|
||||
r => FfiUnsafeWrapper {
|
||||
ty: ty.clone(),
|
||||
reason: fluent::lint_improper_ctypes_fnptr_indirect_reason,
|
||||
help: None,
|
||||
wrapped: Box::new(r),
|
||||
},
|
||||
}
|
||||
self.check_type_for_ffi(acc, ret_ty)
|
||||
}
|
||||
|
||||
ty::Foreign(..) => FfiSafe,
|
||||
|
|
@ -1481,7 +1278,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
&mut self,
|
||||
ty: Ty<'tcx>,
|
||||
sp: Span,
|
||||
mut reasons: Vec<ImproperCTypesLayer<'tcx>>,
|
||||
note: DiagMessage,
|
||||
help: Option<DiagMessage>,
|
||||
) {
|
||||
let lint = match self.mode {
|
||||
CItemKind::Declaration => IMPROPER_CTYPES,
|
||||
|
|
@ -1491,17 +1289,21 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
CItemKind::Declaration => "block",
|
||||
CItemKind::Definition => "fn",
|
||||
};
|
||||
for reason in reasons.iter_mut() {
|
||||
reason.span_note = if let ty::Adt(def, _) = reason.ty.kind()
|
||||
&& let Some(sp) = self.cx.tcx.hir().span_if_local(def.did())
|
||||
{
|
||||
Some(sp)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
self.cx.emit_span_lint(lint, sp, ImproperCTypes { ty, desc, label: sp, reasons });
|
||||
let span_note = if let ty::Adt(def, _) = ty.kind()
|
||||
&& let Some(sp) = self.cx.tcx.hir().span_if_local(def.did())
|
||||
{
|
||||
Some(sp)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.cx.emit_span_lint(lint, sp, ImproperCTypes {
|
||||
ty,
|
||||
desc,
|
||||
label: sp,
|
||||
help,
|
||||
note,
|
||||
span_note,
|
||||
});
|
||||
}
|
||||
|
||||
fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
|
||||
|
|
@ -1530,13 +1332,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
.visit_with(&mut ProhibitOpaqueTypes)
|
||||
.break_value()
|
||||
{
|
||||
self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer {
|
||||
ty,
|
||||
note: fluent::lint_improper_ctypes_opaque,
|
||||
span_note: Some(sp),
|
||||
help: None,
|
||||
inner_ty: None,
|
||||
}]);
|
||||
self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
|
@ -1575,71 +1371,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
match self.check_type_for_ffi(&mut acc, ty) {
|
||||
FfiResult::FfiSafe => {}
|
||||
FfiResult::FfiPhantom(ty) => {
|
||||
self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer {
|
||||
self.emit_ffi_unsafe_type_lint(
|
||||
ty,
|
||||
note: fluent::lint_improper_ctypes_only_phantomdata,
|
||||
span_note: None, // filled later
|
||||
help: None,
|
||||
inner_ty: None,
|
||||
}]);
|
||||
sp,
|
||||
fluent::lint_improper_ctypes_only_phantomdata,
|
||||
None,
|
||||
);
|
||||
}
|
||||
FfiResult::FfiUnsafe { ty, reason, help } => {
|
||||
self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer {
|
||||
ty,
|
||||
help,
|
||||
note: reason,
|
||||
span_note: None, // filled later
|
||||
inner_ty: None,
|
||||
}]);
|
||||
}
|
||||
ffir @ FfiResult::FfiUnsafeWrapper { .. } => {
|
||||
let mut ffiresult_recursor = ControlFlow::Continue(&ffir);
|
||||
let mut cimproper_layers: Vec<ImproperCTypesLayer<'tcx>> = vec![];
|
||||
|
||||
// this whole while block converts the arbitrarily-deep
|
||||
// FfiResult stack to an ImproperCTypesLayer Vec
|
||||
while let ControlFlow::Continue(ref ffir_rec) = ffiresult_recursor {
|
||||
match ffir_rec {
|
||||
FfiResult::FfiPhantom(ty) => {
|
||||
if let Some(layer) = cimproper_layers.last_mut() {
|
||||
layer.inner_ty = Some(ty.clone());
|
||||
}
|
||||
cimproper_layers.push(ImproperCTypesLayer {
|
||||
ty: ty.clone(),
|
||||
inner_ty: None,
|
||||
help: None,
|
||||
note: fluent::lint_improper_ctypes_only_phantomdata,
|
||||
span_note: None, // filled later
|
||||
});
|
||||
ffiresult_recursor = ControlFlow::Break(());
|
||||
}
|
||||
FfiResult::FfiUnsafe { ty, reason, help }
|
||||
| FfiResult::FfiUnsafeWrapper { ty, reason, help, .. } => {
|
||||
if let Some(layer) = cimproper_layers.last_mut() {
|
||||
layer.inner_ty = Some(ty.clone());
|
||||
}
|
||||
cimproper_layers.push(ImproperCTypesLayer {
|
||||
ty: ty.clone(),
|
||||
inner_ty: None,
|
||||
help: help.clone(),
|
||||
note: reason.clone(),
|
||||
span_note: None, // filled later
|
||||
});
|
||||
|
||||
if let FfiResult::FfiUnsafeWrapper { wrapped, .. } = ffir_rec {
|
||||
ffiresult_recursor = ControlFlow::Continue(wrapped.as_ref());
|
||||
} else {
|
||||
ffiresult_recursor = ControlFlow::Break(());
|
||||
}
|
||||
}
|
||||
FfiResult::FfiSafe => {
|
||||
bug!("malformed FfiResult stack: it should be unsafe all the way down")
|
||||
}
|
||||
};
|
||||
}
|
||||
// should always have at least one type
|
||||
let last_ty = cimproper_layers.last().unwrap().ty.clone();
|
||||
self.emit_ffi_unsafe_type_lint(last_ty, sp, cimproper_layers);
|
||||
self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1535,7 +1535,7 @@ extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) {
|
|||
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef)
|
||||
|
||||
extern "C" LLVMSMDiagnosticRef LLVMRustGetSMDiagnostic(LLVMDiagnosticInfoRef DI,
|
||||
unsigned *Cookie) {
|
||||
uint64_t *Cookie) {
|
||||
llvm::DiagnosticInfoSrcMgr *SM =
|
||||
static_cast<llvm::DiagnosticInfoSrcMgr *>(unwrap(DI));
|
||||
*Cookie = SM->getLocCookie();
|
||||
|
|
|
|||
|
|
@ -1104,6 +1104,7 @@ impl<'a> CrateMetadataRef<'a> {
|
|||
name: self.item_name(did.index),
|
||||
vis: self.get_visibility(did.index),
|
||||
safety: self.get_safety(did.index),
|
||||
value: self.get_default_field(did.index),
|
||||
})
|
||||
.collect(),
|
||||
adt_kind,
|
||||
|
|
@ -1169,6 +1170,10 @@ impl<'a> CrateMetadataRef<'a> {
|
|||
self.root.tables.safety.get(self, id).unwrap_or_else(|| self.missing("safety", id))
|
||||
}
|
||||
|
||||
fn get_default_field(self, id: DefIndex) -> Option<DefId> {
|
||||
self.root.tables.default_fields.get(self, id).map(|d| d.decode(self))
|
||||
}
|
||||
|
||||
fn get_trait_item_def_id(self, id: DefIndex) -> Option<DefId> {
|
||||
self.root.tables.trait_item_def_id.get(self, id).map(|d| d.decode_from_cdata(self))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1401,6 +1401,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
continue;
|
||||
}
|
||||
|
||||
if def_kind == DefKind::Field
|
||||
&& let hir::Node::Field(field) = tcx.hir_node_by_def_id(local_id)
|
||||
&& let Some(anon) = field.default
|
||||
{
|
||||
record!(self.tables.default_fields[def_id] <- anon.def_id.to_def_id());
|
||||
}
|
||||
|
||||
if should_encode_span(def_kind) {
|
||||
let def_span = tcx.def_span(local_id);
|
||||
record!(self.tables.def_span[def_id] <- def_span);
|
||||
|
|
|
|||
|
|
@ -450,6 +450,7 @@ define_tables! {
|
|||
trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>,
|
||||
trait_item_def_id: Table<DefIndex, RawDefId>,
|
||||
expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>,
|
||||
default_fields: Table<DefIndex, LazyValue<DefId>>,
|
||||
params_in_repr: Table<DefIndex, LazyValue<BitSet<u32>>>,
|
||||
repr_options: Table<DefIndex, LazyValue<ReprOptions>>,
|
||||
// `def_keys` and `def_path_hashes` represent a lazy version of a
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use rustc_session::config::RemapPathScopeComponents;
|
|||
use rustc_span::{DUMMY_SP, Span};
|
||||
use rustc_type_ir::visit::TypeVisitableExt;
|
||||
|
||||
use super::interpret::ReportedErrorInfo;
|
||||
use crate::mir::interpret::{AllocId, ConstAllocation, ErrorHandled, Scalar, alloc_range};
|
||||
use crate::mir::{Promoted, pretty_print_const_value};
|
||||
use crate::ty::print::{pretty_print_const, with_no_trimmed_paths};
|
||||
|
|
@ -331,7 +332,10 @@ impl<'tcx> Const<'tcx> {
|
|||
ConstKind::Expr(_) => {
|
||||
bug!("Normalization of `ty::ConstKind::Expr` is unimplemented")
|
||||
}
|
||||
_ => Err(tcx.dcx().delayed_bug("Unevaluated `ty::Const` in MIR body").into()),
|
||||
_ => Err(ReportedErrorInfo::non_const_eval_error(
|
||||
tcx.dcx().delayed_bug("Unevaluated `ty::Const` in MIR body"),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
Const::Unevaluated(uneval, _) => {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
|
||||
use rustc_span::Span;
|
||||
|
||||
|
|
@ -310,3 +311,41 @@ pub struct MCDCDecisionSpan {
|
|||
pub decision_depth: u16,
|
||||
pub num_conditions: usize,
|
||||
}
|
||||
|
||||
/// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass
|
||||
/// (for compiler option `-Cinstrument-coverage`), after MIR optimizations
|
||||
/// have had a chance to potentially remove some of them.
|
||||
///
|
||||
/// Used by the `coverage_ids_info` query.
|
||||
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable)]
|
||||
pub struct CoverageIdsInfo {
|
||||
pub counters_seen: BitSet<CounterId>,
|
||||
pub zero_expressions: BitSet<ExpressionId>,
|
||||
}
|
||||
|
||||
impl CoverageIdsInfo {
|
||||
/// Coverage codegen needs to know how many coverage counters are ever
|
||||
/// incremented within a function, so that it can set the `num-counters`
|
||||
/// argument of the `llvm.instrprof.increment` intrinsic.
|
||||
///
|
||||
/// This may be less than the highest counter ID emitted by the
|
||||
/// InstrumentCoverage MIR pass, if the highest-numbered counter increments
|
||||
/// were removed by MIR optimizations.
|
||||
pub fn num_counters_after_mir_opts(&self) -> u32 {
|
||||
// FIXME(Zalathar): Currently this treats an unused counter as "used"
|
||||
// if its ID is less than that of the highest counter that really is
|
||||
// used. Fixing this would require adding a renumbering step somewhere.
|
||||
self.counters_seen.last_set_in(..).map_or(0, |max| max.as_u32() + 1)
|
||||
}
|
||||
|
||||
/// Returns `true` if the given term is known to have a value of zero, taking
|
||||
/// into account knowledge of which counters are unused and which expressions
|
||||
/// are always zero.
|
||||
pub fn is_zero_term(&self, term: CovTerm) -> bool {
|
||||
match term {
|
||||
CovTerm::Zero => true,
|
||||
CovTerm::Counter(id) => !self.counters_seen.contains(id),
|
||||
CovTerm::Expression(id) => self.zero_expressions.contains(id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@ pub enum ErrorHandled {
|
|||
TooGeneric(Span),
|
||||
}
|
||||
|
||||
impl From<ErrorGuaranteed> for ErrorHandled {
|
||||
impl From<ReportedErrorInfo> for ErrorHandled {
|
||||
#[inline]
|
||||
fn from(error: ErrorGuaranteed) -> ErrorHandled {
|
||||
ErrorHandled::Reported(error.into(), DUMMY_SP)
|
||||
fn from(error: ReportedErrorInfo) -> ErrorHandled {
|
||||
ErrorHandled::Reported(error, DUMMY_SP)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -64,6 +64,20 @@ pub struct ReportedErrorInfo {
|
|||
}
|
||||
|
||||
impl ReportedErrorInfo {
|
||||
#[inline]
|
||||
pub fn const_eval_error(error: ErrorGuaranteed) -> ReportedErrorInfo {
|
||||
ReportedErrorInfo { allowed_in_infallible: false, error }
|
||||
}
|
||||
|
||||
/// Use this when the error that led to this is *not* a const-eval error
|
||||
/// (e.g., a layout or type checking error).
|
||||
#[inline]
|
||||
pub fn non_const_eval_error(error: ErrorGuaranteed) -> ReportedErrorInfo {
|
||||
ReportedErrorInfo { allowed_in_infallible: true, error }
|
||||
}
|
||||
|
||||
/// Use this when the error that led to this *is* a const-eval error, but
|
||||
/// we do allow it to occur in infallible constants (e.g., resource exhaustion).
|
||||
#[inline]
|
||||
pub fn allowed_in_infallible(error: ErrorGuaranteed) -> ReportedErrorInfo {
|
||||
ReportedErrorInfo { allowed_in_infallible: true, error }
|
||||
|
|
@ -74,13 +88,6 @@ impl ReportedErrorInfo {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<ErrorGuaranteed> for ReportedErrorInfo {
|
||||
#[inline]
|
||||
fn from(error: ErrorGuaranteed) -> ReportedErrorInfo {
|
||||
ReportedErrorInfo { allowed_in_infallible: false, error }
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<ErrorGuaranteed> for ReportedErrorInfo {
|
||||
#[inline]
|
||||
fn into(self) -> ErrorGuaranteed {
|
||||
|
|
@ -180,12 +187,6 @@ fn print_backtrace(backtrace: &Backtrace) {
|
|||
eprintln!("\n\nAn error occurred in the MIR interpreter:\n{backtrace}");
|
||||
}
|
||||
|
||||
impl From<ErrorGuaranteed> for InterpErrorInfo<'_> {
|
||||
fn from(err: ErrorGuaranteed) -> Self {
|
||||
InterpErrorKind::InvalidProgram(InvalidProgramInfo::AlreadyReported(err.into())).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ErrorHandled> for InterpErrorInfo<'_> {
|
||||
fn from(err: ErrorHandled) -> Self {
|
||||
InterpErrorKind::InvalidProgram(match err {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use tracing::{debug, instrument};
|
|||
|
||||
use super::{
|
||||
ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, GlobalId,
|
||||
ReportedErrorInfo,
|
||||
};
|
||||
use crate::mir;
|
||||
use crate::query::TyCtxtEnsure;
|
||||
|
|
@ -81,7 +82,9 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
// For errors during resolution, we deliberately do not point at the usage site of the constant,
|
||||
// since for these errors the place the constant is used shouldn't matter.
|
||||
Ok(None) => Err(ErrorHandled::TooGeneric(DUMMY_SP)),
|
||||
Err(err) => Err(ErrorHandled::Reported(err.into(), DUMMY_SP)),
|
||||
Err(err) => {
|
||||
Err(ErrorHandled::Reported(ReportedErrorInfo::non_const_eval_error(err), DUMMY_SP))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +141,9 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
// For errors during resolution, we deliberately do not point at the usage site of the constant,
|
||||
// since for these errors the place the constant is used shouldn't matter.
|
||||
Ok(None) => Err(ErrorHandled::TooGeneric(DUMMY_SP)),
|
||||
Err(err) => Err(ErrorHandled::Reported(err.into(), DUMMY_SP)),
|
||||
Err(err) => {
|
||||
Err(ErrorHandled::Reported(ReportedErrorInfo::non_const_eval_error(err), DUMMY_SP))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use rustc_target::spec::SymbolVisibility;
|
|||
use tracing::debug;
|
||||
|
||||
use crate::dep_graph::{DepNode, WorkProduct, WorkProductId};
|
||||
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use crate::ty::{GenericArgs, Instance, InstanceKind, SymbolName, TyCtxt};
|
||||
|
||||
/// Describes how a monomorphization will be instantiated in object files.
|
||||
|
|
@ -104,7 +105,9 @@ impl<'tcx> MonoItem<'tcx> {
|
|||
let entry_def_id = tcx.entry_fn(()).map(|(id, _)| id);
|
||||
// If this function isn't inlined or otherwise has an extern
|
||||
// indicator, then we'll be creating a globally shared version.
|
||||
if tcx.codegen_fn_attrs(instance.def_id()).contains_extern_indicator()
|
||||
let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id());
|
||||
if codegen_fn_attrs.contains_extern_indicator()
|
||||
|| codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED)
|
||||
|| !instance.def.generates_cgu_internal_copy(tcx)
|
||||
|| Some(instance.def_id()) == entry_def_id
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use rustc_abi::{FieldIdx, VariantIdx};
|
|||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_index::bit_set::{BitMatrix, BitSet};
|
||||
use rustc_index::bit_set::BitMatrix;
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
|
||||
use rustc_span::Span;
|
||||
|
|
@ -16,7 +16,6 @@ use rustc_span::symbol::Symbol;
|
|||
use smallvec::SmallVec;
|
||||
|
||||
use super::{ConstValue, SourceInfo};
|
||||
use crate::mir;
|
||||
use crate::ty::fold::fold_regions;
|
||||
use crate::ty::{self, CoroutineArgsExt, OpaqueHiddenType, Ty, TyCtxt};
|
||||
|
||||
|
|
@ -351,30 +350,3 @@ pub struct DestructuredConstant<'tcx> {
|
|||
pub variant: Option<VariantIdx>,
|
||||
pub fields: &'tcx [(ConstValue<'tcx>, Ty<'tcx>)],
|
||||
}
|
||||
|
||||
/// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass
|
||||
/// (for compiler option `-Cinstrument-coverage`), after MIR optimizations
|
||||
/// have had a chance to potentially remove some of them.
|
||||
///
|
||||
/// Used by the `coverage_ids_info` query.
|
||||
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable)]
|
||||
pub struct CoverageIdsInfo {
|
||||
pub counters_seen: BitSet<mir::coverage::CounterId>,
|
||||
pub expressions_seen: BitSet<mir::coverage::ExpressionId>,
|
||||
}
|
||||
|
||||
impl CoverageIdsInfo {
|
||||
/// Coverage codegen needs to know how many coverage counters are ever
|
||||
/// incremented within a function, so that it can set the `num-counters`
|
||||
/// argument of the `llvm.instrprof.increment` intrinsic.
|
||||
///
|
||||
/// This may be less than the highest counter ID emitted by the
|
||||
/// InstrumentCoverage MIR pass, if the highest-numbered counter increments
|
||||
/// were removed by MIR optimizations.
|
||||
pub fn num_counters_after_mir_opts(&self) -> u32 {
|
||||
// FIXME(Zalathar): Currently this treats an unused counter as "used"
|
||||
// if its ID is less than that of the highest counter that really is
|
||||
// used. Fixing this would require adding a renumbering step somewhere.
|
||||
self.counters_seen.last_set_in(..).map_or(0, |max| max.as_u32() + 1)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -581,7 +581,7 @@ rustc_queries! {
|
|||
/// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass
|
||||
/// (for compiler option `-Cinstrument-coverage`), after MIR optimizations
|
||||
/// have had a chance to potentially remove some of them.
|
||||
query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> &'tcx mir::CoverageIdsInfo {
|
||||
query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> &'tcx mir::coverage::CoverageIdsInfo {
|
||||
desc { |tcx| "retrieving coverage IDs info from MIR for `{}`", tcx.def_path_str(key.def_id()) }
|
||||
arena_cache
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,8 +158,21 @@ pub struct AdtExpr<'tcx> {
|
|||
pub user_ty: UserTy<'tcx>,
|
||||
|
||||
pub fields: Box<[FieldExpr]>,
|
||||
/// The base, e.g. `Foo {x: 1, .. base}`.
|
||||
pub base: Option<FruInfo<'tcx>>,
|
||||
/// The base, e.g. `Foo {x: 1, ..base}`.
|
||||
pub base: AdtExprBase<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable)]
|
||||
pub enum AdtExprBase<'tcx> {
|
||||
/// A struct expression where all the fields are explicitly enumerated: `Foo { a, b }`.
|
||||
None,
|
||||
/// A struct expression with a "base", an expression of the same type as the outer struct that
|
||||
/// will be used to populate any fields not explicitly mentioned: `Foo { ..base }`
|
||||
Base(FruInfo<'tcx>),
|
||||
/// A struct expression with a `..` tail but no "base" expression. The values from the struct
|
||||
/// fields' default values will be used to populate any fields not explicitly mentioned:
|
||||
/// `Foo { .. }`.
|
||||
DefaultFields(Box<[Ty<'tcx>]>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable)]
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use super::{
|
|||
AdtExpr, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand, Pat,
|
||||
PatKind, Stmt, StmtKind, Thir,
|
||||
};
|
||||
use crate::thir::AdtExprBase;
|
||||
|
||||
pub trait Visitor<'thir, 'tcx: 'thir>: Sized {
|
||||
fn thir(&self) -> &'thir Thir<'tcx>;
|
||||
|
|
@ -127,7 +128,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
|
|||
for field in &**fields {
|
||||
visitor.visit_expr(&visitor.thir()[field.expr]);
|
||||
}
|
||||
if let Some(base) = base {
|
||||
if let AdtExprBase::Base(base) = base {
|
||||
visitor.visit_expr(&visitor.thir()[base.base]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1479,7 +1479,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
self.mk_adt_def_from_data(ty::AdtDefData::new(self, did, kind, variants, repr))
|
||||
}
|
||||
|
||||
/// Allocates a read-only byte or string literal for `mir::interpret`.
|
||||
/// Allocates a read-only byte or string literal for `mir::interpret` with alignment 1.
|
||||
/// Returns the same `AllocId` if called again with the same bytes.
|
||||
pub fn allocate_bytes_dedup(self, bytes: &[u8], salt: usize) -> interpret::AllocId {
|
||||
// Create an allocation that just contains these bytes.
|
||||
|
|
|
|||
|
|
@ -1364,6 +1364,7 @@ pub struct FieldDef {
|
|||
pub name: Symbol,
|
||||
pub vis: Visibility<DefId>,
|
||||
pub safety: hir::Safety,
|
||||
pub value: Option<DefId>,
|
||||
}
|
||||
|
||||
impl PartialEq for FieldDef {
|
||||
|
|
@ -1376,9 +1377,9 @@ impl PartialEq for FieldDef {
|
|||
// of `FieldDef` changes, a compile-error will be produced, reminding
|
||||
// us to revisit this assumption.
|
||||
|
||||
let Self { did: lhs_did, name: _, vis: _, safety: _ } = &self;
|
||||
let Self { did: lhs_did, name: _, vis: _, safety: _, value: _ } = &self;
|
||||
|
||||
let Self { did: rhs_did, name: _, vis: _, safety: _ } = other;
|
||||
let Self { did: rhs_did, name: _, vis: _, safety: _, value: _ } = other;
|
||||
|
||||
let res = lhs_did == rhs_did;
|
||||
|
||||
|
|
@ -1405,7 +1406,7 @@ impl Hash for FieldDef {
|
|||
// of `FieldDef` changes, a compile-error will be produced, reminding
|
||||
// us to revisit this assumption.
|
||||
|
||||
let Self { did, name: _, vis: _, safety: _ } = &self;
|
||||
let Self { did, name: _, vis: _, safety: _, value: _ } = &self;
|
||||
|
||||
did.hash(s)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1020,7 +1020,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
|
|||
|
||||
self.insert_trait_and_projection(
|
||||
trait_ref,
|
||||
Some((proj.projection_def_id(), proj.term())),
|
||||
Some((proj.item_def_id(), proj.term())),
|
||||
&mut traits,
|
||||
&mut fn_traits,
|
||||
);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue