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-expr.h"
29 : #include "rust-hir-path-probe-type.h"
30 : #include "rust-type-util.h"
31 : #include "rust-hir-type-bounds.h"
32 : #include "rust-hir-item.h"
33 : #include "rust-finalized-name-resolution-context.h"
34 :
35 : namespace Rust {
36 : namespace Resolver {
37 :
38 : void
39 124 : TypeCheckExpr::visit (HIR::QualifiedPathInExpression &expr)
40 : {
41 124 : HIR::QualifiedPathType qual_path_type = expr.get_path_type ();
42 124 : TyTy::BaseType *root = TypeCheckType::Resolve (qual_path_type.get_type ());
43 124 : if (root->get_kind () == TyTy::TypeKind::ERROR)
44 : return;
45 :
46 124 : if (!qual_path_type.has_as_clause ())
47 : {
48 42 : NodeId root_resolved_node_id = UNKNOWN_NODEID;
49 42 : resolve_segments (root_resolved_node_id, expr.get_segments (), 0, root,
50 : expr.get_mappings (), expr.get_locus ());
51 42 : 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 80 : root->inherit_bound (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 22 : for (auto &i :
98 75 : associated_impl_trait->get_impl_block ()->get_impl_items ())
99 : {
100 38 : bool found = i->get_impl_item_name ().compare (
101 38 : item_seg_identifier.to_string ())
102 38 : == 0;
103 38 : if (found)
104 : {
105 16 : impl_item = i.get ();
106 16 : break;
107 : }
108 : }
109 : }
110 :
111 117 : NodeId root_resolved_node_id = UNKNOWN_NODEID;
112 53 : 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 64 : infered = item->get_tyty_for_receiver (root);
119 64 : root_resolved_node_id
120 64 : = item->get_raw_item ()->get_mappings ().get_nodeid ();
121 : }
122 : else
123 : {
124 16 : HirId impl_item_id = impl_item->get_impl_mappings ().get_hirid ();
125 16 : bool ok = query_type (impl_item_id, &infered);
126 16 : if (!ok)
127 : {
128 : // FIXME
129 : // I think query_type should error if required here anyway
130 : return;
131 : }
132 :
133 16 : 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 204 : }
170 :
171 : void
172 54659 : TypeCheckExpr::visit (HIR::PathInExpression &expr)
173 : {
174 54659 : NodeId resolved_node_id = UNKNOWN_NODEID;
175 54659 : if (expr.is_lang_item ())
176 : {
177 178 : auto lookup
178 178 : = Analysis::Mappings::get ().get_lang_item_node (expr.get_lang_item ());
179 178 : 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 178 : if (LangItem::IsEnumVariant (expr.get_lang_item ()))
188 : {
189 122 : std::pair<HIR::Enum *, HIR::EnumItem *> enum_item_lookup
190 122 : = mappings.lookup_hir_enumitem (*hir_id);
191 244 : bool enum_item_ok = enum_item_lookup.first != nullptr
192 122 : && enum_item_lookup.second != nullptr;
193 0 : rust_assert (enum_item_ok);
194 :
195 122 : HirId variant_id
196 122 : = enum_item_lookup.second->get_mappings ().get_hirid ();
197 :
198 122 : HIR::EnumItem *enum_item = enum_item_lookup.second;
199 122 : resolved_node_id = enum_item->get_mappings ().get_nodeid ();
200 :
201 : // insert the id of the variant we are resolved to
202 122 : context->insert_variant_definition (expr.get_mappings ().get_hirid (),
203 : variant_id);
204 :
205 122 : query_type (variant_id, &infered);
206 122 : infered = SubstMapper::InferSubst (infered, expr.get_locus ());
207 : }
208 : else
209 : {
210 56 : TyTy::BaseType *resolved = nullptr;
211 56 : auto trait_item = mappings.lookup_hir_trait_item (*hir_id);
212 56 : if (trait_item.has_value ())
213 : {
214 49 : HIR::Trait *trait = mappings.lookup_trait_item_mapping (*hir_id);
215 49 : rust_assert (trait != nullptr);
216 :
217 49 : TraitReference *trait_ref = TraitResolver::Resolve (*trait);
218 49 : if (trait_ref->is_error ())
219 0 : return;
220 :
221 49 : TraitItemReference *trait_item_ref = nullptr;
222 49 : bool ok = trait_ref->lookup_hir_trait_item (**trait_item,
223 : &trait_item_ref);
224 49 : rust_assert (ok);
225 49 : resolved = trait_item_ref->get_tyty ();
226 : }
227 7 : else if (!query_type (*hir_id, &resolved))
228 : return;
229 :
230 56 : if (resolved == nullptr
231 56 : || resolved->get_kind () == TyTy::TypeKind::ERROR)
232 : return;
233 :
234 56 : infered = SubstMapper::InferSubst (resolved, expr.get_locus ());
235 : }
236 :
237 : // FIXME: also we probably need to insert resolved types in the name
238 : // resolver here
239 : }
240 : else
241 : {
242 54481 : size_t offset = -1;
243 54481 : TyTy::BaseType *tyseg
244 54481 : = resolve_root_path (expr, &offset, &resolved_node_id);
245 54481 : if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
246 52968 : return;
247 :
248 54465 : bool fully_resolved = offset == expr.get_segments ().size ();
249 54465 : if (fully_resolved)
250 : {
251 52952 : infered = tyseg;
252 52952 : return;
253 : }
254 :
255 1513 : resolve_segments (resolved_node_id, expr.get_segments (), offset, tyseg,
256 : expr.get_mappings (), expr.get_locus ());
257 : }
258 : }
259 :
260 : TyTy::BaseType *
261 54481 : TypeCheckExpr::resolve_root_path (HIR::PathInExpression &expr, size_t *offset,
262 : NodeId *root_resolved_node_id)
263 : {
264 54481 : TyTy::BaseType *root_tyty = nullptr;
265 54481 : *offset = 0;
266 117941 : for (size_t i = 0; i < expr.get_num_segments (); i++)
267 : {
268 64985 : HIR::PathExprSegment &seg = expr.get_segments ().at (i);
269 :
270 64985 : bool have_more_segments = (expr.get_num_segments () - 1 != i);
271 64985 : bool is_root = *offset == 0;
272 64985 : NodeId ast_node_id = seg.get_mappings ().get_nodeid ();
273 :
274 64985 : auto &nr_ctx = Resolver2_0::FinalizedNameResolutionContext::get ();
275 :
276 : // lookup the reference_node_id
277 64985 : NodeId ref_node_id;
278 64985 : if (auto res = nr_ctx.lookup (ast_node_id, Resolver2_0::Namespace::Types,
279 64985 : Resolver2_0::Namespace::Values))
280 : {
281 63469 : ref_node_id = res->id;
282 : }
283 : else
284 : {
285 1516 : if (root_tyty != nullptr && *offset > 0)
286 : {
287 : // then we can let the impl path probe take over now
288 1516 : return root_tyty;
289 : }
290 :
291 2 : rust_error_at (seg.get_locus (),
292 : "failed to type resolve root segment");
293 2 : return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
294 : }
295 :
296 : // node back to HIR
297 63469 : tl::optional<HirId> hid = mappings.lookup_node_to_hir (ref_node_id);
298 63469 : if (!hid.has_value ())
299 : {
300 0 : rust_error_at (seg.get_locus (), "456 reverse lookup failure");
301 0 : rust_debug_loc (seg.get_locus (),
302 : "failure with [%s] mappings [%s] ref_node_id [%u]",
303 : seg.to_string ().c_str (),
304 : seg.get_mappings ().as_string ().c_str (),
305 : ref_node_id);
306 :
307 0 : return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
308 : }
309 63469 : auto ref = hid.value ();
310 :
311 63469 : auto seg_is_module = mappings.lookup_module (ref).has_value ();
312 63469 : auto seg_is_crate = mappings.is_local_hirid_crate (ref);
313 63469 : auto seg_is_pattern = mappings.lookup_hir_pattern (ref).has_value ();
314 63469 : auto seg_is_self = is_root && !have_more_segments
315 109519 : && seg.get_segment ().to_string () == "self";
316 63469 : if (seg_is_module || seg_is_crate)
317 : {
318 : // A::B::C::this_is_a_module::D::E::F
319 : // ^^^^^^^^^^^^^^^^
320 : // Currently handling this.
321 4760 : if (have_more_segments)
322 : {
323 4759 : (*offset)++;
324 4759 : continue;
325 : }
326 :
327 : // In the case of :
328 : // A::B::C::this_is_a_module
329 : // ^^^^^^^^^^^^^^^^
330 : // This is an error, we are not expecting a module.
331 1 : rust_error_at (seg.get_locus (), "expected value");
332 1 : return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
333 : }
334 :
335 58709 : TyTy::BaseType *lookup = nullptr;
336 58709 : if (!query_type (ref, &lookup))
337 : {
338 4 : if (is_root || root_tyty == nullptr)
339 : {
340 8 : rust_error_at (expr.get_locus (), ErrorCode::E0425,
341 : "cannot find value %qs in this scope",
342 8 : expr.as_simple_path ().as_string ().c_str ());
343 :
344 4 : return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
345 : }
346 : return root_tyty;
347 : }
348 :
349 : // is it an enum item?
350 58705 : std::pair<HIR::Enum *, HIR::EnumItem *> enum_item_lookup
351 58705 : = mappings.lookup_hir_enumitem (ref);
352 121783 : bool is_enum_item = enum_item_lookup.first != nullptr
353 58705 : && enum_item_lookup.second != nullptr;
354 4373 : if (is_enum_item)
355 : {
356 4373 : HirId expr_id = expr.get_mappings ().get_hirid ();
357 4373 : HirId variant_id
358 4373 : = enum_item_lookup.second->get_mappings ().get_hirid ();
359 4373 : context->insert_variant_definition (expr_id, variant_id);
360 : }
361 :
362 : // if we have a previous segment type
363 58705 : if (root_tyty != nullptr)
364 : {
365 : // if this next segment needs substitution we must apply the
366 : // previous type arguments
367 : //
368 : // such as: GenericStruct::<_>::new(123, 456)
369 4231 : if (lookup->needs_generic_substitutions ())
370 : {
371 1803 : if (!root_tyty->needs_generic_substitutions ())
372 : {
373 1803 : auto used_args_in_prev_segment
374 1803 : = GetUsedSubstArgs::From (root_tyty);
375 1803 : lookup
376 1803 : = SubstMapperInternal::Resolve (lookup,
377 : used_args_in_prev_segment);
378 1803 : }
379 : }
380 : }
381 :
382 : // turbo-fish segment path::<ty>
383 58705 : if (seg.has_generic_args ())
384 : {
385 965 : lookup = SubstMapper::Resolve (lookup, expr.get_locus (),
386 965 : &seg.get_generic_args (),
387 965 : context->regions_from_generic_args (
388 965 : seg.get_generic_args ()));
389 965 : if (lookup->get_kind () == TyTy::TypeKind::ERROR)
390 4 : return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
391 : }
392 57740 : else if (lookup->needs_generic_substitutions () && !seg_is_pattern
393 57740 : && !seg_is_self)
394 : {
395 6085 : lookup = SubstMapper::InferSubst (lookup, expr.get_locus ());
396 : }
397 :
398 58701 : *root_resolved_node_id = ref_node_id;
399 58701 : *offset = *offset + 1;
400 58701 : root_tyty = lookup;
401 : }
402 :
403 : return root_tyty;
404 : }
405 :
406 : void
407 1555 : TypeCheckExpr::resolve_segments (NodeId root_resolved_node_id,
408 : std::vector<HIR::PathExprSegment> &segments,
409 : size_t offset, TyTy::BaseType *tyseg,
410 : const Analysis::NodeMapping &expr_mappings,
411 : location_t expr_locus)
412 : {
413 1555 : NodeId resolved_node_id = root_resolved_node_id;
414 1555 : TyTy::BaseType *prev_segment = tyseg;
415 1555 : bool receiver_is_generic = prev_segment->get_kind () == TyTy::TypeKind::PARAM;
416 1555 : bool receiver_is_dyn = prev_segment->get_kind () == TyTy::TypeKind::DYNAMIC;
417 :
418 4661 : for (size_t i = offset; i < segments.size (); i++)
419 : {
420 1555 : bool last_seg = i == segments.size () - 1;
421 1555 : HIR::PathExprSegment &seg = segments.at (i);
422 :
423 1555 : std::set<PathProbeCandidate> candidates;
424 1555 : if (last_seg)
425 1555 : candidates = PathProbeExpr::Probe (tyseg, seg.get_segment ());
426 : else
427 : {
428 0 : auto type_candidates
429 0 : = TypePathProbe::Probe (tyseg, seg.get_segment ());
430 0 : candidates = type_candidates.type_candidates;
431 0 : }
432 :
433 1555 : if (candidates.size () == 0)
434 : {
435 2 : rust_error_at (seg.get_locus (),
436 : "failed to resolve path segment using an impl Probe");
437 2 : return;
438 : }
439 :
440 1553 : if (candidates.size () > 1)
441 : {
442 1 : ReportMultipleCandidateError::Report (candidates, seg.get_segment (),
443 : seg.get_locus ());
444 1 : return;
445 : }
446 :
447 1552 : auto &candidate = *candidates.begin ();
448 1552 : prev_segment = tyseg;
449 1552 : tyseg = candidate.ty;
450 :
451 1552 : HIR::ImplBlock *associated_impl_block = nullptr;
452 1552 : if (candidate.is_enum_candidate ())
453 : {
454 1 : const TyTy::VariantDef *variant = candidate.item.enum_field.variant;
455 :
456 1 : HirId variant_id = variant->get_id ();
457 1 : std::pair<HIR::Enum *, HIR::EnumItem *> enum_item_lookup
458 1 : = mappings.lookup_hir_enumitem (variant_id);
459 2 : bool enum_item_ok = enum_item_lookup.first != nullptr
460 1 : && enum_item_lookup.second != nullptr;
461 0 : rust_assert (enum_item_ok);
462 :
463 1 : HIR::EnumItem *enum_item = enum_item_lookup.second;
464 1 : resolved_node_id = enum_item->get_mappings ().get_nodeid ();
465 :
466 : // insert the id of the variant we are resolved to
467 1 : context->insert_variant_definition (expr_mappings.get_hirid (),
468 : variant_id);
469 : }
470 1551 : else if (candidate.is_impl_candidate ())
471 : {
472 999 : resolved_node_id
473 999 : = candidate.item.impl.impl_item->get_impl_mappings ().get_nodeid ();
474 :
475 999 : associated_impl_block = candidate.item.impl.parent;
476 : }
477 : else
478 : {
479 552 : resolved_node_id
480 552 : = candidate.item.trait.item_ref->get_mappings ().get_nodeid ();
481 :
482 : // lookup the associated-impl-trait
483 552 : HIR::ImplBlock *impl = candidate.item.trait.impl;
484 552 : if (impl != nullptr)
485 : {
486 : // get the associated impl block
487 : associated_impl_block = impl;
488 : }
489 : }
490 :
491 1552 : if (associated_impl_block != nullptr && !receiver_is_dyn)
492 : {
493 : // unify the segments receiver against the impls self so
494 : // infer vars on either side get pinned
495 1043 : auto mappings = TyTy::SubstitutionArgumentMappings::error ();
496 1043 : TyTy::BaseType *impl_block_ty
497 1043 : = TypeCheckItem::ResolveImplBlockSelfWithInference (
498 : *associated_impl_block, seg.get_locus (), &mappings);
499 :
500 1043 : if (!mappings.is_error ())
501 192 : tyseg = SubstMapperInternal::Resolve (tyseg, mappings);
502 :
503 2086 : prev_segment = unify_site (seg.get_mappings ().get_hirid (),
504 1043 : TyTy::TyWithLocation (prev_segment),
505 1043 : TyTy::TyWithLocation (impl_block_ty),
506 : seg.get_locus ());
507 1043 : if (prev_segment->get_kind () == TyTy::TypeKind::ERROR)
508 1 : return;
509 1043 : }
510 :
511 1551 : if (seg.has_generic_args ())
512 : {
513 46 : rust_debug_loc (seg.get_locus (), "applying segment generics: %s",
514 : tyseg->as_string ().c_str ());
515 46 : tyseg
516 46 : = SubstMapper::Resolve (tyseg, expr_locus, &seg.get_generic_args (),
517 46 : context->regions_from_generic_args (
518 46 : seg.get_generic_args ()));
519 46 : if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
520 : return;
521 : }
522 1505 : else if (tyseg->needs_generic_substitutions () && !receiver_is_generic)
523 : {
524 420 : location_t locus = seg.get_locus ();
525 420 : tyseg = SubstMapper::InferSubst (tyseg, locus);
526 420 : if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
527 : return;
528 : }
529 1555 : }
530 :
531 1551 : rust_assert (resolved_node_id != UNKNOWN_NODEID);
532 :
533 1551 : auto &nr_ctx = Resolver2_0::FinalizedNameResolutionContext::get ();
534 :
535 1551 : nr_ctx.map_usage (Resolver2_0::Usage (expr_mappings.get_nodeid ()),
536 1551 : Resolver2_0::Definition (resolved_node_id),
537 : Resolver2_0::Namespace::Values);
538 :
539 1551 : infered = tyseg;
540 : }
541 :
542 : } // namespace Resolver
543 : } // namespace Rust
|