1 // Copyright (C) 2020-2024 Free Software Foundation, Inc.
3 // This file is part of GCC.
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
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
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/>.
19 #include "rust-hir-type-check-expr.h"
20 #include "rust-hir-type-check-type.h"
21 #include "rust-hir-type-check-item.h"
22 #include "rust-hir-trait-resolve.h"
23 #include "rust-substitution-mapper.h"
24 #include "rust-hir-path-probe.h"
25 #include "rust-type-util.h"
26 #include "rust-hir-type-bounds.h"
27 #include "rust-session-manager.h"
28 #include "rust-immutable-name-resolution-context.h"
34 TypeCheckExpr::visit (HIR::QualifiedPathInExpression
&expr
)
36 HIR::QualifiedPathType qual_path_type
= expr
.get_path_type ();
38 = TypeCheckType::Resolve (qual_path_type
.get_type ().get ());
39 if (root
->get_kind () == TyTy::TypeKind::ERROR
)
42 if (!qual_path_type
.has_as_clause ())
44 NodeId root_resolved_node_id
= UNKNOWN_NODEID
;
45 resolve_segments (root_resolved_node_id
, expr
.get_segments (), 0, root
,
46 expr
.get_mappings (), expr
.get_locus ());
50 // Resolve the trait now
51 std::unique_ptr
<HIR::TypePath
> &trait_path_ref
= qual_path_type
.get_trait ();
52 TraitReference
*trait_ref
= TraitResolver::Resolve (*trait_path_ref
.get ());
53 if (trait_ref
->is_error ())
56 // does this type actually implement this type-bound?
57 if (!TypeBoundsProbe::is_bound_satisfied_for_type (root
, trait_ref
))
60 // then we need to look at the next segment to create perform the correct
62 if (expr
.get_segments ().empty ())
65 // get the predicate for the bound
67 = get_predicate_from_bound (*trait_path_ref
.get (),
68 qual_path_type
.get_type ().get ());
69 if (specified_bound
.is_error ())
73 root
->inherit_bounds ({specified_bound
});
75 // lookup the associated item from the specified bound
76 HIR::PathExprSegment
&item_seg
= expr
.get_segments ().at (0);
77 HIR::PathIdentSegment item_seg_identifier
= item_seg
.get_segment ();
78 TyTy::TypeBoundPredicateItem item
79 = specified_bound
.lookup_associated_item (item_seg_identifier
.as_string ());
82 rust_error_at (item_seg
.get_locus (), "unknown associated item");
86 // we try to look for the real impl item if possible
87 HIR::ImplItem
*impl_item
= nullptr;
89 // lookup the associated impl trait for this if we can (it might be generic)
90 AssociatedImplTrait
*associated_impl_trait
91 = lookup_associated_impl_block (specified_bound
, root
);
92 if (associated_impl_trait
!= nullptr)
94 associated_impl_trait
->setup_associated_types (root
, specified_bound
);
97 associated_impl_trait
->get_impl_block ()->get_impl_items ())
99 bool found
= i
->get_impl_item_name ().compare (
100 item_seg_identifier
.as_string ())
104 impl_item
= i
.get ();
110 NodeId root_resolved_node_id
= UNKNOWN_NODEID
;
111 if (impl_item
== nullptr)
113 // this may be valid as there could be a default trait implementation here
114 // and we dont need to worry if the trait item is actually implemented or
115 // not because this will have already been validated as part of the trait
117 infered
= item
.get_tyty_for_receiver (root
);
118 root_resolved_node_id
119 = item
.get_raw_item ()->get_mappings ().get_nodeid ();
123 HirId impl_item_id
= impl_item
->get_impl_mappings ().get_hirid ();
124 bool ok
= query_type (impl_item_id
, &infered
);
128 // I think query_type should error if required here anyway
132 root_resolved_node_id
= impl_item
->get_impl_mappings ().get_nodeid ();
135 // turbo-fish segment path::<ty>
136 if (item_seg
.has_generic_args ())
138 if (!infered
->has_substitutions_defined ())
140 rust_error_at (item_seg
.get_locus (),
141 "substitutions not supported for %s",
142 infered
->as_string ().c_str ());
143 infered
= new TyTy::ErrorType (expr
.get_mappings ().get_hirid ());
146 std::vector
<TyTy::Region
> regions
;
148 infered
= SubstMapper::Resolve (infered
, expr
.get_locus (),
149 &item_seg
.get_generic_args (),
150 context
->regions_from_generic_args (
151 item_seg
.get_generic_args ()));
154 // continue on as a path-in-expression
155 bool fully_resolved
= expr
.get_segments ().size () <= 1;
158 resolver
->insert_resolved_name (expr
.get_mappings ().get_nodeid (),
159 root_resolved_node_id
);
160 context
->insert_receiver (expr
.get_mappings ().get_hirid (), root
);
164 resolve_segments (root_resolved_node_id
, expr
.get_segments (), 1, infered
,
165 expr
.get_mappings (), expr
.get_locus ());
169 TypeCheckExpr::visit (HIR::PathInExpression
&expr
)
171 NodeId resolved_node_id
= UNKNOWN_NODEID
;
173 TyTy::BaseType
*tyseg
= resolve_root_path (expr
, &offset
, &resolved_node_id
);
174 if (tyseg
->get_kind () == TyTy::TypeKind::ERROR
)
177 bool fully_resolved
= offset
== expr
.get_segments ().size ();
184 resolve_segments (resolved_node_id
, expr
.get_segments (), offset
, tyseg
,
185 expr
.get_mappings (), expr
.get_locus ());
189 TypeCheckExpr::resolve_root_path (HIR::PathInExpression
&expr
, size_t *offset
,
190 NodeId
*root_resolved_node_id
)
192 TyTy::BaseType
*root_tyty
= nullptr;
194 for (size_t i
= 0; i
< expr
.get_num_segments (); i
++)
196 HIR::PathExprSegment
&seg
= expr
.get_segments ().at (i
);
198 bool have_more_segments
= (expr
.get_num_segments () - 1 != i
);
199 bool is_root
= *offset
== 0;
200 NodeId ast_node_id
= seg
.get_mappings ().get_nodeid ();
202 // then lookup the reference_node_id
203 NodeId ref_node_id
= UNKNOWN_NODEID
;
205 if (flag_name_resolution_2_0
)
208 = Resolver2_0::ImmutableNameResolutionContext::get ().resolver ();
210 // assign the ref_node_id if we've found something
211 nr_ctx
.lookup (expr
.get_mappings ().get_nodeid ())
212 .map ([&ref_node_id
] (NodeId resolved
) { ref_node_id
= resolved
; });
214 else if (!resolver
->lookup_resolved_name (ast_node_id
, &ref_node_id
))
215 resolver
->lookup_resolved_type (ast_node_id
, &ref_node_id
);
217 // ref_node_id is the NodeId that the segments refers to.
218 if (ref_node_id
== UNKNOWN_NODEID
)
220 if (root_tyty
!= nullptr && *offset
> 0)
222 // then we can let the impl path probe take over now
226 rust_error_at (seg
.get_locus (),
227 "failed to type resolve root segment");
228 return new TyTy::ErrorType (expr
.get_mappings ().get_hirid ());
233 if (!mappings
->lookup_node_to_hir (ref_node_id
, &ref
))
235 rust_error_at (seg
.get_locus (), "456 reverse lookup failure");
236 rust_debug_loc (seg
.get_locus (),
237 "failure with [%s] mappings [%s] ref_node_id [%u]",
238 seg
.as_string ().c_str (),
239 seg
.get_mappings ().as_string ().c_str (),
242 return new TyTy::ErrorType (expr
.get_mappings ().get_hirid ());
245 auto seg_is_module
= (nullptr != mappings
->lookup_module (ref
));
246 auto seg_is_crate
= mappings
->is_local_hirid_crate (ref
);
247 if (seg_is_module
|| seg_is_crate
)
249 // A::B::C::this_is_a_module::D::E::F
251 // Currently handling this.
252 if (have_more_segments
)
259 // A::B::C::this_is_a_module
261 // This is an error, we are not expecting a module.
262 rust_error_at (seg
.get_locus (), "expected value");
263 return new TyTy::ErrorType (expr
.get_mappings ().get_hirid ());
266 TyTy::BaseType
*lookup
= nullptr;
267 if (!query_type (ref
, &lookup
))
271 rust_error_at (expr
.get_locus (), ErrorCode::E0425
,
272 "cannot find value %qs in this scope",
273 expr
.as_simple_path ().as_string ().c_str ());
275 return new TyTy::ErrorType (expr
.get_mappings ().get_hirid ());
280 // is it an enum item?
281 std::pair
<HIR::Enum
*, HIR::EnumItem
*> enum_item_lookup
282 = mappings
->lookup_hir_enumitem (ref
);
283 bool is_enum_item
= enum_item_lookup
.first
!= nullptr
284 && enum_item_lookup
.second
!= nullptr;
287 HirId expr_id
= expr
.get_mappings ().get_hirid ();
289 = enum_item_lookup
.second
->get_mappings ().get_hirid ();
290 context
->insert_variant_definition (expr_id
, variant_id
);
293 // if we have a previous segment type
294 if (root_tyty
!= nullptr)
296 // if this next segment needs substitution we must apply the
297 // previous type arguments
299 // such as: GenericStruct::<_>::new(123, 456)
300 if (lookup
->needs_generic_substitutions ())
302 if (!root_tyty
->needs_generic_substitutions ())
304 auto used_args_in_prev_segment
305 = GetUsedSubstArgs::From (root_tyty
);
307 = SubstMapperInternal::Resolve (lookup
,
308 used_args_in_prev_segment
);
313 // turbo-fish segment path::<ty>
314 if (seg
.has_generic_args ())
316 lookup
= SubstMapper::Resolve (lookup
, expr
.get_locus (),
317 &seg
.get_generic_args (),
318 context
->regions_from_generic_args (
319 seg
.get_generic_args ()));
320 if (lookup
->get_kind () == TyTy::TypeKind::ERROR
)
321 return new TyTy::ErrorType (expr
.get_mappings ().get_hirid ());
323 else if (lookup
->needs_generic_substitutions ())
325 lookup
= SubstMapper::InferSubst (lookup
, expr
.get_locus ());
328 *root_resolved_node_id
= ref_node_id
;
329 *offset
= *offset
+ 1;
337 TypeCheckExpr::resolve_segments (NodeId root_resolved_node_id
,
338 std::vector
<HIR::PathExprSegment
> &segments
,
339 size_t offset
, TyTy::BaseType
*tyseg
,
340 const Analysis::NodeMapping
&expr_mappings
,
341 location_t expr_locus
)
343 NodeId resolved_node_id
= root_resolved_node_id
;
344 TyTy::BaseType
*prev_segment
= tyseg
;
345 bool reciever_is_generic
= prev_segment
->get_kind () == TyTy::TypeKind::PARAM
;
347 for (size_t i
= offset
; i
< segments
.size (); i
++)
349 HIR::PathExprSegment
&seg
= segments
.at (i
);
350 bool probe_impls
= !reciever_is_generic
;
352 // probe the path is done in two parts one where we search impls if no
353 // candidate is found then we search extensions from traits
355 = PathProbeType::Probe (prev_segment
, seg
.get_segment (), probe_impls
,
356 false /*probe_bounds*/,
357 true /*ignore_mandatory_trait_items*/);
358 if (candidates
.size () == 0)
361 = PathProbeType::Probe (prev_segment
, seg
.get_segment (), false,
362 true /*probe_bounds*/,
363 false /*ignore_mandatory_trait_items*/);
365 if (candidates
.size () == 0)
369 "failed to resolve path segment using an impl Probe");
374 if (candidates
.size () > 1)
376 ReportMultipleCandidateError::Report (candidates
, seg
.get_segment (),
381 auto &candidate
= *candidates
.begin ();
382 prev_segment
= tyseg
;
383 tyseg
= candidate
.ty
;
385 HIR::ImplBlock
*associated_impl_block
= nullptr;
386 if (candidate
.is_enum_candidate ())
388 const TyTy::VariantDef
*variant
= candidate
.item
.enum_field
.variant
;
390 HirId variant_id
= variant
->get_id ();
391 std::pair
<HIR::Enum
*, HIR::EnumItem
*> enum_item_lookup
392 = mappings
->lookup_hir_enumitem (variant_id
);
393 bool enum_item_ok
= enum_item_lookup
.first
!= nullptr
394 && enum_item_lookup
.second
!= nullptr;
395 rust_assert (enum_item_ok
);
397 HIR::EnumItem
*enum_item
= enum_item_lookup
.second
;
398 resolved_node_id
= enum_item
->get_mappings ().get_nodeid ();
400 // insert the id of the variant we are resolved to
401 context
->insert_variant_definition (expr_mappings
.get_hirid (),
404 else if (candidate
.is_impl_candidate ())
407 = candidate
.item
.impl
.impl_item
->get_impl_mappings ().get_nodeid ();
409 associated_impl_block
= candidate
.item
.impl
.parent
;
414 = candidate
.item
.trait
.item_ref
->get_mappings ().get_nodeid ();
416 // lookup the associated-impl-trait
417 HIR::ImplBlock
*impl
= candidate
.item
.trait
.impl
;
420 // get the associated impl block
421 associated_impl_block
= impl
;
425 if (associated_impl_block
!= nullptr)
429 = associated_impl_block
->get_mappings ().get_hirid ();
431 AssociatedImplTrait
*associated
= nullptr;
432 bool found_impl_trait
433 = context
->lookup_associated_trait_impl (impl_block_id
,
436 auto mappings
= TyTy::SubstitutionArgumentMappings::error ();
437 TyTy::BaseType
*impl_block_ty
438 = TypeCheckItem::ResolveImplBlockSelfWithInference (
439 *associated_impl_block
, seg
.get_locus (), &mappings
);
441 // we need to apply the arguments to the segment type so they get
443 if (!mappings
.is_error ())
444 tyseg
= SubstMapperInternal::Resolve (tyseg
, mappings
);
446 prev_segment
= unify_site (seg
.get_mappings ().get_hirid (),
447 TyTy::TyWithLocation (prev_segment
),
448 TyTy::TyWithLocation (impl_block_ty
),
450 bool ok
= prev_segment
->get_kind () != TyTy::TypeKind::ERROR
;
454 if (found_impl_trait
)
456 // we need to setup with apropriate bounds
457 HIR::TypePath
&bound_path
458 = *associated
->get_impl_block ()->get_trait_ref ().get ();
459 const auto &trait_ref
= *TraitResolver::Resolve (bound_path
);
460 rust_assert (!trait_ref
.is_error ());
462 const auto &predicate
463 = impl_block_ty
->lookup_predicate (trait_ref
.get_defid ());
464 if (!predicate
.is_error ())
466 = associated
->setup_associated_types (prev_segment
,
471 if (seg
.has_generic_args ())
473 rust_debug_loc (seg
.get_locus (), "applying segment generics: %s",
474 tyseg
->as_string ().c_str ());
476 = SubstMapper::Resolve (tyseg
, expr_locus
, &seg
.get_generic_args (),
477 context
->regions_from_generic_args (
478 seg
.get_generic_args ()));
479 if (tyseg
->get_kind () == TyTy::TypeKind::ERROR
)
482 else if (tyseg
->needs_generic_substitutions () && !reciever_is_generic
)
484 location_t locus
= seg
.get_locus ();
485 tyseg
= SubstMapper::InferSubst (tyseg
, locus
);
486 if (tyseg
->get_kind () == TyTy::TypeKind::ERROR
)
491 rust_assert (resolved_node_id
!= UNKNOWN_NODEID
);
492 if (tyseg
->needs_generic_substitutions () && !reciever_is_generic
)
494 location_t locus
= segments
.back ().get_locus ();
495 tyseg
= SubstMapper::InferSubst (tyseg
, locus
);
496 if (tyseg
->get_kind () == TyTy::TypeKind::ERROR
)
500 context
->insert_receiver (expr_mappings
.get_hirid (), prev_segment
);
503 if (resolver
->get_name_scope ().decl_was_declared_here (resolved_node_id
))
505 resolver
->insert_resolved_name (expr_mappings
.get_nodeid (),
508 // check the type scope
509 else if (resolver
->get_type_scope ().decl_was_declared_here (
512 resolver
->insert_resolved_type (expr_mappings
.get_nodeid (),
517 resolver
->insert_resolved_misc (expr_mappings
.get_nodeid (),
524 } // namespace Resolver