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