1use rustc_data_structures::fx::FxIndexMap;
2use rustc_hir::OpaqueTyOrigin;
3use rustc_hir::def_id::LocalDefId;
4use rustc_infer::infer::outlives::env::OutlivesEnvironment;
5use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
6use rustc_middle::ty::{
7 self, DefiningScopeKind, GenericArgKind, GenericArgs, OpaqueTypeKey, TyCtxt, TypeVisitableExt,
8 TypingMode, fold_regions,
9};
10use rustc_span::{ErrorGuaranteed, Span};
11
12use crate::errors::NonGenericOpaqueTypeParam;
13use crate::regions::OutlivesEnvironmentBuildExt;
14use crate::traits::ObligationCtxt;
15
16#[derive(Debug)]
17pub enum InvalidOpaqueTypeArgs<'tcx> {
18 AlreadyReported(ErrorGuaranteed),
19 NotAParam { opaque_type_key: OpaqueTypeKey<'tcx>, param_index: usize, span: Span },
20 DuplicateParam { opaque_type_key: OpaqueTypeKey<'tcx>, param_indices: Vec<usize>, span: Span },
21}
22impl From<ErrorGuaranteed> for InvalidOpaqueTypeArgs<'_> {
23 fn from(guar: ErrorGuaranteed) -> Self {
24 InvalidOpaqueTypeArgs::AlreadyReported(guar)
25 }
26}
27impl<'tcx> InvalidOpaqueTypeArgs<'tcx> {
28 pub fn report(self, infcx: &InferCtxt<'tcx>) -> ErrorGuaranteed {
29 let tcx = infcx.tcx;
30 match self {
31 InvalidOpaqueTypeArgs::AlreadyReported(guar) => guar,
32 InvalidOpaqueTypeArgs::NotAParam { opaque_type_key, param_index, span } => {
33 let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
34 let opaque_param = opaque_generics.param_at(param_index, tcx);
35 let kind = opaque_param.kind.descr();
36 infcx.dcx().emit_err(NonGenericOpaqueTypeParam {
37 arg: opaque_type_key.args[param_index],
38 kind,
39 span,
40 param_span: tcx.def_span(opaque_param.def_id),
41 })
42 }
43 InvalidOpaqueTypeArgs::DuplicateParam { opaque_type_key, param_indices, span } => {
44 let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
45 let descr = opaque_generics.param_at(param_indices[0], tcx).kind.descr();
46 let spans: Vec<_> = param_indices
47 .into_iter()
48 .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id))
49 .collect();
50 infcx
51 .dcx()
52 .struct_span_err(span, "non-defining opaque type use in defining scope")
53 .with_span_note(spans, format!("{descr} used multiple times"))
54 .emit()
55 }
56 }
57 }
58}
59
60pub fn check_opaque_type_parameter_valid<'tcx>(
65 infcx: &InferCtxt<'tcx>,
66 opaque_type_key: OpaqueTypeKey<'tcx>,
67 span: Span,
68 defining_scope_kind: DefiningScopeKind,
69) -> Result<(), InvalidOpaqueTypeArgs<'tcx>> {
70 let tcx = infcx.tcx;
71 let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id);
72 let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
73
74 if let DefiningScopeKind::MirBorrowck = defining_scope_kind {
77 infcx
78 .tcx
79 .type_of_opaque_hir_typeck(opaque_type_key.def_id)
80 .instantiate_identity()
81 .error_reported()?;
82 }
83
84 for (i, arg) in opaque_type_key.iter_captured_args(tcx) {
85 let arg_is_param = match arg.kind() {
86 GenericArgKind::Lifetime(lt) => match defining_scope_kind {
87 DefiningScopeKind::HirTypeck => continue,
88 DefiningScopeKind::MirBorrowck => {
89 matches!(lt.kind(), ty::ReEarlyParam(_) | ty::ReLateParam(_))
90 || (lt.is_static() && opaque_env.param_equal_static(i))
91 }
92 },
93 GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
94 GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)),
95 };
96
97 if arg_is_param {
98 let seen_where = seen_params.entry(arg).or_default();
102 if !seen_where.first().is_some_and(|&prev_i| opaque_env.params_equal(i, prev_i)) {
103 seen_where.push(i);
104 }
105 } else {
106 opaque_env.param_is_error(i)?;
108 return Err(InvalidOpaqueTypeArgs::NotAParam { opaque_type_key, param_index: i, span });
109 }
110 }
111
112 for (_, param_indices) in seen_params {
113 if param_indices.len() > 1 {
114 return Err(InvalidOpaqueTypeArgs::DuplicateParam {
115 opaque_type_key,
116 param_indices,
117 span,
118 });
119 }
120 }
121
122 Ok(())
123}
124
125struct LazyOpaqueTyEnv<'tcx> {
129 tcx: TyCtxt<'tcx>,
130 def_id: LocalDefId,
131
132 canonical_args: std::cell::OnceCell<ty::GenericArgsRef<'tcx>>,
138}
139
140impl<'tcx> LazyOpaqueTyEnv<'tcx> {
141 fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
142 Self { tcx, def_id, canonical_args: std::cell::OnceCell::new() }
143 }
144
145 fn param_equal_static(&self, param_index: usize) -> bool {
146 self.get_canonical_args()[param_index].expect_region().is_static()
147 }
148
149 fn params_equal(&self, param1: usize, param2: usize) -> bool {
150 let canonical_args = self.get_canonical_args();
151 canonical_args[param1] == canonical_args[param2]
152 }
153
154 fn param_is_error(&self, param_index: usize) -> Result<(), ErrorGuaranteed> {
155 self.get_canonical_args()[param_index].error_reported()
156 }
157
158 fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> {
159 if let Some(&canonical_args) = self.canonical_args.get() {
160 return canonical_args;
161 }
162
163 let &Self { tcx, def_id, .. } = self;
164 let origin = tcx.local_opaque_ty_origin(def_id);
165 let parent = match origin {
166 OpaqueTyOrigin::FnReturn { parent, .. }
167 | OpaqueTyOrigin::AsyncFn { parent, .. }
168 | OpaqueTyOrigin::TyAlias { parent, .. } => parent,
169 };
170 let param_env = tcx.param_env(parent);
171 let args = GenericArgs::identity_for_item(tcx, parent).extend_to(
172 tcx,
173 def_id.to_def_id(),
174 |param, _| {
175 tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into()
176 },
177 );
178
179 let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
182 let ocx = ObligationCtxt::new(&infcx);
183
184 let wf_tys = ocx.assumed_wf_types(param_env, parent).unwrap_or_else(|_| {
185 tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds");
186 Default::default()
187 });
188 let outlives_env = OutlivesEnvironment::new(&infcx, parent, param_env, wf_tys);
189
190 let mut seen = vec![tcx.lifetimes.re_static];
191 let canonical_args = fold_regions(tcx, args, |r1, _| {
192 if r1.is_error() {
193 r1
194 } else if let Some(&r2) = seen.iter().find(|&&r2| {
195 let free_regions = outlives_env.free_region_map();
196 free_regions.sub_free_regions(tcx, r1, r2)
197 && free_regions.sub_free_regions(tcx, r2, r1)
198 }) {
199 r2
200 } else {
201 seen.push(r1);
202 r1
203 }
204 });
205 self.canonical_args.set(canonical_args).unwrap();
206 canonical_args
207 }
208}