Skip to main content

flux_driver/collector/
extern_specs.rs

1use std::iter;
2
3use flux_middle::ExternSpecMappingErr;
4use flux_rustc_bridge::lowering;
5use flux_syntax::surface;
6use rustc_errors::Diagnostic;
7use rustc_hir as hir;
8use rustc_hir::{
9    BodyId, OwnerId,
10    def_id::{DefId, LocalDefId},
11};
12use rustc_middle::ty::{self, TyCtxt};
13use rustc_span::{ErrorGuaranteed, Span, symbol::kw};
14
15use super::{FluxAttrs, SpecCollector};
16
17type Result<T = ()> = std::result::Result<T, ErrorGuaranteed>;
18
19pub(super) struct ExternSpecCollector<'a, 'sess, 'tcx> {
20    inner: &'a mut SpecCollector<'sess, 'tcx>,
21    /// The block corresponding to the `const _: () = { ... }` annotated with `flux::extern_spec`
22    block: &'tcx hir::Block<'tcx>,
23}
24
25struct ExternImplItem {
26    impl_id: DefId,
27    item_id: DefId,
28}
29
30impl<'a, 'sess, 'tcx> ExternSpecCollector<'a, 'sess, 'tcx> {
31    pub(super) fn collect(inner: &'a mut SpecCollector<'sess, 'tcx>, body_id: BodyId) -> Result {
32        Self::new(inner, body_id)?.run()
33    }
34
35    fn new(inner: &'a mut SpecCollector<'sess, 'tcx>, body_id: BodyId) -> Result<Self> {
36        let body = inner.tcx.hir_body(body_id);
37        if let hir::ExprKind::Block(block, _) = body.value.kind {
38            Ok(Self { inner, block })
39        } else {
40            Err(inner
41                .errors
42                .emit(errors::MalformedExternSpec::new(body.value.span)))
43        }
44    }
45
46    fn run(mut self) -> Result {
47        let item = self.item_at(0)?;
48
49        let attrs = self
50            .inner
51            .parse_attrs_and_report_dups(item.owner_id.def_id)?;
52
53        match &item.kind {
54            hir::ItemKind::Fn { .. } => self.collect_extern_fn(item, attrs),
55            hir::ItemKind::Enum(_, _, enum_def) => {
56                self.collect_extern_enum(item.owner_id, enum_def, attrs)
57            }
58            hir::ItemKind::Struct(_, _, variant) => {
59                self.collect_extern_struct(item.owner_id, variant, attrs)
60            }
61            hir::ItemKind::Trait { bounds, items, .. } => {
62                self.collect_extern_trait(item.owner_id, bounds, items, attrs)
63            }
64            hir::ItemKind::Impl(impl_) => self.collect_extern_impl(item.owner_id, impl_, attrs),
65            _ => Err(self.malformed()),
66        }
67    }
68
69    fn collect_extern_fn(&mut self, item: &hir::Item, mut attrs: FluxAttrs) -> Result {
70        if attrs.has_attrs() {
71            let (sig, attr_span) = if let Some((sig, span)) = attrs.fn_sig_with_attr_span() {
72                (Some(sig), Some(span))
73            } else {
74                (None, None)
75            };
76            self.inner.check_fn_sig_name(item.owner_id, sig.as_ref())?;
77            let node_id = self.inner.next_node_id();
78            self.inner.insert_item(
79                item.owner_id,
80                surface::Item {
81                    attrs: attrs.into_attr_vec(),
82                    kind: surface::ItemKind::Fn(sig),
83                    node_id,
84                },
85            )?;
86            if let Some(span) = attr_span {
87                self.inner
88                    .specs
89                    .set_spec_attr_span(item.owner_id.def_id.to_def_id(), span);
90            }
91        }
92
93        let extern_id = self.extract_extern_id_from_fn(item)?;
94        self.insert_extern_id(item.owner_id.def_id, extern_id)?;
95        self.check_generics(item.owner_id, extern_id)?;
96
97        Ok(())
98    }
99
100    fn collect_extern_struct(
101        &mut self,
102        struct_id: OwnerId,
103        variant: &hir::VariantData,
104        attrs: FluxAttrs,
105    ) -> Result {
106        let dummy_struct = self.item_at(1)?;
107        self.inner.specs.insert_dummy(dummy_struct.owner_id.def_id);
108
109        let extern_id = self.extract_extern_id_from_struct(dummy_struct).unwrap();
110        self.insert_extern_id(struct_id.def_id, extern_id)?;
111        self.check_generics(struct_id, extern_id)?;
112
113        if let Some(ctor_id) = variant.ctor_def_id() {
114            self.inner.specs.insert_dummy(ctor_id);
115        }
116
117        self.inner.collect_struct_def(struct_id, attrs, variant)?;
118
119        Ok(())
120    }
121
122    fn collect_extern_enum(
123        &mut self,
124        enum_id: OwnerId,
125        enum_def: &hir::EnumDef,
126        attrs: FluxAttrs,
127    ) -> Result {
128        let dummy_struct = self.item_at(1)?;
129        self.inner.specs.insert_dummy(dummy_struct.owner_id.def_id);
130
131        let extern_id = self.extract_extern_id_from_struct(dummy_struct).unwrap();
132        self.insert_extern_id(enum_id.def_id, extern_id)?;
133        self.check_generics(enum_id, extern_id)?;
134
135        self.inner.collect_enum_def(enum_id, attrs, enum_def)?;
136
137        // Add stuff about Ctor
138        // Get the AdtDef for the enum
139        let extern_enum_def = self.tcx().adt_def(extern_id);
140
141        // Collect all constructor DefIds from variants
142        let extern_variants = extern_enum_def.variants();
143        let enum_variants = enum_def.variants;
144        let extern_len = extern_variants.len();
145        let enum_len = enum_variants.len();
146        if extern_len != enum_len {
147            let reason = format!("expected {extern_len:?} variants but only have {enum_len:?}");
148            return Err(self.invalid_enum_extern_spec(reason));
149        }
150        for (extern_variant, variant) in extern_enum_def.variants().iter().zip(enum_def.variants) {
151            if let Some(extern_ctor) = extern_variant.ctor_def_id()
152                && let Some(ctor) = variant.data.ctor_def_id()
153                && self.tcx().def_kind(extern_ctor) == self.tcx().def_kind(ctor)
154            {
155                self.insert_extern_id(ctor, extern_ctor)?;
156            } else {
157                let reason = format!(
158                    "extern variant `{}` incompatible with specified `{}`",
159                    extern_variant.ident(self.tcx()),
160                    rustc_hir_pretty::id_to_string(&self.tcx(), variant.hir_id)
161                );
162                return Err(self.invalid_enum_extern_spec(reason));
163            }
164        }
165        Ok(())
166    }
167
168    fn collect_extern_impl(
169        &mut self,
170        impl_id: OwnerId,
171        impl_: &hir::Impl,
172        attrs: FluxAttrs,
173    ) -> Result {
174        self.inner.collect_impl(impl_id, attrs)?;
175
176        let dummy_item = self.item_at(1)?;
177        self.inner.specs.insert_dummy(dummy_item.owner_id.def_id);
178
179        // If this is a trait impl compute the impl_id from the trait_ref
180        let mut impl_of_trait = None;
181        let mut local_trait_self_ty = None;
182        if let hir::ItemKind::Impl(dummy_impl) = &dummy_item.kind {
183            let (extern_id, self_ty) =
184                self.extract_extern_id_from_impl(dummy_item.owner_id, dummy_impl)?;
185            impl_of_trait = Some(extern_id);
186            local_trait_self_ty = Some(self_ty);
187
188            self.inner
189                .specs
190                .insert_dummy(self.item_at(2)?.owner_id.def_id);
191        }
192
193        let mut extern_impl_id = impl_of_trait;
194        for item_id in impl_.items {
195            let item = self.tcx().hir_impl_item(*item_id);
196            let extern_item = if let hir::ImplItemKind::Fn { .. } = item.kind {
197                let attrs = self
198                    .inner
199                    .parse_attrs_and_report_dups(item_id.owner_id.def_id)?;
200                self.collect_extern_impl_fn(impl_of_trait, item, attrs)?
201            } else {
202                continue;
203            };
204
205            if *extern_impl_id.get_or_insert(extern_item.impl_id) != extern_item.impl_id {
206                return Err(self.invalid_impl_block());
207            }
208        }
209
210        if let Some(extern_impl_id) = extern_impl_id {
211            self.check_generics(impl_id, extern_impl_id)?;
212            // For trait impls, check that the self type matches the external definition
213            if let Some(local_self_ty) = local_trait_self_ty {
214                self.check_extern_impl_self_ty(impl_id, local_self_ty, extern_impl_id)?;
215            }
216            self.insert_extern_id(impl_id.def_id, extern_impl_id)?;
217        }
218
219        Ok(())
220    }
221
222    fn collect_extern_impl_fn(
223        &mut self,
224        impl_of_trait: Option<DefId>,
225        item: &hir::ImplItem,
226        mut attrs: FluxAttrs,
227    ) -> Result<ExternImplItem> {
228        if attrs.has_attrs() {
229            let (sig, attr_span) = if let Some((sig, span)) = attrs.fn_sig_with_attr_span() {
230                (Some(sig), Some(span))
231            } else {
232                (None, None)
233            };
234            self.inner.check_fn_sig_name(item.owner_id, sig.as_ref())?;
235            let node_id = self.inner.next_node_id();
236            self.inner.insert_impl_item(
237                item.owner_id,
238                surface::ImplItemFn { attrs: attrs.into_attr_vec(), sig, node_id },
239            )?;
240            if let Some(span) = attr_span {
241                self.inner
242                    .specs
243                    .set_spec_attr_span(item.owner_id.def_id.to_def_id(), span);
244            }
245        }
246
247        let extern_impl_item = self.extract_extern_id_from_impl_fn(impl_of_trait, item)?;
248        self.insert_extern_id(item.owner_id.def_id, extern_impl_item.item_id)?;
249        self.check_generics(item.owner_id, extern_impl_item.item_id)?;
250
251        Ok(extern_impl_item)
252    }
253
254    fn collect_extern_trait(
255        &mut self,
256        trait_id: OwnerId,
257        bounds: &hir::GenericBounds,
258        items: &[hir::TraitItemId],
259        attrs: FluxAttrs,
260    ) -> Result {
261        self.inner.collect_trait(trait_id, attrs)?;
262
263        let extern_trait_id = self.extract_extern_id_from_trait(bounds)?;
264        self.insert_extern_id(trait_id.def_id, extern_trait_id)?;
265        self.check_generics(trait_id, extern_trait_id)?;
266
267        for item_id in items {
268            let item = self.tcx().hir_trait_item(*item_id);
269            if let hir::TraitItemKind::Fn { .. } = item.kind {
270                let attrs = self
271                    .inner
272                    .parse_attrs_and_report_dups(item.owner_id.def_id)?;
273                self.collect_extern_trait_fn(extern_trait_id, item, attrs)?;
274            } else {
275                continue;
276            }
277        }
278
279        Ok(())
280    }
281
282    fn collect_extern_trait_fn(
283        &mut self,
284        extern_trait_id: DefId,
285        item: &hir::TraitItem,
286        mut attrs: FluxAttrs,
287    ) -> Result {
288        let item_id = item.owner_id;
289        if attrs.has_attrs() {
290            let (sig, attr_span) = if let Some((sig, span)) = attrs.fn_sig_with_attr_span() {
291                (Some(sig), Some(span))
292            } else {
293                (None, None)
294            };
295            self.inner.check_fn_sig_name(item.owner_id, sig.as_ref())?;
296            let node_id = self.inner.next_node_id();
297            self.inner.insert_trait_item(
298                item.owner_id,
299                surface::TraitItemFn { attrs: attrs.into_attr_vec(), sig, node_id },
300            )?;
301            if let Some(span) = attr_span {
302                self.inner
303                    .specs
304                    .set_spec_attr_span(item.owner_id.def_id.to_def_id(), span);
305            }
306        }
307
308        let extern_fn_id = self.extract_extern_id_from_trait_fn(extern_trait_id, item)?;
309        self.insert_extern_id(item.owner_id.def_id, extern_fn_id)?;
310        self.check_generics(item_id, extern_fn_id)?;
311
312        Ok(())
313    }
314
315    fn extract_extern_id_from_struct(&self, item: &hir::Item) -> Result<DefId> {
316        if let hir::ItemKind::Struct(_, _, data) = item.kind
317            && let Some(extern_field) = data.fields().last()
318            && let ty = self.tcx().type_of(extern_field.def_id)
319            && let Some(adt_def) = ty.skip_binder().ty_adt_def()
320        {
321            Ok(adt_def.did())
322        } else {
323            Err(self.malformed())
324        }
325    }
326
327    fn extract_extern_id_from_fn(&self, item: &hir::Item) -> Result<DefId> {
328        if let hir::ItemKind::Fn { body, .. } = item.kind {
329            self.extract_callee_from_body(body)
330        } else {
331            Err(self.malformed())
332        }
333    }
334
335    fn extract_extern_id_from_impl_fn(
336        &self,
337        impl_of_trait: Option<DefId>,
338        item: &hir::ImplItem,
339    ) -> Result<ExternImplItem> {
340        if let hir::ImplItemKind::Fn(_, body_id) = item.kind {
341            let callee_id = self.extract_callee_from_body(body_id)?;
342            if let Some(extern_impl_id) = impl_of_trait {
343                let map = self.tcx().impl_item_implementor_ids(extern_impl_id);
344                if let Some(extern_item_id) = map.get(&callee_id) {
345                    Ok(ExternImplItem { impl_id: extern_impl_id, item_id: *extern_item_id })
346                } else {
347                    Err(self.item_not_in_trait_impl(item.owner_id, callee_id, extern_impl_id))
348                }
349            } else {
350                let opt_extern_impl_id = self.tcx().impl_of_assoc(callee_id);
351                if let Some(extern_impl_id) = opt_extern_impl_id {
352                    debug_assert!(!self.tcx().impl_is_of_trait(extern_impl_id));
353                    Ok(ExternImplItem { impl_id: extern_impl_id, item_id: callee_id })
354                } else {
355                    Err(self.invalid_item_in_inherent_impl(item.owner_id, callee_id))
356                }
357            }
358        } else {
359            Err(self.malformed())
360        }
361    }
362
363    fn extract_extern_id_from_trait(&self, bounds: &hir::GenericBounds) -> Result<DefId> {
364        if let Some(bound) = bounds.first()
365            && let Some(trait_ref) = bound.trait_ref()
366            && let Some(trait_id) = trait_ref.trait_def_id()
367        {
368            Ok(trait_id)
369        } else {
370            Err(self.malformed())
371        }
372    }
373
374    fn extract_extern_id_from_trait_fn(
375        &self,
376        trait_id: DefId,
377        item: &hir::TraitItem,
378    ) -> Result<DefId> {
379        if let hir::TraitItemKind::Fn(_, trait_fn) = item.kind
380            && let hir::TraitFn::Provided(body_id) = trait_fn
381        {
382            let callee_id = self.extract_callee_from_body(body_id)?;
383            if let Some(callee_trait_id) = self.tcx().trait_of_assoc(callee_id)
384                && trait_id == callee_trait_id
385            {
386                Ok(callee_id)
387            } else {
388                // I can't figure out how to trigger this via code generated with the extern spec
389                // macro that also type checks but leaving it here as a precaution.
390                Err(self.item_not_in_trait(item.owner_id, callee_id, trait_id))
391            }
392        } else {
393            Err(self.malformed())
394        }
395    }
396
397    fn extract_extern_id_from_impl(
398        &self,
399        impl_id: OwnerId,
400        impl_: &hir::Impl,
401    ) -> Result<(DefId, ty::Ty<'tcx>)> {
402        if let Some(item_id) = impl_.items.first()
403            && let hir::ImplItemKind::Fn { .. } = self.tcx().hir_impl_item(*item_id).kind
404            && let Some((clause, _)) = self
405                .tcx()
406                .clauses_of(item_id.owner_id.def_id)
407                .clauses
408                .first()
409            && let Some(poly_trait_pred) = clause.as_trait_clause()
410            && let Some(trait_pred) = poly_trait_pred.no_bound_vars()
411        {
412            let trait_ref = trait_pred.trait_ref;
413            let local_self_ty = trait_ref.self_ty();
414            lowering::resolve_trait_ref_impl_id(self.tcx(), impl_id.to_def_id(), trait_ref)
415                .map(|(impl_id, _)| (impl_id, local_self_ty))
416                .ok_or_else(|| self.cannot_resolve_trait_impl())
417        } else {
418            Err(self.malformed())
419        }
420    }
421
422    fn extract_callee_from_body(&self, body_id: hir::BodyId) -> Result<DefId> {
423        let owner = self.tcx().hir_body_owner_def_id(body_id);
424        let typeck = self.tcx().typeck(owner);
425        if let hir::ExprKind::Block(b, _) = self.tcx().hir_body(body_id).value.kind
426            && let Some(e) = b.expr
427        {
428            // Peel through an optional `unsafe { ... }` block
429            let call_expr =
430                if let hir::ExprKind::Block(inner_b, _) = e.kind { inner_b.expr } else { Some(e) };
431            if let Some(e) = call_expr
432                && let hir::ExprKind::Call(callee, _) = e.kind
433                && let rustc_middle::ty::FnDef(callee_id, _) =
434                    typeck.node_type(callee.hir_id).kind()
435            {
436                return Ok(*callee_id);
437            }
438        }
439        Err(self.malformed())
440    }
441
442    /// Returns the item inside the const block at position `i` starting from the end.
443    #[track_caller]
444    fn item_at(&self, i: usize) -> Result<&'tcx hir::Item<'tcx>> {
445        let stmts = self.block.stmts;
446        let index = stmts
447            .len()
448            .checked_sub(i + 1)
449            .ok_or_else(|| self.malformed())?;
450        let st = stmts.get(index).ok_or_else(|| self.malformed())?;
451        if let hir::StmtKind::Item(item_id) = st.kind {
452            Ok(self.tcx().hir_item(item_id))
453        } else {
454            Err(self.malformed())
455        }
456    }
457
458    fn insert_extern_id(&mut self, local_id: LocalDefId, extern_id: DefId) -> Result {
459        self.inner
460            .specs
461            .insert_extern_spec_id_mapping(local_id, extern_id)
462            .map_err(|err| {
463                match err {
464                    ExternSpecMappingErr::IsLocal(extern_id_local) => {
465                        self.emit(errors::ExternSpecForLocalDef {
466                            span: ident_or_def_span(self.tcx(), local_id),
467                            local_def_span: ident_or_def_span(self.tcx(), extern_id_local),
468                            name: self.tcx().def_path_str(extern_id),
469                        })
470                    }
471                    ExternSpecMappingErr::Dup(previous_extern_spec) => {
472                        self.emit(errors::DupExternSpec {
473                            span: ident_or_def_span(self.tcx(), local_id),
474                            previous_span: ident_or_def_span(self.tcx(), previous_extern_spec),
475                            name: self.tcx().def_path_str(extern_id),
476                        })
477                    }
478                }
479            })
480    }
481
482    fn check_generics(&mut self, local_id: OwnerId, extern_id: DefId) -> Result {
483        let tcx = self.tcx();
484        let local_params = &tcx.generics_of(local_id).own_params;
485        let extern_params = &tcx.generics_of(extern_id).own_params;
486
487        let mismatch = 'mismatch: {
488            if local_params.len() != extern_params.len() {
489                break 'mismatch true;
490            }
491            for (local_param, extern_param) in iter::zip(local_params, extern_params) {
492                if !cmp_generic_param_def(local_param, extern_param) {
493                    break 'mismatch true;
494                }
495                // We skip the self parameter because its id is the same as the trait's id, which
496                // has already been inserted.
497                if local_param.name != kw::SelfUpper {
498                    #[expect(clippy::disallowed_methods)]
499                    self.insert_extern_id(local_param.def_id.expect_local(), extern_param.def_id)?;
500                }
501            }
502            false
503        };
504        if mismatch {
505            let local_hir_generics = tcx.hir_get_generics(local_id.def_id).unwrap();
506            let span = local_hir_generics.span;
507            Err(self.emit(errors::MismatchedGenerics {
508                span,
509                extern_def: tcx.def_span(extern_id),
510                def_descr: tcx.def_descr(extern_id),
511            }))
512        } else {
513            Ok(())
514        }
515    }
516
517    fn check_extern_impl_self_ty(
518        &mut self,
519        local_impl_id: OwnerId,
520        local_self_ty: ty::Ty<'tcx>,
521        extern_impl_id: DefId,
522    ) -> Result {
523        let tcx = self.tcx();
524
525        // Get the self type from the external impl, in terms of the local impl's generics
526        let local_args = ty::GenericArgs::identity_for_item(tcx, local_impl_id);
527        let extern_self_ty = tcx
528            .type_of(extern_impl_id)
529            .instantiate(tcx, local_args)
530            .skip_norm_wip();
531
532        // Compare self types. `local_self_ty` is the user-written self type from the extern
533        // spec's trait_ref (not the `__FluxExternImplStruct` wrapper that the macro retargets
534        // the impl onto). After `check_generics` has verified the params match, raw Ty equality
535        // is sound for the cases we want to catch (concrete type mismatches like `Range<usize>`
536        // vs `Range<A>`).
537        if local_self_ty != extern_self_ty {
538            // Emit on the user's impl block (compiletest matches `//~ ERROR` against the
539            // primary span line). The dummy `__FluxExternImplStruct` wrapper's self_ty has
540            // a macro-generated span that doesn't land on user source.
541            let span = tcx.def_span(local_impl_id);
542            Err(self.emit(errors::MismatchedImplSelfTy {
543                span,
544                local_self_ty: local_self_ty.to_string(),
545                extern_self_ty: extern_self_ty.to_string(),
546                extern_impl_span: tcx.def_span(extern_impl_id),
547            }))
548        } else {
549            Ok(())
550        }
551    }
552
553    #[track_caller]
554    fn malformed(&self) -> ErrorGuaranteed {
555        self.emit(errors::MalformedExternSpec::new(self.block.span))
556    }
557
558    #[track_caller]
559    fn invalid_enum_extern_spec(&self, reason: String) -> ErrorGuaranteed {
560        self.emit(errors::InvalidEnumExternSpec::new(self.block.span, reason))
561    }
562
563    #[track_caller]
564    fn item_not_in_trait_impl(
565        &self,
566        local_id: OwnerId,
567        extern_id: DefId,
568        extern_impl_id: DefId,
569    ) -> ErrorGuaranteed {
570        let tcx = self.tcx();
571        self.emit(errors::ItemNotInTraitImpl {
572            span: ident_or_def_span(tcx, local_id),
573            name: tcx.def_path_str(extern_id),
574            extern_impl_span: tcx.def_span(extern_impl_id),
575        })
576    }
577
578    fn invalid_item_in_inherent_impl(
579        &self,
580        local_id: OwnerId,
581        extern_id: DefId,
582    ) -> ErrorGuaranteed {
583        let tcx = self.tcx();
584        self.emit(errors::InvalidItemInInherentImpl {
585            span: ident_or_def_span(tcx, local_id),
586            name: tcx.def_path_str(extern_id),
587            extern_item_span: tcx.def_span(extern_id),
588        })
589    }
590
591    #[track_caller]
592    fn invalid_impl_block(&self) -> ErrorGuaranteed {
593        self.emit(errors::InvalidImplBlock { span: self.block.span })
594    }
595
596    #[track_caller]
597    fn cannot_resolve_trait_impl(&self) -> ErrorGuaranteed {
598        self.emit(errors::CannotResolveTraitImpl { span: self.block.span })
599    }
600
601    #[track_caller]
602    fn item_not_in_trait(
603        &self,
604        local_id: OwnerId,
605        extern_id: DefId,
606        extern_trait_id: DefId,
607    ) -> ErrorGuaranteed {
608        let tcx = self.tcx();
609        self.emit(errors::ItemNotInTrait {
610            span: ident_or_def_span(tcx, local_id),
611            name: tcx.def_path_str(extern_id),
612            extern_trait_span: tcx.def_span(extern_trait_id),
613        })
614    }
615
616    #[track_caller]
617    fn emit<'b>(&'b self, err: impl Diagnostic<'b>) -> ErrorGuaranteed {
618        self.inner.errors.emit(err)
619    }
620
621    fn tcx(&self) -> TyCtxt<'tcx> {
622        self.inner.tcx
623    }
624}
625
626fn cmp_generic_param_def(a: &ty::GenericParamDef, b: &ty::GenericParamDef) -> bool {
627    if a.name != b.name && !b.is_anonymous_lifetime() {
628        return false;
629    }
630    if a.index != b.index {
631        return false;
632    }
633    matches!(
634        (&a.kind, &b.kind),
635        (ty::GenericParamDefKind::Lifetime, ty::GenericParamDefKind::Lifetime)
636            | (ty::GenericParamDefKind::Type { .. }, ty::GenericParamDefKind::Type { .. })
637            | (ty::GenericParamDefKind::Const { .. }, ty::GenericParamDefKind::Const { .. })
638    )
639}
640
641fn ident_or_def_span(tcx: TyCtxt, def_id: impl Into<DefId>) -> Span {
642    let def_id = def_id.into();
643    tcx.def_ident_span(def_id)
644        .unwrap_or_else(|| tcx.def_span(def_id))
645}
646
647mod errors {
648    use flux_errors::E0999;
649    use flux_macros::Diagnostic;
650    use rustc_span::Span;
651
652    #[derive(Diagnostic)]
653    #[diag("malformed extern_spec, this should never happen if you are using the extern_spec macro. Did you accidentally use the internal flux::extern_spec attribute?", code = E0999)]
654    pub(super) struct MalformedExternSpec {
655        #[primary_span]
656        span: Span,
657    }
658
659    impl MalformedExternSpec {
660        pub(super) fn new(span: Span) -> Self {
661            Self { span }
662        }
663    }
664
665    #[derive(Diagnostic)]
666    #[diag("invalid extern_spec for enum: {$reason}", code = E0999)]
667    pub(super) struct InvalidEnumExternSpec {
668        #[primary_span]
669        span: Span,
670        reason: String,
671    }
672
673    impl InvalidEnumExternSpec {
674        pub(super) fn new(span: Span, reason: String) -> Self {
675            Self { span, reason }
676        }
677    }
678
679    #[derive(Diagnostic)]
680    #[diag("cannot resolve trait implementation", code = E0999)]
681    #[note("this is probably a bug in Flux")]
682    pub(super) struct CannotResolveTraitImpl {
683        #[primary_span]
684        pub span: Span,
685    }
686
687    #[derive(Diagnostic)]
688    #[diag("invalid impl extern spec", code = E0999)]
689    pub(super) struct InvalidImplBlock {
690        #[primary_span]
691        #[label("items in this extern spec are not defined in the same extern impl block")]
692        pub span: Span,
693    }
694
695    #[derive(Diagnostic)]
696    #[diag("invalid extern spec for trait impl", code = E0999)]
697    pub(super) struct ItemNotInTraitImpl {
698        #[primary_span]
699        #[label("`{$name}` is not defined in extern trait impl")]
700        pub span: Span,
701        pub name: String,
702        #[note("extern trait impl defined here")]
703        pub extern_impl_span: Span,
704    }
705
706    #[derive(Diagnostic)]
707    #[diag("invalid extern spec for inherent impl", code = E0999)]
708    pub(super) struct InvalidItemInInherentImpl {
709        #[primary_span]
710        #[label("`{$name}` is not a member of an inherent impl")]
711        pub span: Span,
712        pub name: String,
713        #[note("`{$name}` defined here")]
714        pub extern_item_span: Span,
715    }
716
717    #[derive(Diagnostic)]
718    #[diag("invalid extern spec for trait", code = E0999)]
719    pub(super) struct ItemNotInTrait {
720        #[primary_span]
721        #[label("`{$name}` is not defined in extern trait")]
722        pub span: Span,
723        pub name: String,
724        #[note("extern trait defined here")]
725        pub extern_trait_span: Span,
726    }
727
728    #[derive(Diagnostic)]
729    #[diag("cannot add extern specs to local definition", code = E0999)]
730    pub(super) struct ExternSpecForLocalDef {
731        #[primary_span]
732        pub span: Span,
733        #[help(
734            "`{$name}` defined here in the same crate, use a detached spec https://flux-rs.github.io/flux/guide/specifications.html?highlight=detached#detached-specifications"
735        )]
736        pub local_def_span: Span,
737        pub name: String,
738    }
739
740    #[derive(Diagnostic)]
741    #[diag("multiple extern specs for `{$name}`", code = E0999)]
742    pub(super) struct DupExternSpec {
743        #[primary_span]
744        #[label("extern spec for `{$name}` redefined here")]
745        pub span: Span,
746        #[note("previous extern spec for `{$name}` defined here")]
747        pub previous_span: Span,
748        pub name: String,
749    }
750
751    #[derive(Diagnostic)]
752    #[diag("invalid extern spec for {$def_descr}", code = E0999)]
753    #[note(
754        "extern specs must exactly match the external definition, including the list of generic parameters and their names"
755    )]
756    pub(super) struct MismatchedGenerics {
757        #[primary_span]
758        #[label("generic parameters don't match the external {$def_descr}")]
759        pub span: Span,
760        #[label("external {$def_descr} found here")]
761        pub extern_def: Span,
762        pub def_descr: &'static str,
763    }
764
765    #[derive(Diagnostic)]
766    #[diag("invalid extern spec for trait impl", code = E0999)]
767    #[note(
768        "the self type in an extern spec for a trait impl must exactly match the external implementation"
769    )]
770    pub(super) struct MismatchedImplSelfTy {
771        #[primary_span]
772        #[label("self type `{$local_self_ty}` doesn't match the external trait impl")]
773        pub span: Span,
774        pub local_self_ty: String,
775        pub extern_self_ty: String,
776        #[label("external trait impl has self type `{$extern_self_ty}`")]
777        pub extern_impl_span: Span,
778    }
779}