Auto merge of #133893 - fmease:rollup-11pi6fg, r=fmease
Rollup of 10 pull requests Successful merges: - #118833 (Add lint against function pointer comparisons) - #122161 (Fix suggestion when shorthand `self` has erroneous type) - #133233 (Add context to "const in pattern" errors) - #133761 (Update books) - #133843 (Do not emit empty suggestion) - #133863 (Rename `core_pattern_type` and `core_pattern_types` lib feature gates to `pattern_type_macro`) - #133872 (No need to create placeholders for GAT args in confirm_object_candidate) - #133874 (`fn_sig_for_fn_abi` should return a `ty::FnSig`, no need for a binder) - #133890 (Add a new test ui/incoherent-inherent-impls/no-other-unrelated-errors to check E0116 does not cause unrelated errors) - #133892 (Revert #133817) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
0e98766a54
201 changed files with 2299 additions and 1115 deletions
|
@ -2566,6 +2566,18 @@ pub enum SelfKind {
|
|||
Explicit(P<Ty>, Mutability),
|
||||
}
|
||||
|
||||
impl SelfKind {
|
||||
pub fn to_ref_suggestion(&self) -> String {
|
||||
match self {
|
||||
SelfKind::Region(None, mutbl) => mutbl.ref_prefix_str().to_string(),
|
||||
SelfKind::Region(Some(lt), mutbl) => format!("&{lt} {}", mutbl.prefix_str()),
|
||||
SelfKind::Value(_) | SelfKind::Explicit(_, _) => {
|
||||
unreachable!("if we had an explicit self, we wouldn't be here")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type ExplicitSelf = Spanned<SelfKind>;
|
||||
|
||||
impl Param {
|
||||
|
|
|
@ -3394,7 +3394,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let Some(ty) = self.node_ty_opt(tail_expr.hir_id) else {
|
||||
return;
|
||||
};
|
||||
if self.can_eq(self.param_env, expected_ty, ty) {
|
||||
if self.can_eq(self.param_env, expected_ty, ty)
|
||||
// FIXME: this happens with macro calls. Need to figure out why the stmt
|
||||
// `println!();` doesn't include the `;` in its `Span`. (#133845)
|
||||
// We filter these out to avoid ICEs with debug assertions on caused by
|
||||
// empty suggestions.
|
||||
&& stmt.span.hi() != tail_expr.span.hi()
|
||||
{
|
||||
err.span_suggestion_short(
|
||||
stmt.span.with_lo(tail_expr.span.hi()),
|
||||
"remove this semicolon",
|
||||
|
|
|
@ -885,6 +885,12 @@ lint_unnameable_test_items = cannot test inner items
|
|||
lint_unnecessary_qualification = unnecessary qualification
|
||||
.suggestion = remove the unnecessary path segments
|
||||
|
||||
lint_unpredictable_fn_pointer_comparisons = function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
|
||||
.note_duplicated_fn = the address of the same function can vary between different codegen units
|
||||
.note_deduplicated_fn = furthermore, different functions could have the same address after being merged together
|
||||
.note_visit_fn_addr_eq = for more information visit <https://doc.rust-lang.org/nightly/core/ptr/fn.fn_addr_eq.html>
|
||||
.fn_addr_eq_suggestion = refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint
|
||||
|
||||
lint_unqualified_local_imports = `use` of a local item without leading `self::`, `super::`, or `crate::`
|
||||
|
||||
lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe
|
||||
|
|
|
@ -1815,6 +1815,42 @@ pub(crate) enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
pub(crate) enum UnpredictableFunctionPointerComparisons<'a> {
|
||||
#[diag(lint_unpredictable_fn_pointer_comparisons)]
|
||||
#[note(lint_note_duplicated_fn)]
|
||||
#[note(lint_note_deduplicated_fn)]
|
||||
#[note(lint_note_visit_fn_addr_eq)]
|
||||
Suggestion {
|
||||
#[subdiagnostic]
|
||||
sugg: UnpredictableFunctionPointerComparisonsSuggestion<'a>,
|
||||
},
|
||||
#[diag(lint_unpredictable_fn_pointer_comparisons)]
|
||||
#[note(lint_note_duplicated_fn)]
|
||||
#[note(lint_note_deduplicated_fn)]
|
||||
#[note(lint_note_visit_fn_addr_eq)]
|
||||
Warn,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(
|
||||
lint_fn_addr_eq_suggestion,
|
||||
style = "verbose",
|
||||
applicability = "maybe-incorrect"
|
||||
)]
|
||||
pub(crate) struct UnpredictableFunctionPointerComparisonsSuggestion<'a> {
|
||||
pub ne: &'a str,
|
||||
pub cast_right: String,
|
||||
pub deref_left: &'a str,
|
||||
pub deref_right: &'a str,
|
||||
#[suggestion_part(code = "{ne}std::ptr::fn_addr_eq({deref_left}")]
|
||||
pub left: Span,
|
||||
#[suggestion_part(code = ", {deref_right}")]
|
||||
pub middle: Span,
|
||||
#[suggestion_part(code = "{cast_right})")]
|
||||
pub right: Span,
|
||||
}
|
||||
|
||||
pub(crate) struct ImproperCTypes<'a> {
|
||||
pub ty: Ty<'a>,
|
||||
pub desc: &'a str,
|
||||
|
|
|
@ -23,7 +23,9 @@ use crate::lints::{
|
|||
AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
|
||||
AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
|
||||
AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons,
|
||||
InvalidNanComparisonsSuggestion, UnusedComparisons, VariantSizeDifferencesDiag,
|
||||
InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons,
|
||||
UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons,
|
||||
VariantSizeDifferencesDiag,
|
||||
};
|
||||
use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
|
||||
|
||||
|
@ -166,6 +168,35 @@ declare_lint! {
|
|||
"detects ambiguous wide pointer comparisons"
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `unpredictable_function_pointer_comparisons` lint checks comparison
|
||||
/// of function pointer as the operands.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// fn a() {}
|
||||
/// fn b() {}
|
||||
///
|
||||
/// let f: fn() = a;
|
||||
/// let g: fn() = b;
|
||||
///
|
||||
/// let _ = f == g;
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Function pointers comparisons do not produce meaningful result since
|
||||
/// they are never guaranteed to be unique and could vary between different
|
||||
/// code generation units. Furthermore, different functions could have the
|
||||
/// same address after being merged together.
|
||||
UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
|
||||
Warn,
|
||||
"detects unpredictable function pointer comparisons"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub(crate) struct TypeLimits {
|
||||
/// Id of the last visited negated expression
|
||||
|
@ -178,7 +209,8 @@ impl_lint_pass!(TypeLimits => [
|
|||
UNUSED_COMPARISONS,
|
||||
OVERFLOWING_LITERALS,
|
||||
INVALID_NAN_COMPARISONS,
|
||||
AMBIGUOUS_WIDE_POINTER_COMPARISONS
|
||||
AMBIGUOUS_WIDE_POINTER_COMPARISONS,
|
||||
UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS
|
||||
]);
|
||||
|
||||
impl TypeLimits {
|
||||
|
@ -255,7 +287,7 @@ fn lint_nan<'tcx>(
|
|||
cx.emit_span_lint(INVALID_NAN_COMPARISONS, e.span, lint);
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
enum ComparisonOp {
|
||||
BinOp(hir::BinOpKind),
|
||||
Other,
|
||||
|
@ -383,6 +415,100 @@ fn lint_wide_pointer<'tcx>(
|
|||
);
|
||||
}
|
||||
|
||||
fn lint_fn_pointer<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx hir::Expr<'tcx>,
|
||||
cmpop: ComparisonOp,
|
||||
l: &'tcx hir::Expr<'tcx>,
|
||||
r: &'tcx hir::Expr<'tcx>,
|
||||
) {
|
||||
let peel_refs = |mut ty: Ty<'tcx>| -> (Ty<'tcx>, usize) {
|
||||
let mut refs = 0;
|
||||
|
||||
while let ty::Ref(_, inner_ty, _) = ty.kind() {
|
||||
ty = *inner_ty;
|
||||
refs += 1;
|
||||
}
|
||||
|
||||
(ty, refs)
|
||||
};
|
||||
|
||||
// Left and right operands can have borrows, remove them
|
||||
let l = l.peel_borrows();
|
||||
let r = r.peel_borrows();
|
||||
|
||||
let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else { return };
|
||||
let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else { return };
|
||||
|
||||
// Remove any references as `==` will deref through them (and count the
|
||||
// number of references removed, for latter).
|
||||
let (l_ty, l_ty_refs) = peel_refs(l_ty);
|
||||
let (r_ty, r_ty_refs) = peel_refs(r_ty);
|
||||
|
||||
if !l_ty.is_fn() || !r_ty.is_fn() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's try to suggest `ptr::fn_addr_eq` if/when possible.
|
||||
|
||||
let is_eq_ne = matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne));
|
||||
|
||||
if !is_eq_ne {
|
||||
// Neither `==` nor `!=`, we can't suggest `ptr::fn_addr_eq`, just show the warning.
|
||||
return cx.emit_span_lint(
|
||||
UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
|
||||
e.span,
|
||||
UnpredictableFunctionPointerComparisons::Warn,
|
||||
);
|
||||
}
|
||||
|
||||
let (Some(l_span), Some(r_span)) =
|
||||
(l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span))
|
||||
else {
|
||||
// No appropriate spans for the left and right operands, just show the warning.
|
||||
return cx.emit_span_lint(
|
||||
UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
|
||||
e.span,
|
||||
UnpredictableFunctionPointerComparisons::Warn,
|
||||
);
|
||||
};
|
||||
|
||||
let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" };
|
||||
|
||||
// `ptr::fn_addr_eq` only works with raw pointer, deref any references.
|
||||
let deref_left = &*"*".repeat(l_ty_refs);
|
||||
let deref_right = &*"*".repeat(r_ty_refs);
|
||||
|
||||
let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo());
|
||||
let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo());
|
||||
let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi());
|
||||
|
||||
// We only check for a right cast as `FnDef` == `FnPtr` is not possible,
|
||||
// only `FnPtr == FnDef` is possible.
|
||||
let cast_right = if !r_ty.is_fn_ptr() {
|
||||
let fn_sig = r_ty.fn_sig(cx.tcx);
|
||||
format!(" as {fn_sig}")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
cx.emit_span_lint(
|
||||
UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
|
||||
e.span,
|
||||
UnpredictableFunctionPointerComparisons::Suggestion {
|
||||
sugg: UnpredictableFunctionPointerComparisonsSuggestion {
|
||||
ne,
|
||||
deref_left,
|
||||
deref_right,
|
||||
left,
|
||||
middle,
|
||||
right,
|
||||
cast_right,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for TypeLimits {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
|
||||
match e.kind {
|
||||
|
@ -399,7 +525,9 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
|
|||
cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
|
||||
} else {
|
||||
lint_nan(cx, e, binop, l, r);
|
||||
lint_wide_pointer(cx, e, ComparisonOp::BinOp(binop.node), l, r);
|
||||
let cmpop = ComparisonOp::BinOp(binop.node);
|
||||
lint_wide_pointer(cx, e, cmpop, l, r);
|
||||
lint_fn_pointer(cx, e, cmpop, l, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -411,6 +539,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
|
|||
&& let Some(cmpop) = diag_item_cmpop(diag_item) =>
|
||||
{
|
||||
lint_wide_pointer(cx, e, cmpop, l, r);
|
||||
lint_fn_pointer(cx, e, cmpop, l, r);
|
||||
}
|
||||
hir::ExprKind::MethodCall(_, l, [r], _)
|
||||
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
|
||||
|
@ -418,6 +547,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
|
|||
&& let Some(cmpop) = diag_item_cmpop(diag_item) =>
|
||||
{
|
||||
lint_wide_pointer(cx, e, cmpop, l, r);
|
||||
lint_fn_pointer(cx, e, cmpop, l, r);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
|
|
@ -84,12 +84,17 @@ mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
|
|||
|
||||
mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as a constant pattern, not a new variable
|
||||
|
||||
mir_build_const_param_in_pattern = const parameters cannot be referenced in patterns
|
||||
mir_build_const_defined_here = constant defined here
|
||||
|
||||
mir_build_const_pattern_depends_on_generic_parameter =
|
||||
constant pattern depends on a generic parameter
|
||||
mir_build_const_param_in_pattern = constant parameters cannot be referenced in patterns
|
||||
.label = can't be used in patterns
|
||||
mir_build_const_param_in_pattern_def = constant defined here
|
||||
|
||||
mir_build_const_pattern_depends_on_generic_parameter = constant pattern cannot depend on generic parameters
|
||||
.label = `const` depends on a generic parameter
|
||||
|
||||
mir_build_could_not_eval_const_pattern = could not evaluate constant pattern
|
||||
.label = could not evaluate constant
|
||||
|
||||
mir_build_deref_raw_pointer_requires_unsafe =
|
||||
dereference of raw pointer is unsafe and requires unsafe block
|
||||
|
@ -147,7 +152,8 @@ mir_build_inline_assembly_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
|
|||
|
||||
mir_build_interpreted_as_const = introduce a variable instead
|
||||
|
||||
mir_build_invalid_pattern = `{$non_sm_ty}` cannot be used in patterns
|
||||
mir_build_invalid_pattern = {$prefix} `{$non_sm_ty}` cannot be used in patterns
|
||||
.label = {$prefix} can't be used in patterns
|
||||
|
||||
mir_build_irrefutable_let_patterns_if_let = irrefutable `if let` {$count ->
|
||||
[one] pattern
|
||||
|
@ -244,10 +250,12 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa
|
|||
.label = mutation of layout constrained field
|
||||
|
||||
mir_build_nan_pattern = cannot use NaN in patterns
|
||||
.label = evaluates to `NaN`, which is not allowed in patterns
|
||||
.note = NaNs compare inequal to everything, even themselves, so this pattern would never match
|
||||
.help = try using the `is_nan` method instead
|
||||
|
||||
mir_build_non_const_path = runtime values cannot be referenced in patterns
|
||||
.label = references a runtime value
|
||||
|
||||
mir_build_non_empty_never_pattern =
|
||||
mismatched types
|
||||
|
@ -265,13 +273,15 @@ mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type
|
|||
.suggestion = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
||||
.help = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
|
||||
|
||||
mir_build_non_partial_eq_match =
|
||||
to use a constant of type `{$non_peq_ty}` in a pattern, the type must implement `PartialEq`
|
||||
mir_build_non_partial_eq_match = constant of non-structural type `{$ty}` in a pattern
|
||||
.label = constant of non-structural type
|
||||
|
||||
mir_build_pattern_not_covered = refutable pattern in {$origin}
|
||||
.pattern_ty = the matched value is of type `{$pattern_ty}`
|
||||
|
||||
mir_build_pointer_pattern = function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details.
|
||||
mir_build_pointer_pattern = function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon
|
||||
.label = can't be used in patterns
|
||||
.note = see https://github.com/rust-lang/rust/issues/70861 for details
|
||||
|
||||
mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
|
||||
|
||||
|
@ -283,6 +293,8 @@ mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly
|
|||
.missing_box = `#[rustc_box]` requires the `owned_box` lang item
|
||||
|
||||
mir_build_static_in_pattern = statics cannot be referenced in patterns
|
||||
.label = can't be used in patterns
|
||||
mir_build_static_in_pattern_def = `static` defined here
|
||||
|
||||
mir_build_suggest_attempted_int_lit = alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
|
||||
|
||||
|
@ -310,12 +322,12 @@ mir_build_trailing_irrefutable_let_patterns = trailing irrefutable {$count ->
|
|||
*[other] them
|
||||
} into the body
|
||||
|
||||
mir_build_type_not_structural =
|
||||
to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq)]`
|
||||
|
||||
mir_build_type_not_structural = constant of non-structural type `{$ty}` in a pattern
|
||||
.label = constant of non-structural type
|
||||
mir_build_type_not_structural_def = `{$ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns
|
||||
mir_build_type_not_structural_more_info = see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
|
||||
|
||||
mir_build_type_not_structural_tip = the traits must be derived, manual `impl`s are not sufficient
|
||||
mir_build_type_not_structural_tip =
|
||||
the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
|
||||
|
||||
mir_build_unconditional_recursion = function cannot return without recursing
|
||||
.label = cannot return without recursing
|
||||
|
@ -334,6 +346,7 @@ mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
|
|||
.label = access to union field
|
||||
|
||||
mir_build_union_pattern = cannot use unions in constant patterns
|
||||
.label = can't use a `union` here
|
||||
|
||||
mir_build_unreachable_making_this_unreachable = collectively making this unreachable
|
||||
|
||||
|
|
|
@ -631,20 +631,27 @@ pub(crate) struct NonExhaustiveMatchAllArmsGuarded;
|
|||
#[diag(mir_build_static_in_pattern, code = E0158)]
|
||||
pub(crate) struct StaticInPattern {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub(crate) span: Span,
|
||||
#[label(mir_build_static_in_pattern_def)]
|
||||
pub(crate) static_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_build_const_param_in_pattern, code = E0158)]
|
||||
pub(crate) struct ConstParamInPattern {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub(crate) span: Span,
|
||||
#[label(mir_build_const_param_in_pattern_def)]
|
||||
pub(crate) const_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_build_non_const_path, code = E0080)]
|
||||
pub(crate) struct NonConstPath {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
|
@ -695,6 +702,7 @@ pub(crate) struct WantedConstant {
|
|||
#[diag(mir_build_const_pattern_depends_on_generic_parameter, code = E0158)]
|
||||
pub(crate) struct ConstPatternDependsOnGenericParameter {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
|
@ -702,6 +710,7 @@ pub(crate) struct ConstPatternDependsOnGenericParameter {
|
|||
#[diag(mir_build_could_not_eval_const_pattern)]
|
||||
pub(crate) struct CouldNotEvalConstPattern {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
|
@ -867,33 +876,43 @@ pub(crate) enum Conflict {
|
|||
#[diag(mir_build_union_pattern)]
|
||||
pub(crate) struct UnionPattern {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_build_type_not_structural)]
|
||||
#[note(mir_build_type_not_structural_tip)]
|
||||
#[note(mir_build_type_not_structural_more_info)]
|
||||
pub(crate) struct TypeNotStructural<'tcx> {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) non_sm_ty: Ty<'tcx>,
|
||||
#[label(mir_build_type_not_structural_def)]
|
||||
pub(crate) ty_def_span: Span,
|
||||
pub(crate) ty: Ty<'tcx>,
|
||||
#[note(mir_build_type_not_structural_tip)]
|
||||
pub(crate) manual_partialeq_impl_span: Option<Span>,
|
||||
#[note(mir_build_type_not_structural_more_info)]
|
||||
pub(crate) manual_partialeq_impl_note: bool,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_build_non_partial_eq_match)]
|
||||
#[note(mir_build_type_not_structural_more_info)]
|
||||
pub(crate) struct TypeNotPartialEq<'tcx> {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) non_peq_ty: Ty<'tcx>,
|
||||
pub(crate) ty: Ty<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_build_invalid_pattern)]
|
||||
pub(crate) struct InvalidPattern<'tcx> {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) non_sm_ty: Ty<'tcx>,
|
||||
pub(crate) prefix: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
@ -910,13 +929,16 @@ pub(crate) struct UnsizedPattern<'tcx> {
|
|||
#[help]
|
||||
pub(crate) struct NaNPattern {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_build_pointer_pattern)]
|
||||
#[note]
|
||||
pub(crate) struct PointerPattern {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
use rustc_abi::{FieldIdx, VariantIdx};
|
||||
use rustc_apfloat::Float;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Diag;
|
||||
use rustc_hir as hir;
|
||||
use rustc_index::Idx;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::traits::Obligation;
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::thir::{FieldPat, Pat, PatKind};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, ValTree};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeVisitor, ValTree};
|
||||
use rustc_middle::{mir, span_bug};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::{Span, sym};
|
||||
use rustc_trait_selection::traits::ObligationCause;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
@ -35,7 +38,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
id: hir::HirId,
|
||||
span: Span,
|
||||
) -> Box<Pat<'tcx>> {
|
||||
let mut convert = ConstToPat::new(self, id, span);
|
||||
let mut convert = ConstToPat::new(self, id, span, c);
|
||||
|
||||
match c.kind() {
|
||||
ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty),
|
||||
|
@ -49,21 +52,26 @@ struct ConstToPat<'tcx> {
|
|||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
span: Span,
|
||||
id: hir::HirId,
|
||||
|
||||
treat_byte_string_as_slice: bool,
|
||||
|
||||
c: ty::Const<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> ConstToPat<'tcx> {
|
||||
fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span) -> Self {
|
||||
fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self {
|
||||
trace!(?pat_ctxt.typeck_results.hir_owner);
|
||||
ConstToPat {
|
||||
tcx: pat_ctxt.tcx,
|
||||
typing_env: pat_ctxt.typing_env,
|
||||
span,
|
||||
id,
|
||||
treat_byte_string_as_slice: pat_ctxt
|
||||
.typeck_results
|
||||
.treat_byte_string_as_slice
|
||||
.contains(&id.local_id),
|
||||
c,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,13 +79,32 @@ impl<'tcx> ConstToPat<'tcx> {
|
|||
ty.is_structural_eq_shallow(self.tcx)
|
||||
}
|
||||
|
||||
/// We errored. Signal that in the pattern, so that follow up errors can be silenced.
|
||||
fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
|
||||
if let ty::ConstKind::Unevaluated(uv) = self.c.kind() {
|
||||
let def_kind = self.tcx.def_kind(uv.def);
|
||||
if let hir::def::DefKind::AssocConst = def_kind
|
||||
&& let Some(def_id) = uv.def.as_local()
|
||||
{
|
||||
// Include the container item in the output.
|
||||
err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), "");
|
||||
}
|
||||
if let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = def_kind {
|
||||
err.span_label(
|
||||
self.tcx.def_span(uv.def),
|
||||
crate::fluent_generated::mir_build_const_defined_here,
|
||||
);
|
||||
}
|
||||
}
|
||||
Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()) })
|
||||
}
|
||||
|
||||
fn unevaluated_to_pat(
|
||||
&mut self,
|
||||
uv: ty::UnevaluatedConst<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Box<Pat<'tcx>> {
|
||||
trace!(self.treat_byte_string_as_slice);
|
||||
let pat_from_kind = |kind| Box::new(Pat { span: self.span, ty, kind });
|
||||
|
||||
// It's not *technically* correct to be revealing opaque types here as borrowcheck has
|
||||
// not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even
|
||||
|
@ -96,32 +123,60 @@ impl<'tcx> ConstToPat<'tcx> {
|
|||
Ok(Ok(c)) => c,
|
||||
Err(ErrorHandled::Reported(_, _)) => {
|
||||
// Let's tell the use where this failing const occurs.
|
||||
let e = self.tcx.dcx().emit_err(CouldNotEvalConstPattern { span: self.span });
|
||||
return pat_from_kind(PatKind::Error(e));
|
||||
let mut err =
|
||||
self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span });
|
||||
// We've emitted an error on the original const, it would be redundant to complain
|
||||
// on its use as well.
|
||||
if let ty::ConstKind::Unevaluated(uv) = self.c.kind()
|
||||
&& let hir::def::DefKind::Const | hir::def::DefKind::AssocConst =
|
||||
self.tcx.def_kind(uv.def)
|
||||
{
|
||||
err.downgrade_to_delayed_bug();
|
||||
}
|
||||
return self.mk_err(err, ty);
|
||||
}
|
||||
Err(ErrorHandled::TooGeneric(_)) => {
|
||||
let e = self
|
||||
let mut e = self
|
||||
.tcx
|
||||
.dcx()
|
||||
.emit_err(ConstPatternDependsOnGenericParameter { span: self.span });
|
||||
return pat_from_kind(PatKind::Error(e));
|
||||
.create_err(ConstPatternDependsOnGenericParameter { span: self.span });
|
||||
for arg in uv.args {
|
||||
if let ty::GenericArgKind::Type(ty) = arg.unpack()
|
||||
&& let ty::Param(param_ty) = ty.kind()
|
||||
{
|
||||
let def_id = self.tcx.hir().enclosing_body_owner(self.id);
|
||||
let generics = self.tcx.generics_of(def_id);
|
||||
let param = generics.type_param(*param_ty, self.tcx);
|
||||
let span = self.tcx.def_span(param.def_id);
|
||||
e.span_label(span, "constant depends on this generic parameter");
|
||||
if let Some(ident) = self.tcx.def_ident_span(def_id)
|
||||
&& self.tcx.sess.source_map().is_multiline(ident.between(span))
|
||||
{
|
||||
// Display the `fn` name as well in the diagnostic, as the generic isn't
|
||||
// in the same line and it could be confusing otherwise.
|
||||
e.span_label(ident, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
return self.mk_err(e, ty);
|
||||
}
|
||||
Ok(Err(bad_ty)) => {
|
||||
// The pattern cannot be turned into a valtree.
|
||||
let e = match bad_ty.kind() {
|
||||
ty::Adt(def, ..) => {
|
||||
assert!(def.is_union());
|
||||
self.tcx.dcx().emit_err(UnionPattern { span: self.span })
|
||||
self.tcx.dcx().create_err(UnionPattern { span: self.span })
|
||||
}
|
||||
ty::FnPtr(..) | ty::RawPtr(..) => {
|
||||
self.tcx.dcx().emit_err(PointerPattern { span: self.span })
|
||||
self.tcx.dcx().create_err(PointerPattern { span: self.span })
|
||||
}
|
||||
_ => self
|
||||
.tcx
|
||||
.dcx()
|
||||
.emit_err(InvalidPattern { span: self.span, non_sm_ty: bad_ty }),
|
||||
_ => self.tcx.dcx().create_err(InvalidPattern {
|
||||
span: self.span,
|
||||
non_sm_ty: bad_ty,
|
||||
prefix: bad_ty.prefix_string(self.tcx).to_string(),
|
||||
}),
|
||||
};
|
||||
return pat_from_kind(PatKind::Error(e));
|
||||
return self.mk_err(e, ty);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -130,42 +185,16 @@ impl<'tcx> ConstToPat<'tcx> {
|
|||
|
||||
if !inlined_const_as_pat.references_error() {
|
||||
// Always check for `PartialEq` if we had no other errors yet.
|
||||
if !self.type_has_partial_eq_impl(ty) {
|
||||
let err = TypeNotPartialEq { span: self.span, non_peq_ty: ty };
|
||||
let e = self.tcx.dcx().emit_err(err);
|
||||
return pat_from_kind(PatKind::Error(e));
|
||||
if !type_has_partial_eq_impl(self.tcx, typing_env, ty).0 {
|
||||
let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty });
|
||||
extend_type_not_partial_eq(self.tcx, typing_env, ty, &mut err);
|
||||
return self.mk_err(err, ty);
|
||||
}
|
||||
}
|
||||
|
||||
inlined_const_as_pat
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
fn type_has_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
|
||||
let (infcx, param_env) = self.tcx.infer_ctxt().build_with_typing_env(self.typing_env);
|
||||
// double-check there even *is* a semantic `PartialEq` to dispatch to.
|
||||
//
|
||||
// (If there isn't, then we can safely issue a hard
|
||||
// error, because that's never worked, due to compiler
|
||||
// using `PartialEq::eq` in this scenario in the past.)
|
||||
let partial_eq_trait_id =
|
||||
self.tcx.require_lang_item(hir::LangItem::PartialEq, Some(self.span));
|
||||
let partial_eq_obligation = Obligation::new(
|
||||
self.tcx,
|
||||
ObligationCause::dummy(),
|
||||
param_env,
|
||||
ty::TraitRef::new(self.tcx, partial_eq_trait_id, [ty, ty]),
|
||||
);
|
||||
|
||||
// This *could* accept a type that isn't actually `PartialEq`, because region bounds get
|
||||
// ignored. However that should be pretty much impossible since consts that do not depend on
|
||||
// generics can only mention the `'static` lifetime, and how would one have a type that's
|
||||
// `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem
|
||||
// we'll need to leave some sort of trace of this requirement in the MIR so that borrowck
|
||||
// can ensure that the type really implements `PartialEq`.
|
||||
infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation)
|
||||
}
|
||||
|
||||
fn field_pats(
|
||||
&self,
|
||||
vals: impl Iterator<Item = (ValTree<'tcx>, Ty<'tcx>)>,
|
||||
|
@ -190,10 +219,25 @@ impl<'tcx> ConstToPat<'tcx> {
|
|||
// Extremely important check for all ADTs! Make sure they opted-in to be used in
|
||||
// patterns.
|
||||
debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty);
|
||||
let err = TypeNotStructural { span, non_sm_ty: ty };
|
||||
let e = tcx.dcx().emit_err(err);
|
||||
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
|
||||
PatKind::Error(e)
|
||||
let (_impls_partial_eq, derived, structural, impl_def_id) =
|
||||
type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
|
||||
let (manual_partialeq_impl_span, manual_partialeq_impl_note) =
|
||||
match (structural, impl_def_id) {
|
||||
(true, _) => (None, false),
|
||||
(_, Some(def_id)) if def_id.is_local() && !derived => {
|
||||
(Some(tcx.def_span(def_id)), false)
|
||||
}
|
||||
_ => (None, true),
|
||||
};
|
||||
let ty_def_span = tcx.def_span(adt_def.did());
|
||||
let err = TypeNotStructural {
|
||||
span,
|
||||
ty,
|
||||
ty_def_span,
|
||||
manual_partialeq_impl_span,
|
||||
manual_partialeq_impl_note,
|
||||
};
|
||||
return self.mk_err(tcx.dcx().create_err(err), ty);
|
||||
}
|
||||
ty::Adt(adt_def, args) if adt_def.is_enum() => {
|
||||
let (&variant_index, fields) = cv.unwrap_branch().split_first().unwrap();
|
||||
|
@ -207,7 +251,7 @@ impl<'tcx> ConstToPat<'tcx> {
|
|||
adt_def.variants()[variant_index]
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| field.ty(self.tcx, args)),
|
||||
.map(|field| field.ty(tcx, args)),
|
||||
),
|
||||
),
|
||||
}
|
||||
|
@ -216,7 +260,7 @@ impl<'tcx> ConstToPat<'tcx> {
|
|||
assert!(!def.is_union()); // Valtree construction would never succeed for unions.
|
||||
PatKind::Leaf {
|
||||
subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip(
|
||||
def.non_enum_variant().fields.iter().map(|field| field.ty(self.tcx, args)),
|
||||
def.non_enum_variant().fields.iter().map(|field| field.ty(tcx, args)),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
@ -252,10 +296,10 @@ impl<'tcx> ConstToPat<'tcx> {
|
|||
// deref pattern.
|
||||
_ => {
|
||||
if !pointee_ty.is_sized(tcx, self.typing_env) && !pointee_ty.is_slice() {
|
||||
let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
|
||||
let e = tcx.dcx().emit_err(err);
|
||||
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
|
||||
PatKind::Error(e)
|
||||
return self.mk_err(
|
||||
tcx.dcx().create_err(UnsizedPattern { span, non_sm_ty: *pointee_ty }),
|
||||
ty,
|
||||
);
|
||||
} else {
|
||||
// `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
|
||||
// matching against references, you can only use byte string literals.
|
||||
|
@ -286,8 +330,7 @@ impl<'tcx> ConstToPat<'tcx> {
|
|||
if is_nan {
|
||||
// NaNs are not ever equal to anything so they make no sense as patterns.
|
||||
// Also see <https://github.com/rust-lang/rfcs/pull/3535>.
|
||||
let e = tcx.dcx().emit_err(NaNPattern { span });
|
||||
PatKind::Error(e)
|
||||
return self.mk_err(tcx.dcx().create_err(NaNPattern { span }), ty);
|
||||
} else {
|
||||
PatKind::Constant {
|
||||
value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)),
|
||||
|
@ -305,13 +348,153 @@ impl<'tcx> ConstToPat<'tcx> {
|
|||
)
|
||||
}
|
||||
_ => {
|
||||
let err = InvalidPattern { span, non_sm_ty: ty };
|
||||
let e = tcx.dcx().emit_err(err);
|
||||
// We errored. Signal that in the pattern, so that follow up errors can be silenced.
|
||||
PatKind::Error(e)
|
||||
let err = InvalidPattern {
|
||||
span,
|
||||
non_sm_ty: ty,
|
||||
prefix: ty.prefix_string(tcx).to_string(),
|
||||
};
|
||||
return self.mk_err(tcx.dcx().create_err(err), ty);
|
||||
}
|
||||
};
|
||||
|
||||
Box::new(Pat { span, ty, kind })
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a type with type parameters, visit every ADT looking for types that need to
|
||||
/// `#[derive(PartialEq)]` for it to be a structural type.
|
||||
fn extend_type_not_partial_eq<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
) {
|
||||
/// Collect all types that need to be `StructuralPartialEq`.
|
||||
struct UsedParamsNeedInstantiationVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
/// The user has written `impl PartialEq for Ty` which means it's non-structual.
|
||||
adts_with_manual_partialeq: FxHashSet<Span>,
|
||||
/// The type has no `PartialEq` implementation, neither manual or derived.
|
||||
adts_without_partialeq: FxHashSet<Span>,
|
||||
/// The user has written `impl PartialEq for Ty` which means it's non-structual,
|
||||
/// but we don't have a span to point at, so we'll just add them as a `note`.
|
||||
manual: Vec<Ty<'tcx>>,
|
||||
/// The type has no `PartialEq` implementation, neither manual or derived, but
|
||||
/// we don't have a span to point at, so we'll just add them as a `note`.
|
||||
without: Vec<Ty<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor<'tcx> {
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
|
||||
if let ty::Adt(def, _args) = ty.kind() {
|
||||
let ty_def_id = def.did();
|
||||
let ty_def_span = self.tcx.def_span(ty_def_id);
|
||||
let (impls_partial_eq, derived, structural, impl_def_id) =
|
||||
type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
|
||||
match (impls_partial_eq, derived, structural, impl_def_id) {
|
||||
(_, _, true, _) => {}
|
||||
(true, false, _, Some(def_id)) if def_id.is_local() => {
|
||||
self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id));
|
||||
}
|
||||
(true, false, _, _) if ty_def_id.is_local() => {
|
||||
self.adts_with_manual_partialeq.insert(ty_def_span);
|
||||
}
|
||||
(false, _, _, _) if ty_def_id.is_local() => {
|
||||
self.adts_without_partialeq.insert(ty_def_span);
|
||||
}
|
||||
(true, false, _, _) => {
|
||||
self.manual.push(ty);
|
||||
}
|
||||
(false, _, _, _) => {
|
||||
self.without.push(ty);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
use rustc_middle::ty::TypeSuperVisitable;
|
||||
ty.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
let mut v = UsedParamsNeedInstantiationVisitor {
|
||||
tcx,
|
||||
typing_env,
|
||||
adts_with_manual_partialeq: FxHashSet::default(),
|
||||
adts_without_partialeq: FxHashSet::default(),
|
||||
manual: vec![],
|
||||
without: vec![],
|
||||
};
|
||||
v.visit_ty(ty);
|
||||
#[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering
|
||||
for span in v.adts_with_manual_partialeq {
|
||||
err.span_note(span, "the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details");
|
||||
}
|
||||
#[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering
|
||||
for span in v.adts_without_partialeq {
|
||||
err.span_label(
|
||||
span,
|
||||
"must be annotated with `#[derive(PartialEq)]` to be usable in patterns",
|
||||
);
|
||||
}
|
||||
for ty in v.manual {
|
||||
err.note(format!(
|
||||
"`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details"
|
||||
));
|
||||
}
|
||||
for ty in v.without {
|
||||
err.note(format!(
|
||||
"`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(tcx), ret)]
|
||||
fn type_has_partial_eq_impl<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> (
|
||||
/* has impl */ bool,
|
||||
/* is derived */ bool,
|
||||
/* structural partial eq */ bool,
|
||||
/* non-blanket impl */ Option<DefId>,
|
||||
) {
|
||||
let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
|
||||
// double-check there even *is* a semantic `PartialEq` to dispatch to.
|
||||
//
|
||||
// (If there isn't, then we can safely issue a hard
|
||||
// error, because that's never worked, due to compiler
|
||||
// using `PartialEq::eq` in this scenario in the past.)
|
||||
let partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::PartialEq, None);
|
||||
let structural_partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::StructuralPeq, None);
|
||||
|
||||
let partial_eq_obligation = Obligation::new(
|
||||
tcx,
|
||||
ObligationCause::dummy(),
|
||||
param_env,
|
||||
ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]),
|
||||
);
|
||||
|
||||
let mut automatically_derived = false;
|
||||
let mut structural_peq = false;
|
||||
let mut impl_def_id = None;
|
||||
for def_id in tcx.non_blanket_impls_for_ty(partial_eq_trait_id, ty) {
|
||||
automatically_derived = tcx.has_attr(def_id, sym::automatically_derived);
|
||||
impl_def_id = Some(def_id);
|
||||
}
|
||||
for _ in tcx.non_blanket_impls_for_ty(structural_partial_eq_trait_id, ty) {
|
||||
structural_peq = true;
|
||||
}
|
||||
// This *could* accept a type that isn't actually `PartialEq`, because region bounds get
|
||||
// ignored. However that should be pretty much impossible since consts that do not depend on
|
||||
// generics can only mention the `'static` lifetime, and how would one have a type that's
|
||||
// `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem
|
||||
// we'll need to leave some sort of trace of this requirement in the MIR so that borrowck
|
||||
// can ensure that the type really implements `PartialEq`.
|
||||
(
|
||||
infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation),
|
||||
automatically_derived,
|
||||
structural_peq,
|
||||
impl_def_id,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -528,11 +528,17 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
| Res::SelfCtor(..) => PatKind::Leaf { subpatterns },
|
||||
_ => {
|
||||
let e = match res {
|
||||
Res::Def(DefKind::ConstParam, _) => {
|
||||
self.tcx.dcx().emit_err(ConstParamInPattern { span })
|
||||
Res::Def(DefKind::ConstParam, def_id) => {
|
||||
self.tcx.dcx().emit_err(ConstParamInPattern {
|
||||
span,
|
||||
const_span: self.tcx().def_span(def_id),
|
||||
})
|
||||
}
|
||||
Res::Def(DefKind::Static { .. }, _) => {
|
||||
self.tcx.dcx().emit_err(StaticInPattern { span })
|
||||
Res::Def(DefKind::Static { .. }, def_id) => {
|
||||
self.tcx.dcx().emit_err(StaticInPattern {
|
||||
span,
|
||||
static_span: self.tcx().def_span(def_id),
|
||||
})
|
||||
}
|
||||
_ => self.tcx.dcx().emit_err(NonConstPath { span }),
|
||||
};
|
||||
|
|
|
@ -747,8 +747,8 @@ fn build_call_shim<'tcx>(
|
|||
sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output);
|
||||
}
|
||||
|
||||
// FIXME(eddyb) avoid having this snippet both here and in
|
||||
// `Instance::fn_sig` (introduce `InstanceKind::fn_sig`?).
|
||||
// FIXME: Avoid having to adjust the signature both here and in
|
||||
// `fn_sig_for_fn_abi`.
|
||||
if let ty::InstanceKind::VTableShim(..) = instance {
|
||||
// Modify fn(self, ...) to fn(self: *mut Self, ...)
|
||||
let mut inputs_and_output = sig.inputs_and_output.to_vec();
|
||||
|
|
|
@ -343,6 +343,9 @@ parse_incorrect_semicolon =
|
|||
.suggestion = remove this semicolon
|
||||
.help = {$name} declarations are not followed by a semicolon
|
||||
|
||||
parse_incorrect_type_on_self = type not allowed for shorthand `self` parameter
|
||||
.suggestion = move the modifiers on `self` to the type
|
||||
|
||||
parse_incorrect_use_of_await = incorrect use of `await`
|
||||
.parentheses_suggestion = `await` is not a method call, remove the parentheses
|
||||
|
||||
|
|
|
@ -3409,3 +3409,22 @@ pub(crate) struct PolarityAndModifiers {
|
|||
pub polarity: &'static str,
|
||||
pub modifiers_concatenated: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(parse_incorrect_type_on_self)]
|
||||
pub(crate) struct IncorrectTypeOnSelf {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[subdiagnostic]
|
||||
pub move_self_modifier: MoveSelfModifier,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
|
||||
pub(crate) struct MoveSelfModifier {
|
||||
#[suggestion_part(code = "")]
|
||||
pub removal_span: Span,
|
||||
#[suggestion_part(code = "{modifier}")]
|
||||
pub insertion_span: Span,
|
||||
pub modifier: String,
|
||||
}
|
||||
|
|
|
@ -2941,6 +2941,32 @@ impl<'a> Parser<'a> {
|
|||
};
|
||||
Ok((eself, eself_ident, eself_hi))
|
||||
};
|
||||
let expect_self_ident_not_typed =
|
||||
|this: &mut Self, modifier: &SelfKind, modifier_span: Span| {
|
||||
let eself_ident = expect_self_ident(this);
|
||||
|
||||
// Recover `: Type` after a qualified self
|
||||
if this.may_recover() && this.eat_noexpect(&token::Colon) {
|
||||
let snap = this.create_snapshot_for_diagnostic();
|
||||
match this.parse_ty() {
|
||||
Ok(ty) => {
|
||||
this.dcx().emit_err(errors::IncorrectTypeOnSelf {
|
||||
span: ty.span,
|
||||
move_self_modifier: errors::MoveSelfModifier {
|
||||
removal_span: modifier_span,
|
||||
insertion_span: ty.span.shrink_to_lo(),
|
||||
modifier: modifier.to_ref_suggestion(),
|
||||
},
|
||||
});
|
||||
}
|
||||
Err(diag) => {
|
||||
diag.cancel();
|
||||
this.restore_snapshot(snap);
|
||||
}
|
||||
}
|
||||
}
|
||||
eself_ident
|
||||
};
|
||||
// Recover for the grammar `*self`, `*const self`, and `*mut self`.
|
||||
let recover_self_ptr = |this: &mut Self| {
|
||||
this.dcx().emit_err(errors::SelfArgumentPointer { span: this.token.span });
|
||||
|
@ -2978,7 +3004,9 @@ impl<'a> Parser<'a> {
|
|||
// `¬_self`
|
||||
return Ok(None);
|
||||
};
|
||||
(eself, expect_self_ident(self), self.prev_token.span)
|
||||
let hi = self.token.span;
|
||||
let self_ident = expect_self_ident_not_typed(self, &eself, eself_lo.until(hi));
|
||||
(eself, self_ident, hi)
|
||||
}
|
||||
// `*self`
|
||||
token::BinOp(token::Star) if is_isolated_self(self, 1) => {
|
||||
|
|
|
@ -3838,6 +3838,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
&& self.predicate_must_hold_modulo_regions(&Obligation::misc(
|
||||
tcx, expr.span, body_id, param_env, pred,
|
||||
))
|
||||
&& expr.span.hi() != rcvr.span.hi()
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
expr.span.with_lo(rcvr.span.hi()),
|
||||
|
@ -4115,6 +4116,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
// the expected is a projection that we need to resolve.
|
||||
// && let Some(tail_ty) = typeck_results.expr_ty_opt(expr)
|
||||
&& expected_found.found.is_unit()
|
||||
// FIXME: this happens with macro calls. Need to figure out why the stmt
|
||||
// `println!();` doesn't include the `;` in its `Span`. (#133845)
|
||||
// We filter these out to avoid ICEs with debug assertions on caused by
|
||||
// empty suggestions.
|
||||
&& expr.span.hi() != stmt.span.hi()
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
expr.span.shrink_to_hi().with_hi(stmt.span.hi()),
|
||||
|
|
|
@ -16,9 +16,7 @@ use rustc_hir::lang_items::LangItem;
|
|||
use rustc_infer::infer::{DefineOpaqueTypes, HigherRankedType, InferOk};
|
||||
use rustc_infer::traits::ObligationCauseCode;
|
||||
use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData};
|
||||
use rustc_middle::ty::{
|
||||
self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, Ty, TyCtxt, Upcast,
|
||||
};
|
||||
use rustc_middle::ty::{self, GenericArgsRef, ToPolyTraitRef, Ty, TyCtxt, Upcast};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::def_id::DefId;
|
||||
use tracing::{debug, instrument};
|
||||
|
@ -638,60 +636,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
// higher-ranked things.
|
||||
// Prevent, e.g., `dyn Iterator<Item = str>`.
|
||||
for bound in self.tcx().item_bounds(assoc_type).transpose_iter() {
|
||||
let arg_bound = if defs.is_empty() {
|
||||
bound.instantiate(tcx, trait_predicate.trait_ref.args)
|
||||
} else {
|
||||
let mut args = smallvec::SmallVec::with_capacity(defs.count());
|
||||
args.extend(trait_predicate.trait_ref.args.iter());
|
||||
let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
|
||||
smallvec::SmallVec::with_capacity(
|
||||
bound.skip_binder().kind().bound_vars().len() + defs.count(),
|
||||
);
|
||||
bound_vars.extend(bound.skip_binder().kind().bound_vars().into_iter());
|
||||
GenericArgs::fill_single(&mut args, defs, &mut |param, _| match param.kind {
|
||||
GenericParamDefKind::Type { .. } => {
|
||||
let kind = ty::BoundTyKind::Param(param.def_id, param.name);
|
||||
let bound_var = ty::BoundVariableKind::Ty(kind);
|
||||
bound_vars.push(bound_var);
|
||||
Ty::new_bound(tcx, ty::INNERMOST, ty::BoundTy {
|
||||
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
|
||||
kind,
|
||||
})
|
||||
.into()
|
||||
}
|
||||
GenericParamDefKind::Lifetime => {
|
||||
let kind = ty::BoundRegionKind::Named(param.def_id, param.name);
|
||||
let bound_var = ty::BoundVariableKind::Region(kind);
|
||||
bound_vars.push(bound_var);
|
||||
ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion {
|
||||
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
|
||||
kind,
|
||||
})
|
||||
.into()
|
||||
}
|
||||
GenericParamDefKind::Const { .. } => {
|
||||
let bound_var = ty::BoundVariableKind::Const;
|
||||
bound_vars.push(bound_var);
|
||||
ty::Const::new_bound(
|
||||
tcx,
|
||||
ty::INNERMOST,
|
||||
ty::BoundVar::from_usize(bound_vars.len() - 1),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
});
|
||||
let bound_vars = tcx.mk_bound_variable_kinds(&bound_vars);
|
||||
let assoc_ty_args = tcx.mk_args(&args);
|
||||
let bound =
|
||||
bound.map_bound(|b| b.kind().skip_binder()).instantiate(tcx, assoc_ty_args);
|
||||
ty::Binder::bind_with_vars(bound, bound_vars).upcast(tcx)
|
||||
};
|
||||
let normalized_bound = normalize_with_depth_to(
|
||||
self,
|
||||
obligation.param_env,
|
||||
obligation.cause.clone(),
|
||||
obligation.recursion_depth + 1,
|
||||
arg_bound,
|
||||
bound.instantiate(tcx, trait_predicate.trait_ref.args),
|
||||
&mut nested,
|
||||
);
|
||||
nested.push(obligation.with(tcx, normalized_bound));
|
||||
|
|
|
@ -31,20 +31,20 @@ fn fn_sig_for_fn_abi<'tcx>(
|
|||
tcx: TyCtxt<'tcx>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
) -> ty::PolyFnSig<'tcx> {
|
||||
) -> ty::FnSig<'tcx> {
|
||||
if let InstanceKind::ThreadLocalShim(..) = instance.def {
|
||||
return ty::Binder::dummy(tcx.mk_fn_sig(
|
||||
return tcx.mk_fn_sig(
|
||||
[],
|
||||
tcx.thread_local_ptr_ty(instance.def_id()),
|
||||
false,
|
||||
hir::Safety::Safe,
|
||||
rustc_abi::ExternAbi::Unadjusted,
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
let ty = instance.ty(tcx, typing_env);
|
||||
match *ty.kind() {
|
||||
ty::FnDef(..) => {
|
||||
ty::FnDef(def_id, args) => {
|
||||
// HACK(davidtwco,eddyb): This is a workaround for polymorphization considering
|
||||
// parameters unused if they show up in the signature, but not in the `mir::Body`
|
||||
// (i.e. due to being inside a projection that got normalized, see
|
||||
|
@ -52,9 +52,8 @@ fn fn_sig_for_fn_abi<'tcx>(
|
|||
// track of a polymorphization `ParamEnv` to allow normalizing later.
|
||||
//
|
||||
// We normalize the `fn_sig` again after instantiating at a later point.
|
||||
let mut sig = match *ty.kind() {
|
||||
ty::FnDef(def_id, args) => tcx
|
||||
.fn_sig(def_id)
|
||||
let mut sig = tcx.instantiate_bound_regions_with_erased(
|
||||
tcx.fn_sig(def_id)
|
||||
.map_bound(|fn_sig| {
|
||||
tcx.normalize_erasing_regions(
|
||||
ty::TypingEnv::non_body_analysis(tcx, def_id),
|
||||
|
@ -62,62 +61,36 @@ fn fn_sig_for_fn_abi<'tcx>(
|
|||
)
|
||||
})
|
||||
.instantiate(tcx, args),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
);
|
||||
|
||||
if let ty::InstanceKind::VTableShim(..) = instance.def {
|
||||
// Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`.
|
||||
sig = sig.map_bound(|mut sig| {
|
||||
let mut inputs_and_output = sig.inputs_and_output.to_vec();
|
||||
inputs_and_output[0] = Ty::new_mut_ptr(tcx, inputs_and_output[0]);
|
||||
sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output);
|
||||
sig
|
||||
});
|
||||
let mut inputs_and_output = sig.inputs_and_output.to_vec();
|
||||
inputs_and_output[0] = Ty::new_mut_ptr(tcx, inputs_and_output[0]);
|
||||
sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output);
|
||||
}
|
||||
|
||||
sig
|
||||
}
|
||||
ty::Closure(def_id, args) => {
|
||||
let sig = args.as_closure().sig();
|
||||
|
||||
let bound_vars =
|
||||
tcx.mk_bound_variable_kinds_from_iter(sig.bound_vars().iter().chain(iter::once(
|
||||
ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv),
|
||||
)));
|
||||
let br = ty::BoundRegion {
|
||||
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
|
||||
kind: ty::BoundRegionKind::ClosureEnv,
|
||||
};
|
||||
let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br);
|
||||
let sig = tcx.instantiate_bound_regions_with_erased(args.as_closure().sig());
|
||||
let env_ty = tcx.closure_env_ty(
|
||||
Ty::new_closure(tcx, def_id, args),
|
||||
args.as_closure().kind(),
|
||||
env_region,
|
||||
tcx.lifetimes.re_erased,
|
||||
);
|
||||
|
||||
let sig = sig.skip_binder();
|
||||
ty::Binder::bind_with_vars(
|
||||
tcx.mk_fn_sig(
|
||||
iter::once(env_ty).chain(sig.inputs().iter().cloned()),
|
||||
sig.output(),
|
||||
sig.c_variadic,
|
||||
sig.safety,
|
||||
sig.abi,
|
||||
),
|
||||
bound_vars,
|
||||
tcx.mk_fn_sig(
|
||||
iter::once(env_ty).chain(sig.inputs().iter().cloned()),
|
||||
sig.output(),
|
||||
sig.c_variadic,
|
||||
sig.safety,
|
||||
sig.abi,
|
||||
)
|
||||
}
|
||||
ty::CoroutineClosure(def_id, args) => {
|
||||
let coroutine_ty = Ty::new_coroutine_closure(tcx, def_id, args);
|
||||
let sig = args.as_coroutine_closure().coroutine_closure_sig();
|
||||
let bound_vars =
|
||||
tcx.mk_bound_variable_kinds_from_iter(sig.bound_vars().iter().chain(iter::once(
|
||||
ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv),
|
||||
)));
|
||||
let br = ty::BoundRegion {
|
||||
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
|
||||
kind: ty::BoundRegionKind::ClosureEnv,
|
||||
};
|
||||
let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br);
|
||||
|
||||
// When this `CoroutineClosure` comes from a `ConstructCoroutineInClosureShim`,
|
||||
// make sure we respect the `target_kind` in that shim.
|
||||
// FIXME(async_closures): This shouldn't be needed, and we should be populating
|
||||
|
@ -138,42 +111,32 @@ fn fn_sig_for_fn_abi<'tcx>(
|
|||
coroutine_ty
|
||||
}
|
||||
} else {
|
||||
tcx.closure_env_ty(coroutine_ty, coroutine_kind, env_region)
|
||||
tcx.closure_env_ty(coroutine_ty, coroutine_kind, tcx.lifetimes.re_erased)
|
||||
};
|
||||
|
||||
let sig = sig.skip_binder();
|
||||
ty::Binder::bind_with_vars(
|
||||
tcx.mk_fn_sig(
|
||||
iter::once(env_ty).chain([sig.tupled_inputs_ty]),
|
||||
sig.to_coroutine_given_kind_and_upvars(
|
||||
tcx,
|
||||
args.as_coroutine_closure().parent_args(),
|
||||
tcx.coroutine_for_closure(def_id),
|
||||
coroutine_kind,
|
||||
env_region,
|
||||
args.as_coroutine_closure().tupled_upvars_ty(),
|
||||
args.as_coroutine_closure().coroutine_captures_by_ref_ty(),
|
||||
),
|
||||
sig.c_variadic,
|
||||
sig.safety,
|
||||
sig.abi,
|
||||
let sig = tcx.instantiate_bound_regions_with_erased(sig);
|
||||
|
||||
tcx.mk_fn_sig(
|
||||
iter::once(env_ty).chain([sig.tupled_inputs_ty]),
|
||||
sig.to_coroutine_given_kind_and_upvars(
|
||||
tcx,
|
||||
args.as_coroutine_closure().parent_args(),
|
||||
tcx.coroutine_for_closure(def_id),
|
||||
coroutine_kind,
|
||||
tcx.lifetimes.re_erased,
|
||||
args.as_coroutine_closure().tupled_upvars_ty(),
|
||||
args.as_coroutine_closure().coroutine_captures_by_ref_ty(),
|
||||
),
|
||||
bound_vars,
|
||||
sig.c_variadic,
|
||||
sig.safety,
|
||||
sig.abi,
|
||||
)
|
||||
}
|
||||
ty::Coroutine(did, args) => {
|
||||
let coroutine_kind = tcx.coroutine_kind(did).unwrap();
|
||||
let sig = args.as_coroutine().sig();
|
||||
|
||||
let bound_vars = tcx.mk_bound_variable_kinds_from_iter(iter::once(
|
||||
ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv),
|
||||
));
|
||||
let br = ty::BoundRegion {
|
||||
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
|
||||
kind: ty::BoundRegionKind::ClosureEnv,
|
||||
};
|
||||
|
||||
let env_ty = Ty::new_mut_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), ty);
|
||||
let env_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, ty);
|
||||
|
||||
let pin_did = tcx.require_lang_item(LangItem::Pin, None);
|
||||
let pin_adt_ref = tcx.adt_def(pin_did);
|
||||
|
@ -268,7 +231,7 @@ fn fn_sig_for_fn_abi<'tcx>(
|
|||
}
|
||||
};
|
||||
|
||||
let fn_sig = if let Some(resume_ty) = resume_ty {
|
||||
if let Some(resume_ty) = resume_ty {
|
||||
tcx.mk_fn_sig(
|
||||
[env_ty, resume_ty],
|
||||
ret_ty,
|
||||
|
@ -285,8 +248,7 @@ fn fn_sig_for_fn_abi<'tcx>(
|
|||
hir::Safety::Safe,
|
||||
rustc_abi::ExternAbi::Rust,
|
||||
)
|
||||
};
|
||||
ty::Binder::bind_with_vars(fn_sig, bound_vars)
|
||||
}
|
||||
}
|
||||
_ => bug!("unexpected type {:?} in Instance::fn_sig", ty),
|
||||
}
|
||||
|
@ -335,8 +297,16 @@ fn fn_abi_of_fn_ptr<'tcx>(
|
|||
query: ty::PseudoCanonicalInput<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List<Ty<'tcx>>)>,
|
||||
) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> {
|
||||
let ty::PseudoCanonicalInput { typing_env, value: (sig, extra_args) } = query;
|
||||
|
||||
let cx = LayoutCx::new(tcx, typing_env);
|
||||
fn_abi_new_uncached(&cx, sig, extra_args, None, None, false)
|
||||
fn_abi_new_uncached(
|
||||
&cx,
|
||||
tcx.instantiate_bound_regions_with_erased(sig),
|
||||
extra_args,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
fn fn_abi_of_instance<'tcx>(
|
||||
|
@ -567,7 +537,7 @@ fn fn_abi_sanity_check<'tcx>(
|
|||
#[tracing::instrument(level = "debug", skip(cx, caller_location, fn_def_id, force_thin_self_ptr))]
|
||||
fn fn_abi_new_uncached<'tcx>(
|
||||
cx: &LayoutCx<'tcx>,
|
||||
sig: ty::PolyFnSig<'tcx>,
|
||||
sig: ty::FnSig<'tcx>,
|
||||
extra_args: &[Ty<'tcx>],
|
||||
caller_location: Option<Ty<'tcx>>,
|
||||
fn_def_id: Option<DefId>,
|
||||
|
@ -575,7 +545,7 @@ fn fn_abi_new_uncached<'tcx>(
|
|||
force_thin_self_ptr: bool,
|
||||
) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> {
|
||||
let tcx = cx.tcx();
|
||||
let sig = tcx.normalize_erasing_late_bound_regions(cx.typing_env, sig);
|
||||
let sig = tcx.normalize_erasing_regions(cx.typing_env, sig);
|
||||
|
||||
let conv = conv_from_spec_abi(cx.tcx(), sig.abi, sig.c_variadic);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue