|
1 | 1 | //! Concrete error types for all operations which may be invalid in a certain const context.
|
2 | 2 |
|
3 |
| -use rustc_errors::{struct_span_err, DiagnosticBuilder}; |
| 3 | +use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; |
4 | 4 | use rustc_hir as hir;
|
5 | 5 | use rustc_hir::def_id::DefId;
|
6 |
| -use rustc_middle::mir; |
| 6 | +use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; |
| 7 | +use rustc_middle::{mir, ty::AssocKind}; |
7 | 8 | use rustc_session::parse::feature_err;
|
8 | 9 | use rustc_span::symbol::sym;
|
9 |
| -use rustc_span::{Span, Symbol}; |
| 10 | +use rustc_span::{symbol::Ident, Span, Symbol}; |
| 11 | +use rustc_span::{BytePos, Pos}; |
10 | 12 |
|
11 | 13 | use super::ConstCx;
|
12 | 14 |
|
@@ -72,17 +74,71 @@ impl NonConstOp for FnCallIndirect {
|
72 | 74 |
|
73 | 75 | /// A function call where the callee is not marked as `const`.
|
74 | 76 | #[derive(Debug)]
|
75 |
| -pub struct FnCallNonConst; |
76 |
| -impl NonConstOp for FnCallNonConst { |
| 77 | +pub struct FnCallNonConst<'tcx>(pub Option<(DefId, SubstsRef<'tcx>)>); |
| 78 | +impl<'a> NonConstOp for FnCallNonConst<'a> { |
77 | 79 | fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
|
78 |
| - struct_span_err!( |
| 80 | + let mut err = struct_span_err!( |
79 | 81 | ccx.tcx.sess,
|
80 | 82 | span,
|
81 | 83 | E0015,
|
82 | 84 | "calls in {}s are limited to constant functions, \
|
83 | 85 | tuple structs and tuple variants",
|
84 | 86 | ccx.const_kind(),
|
85 |
| - ) |
| 87 | + ); |
| 88 | + |
| 89 | + if let FnCallNonConst(Some((callee, substs))) = *self { |
| 90 | + if let Some(trait_def_id) = ccx.tcx.lang_items().eq_trait() { |
| 91 | + if let Some(eq_item) = ccx.tcx.associated_items(trait_def_id).find_by_name_and_kind( |
| 92 | + ccx.tcx, |
| 93 | + Ident::with_dummy_span(sym::eq), |
| 94 | + AssocKind::Fn, |
| 95 | + trait_def_id, |
| 96 | + ) { |
| 97 | + if callee == eq_item.def_id && substs.len() == 2 { |
| 98 | + match (substs[0].unpack(), substs[1].unpack()) { |
| 99 | + (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty)) |
| 100 | + if self_ty == rhs_ty |
| 101 | + && self_ty.is_ref() |
| 102 | + && self_ty.peel_refs().is_primitive() => |
| 103 | + { |
| 104 | + let mut num_refs = 0; |
| 105 | + let mut tmp_ty = self_ty; |
| 106 | + while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() { |
| 107 | + num_refs += 1; |
| 108 | + tmp_ty = inner_ty; |
| 109 | + } |
| 110 | + let deref = "*".repeat(num_refs); |
| 111 | + |
| 112 | + if let Ok(call_str) = |
| 113 | + ccx.tcx.sess.source_map().span_to_snippet(span) |
| 114 | + { |
| 115 | + if let Some(eq_idx) = call_str.find("==") { |
| 116 | + if let Some(rhs_idx) = call_str[(eq_idx + 2)..] |
| 117 | + .find(|c: char| !c.is_whitespace()) |
| 118 | + { |
| 119 | + let rhs_pos = span.lo() |
| 120 | + + BytePos::from_usize(eq_idx + 2 + rhs_idx); |
| 121 | + let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos); |
| 122 | + err.multipart_suggestion( |
| 123 | + "consider dereferencing here", |
| 124 | + vec![ |
| 125 | + (span.shrink_to_lo(), deref.clone()), |
| 126 | + (rhs_span, deref), |
| 127 | + ], |
| 128 | + Applicability::MachineApplicable, |
| 129 | + ); |
| 130 | + } |
| 131 | + } |
| 132 | + } |
| 133 | + } |
| 134 | + _ => {} |
| 135 | + } |
| 136 | + } |
| 137 | + } |
| 138 | + } |
| 139 | + } |
| 140 | + |
| 141 | + err |
86 | 142 | }
|
87 | 143 | }
|
88 | 144 |
|
|
0 commit comments