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