1#![feature(
4 associated_type_defaults,
5 box_patterns,
6 if_let_guard,
7 min_specialization,
8 never_type,
9 rustc_private,
10 unwrap_infallible
11)]
12
13extern crate rustc_abi;
14extern crate rustc_data_structures;
15extern crate rustc_errors;
16extern crate rustc_hir;
17extern crate rustc_index;
18extern crate rustc_infer;
19extern crate rustc_middle;
20extern crate rustc_mir_dataflow;
21extern crate rustc_span;
22extern crate rustc_type_ir;
23
24mod checker;
25pub mod compare_impl_item;
26mod ghost_statements;
27pub mod invariants;
28mod primops;
29mod queue;
30mod type_env;
31
32use checker::{Checker, trait_impl_subtyping};
33use flux_common::{dbg, result::ResultExt as _};
34use flux_infer::{
35 fixpoint_encoding::FixQueryCache,
36 infer::{ConstrReason, SubtypeReason, Tag},
37};
38use flux_macros::fluent_messages;
39use flux_middle::{
40 FixpointQueryKind,
41 def_id::MaybeExternId,
42 global_env::GlobalEnv,
43 metrics::{self, Metric, TimingKind},
44 rty::{self, ESpan},
45};
46use rustc_data_structures::unord::UnordMap;
47use rustc_errors::ErrorGuaranteed;
48use rustc_hir::def_id::LocalDefId;
49use rustc_span::Span;
50
51use crate::{checker::errors::ResultExt as _, ghost_statements::compute_ghost_statements};
52
53fluent_messages! { "../locales/en-US.ftl" }
54
55fn report_fixpoint_errors(
56 genv: GlobalEnv,
57 local_id: LocalDefId,
58 errors: Vec<Tag>,
59) -> Result<(), ErrorGuaranteed> {
60 #[expect(clippy::collapsible_else_if, reason = "it looks better")]
61 if genv.should_fail(local_id) {
62 if errors.is_empty() { report_expected_neg(genv, local_id) } else { Ok(()) }
63 } else {
64 if errors.is_empty() { Ok(()) } else { report_errors(genv, errors) }
65 }
66}
67
68pub fn check_fn(
69 genv: GlobalEnv,
70 cache: &mut FixQueryCache,
71 def_id: LocalDefId,
72) -> Result<(), ErrorGuaranteed> {
73 let span = genv.tcx().def_span(def_id);
74
75 if !genv.tcx().def_span(def_id).ctxt().is_root() {
80 metrics::incr_metric(Metric::FnTrusted, 1);
81 return Ok(());
82 }
83
84 let opts = genv.infer_opts(def_id);
85
86 if let Some(infcx_root) = trait_impl_subtyping(genv, def_id, opts, span)
88 .with_span(span)
89 .map_err(|err| err.emit(genv, def_id))?
90 {
91 tracing::info!("check_fn::refine-subtyping");
92 let answer = infcx_root
93 .execute_fixpoint_query(cache, MaybeExternId::Local(def_id), FixpointQueryKind::Impl)
94 .emit(&genv)?;
95 tracing::info!("check_fn::fixpoint-subtyping");
96 let errors = answer.errors;
97 report_fixpoint_errors(genv, def_id, errors)?;
98 }
99
100 if genv.trusted(def_id) {
102 metrics::incr_metric(Metric::FnTrusted, 1);
103 return Ok(());
104 }
105
106 metrics::incr_metric(Metric::FnChecked, 1);
107 metrics::time_it(TimingKind::CheckFn(def_id), || -> Result<(), ErrorGuaranteed> {
108 let ghost_stmts = compute_ghost_statements(genv, def_id)
109 .with_span(span)
110 .map_err(|err| err.emit(genv, def_id))?;
111 let mut closures = UnordMap::default();
112 let shape_result =
114 Checker::run_in_shape_mode(genv, def_id, &ghost_stmts, &mut closures, opts)
115 .map_err(|err| err.emit(genv, def_id))?;
116
117 let infcx_root = Checker::run_in_refine_mode(
119 genv,
120 def_id,
121 &ghost_stmts,
122 &mut closures,
123 shape_result,
124 opts,
125 )
126 .map_err(|err| err.emit(genv, def_id))?;
127
128 if genv.proven_externally(def_id) {
129 if flux_config::emit_lean_defs() {
130 infcx_root
131 .execute_lean_query(MaybeExternId::Local(def_id))
132 .emit(&genv)
133 } else {
134 panic!("emit_lean_defs should be enabled if there are externally proven items");
135 }
136 } else {
137 let answer = infcx_root
139 .execute_fixpoint_query(
140 cache,
141 MaybeExternId::Local(def_id),
142 FixpointQueryKind::Body,
143 )
144 .emit(&genv)?;
145 let errors = answer.errors;
146 report_fixpoint_errors(genv, def_id, errors)
147 }
148 })?;
149
150 dbg::check_fn_span!(genv.tcx(), def_id).in_scope(|| Ok(()))
151}
152
153fn call_error(genv: GlobalEnv, span: Span, dst_span: Option<ESpan>) -> ErrorGuaranteed {
154 genv.sess()
155 .emit_err(errors::RefineError::call(span, dst_span))
156}
157
158fn ret_error(genv: GlobalEnv, span: Span, dst_span: Option<ESpan>) -> ErrorGuaranteed {
159 genv.sess()
160 .emit_err(errors::RefineError::ret(span, dst_span))
161}
162
163fn report_errors(genv: GlobalEnv, errors: Vec<Tag>) -> Result<(), ErrorGuaranteed> {
164 let mut e = None;
165 for err in errors {
166 let span = err.src_span;
167 e = Some(match err.reason {
168 ConstrReason::Call
169 | ConstrReason::Subtype(SubtypeReason::Input)
170 | ConstrReason::Subtype(SubtypeReason::Requires)
171 | ConstrReason::Predicate => call_error(genv, span, err.dst_span),
172 ConstrReason::Assign => genv.sess().emit_err(errors::AssignError { span }),
173 ConstrReason::Ret
174 | ConstrReason::Subtype(SubtypeReason::Output)
175 | ConstrReason::Subtype(SubtypeReason::Ensures) => ret_error(genv, span, err.dst_span),
176 ConstrReason::Div => genv.sess().emit_err(errors::DivError { span }),
177 ConstrReason::Rem => genv.sess().emit_err(errors::RemError { span }),
178 ConstrReason::Goto(_) => genv.sess().emit_err(errors::GotoError { span }),
179 ConstrReason::Assert(msg) => genv.sess().emit_err(errors::AssertError { span, msg }),
180 ConstrReason::Fold | ConstrReason::FoldLocal => {
181 genv.sess().emit_err(errors::FoldError { span })
182 }
183 ConstrReason::Overflow => genv.sess().emit_err(errors::OverflowError { span }),
184 ConstrReason::Underflow => genv.sess().emit_err(errors::UnderflowError { span }),
185 ConstrReason::Other => genv.sess().emit_err(errors::UnknownError { span }),
186 });
187 }
188
189 if let Some(e) = e { Err(e) } else { Ok(()) }
190}
191
192fn report_expected_neg(genv: GlobalEnv, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
193 Err(genv.sess().emit_err(errors::ExpectedNeg {
194 span: genv.tcx().def_span(def_id),
195 def_descr: genv.tcx().def_descr(def_id.to_def_id()),
196 }))
197}
198
199mod errors {
200 use flux_errors::E0999;
201 use flux_macros::{Diagnostic, Subdiagnostic};
202 use flux_middle::rty::ESpan;
203 use rustc_span::Span;
204
205 #[derive(Diagnostic)]
206 #[diag(refineck_goto_error, code = E0999)]
207 pub struct GotoError {
208 #[primary_span]
209 pub span: Span,
210 }
211
212 #[derive(Diagnostic)]
213 #[diag(refineck_assign_error, code = E0999)]
214 pub struct AssignError {
215 #[primary_span]
216 pub span: Span,
217 }
218
219 #[derive(Subdiagnostic)]
220 #[note(refineck_condition_span_note)]
221 pub(crate) struct ConditionSpanNote {
222 #[primary_span]
223 pub span: Span,
224 }
225
226 #[derive(Subdiagnostic)]
227 #[note(refineck_call_span_note)]
228 pub(crate) struct CallSpanNote {
229 #[primary_span]
230 pub span: Span,
231 }
232
233 #[derive(Diagnostic)]
234 #[diag(refineck_refine_error, code = E0999)]
235 pub struct RefineError {
236 #[primary_span]
237 #[label]
238 pub span: Span,
239 cond: &'static str,
240 #[subdiagnostic]
241 span_note: Option<ConditionSpanNote>,
242 #[subdiagnostic]
243 call_span_note: Option<CallSpanNote>,
244 }
245
246 impl RefineError {
247 pub fn call(span: Span, espan: Option<ESpan>) -> Self {
248 RefineError::new("precondition", span, espan)
249 }
250
251 pub fn ret(span: Span, espan: Option<ESpan>) -> Self {
252 RefineError::new("postcondition", span, espan)
253 }
254
255 fn new(cond: &'static str, span: Span, espan: Option<ESpan>) -> RefineError {
256 match espan {
257 Some(dst_span) => {
258 let span_note = Some(ConditionSpanNote { span: dst_span.span });
259 let call_span_note = dst_span.base.map(|span| CallSpanNote { span });
260 RefineError { span, cond, span_note, call_span_note }
261 }
262 None => RefineError { span, cond, span_note: None, call_span_note: None },
263 }
264 }
265 }
266
267 #[derive(Diagnostic)]
268 #[diag(refineck_div_error, code = E0999)]
269 pub struct DivError {
270 #[primary_span]
271 pub span: Span,
272 }
273
274 #[derive(Diagnostic)]
275 #[diag(refineck_rem_error, code = E0999)]
276 pub struct RemError {
277 #[primary_span]
278 pub span: Span,
279 }
280
281 #[derive(Diagnostic)]
282 #[diag(refineck_assert_error, code = E0999)]
283 pub struct AssertError {
284 #[primary_span]
285 pub span: Span,
286 pub msg: &'static str,
287 }
288
289 #[derive(Diagnostic)]
290 #[diag(refineck_fold_error, code = E0999)]
291 pub struct FoldError {
292 #[primary_span]
293 pub span: Span,
294 }
295
296 #[derive(Diagnostic)]
297 #[diag(refineck_overflow_error, code = E0999)]
298 pub struct OverflowError {
299 #[primary_span]
300 pub span: Span,
301 }
302
303 #[derive(Diagnostic)]
304 #[diag(refineck_underflow_error, code = E0999)]
305 pub struct UnderflowError {
306 #[primary_span]
307 pub span: Span,
308 }
309
310 #[derive(Diagnostic)]
311 #[diag(refineck_unknown_error, code = E0999)]
312 pub struct UnknownError {
313 #[primary_span]
314 pub span: Span,
315 }
316
317 #[derive(Diagnostic)]
318 #[diag(refineck_expected_neg, code = E0999)]
319 pub struct ExpectedNeg {
320 #[primary_span]
321 pub span: Span,
322 pub def_descr: &'static str,
323 }
324}