Line data Source code
1 : // Copyright (C) 2020-2026 Free Software Foundation, Inc.
2 :
3 : // This file is part of GCC.
4 :
5 : // GCC is free software; you can redistribute it and/or modify it under
6 : // the terms of the GNU General Public License as published by the Free
7 : // Software Foundation; either version 3, or (at your option) any later
8 : // version.
9 :
10 : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 : // for more details.
14 :
15 : // You should have received a copy of the GNU General Public License
16 : // along with GCC; see the file COPYING3. If not see
17 : // <http://www.gnu.org/licenses/>.
18 :
19 : #include "rust-diagnostics.h"
20 : #include "rust-hir-map.h"
21 : #include "rust-hir-path.h"
22 : #include "rust-hir-type-check-expr.h"
23 : #include "rust-hir-type-check-type.h"
24 : #include "rust-hir-type-check-item.h"
25 : #include "rust-hir-trait-resolve.h"
26 : #include "rust-rib.h"
27 : #include "rust-substitution-mapper.h"
28 : #include "rust-hir-path-probe.h"
29 : #include "rust-type-util.h"
30 : #include "rust-hir-type-bounds.h"
31 : #include "rust-hir-item.h"
32 : #include "rust-session-manager.h"
33 : #include "rust-finalized-name-resolution-context.h"
34 :
35 : namespace Rust {
36 : namespace Resolver {
37 :
38 : void
39 116 : TypeCheckExpr::visit (HIR::QualifiedPathInExpression &expr)
40 : {
41 116 : HIR::QualifiedPathType qual_path_type = expr.get_path_type ();
42 116 : TyTy::BaseType *root = TypeCheckType::Resolve (qual_path_type.get_type ());
43 116 : if (root->get_kind () == TyTy::TypeKind::ERROR)
44 : return;
45 :
46 116 : if (!qual_path_type.has_as_clause ())
47 : {
48 34 : NodeId root_resolved_node_id = UNKNOWN_NODEID;
49 34 : resolve_segments (root_resolved_node_id, expr.get_segments (), 0, root,
50 : expr.get_mappings (), expr.get_locus ());
51 34 : return;
52 : }
53 :
54 : // Resolve the trait now
55 82 : HIR::TypePath &trait_path_ref = qual_path_type.get_trait ();
56 82 : TraitReference *trait_ref = TraitResolver::Resolve (trait_path_ref);
57 82 : if (trait_ref->is_error ())
58 : return;
59 :
60 : // does this type actually implement this type-bound?
61 82 : if (!TypeBoundsProbe::is_bound_satisfied_for_type (root, trait_ref))
62 : return;
63 :
64 : // then we need to look at the next segment to create perform the correct
65 : // projection type
66 80 : if (expr.get_segments ().empty ())
67 : return;
68 :
69 : // get the predicate for the bound
70 80 : auto specified_bound
71 80 : = get_predicate_from_bound (trait_path_ref, qual_path_type.get_type ());
72 80 : if (specified_bound.is_error ())
73 : return;
74 :
75 : // inherit the bound
76 160 : root->inherit_bounds ({specified_bound});
77 :
78 : // lookup the associated item from the specified bound
79 80 : HIR::PathExprSegment &item_seg = expr.get_segments ().at (0);
80 80 : HIR::PathIdentSegment item_seg_identifier = item_seg.get_segment ();
81 80 : tl::optional<TyTy::TypeBoundPredicateItem> item
82 80 : = specified_bound.lookup_associated_item (item_seg_identifier.to_string ());
83 80 : if (!item.has_value ())
84 : {
85 0 : rust_error_at (item_seg.get_locus (), "unknown associated item");
86 0 : return;
87 : }
88 :
89 : // we try to look for the real impl item if possible
90 80 : HIR::ImplItem *impl_item = nullptr;
91 :
92 : // lookup the associated impl trait for this if we can (it might be generic)
93 80 : AssociatedImplTrait *associated_impl_trait
94 80 : = lookup_associated_impl_block (specified_bound, root);
95 80 : if (associated_impl_trait != nullptr)
96 : {
97 76 : for (auto &i :
98 76 : associated_impl_trait->get_impl_block ()->get_impl_items ())
99 : {
100 39 : bool found = i->get_impl_item_name ().compare (
101 39 : item_seg_identifier.to_string ())
102 39 : == 0;
103 39 : if (found)
104 : {
105 17 : impl_item = i.get ();
106 17 : break;
107 : }
108 : }
109 : }
110 :
111 117 : NodeId root_resolved_node_id = UNKNOWN_NODEID;
112 54 : if (impl_item == nullptr)
113 : {
114 : // this may be valid as there could be a default trait implementation here
115 : // and we dont need to worry if the trait item is actually implemented or
116 : // not because this will have already been validated as part of the trait
117 : // impl block
118 63 : infered = item->get_tyty_for_receiver (root);
119 63 : root_resolved_node_id
120 63 : = item->get_raw_item ()->get_mappings ().get_nodeid ();
121 : }
122 : else
123 : {
124 17 : HirId impl_item_id = impl_item->get_impl_mappings ().get_hirid ();
125 17 : bool ok = query_type (impl_item_id, &infered);
126 17 : if (!ok)
127 : {
128 : // FIXME
129 : // I think query_type should error if required here anyway
130 : return;
131 : }
132 :
133 17 : root_resolved_node_id = impl_item->get_impl_mappings ().get_nodeid ();
134 : }
135 :
136 : // turbo-fish segment path::<ty>
137 80 : if (item_seg.has_generic_args ())
138 : {
139 0 : if (!infered->has_substitutions_defined ())
140 : {
141 0 : rust_error_at (item_seg.get_locus (),
142 : "substitutions not supported for %s",
143 0 : infered->as_string ().c_str ());
144 0 : infered = new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
145 0 : return;
146 : }
147 0 : std::vector<TyTy::Region> regions;
148 :
149 0 : infered = SubstMapper::Resolve (infered, expr.get_locus (),
150 0 : &item_seg.get_generic_args (),
151 0 : context->regions_from_generic_args (
152 0 : item_seg.get_generic_args ()));
153 0 : }
154 :
155 : // continue on as a path-in-expression
156 80 : bool fully_resolved = expr.get_segments ().size () <= 1;
157 80 : if (fully_resolved)
158 : {
159 80 : auto &nr_ctx = Resolver2_0::FinalizedNameResolutionContext::get ();
160 :
161 80 : nr_ctx.map_usage (Resolver2_0::Usage (expr.get_mappings ().get_nodeid ()),
162 80 : Resolver2_0::Definition (root_resolved_node_id),
163 : Resolver2_0::Namespace::Values);
164 80 : return;
165 : }
166 :
167 0 : resolve_segments (root_resolved_node_id, expr.get_segments (), 1, infered,
168 : expr.get_mappings (), expr.get_locus ());
169 196 : }
170 :
171 : void
172 50565 : TypeCheckExpr::visit (HIR::PathInExpression &expr)
173 : {
174 50565 : NodeId resolved_node_id = UNKNOWN_NODEID;
175 50565 : if (expr.is_lang_item ())
176 : {
177 139 : auto lookup
178 139 : = Analysis::Mappings::get ().get_lang_item_node (expr.get_lang_item ());
179 139 : auto hir_id = mappings.lookup_node_to_hir (lookup);
180 :
181 : // We can type resolve the path in expression easily as it is a lang
182 : // item path, but we still need to setup the various generics and
183 : // substitutions
184 :
185 : // FIXME: We probably need to check *if* the type needs substitutions
186 : // or not
187 139 : if (LangItem::IsEnumVariant (expr.get_lang_item ()))
188 : {
189 106 : std::pair<HIR::Enum *, HIR::EnumItem *> enum_item_lookup
190 106 : = mappings.lookup_hir_enumitem (*hir_id);
191 212 : bool enum_item_ok = enum_item_lookup.first != nullptr
192 106 : && enum_item_lookup.second != nullptr;
193 0 : rust_assert (enum_item_ok);
194 :
195 106 : HirId variant_id
196 106 : = enum_item_lookup.second->get_mappings ().get_hirid ();
197 :
198 106 : HIR::EnumItem *enum_item = enum_item_lookup.second;
199 106 : resolved_node_id = enum_item->get_mappings ().get_nodeid ();
200 :
201 : // insert the id of the variant we are resolved to
202 106 : context->insert_variant_definition (expr.get_mappings ().get_hirid (),
203 : variant_id);
204 :
205 106 : query_type (variant_id, &infered);
206 106 : infered = SubstMapper::InferSubst (infered, expr.get_locus ());
207 : }
208 : else
209 : {
210 33 : TyTy::BaseType *resolved = nullptr;
211 33 : context->lookup_type (*hir_id, &resolved);
212 :
213 33 : rust_assert (resolved);
214 :
215 33 : query_type (*hir_id, &infered);
216 :
217 33 : infered = SubstMapper::InferSubst (resolved, expr.get_locus ());
218 : }
219 :
220 : // FIXME: also we probably need to insert resolved types in the name
221 : // resolver here
222 : }
223 : else
224 : {
225 50426 : size_t offset = -1;
226 50426 : TyTy::BaseType *tyseg
227 50426 : = resolve_root_path (expr, &offset, &resolved_node_id);
228 50426 : if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
229 49039 : return;
230 :
231 50411 : bool fully_resolved = offset == expr.get_segments ().size ();
232 50411 : if (fully_resolved)
233 : {
234 49024 : infered = tyseg;
235 49024 : return;
236 : }
237 :
238 1387 : resolve_segments (resolved_node_id, expr.get_segments (), offset, tyseg,
239 : expr.get_mappings (), expr.get_locus ());
240 : }
241 : }
242 :
243 : TyTy::BaseType *
244 50426 : TypeCheckExpr::resolve_root_path (HIR::PathInExpression &expr, size_t *offset,
245 : NodeId *root_resolved_node_id)
246 : {
247 50426 : TyTy::BaseType *root_tyty = nullptr;
248 50426 : *offset = 0;
249 108515 : for (size_t i = 0; i < expr.get_num_segments (); i++)
250 : {
251 59488 : HIR::PathExprSegment &seg = expr.get_segments ().at (i);
252 :
253 59488 : bool have_more_segments = (expr.get_num_segments () - 1 != i);
254 59488 : bool is_root = *offset == 0;
255 59488 : NodeId ast_node_id = seg.get_mappings ().get_nodeid ();
256 :
257 59488 : auto &nr_ctx = Resolver2_0::FinalizedNameResolutionContext::get ();
258 :
259 : // lookup the reference_node_id
260 59488 : NodeId ref_node_id;
261 59488 : if (auto res = nr_ctx.lookup (ast_node_id, Resolver2_0::Namespace::Types,
262 59488 : Resolver2_0::Namespace::Values))
263 : {
264 58098 : ref_node_id = res->id;
265 : }
266 : else
267 : {
268 1390 : if (root_tyty != nullptr && *offset > 0)
269 : {
270 : // then we can let the impl path probe take over now
271 1390 : return root_tyty;
272 : }
273 :
274 2 : rust_error_at (seg.get_locus (),
275 : "failed to type resolve root segment");
276 2 : return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
277 : }
278 :
279 : // node back to HIR
280 58098 : tl::optional<HirId> hid = mappings.lookup_node_to_hir (ref_node_id);
281 58098 : if (!hid.has_value ())
282 : {
283 0 : rust_error_at (seg.get_locus (), "456 reverse lookup failure");
284 0 : rust_debug_loc (seg.get_locus (),
285 : "failure with [%s] mappings [%s] ref_node_id [%u]",
286 0 : seg.to_string ().c_str (),
287 0 : seg.get_mappings ().as_string ().c_str (),
288 : ref_node_id);
289 :
290 0 : return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
291 : }
292 58098 : auto ref = hid.value ();
293 :
294 58098 : auto seg_is_module = mappings.lookup_module (ref).has_value ();
295 58098 : auto seg_is_crate = mappings.is_local_hirid_crate (ref);
296 58098 : auto seg_is_pattern = mappings.lookup_hir_pattern (ref).has_value ();
297 58098 : auto seg_is_self = is_root && !have_more_segments
298 100911 : && seg.get_segment ().to_string () == "self";
299 58098 : if (seg_is_module || seg_is_crate)
300 : {
301 : // A::B::C::this_is_a_module::D::E::F
302 : // ^^^^^^^^^^^^^^^^
303 : // Currently handling this.
304 3634 : if (have_more_segments)
305 : {
306 3633 : (*offset)++;
307 3633 : continue;
308 : }
309 :
310 : // In the case of :
311 : // A::B::C::this_is_a_module
312 : // ^^^^^^^^^^^^^^^^
313 : // This is an error, we are not expecting a module.
314 1 : rust_error_at (seg.get_locus (), "expected value");
315 1 : return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
316 : }
317 :
318 54464 : TyTy::BaseType *lookup = nullptr;
319 54464 : if (!query_type (ref, &lookup))
320 : {
321 4 : if (is_root || root_tyty == nullptr)
322 : {
323 8 : rust_error_at (expr.get_locus (), ErrorCode::E0425,
324 : "cannot find value %qs in this scope",
325 8 : expr.as_simple_path ().as_string ().c_str ());
326 :
327 4 : return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
328 : }
329 : return root_tyty;
330 : }
331 :
332 : // is it an enum item?
333 54460 : std::pair<HIR::Enum *, HIR::EnumItem *> enum_item_lookup
334 54460 : = mappings.lookup_hir_enumitem (ref);
335 113087 : bool is_enum_item = enum_item_lookup.first != nullptr
336 54460 : && enum_item_lookup.second != nullptr;
337 4167 : if (is_enum_item)
338 : {
339 4167 : HirId expr_id = expr.get_mappings ().get_hirid ();
340 4167 : HirId variant_id
341 4167 : = enum_item_lookup.second->get_mappings ().get_hirid ();
342 4167 : context->insert_variant_definition (expr_id, variant_id);
343 : }
344 :
345 : // if we have a previous segment type
346 54460 : if (root_tyty != nullptr)
347 : {
348 : // if this next segment needs substitution we must apply the
349 : // previous type arguments
350 : //
351 : // such as: GenericStruct::<_>::new(123, 456)
352 4041 : if (lookup->needs_generic_substitutions ())
353 : {
354 1643 : if (!root_tyty->needs_generic_substitutions ())
355 : {
356 1643 : auto used_args_in_prev_segment
357 1643 : = GetUsedSubstArgs::From (root_tyty);
358 1643 : lookup
359 1643 : = SubstMapperInternal::Resolve (lookup,
360 : used_args_in_prev_segment);
361 1643 : }
362 : }
363 : }
364 :
365 : // turbo-fish segment path::<ty>
366 54460 : if (seg.has_generic_args ())
367 : {
368 839 : lookup = SubstMapper::Resolve (lookup, expr.get_locus (),
369 839 : &seg.get_generic_args (),
370 839 : context->regions_from_generic_args (
371 839 : seg.get_generic_args ()));
372 839 : if (lookup->get_kind () == TyTy::TypeKind::ERROR)
373 4 : return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
374 : }
375 53621 : else if (lookup->needs_generic_substitutions () && !seg_is_pattern
376 53621 : && !seg_is_self)
377 : {
378 5453 : lookup = SubstMapper::InferSubst (lookup, expr.get_locus ());
379 : }
380 :
381 54456 : *root_resolved_node_id = ref_node_id;
382 54456 : *offset = *offset + 1;
383 54456 : root_tyty = lookup;
384 : }
385 :
386 : return root_tyty;
387 : }
388 :
389 : void
390 1421 : TypeCheckExpr::resolve_segments (NodeId root_resolved_node_id,
391 : std::vector<HIR::PathExprSegment> &segments,
392 : size_t offset, TyTy::BaseType *tyseg,
393 : const Analysis::NodeMapping &expr_mappings,
394 : location_t expr_locus)
395 : {
396 1421 : NodeId resolved_node_id = root_resolved_node_id;
397 1421 : TyTy::BaseType *prev_segment = tyseg;
398 1421 : bool receiver_is_generic = prev_segment->get_kind () == TyTy::TypeKind::PARAM;
399 1421 : bool receiver_is_dyn = prev_segment->get_kind () == TyTy::TypeKind::DYNAMIC;
400 :
401 2839 : for (size_t i = offset; i < segments.size (); i++)
402 : {
403 1421 : HIR::PathExprSegment &seg = segments.at (i);
404 1421 : bool probe_impls = !receiver_is_generic;
405 :
406 : // probe the path is done in two parts one where we search impls if no
407 : // candidate is found then we search extensions from traits
408 1421 : auto candidates
409 1421 : = PathProbeType::Probe (prev_segment, seg.get_segment (), probe_impls,
410 : false /*probe_bounds*/,
411 1421 : true /*ignore_mandatory_trait_items*/);
412 1421 : if (candidates.size () == 0)
413 : {
414 519 : candidates
415 519 : = PathProbeType::Probe (prev_segment, seg.get_segment (), false,
416 : true /*probe_bounds*/,
417 519 : false /*ignore_mandatory_trait_items*/);
418 :
419 519 : if (candidates.size () == 0)
420 : {
421 1 : rust_error_at (
422 : seg.get_locus (),
423 : "failed to resolve path segment using an impl Probe");
424 1 : return;
425 : }
426 : }
427 :
428 1420 : if (candidates.size () > 1)
429 : {
430 1 : ReportMultipleCandidateError::Report (candidates, seg.get_segment (),
431 : seg.get_locus ());
432 1 : return;
433 : }
434 :
435 1419 : auto &candidate = *candidates.begin ();
436 1419 : prev_segment = tyseg;
437 1419 : tyseg = candidate.ty;
438 :
439 1419 : HIR::ImplBlock *associated_impl_block = nullptr;
440 1419 : if (candidate.is_enum_candidate ())
441 : {
442 1 : const TyTy::VariantDef *variant = candidate.item.enum_field.variant;
443 :
444 1 : HirId variant_id = variant->get_id ();
445 1 : std::pair<HIR::Enum *, HIR::EnumItem *> enum_item_lookup
446 1 : = mappings.lookup_hir_enumitem (variant_id);
447 2 : bool enum_item_ok = enum_item_lookup.first != nullptr
448 1 : && enum_item_lookup.second != nullptr;
449 0 : rust_assert (enum_item_ok);
450 :
451 1 : HIR::EnumItem *enum_item = enum_item_lookup.second;
452 1 : resolved_node_id = enum_item->get_mappings ().get_nodeid ();
453 :
454 : // insert the id of the variant we are resolved to
455 1 : context->insert_variant_definition (expr_mappings.get_hirid (),
456 : variant_id);
457 : }
458 1418 : else if (candidate.is_impl_candidate ())
459 : {
460 900 : resolved_node_id
461 900 : = candidate.item.impl.impl_item->get_impl_mappings ().get_nodeid ();
462 :
463 900 : associated_impl_block = candidate.item.impl.parent;
464 : }
465 : else
466 : {
467 518 : resolved_node_id
468 518 : = candidate.item.trait.item_ref->get_mappings ().get_nodeid ();
469 :
470 : // lookup the associated-impl-trait
471 518 : HIR::ImplBlock *impl = candidate.item.trait.impl;
472 518 : if (impl != nullptr)
473 : {
474 : // get the associated impl block
475 : associated_impl_block = impl;
476 : }
477 : }
478 :
479 1419 : if (associated_impl_block != nullptr && !receiver_is_dyn)
480 : {
481 : // unify the segments receiver against the impls self so
482 : // infer vars on either side get pinned
483 944 : auto mappings = TyTy::SubstitutionArgumentMappings::error ();
484 944 : TyTy::BaseType *impl_block_ty
485 944 : = TypeCheckItem::ResolveImplBlockSelfWithInference (
486 : *associated_impl_block, seg.get_locus (), &mappings);
487 :
488 944 : if (!mappings.is_error ())
489 174 : tyseg = SubstMapperInternal::Resolve (tyseg, mappings);
490 :
491 1888 : prev_segment = unify_site (seg.get_mappings ().get_hirid (),
492 944 : TyTy::TyWithLocation (prev_segment),
493 944 : TyTy::TyWithLocation (impl_block_ty),
494 : seg.get_locus ());
495 944 : if (prev_segment->get_kind () == TyTy::TypeKind::ERROR)
496 1 : return;
497 944 : }
498 :
499 1418 : if (seg.has_generic_args ())
500 : {
501 46 : rust_debug_loc (seg.get_locus (), "applying segment generics: %s",
502 46 : tyseg->as_string ().c_str ());
503 46 : tyseg
504 46 : = SubstMapper::Resolve (tyseg, expr_locus, &seg.get_generic_args (),
505 46 : context->regions_from_generic_args (
506 46 : seg.get_generic_args ()));
507 46 : if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
508 : return;
509 : }
510 1372 : else if (tyseg->needs_generic_substitutions () && !receiver_is_generic)
511 : {
512 394 : location_t locus = seg.get_locus ();
513 394 : tyseg = SubstMapper::InferSubst (tyseg, locus);
514 394 : if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
515 : return;
516 : }
517 1421 : }
518 :
519 1418 : rust_assert (resolved_node_id != UNKNOWN_NODEID);
520 :
521 1418 : auto &nr_ctx = Resolver2_0::FinalizedNameResolutionContext::get ();
522 :
523 1418 : nr_ctx.map_usage (Resolver2_0::Usage (expr_mappings.get_nodeid ()),
524 1418 : Resolver2_0::Definition (resolved_node_id),
525 : Resolver2_0::Namespace::Values);
526 :
527 1418 : infered = tyseg;
528 : }
529 :
530 : } // namespace Resolver
531 : } // namespace Rust
|