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