flux_refineck/
compare_impl_item.rs1use flux_common::result::ErrorEmitter;
2use flux_infer::{
3 infer::{GlobalEnvExt as _, InferCtxt},
4 projections::NormalizeExt as _,
5};
6use flux_middle::{
7 def_id::{FluxDefId, MaybeExternId},
8 def_id_to_string,
9 global_env::GlobalEnv,
10 queries::QueryResult,
11 rty::TraitRef,
12};
13use rustc_hash::FxHashSet;
14use rustc_infer::infer::TyCtxtInferExt;
15use rustc_middle::ty::TypingMode;
16
17pub fn check_impl_against_trait(genv: GlobalEnv, impl_id: MaybeExternId) -> QueryResult {
18 let trait_id = genv.tcx().trait_id_of_impl(impl_id.resolved_id()).unwrap();
19
20 let impl_assoc_refts = genv.assoc_refinements_of(impl_id)?;
21 let trait_assoc_refts = genv.assoc_refinements_of(trait_id)?;
22 let impl_names: FxHashSet<_> = impl_assoc_refts.items.iter().map(|x| x.name()).collect();
23
24 for trait_assoc_id in &trait_assoc_refts.items {
25 let has_default = genv
26 .default_assoc_refinement_body(*trait_assoc_id)?
27 .is_some();
28 if !impl_names.contains(&trait_assoc_id.name()) && !has_default {
29 let span = genv.tcx().def_span(impl_id);
30 Err(genv.emit(errors::MissingAssocReft::new(span, trait_assoc_id.name())))?;
31 }
32 }
33
34 let impl_trait_ref = genv
35 .impl_trait_ref(impl_id.resolved_id())?
36 .unwrap()
37 .instantiate_identity();
38
39 let rustc_infcx = genv
40 .tcx()
41 .infer_ctxt()
42 .with_next_trait_solver(true)
43 .build(TypingMode::non_body_analysis());
44 let mut root_ctxt = genv
45 .infcx_root(&rustc_infcx, genv.infer_opts(impl_id.local_id()))
46 .with_const_generics(impl_id.resolved_id())?
47 .build()?;
48 let mut infcx = root_ctxt.infcx(impl_id.resolved_id(), &rustc_infcx);
49
50 for impl_assoc_id in &impl_assoc_refts.items {
51 let name = impl_assoc_id.name();
52 if let Some(trait_assoc_id) = trait_assoc_refts.find(name) {
53 check_assoc_reft(&mut infcx, impl_id, &impl_trait_ref, trait_assoc_id, *impl_assoc_id)?;
54 } else {
55 let fhir_impl_assoc_reft = genv
56 .map()
57 .expect_item(impl_id.local_id())?
58 .expect_impl()
59 .find_assoc_reft(name)
60 .unwrap();
61 Err(genv.emit(errors::InvalidAssocReft::new(
62 fhir_impl_assoc_reft.span,
63 name,
64 def_id_to_string(trait_id),
65 )))?;
66 }
67 }
68
69 Ok(())
70}
71
72fn check_assoc_reft(
73 infcx: &mut InferCtxt,
74 impl_id: MaybeExternId,
75 impl_trait_ref: &TraitRef,
76 trait_assoc_id: FluxDefId,
77 impl_assoc_id: FluxDefId,
78) -> QueryResult {
79 debug_assert_eq!(trait_assoc_id.name(), impl_assoc_id.name());
80
81 let impl_span = infcx
82 .genv
83 .map()
84 .expect_item(impl_id.local_id())?
85 .expect_impl()
86 .find_assoc_reft(impl_assoc_id.name())
87 .unwrap()
88 .span;
89
90 let impl_sort = infcx
91 .genv
92 .sort_of_assoc_reft(impl_assoc_id)?
93 .instantiate_identity()
94 .normalize_projections(&mut infcx.at(impl_span))?;
95
96 let trait_sort = infcx.genv.sort_of_assoc_reft(trait_assoc_id)?;
97 let trait_sort = trait_sort.instantiate(infcx.tcx(), &impl_trait_ref.args, &[]);
98 let trait_sort = trait_sort.normalize_projections(&mut infcx.at(impl_span))?;
99
100 if impl_sort != trait_sort {
101 Err(infcx.genv.emit(errors::IncompatibleSort::new(
102 impl_span,
103 impl_assoc_id.name(),
104 trait_sort,
105 impl_sort,
106 )))?;
107 }
108
109 Ok(())
110}
111
112pub(crate) mod errors {
113 use flux_errors::E0999;
114 use flux_macros::Diagnostic;
115 use flux_middle::rty;
116 use rustc_span::{Span, Symbol};
117
118 #[derive(Diagnostic)]
119 #[diag(refineck_incompatible_sort, code = E0999)]
120 pub(super) struct IncompatibleSort {
121 #[primary_span]
122 #[label]
123 span: Span,
124 name: Symbol,
125 expected: rty::FuncSort,
126 found: rty::FuncSort,
127 }
128
129 impl IncompatibleSort {
130 pub(super) fn new(
131 span: Span,
132 name: Symbol,
133 expected: rty::FuncSort,
134 found: rty::FuncSort,
135 ) -> Self {
136 Self { span, name, expected, found }
137 }
138 }
139
140 #[derive(Diagnostic)]
141 #[diag(refineck_missing_assoc_reft, code = E0999)]
142 pub struct MissingAssocReft {
143 #[primary_span]
144 span: Span,
145 name: Symbol,
146 }
147
148 impl MissingAssocReft {
149 pub(crate) fn new(span: Span, name: Symbol) -> Self {
150 Self { span, name }
151 }
152 }
153
154 #[derive(Diagnostic)]
155 #[diag(refineck_invalid_assoc_reft, code = E0999)]
156 pub struct InvalidAssocReft {
157 #[primary_span]
158 span: Span,
159 trait_: String,
160 name: Symbol,
161 }
162
163 impl InvalidAssocReft {
164 pub(crate) fn new(span: Span, name: Symbol, trait_: String) -> Self {
165 Self { span, trait_, name }
166 }
167 }
168}