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, dbg::SpanTrace, result::ResultExt as _};
34use flux_config as config;
35use flux_infer::{
36 fixpoint_encoding::{FixQueryCache, SolutionTrace},
37 infer::{ConstrReason, SubtypeReason, Tag},
38};
39use flux_macros::fluent_messages;
40use flux_middle::{
41 FixpointQueryKind,
42 def_id::MaybeExternId,
43 global_env::GlobalEnv,
44 metrics::{self, Metric, TimingKind},
45 rty::{self, ESpan},
46};
47use rustc_data_structures::unord::UnordMap;
48use rustc_errors::ErrorGuaranteed;
49use rustc_hir::def_id::LocalDefId;
50use rustc_span::Span;
51
52use crate::{checker::errors::ResultExt as _, ghost_statements::compute_ghost_statements};
53
54fluent_messages! { "../locales/en-US.ftl" }
55
56fn report_fixpoint_errors(
57 genv: GlobalEnv,
58 local_id: LocalDefId,
59 errors: Vec<Tag>,
60) -> Result<(), ErrorGuaranteed> {
61 #[expect(clippy::collapsible_else_if, reason = "it looks better")]
62 if genv.should_fail(local_id) {
63 if errors.is_empty() { report_expected_neg(genv, local_id) } else { Ok(()) }
64 } else {
65 if errors.is_empty() { Ok(()) } else { report_errors(genv, errors) }
66 }
67}
68
69fn check_body(
70 genv: GlobalEnv,
71 cache: &mut FixQueryCache,
72 def_id: LocalDefId,
73 poly_sig: &rty::PolyFnSig,
74) -> Result<(), ErrorGuaranteed> {
75 let span = genv.tcx().def_span(def_id);
76 let opts = genv.infer_opts(def_id);
77
78 dbg::log_verbose!("FLUX checking: {def_id:?} {span:?}");
79
80 let ghost_stmts = compute_ghost_statements(genv, def_id)
81 .with_span(span)
82 .map_err(|err| err.emit(genv, def_id))?;
83 let mut closures = UnordMap::default();
84
85 let shape_result =
87 Checker::run_in_shape_mode(genv, def_id, &ghost_stmts, &mut closures, opts, poly_sig)
88 .map_err(|err| err.emit(genv, def_id))?;
89
90 let infcx_root = Checker::run_in_refine_mode(
92 genv,
93 def_id,
94 &ghost_stmts,
95 &mut closures,
96 shape_result,
97 opts,
98 poly_sig,
99 )
100 .map_err(|err| err.emit(genv, def_id))?;
101
102 if (genv.proven_externally(def_id).is_some() && flux_config::lean().is_check())
104 || flux_config::lean().is_emit()
105 {
106 infcx_root
107 .execute_lean_query(cache, MaybeExternId::Local(def_id))
108 .emit(&genv)
109 } else {
110 let answer = infcx_root
111 .execute_fixpoint_query(cache, MaybeExternId::Local(def_id), FixpointQueryKind::Body)
112 .emit(&genv)?;
113
114 let tcx = genv.tcx();
115 let hir_id = tcx.local_def_id_to_hir_id(def_id);
116 let body_span = tcx.hir_span_with_body(hir_id);
117 dbg::solution!(genv, &answer, body_span);
118
119 let errors = answer.errors;
120 report_fixpoint_errors(genv, def_id, errors)
121 }
122}
123
124pub fn check_static(
125 genv: GlobalEnv,
126 cache: &mut FixQueryCache,
127 def_id: LocalDefId,
128 ty: rty::Ty,
129) -> Result<(), ErrorGuaranteed> {
130 let output = rty::Binder::dummy(rty::FnOutput::new(ty, vec![]));
132 let fn_sig = rty::FnSig::new(
133 rustc_hir::Safety::Safe,
134 rustc_abi::ExternAbi::Rust,
135 rty::List::empty(),
136 rty::List::empty(),
137 output,
138 rty::Expr::ff(),
139 false,
140 );
141 let poly_sig = rty::PolyFnSig::dummy(fn_sig);
142
143 metrics::incr_metric(Metric::FnChecked, 1);
144 metrics::time_it(TimingKind::CheckBody(def_id), || check_body(genv, cache, def_id, &poly_sig))
145}
146
147pub fn check_fn(
148 genv: GlobalEnv,
149 cache: &mut FixQueryCache,
150 def_id: LocalDefId,
151) -> Result<(), ErrorGuaranteed> {
152 let span = genv.tcx().def_span(def_id);
153
154 if !genv.tcx().def_span(def_id).ctxt().is_root() {
159 metrics::incr_metric(Metric::FnTrusted, 1);
160 return Ok(());
161 }
162
163 let opts = genv.infer_opts(def_id);
164
165 if let Some(infcx_root) = trait_impl_subtyping(genv, def_id, opts, span)
167 .with_span(span)
168 .map_err(|err| err.emit(genv, def_id))?
169 {
170 tracing::info!("check_fn::refine-subtyping");
171 let answer = infcx_root
172 .execute_fixpoint_query(cache, MaybeExternId::Local(def_id), FixpointQueryKind::Impl)
173 .emit(&genv)?;
174 tracing::info!("check_fn::fixpoint-subtyping");
175 let errors = answer.errors;
176 report_fixpoint_errors(genv, def_id, errors)?;
177 }
178
179 if genv.trusted(def_id) {
181 metrics::incr_metric(Metric::FnTrusted, 1);
182 return Ok(());
183 }
184
185 metrics::incr_metric(Metric::FnChecked, 1);
186 metrics::time_it(TimingKind::CheckBody(def_id), || -> Result<(), ErrorGuaranteed> {
187 let poly_sig = genv
188 .fn_sig(def_id)
189 .with_span(span)
190 .map_err(|err| err.emit(genv, def_id))?
191 .instantiate_identity();
192 let poly_sig = rty::auto_strong(genv, def_id, poly_sig);
193
194 check_body(genv, cache, def_id, &poly_sig)
195 })?;
196
197 dbg::check_fn_span!(genv.tcx(), def_id).in_scope(|| Ok(()))
198}
199
200fn call_error(genv: GlobalEnv, span: Span, dst_span: Option<ESpan>) -> ErrorGuaranteed {
201 genv.sess()
202 .emit_err(errors::RefineError::call(span, dst_span))
203}
204
205fn ret_error(genv: GlobalEnv, span: Span, dst_span: Option<ESpan>) -> ErrorGuaranteed {
206 genv.sess()
207 .emit_err(errors::RefineError::ret(span, dst_span))
208}
209
210fn report_errors(genv: GlobalEnv, errors: Vec<Tag>) -> Result<(), ErrorGuaranteed> {
211 let mut e = None;
212 for err in errors {
213 let span = err.src_span;
214 e = Some(match err.reason {
215 ConstrReason::Call
216 | ConstrReason::Subtype(SubtypeReason::Input)
217 | ConstrReason::Subtype(SubtypeReason::Requires)
218 | ConstrReason::Predicate => call_error(genv, span, err.dst_span),
219 ConstrReason::Assign => genv.sess().emit_err(errors::AssignError { span }),
220 ConstrReason::Ret
221 | ConstrReason::Subtype(SubtypeReason::Output)
222 | ConstrReason::Subtype(SubtypeReason::Ensures) => ret_error(genv, span, err.dst_span),
223 ConstrReason::Div => genv.sess().emit_err(errors::DivError { span }),
224 ConstrReason::Rem => genv.sess().emit_err(errors::RemError { span }),
225 ConstrReason::Goto(_) => genv.sess().emit_err(errors::GotoError { span }),
226 ConstrReason::Assert(msg) => genv.sess().emit_err(errors::AssertError { span, msg }),
227 ConstrReason::Fold | ConstrReason::FoldLocal => {
228 genv.sess()
229 .emit_err(errors::FoldError::new(span, err.dst_span))
230 }
231 ConstrReason::Overflow => genv.sess().emit_err(errors::OverflowError { span }),
232 ConstrReason::Underflow => genv.sess().emit_err(errors::UnderflowError { span }),
233 ConstrReason::Other => genv.sess().emit_err(errors::UnknownError { span }),
234 ConstrReason::NoPanic(callee, reason) => {
235 genv.sess().emit_err(errors::PanicError {
236 span,
237 callee: genv.tcx().def_path_debug_str(callee),
238 reason: format!("{:?}", reason),
239 })
240 }
241 });
242 }
243
244 if let Some(e) = e { Err(e) } else { Ok(()) }
245}
246
247fn report_expected_neg(genv: GlobalEnv, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
248 Err(genv.sess().emit_err(errors::ExpectedNeg {
249 span: genv.tcx().def_span(def_id),
250 def_descr: genv.tcx().def_descr(def_id.to_def_id()),
251 }))
252}
253
254mod errors {
255 use flux_errors::E0999;
256 use flux_macros::{Diagnostic, Subdiagnostic};
257 use flux_middle::rty::ESpan;
258 use rustc_span::Span;
259
260 #[derive(Diagnostic)]
261 #[diag(refineck_goto_error, code = E0999)]
262 pub struct GotoError {
263 #[primary_span]
264 pub span: Span,
265 }
266
267 #[derive(Diagnostic)]
268 #[diag(refineck_assign_error, code = E0999)]
269 pub struct AssignError {
270 #[primary_span]
271 pub span: Span,
272 }
273
274 #[derive(Subdiagnostic)]
275 #[note(refineck_condition_span_note)]
276 pub(crate) struct ConditionSpanNote {
277 #[primary_span]
278 pub span: Span,
279 }
280
281 #[derive(Subdiagnostic)]
282 #[note(refineck_call_span_note)]
283 pub(crate) struct CallSpanNote {
284 #[primary_span]
285 pub span: Span,
286 }
287
288 #[derive(Diagnostic)]
289 #[diag(refineck_refine_error, code = E0999)]
290 pub struct RefineError {
291 #[primary_span]
292 #[label]
293 pub span: Span,
294 cond: &'static str,
295 #[subdiagnostic]
296 span_note: Option<ConditionSpanNote>,
297 #[subdiagnostic]
298 call_span_note: Option<CallSpanNote>,
299 }
300
301 impl RefineError {
302 pub fn call(span: Span, espan: Option<ESpan>) -> Self {
303 RefineError::new("precondition", span, espan)
304 }
305
306 pub fn ret(span: Span, espan: Option<ESpan>) -> Self {
307 RefineError::new("postcondition", span, espan)
308 }
309
310 fn new(cond: &'static str, span: Span, espan: Option<ESpan>) -> RefineError {
311 match espan {
312 Some(dst_span) => {
313 let span_note = Some(ConditionSpanNote { span: dst_span.span });
314 let call_span_note = dst_span.base.map(|span| CallSpanNote { span });
315 RefineError { span, cond, span_note, call_span_note }
316 }
317 None => RefineError { span, cond, span_note: None, call_span_note: None },
318 }
319 }
320 }
321
322 #[derive(Diagnostic)]
323 #[diag(refineck_div_error, code = E0999)]
324 pub struct DivError {
325 #[primary_span]
326 pub span: Span,
327 }
328
329 #[derive(Diagnostic)]
330 #[diag(refineck_rem_error, code = E0999)]
331 pub struct RemError {
332 #[primary_span]
333 pub span: Span,
334 }
335
336 #[derive(Diagnostic)]
337 #[diag(refineck_assert_error, code = E0999)]
338 pub struct AssertError {
339 #[primary_span]
340 pub span: Span,
341 pub msg: &'static str,
342 }
343
344 #[derive(Diagnostic)]
345 #[diag(refineck_fold_error, code = E0999)]
346 pub struct FoldError {
347 #[primary_span]
348 pub span: Span,
349 #[subdiagnostic]
350 span_note: Option<ConditionSpanNote>,
351 }
352
353 impl FoldError {
354 pub fn new(span: Span, espan: Option<ESpan>) -> Self {
355 let span_note = espan.map(|espan| ConditionSpanNote { span: espan.span });
356 FoldError { span, span_note }
357 }
358 }
359
360 #[derive(Diagnostic)]
361 #[diag(refineck_overflow_error, code = E0999)]
362 pub struct OverflowError {
363 #[primary_span]
364 pub span: Span,
365 }
366
367 #[derive(Diagnostic)]
368 #[diag(refineck_underflow_error, code = E0999)]
369 pub struct UnderflowError {
370 #[primary_span]
371 pub span: Span,
372 }
373
374 #[derive(Diagnostic)]
375 #[diag(refineck_unknown_error, code = E0999)]
376 pub struct UnknownError {
377 #[primary_span]
378 pub span: Span,
379 }
380
381 #[derive(Diagnostic)]
382 #[diag(refineck_expected_neg, code = E0999)]
383 pub struct ExpectedNeg {
384 #[primary_span]
385 pub span: Span,
386 pub def_descr: &'static str,
387 }
388
389 #[derive(Diagnostic)]
390 #[diag(refineck_panic_error, code = E0999)]
391 pub(super) struct PanicError {
392 #[primary_span]
393 pub(super) span: Span,
394 pub(super) callee: String,
395 pub(super) reason: String,
396 }
397}