LCOV - code coverage report
Current view: top level - gcc/rust/typecheck - rust-hir-type-check-struct.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 93.0 % 243 226
Test Date: 2026-02-28 14:20:25 Functions: 100.0 % 7 7
Legend: Lines:     hit not hit

            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-type-check.h"
      21              : #include "rust-hir-type-check-expr.h"
      22              : #include "rust-hir-type-check-struct-field.h"
      23              : #include "rust-type-util.h"
      24              : 
      25              : namespace Rust {
      26              : namespace Resolver {
      27              : 
      28         1325 : TypeCheckStructExpr::TypeCheckStructExpr (HIR::Expr &e)
      29              :   : TypeCheckBase (),
      30         1325 :     resolved (new TyTy::ErrorType (e.get_mappings ().get_hirid ())),
      31         1325 :     struct_path_resolved (nullptr),
      32         2650 :     variant (&TyTy::VariantDef::get_error_node ()), parent (e)
      33         1325 : {}
      34              : 
      35              : TyTy::BaseType *
      36         1325 : TypeCheckStructExpr::Resolve (HIR::StructExprStructFields &expr)
      37              : {
      38         1325 :   TypeCheckStructExpr resolver (expr);
      39         1325 :   resolver.resolve (expr);
      40         2650 :   return resolver.resolved;
      41         1325 : }
      42              : 
      43              : void
      44         1325 : TypeCheckStructExpr::resolve (HIR::StructExprStructFields &struct_expr)
      45              : {
      46         1325 :   TyTy::BaseType *struct_path_ty
      47         1325 :     = TypeCheckExpr::Resolve (struct_expr.get_struct_name ());
      48         1325 :   if (struct_path_ty->get_kind () != TyTy::TypeKind::ADT)
      49              :     {
      50            1 :       rust_error_at (struct_expr.get_struct_name ().get_locus (),
      51              :                      "expected an ADT type for constructor");
      52            9 :       return;
      53              :     }
      54              : 
      55         1324 :   struct_path_resolved = static_cast<TyTy::ADTType *> (struct_path_ty);
      56         1324 :   TyTy::ADTType *struct_def = struct_path_resolved;
      57         1324 :   if (struct_expr.has_struct_base ())
      58              :     {
      59           63 :       TyTy::BaseType *base_resolved
      60           63 :         = TypeCheckExpr::Resolve (struct_expr.get_struct_base ().get_base ());
      61          126 :       TyTy::BaseType *base_unify = unify_site (
      62           63 :         struct_expr.get_struct_base ().get_base ().get_mappings ().get_hirid (),
      63           63 :         TyTy::TyWithLocation (struct_path_resolved),
      64           63 :         TyTy::TyWithLocation (base_resolved),
      65           63 :         struct_expr.get_struct_base ().get_base ().get_locus ());
      66              : 
      67           63 :       if (base_unify->get_kind () != struct_path_ty->get_kind ())
      68              :         {
      69            0 :           rust_error_at (
      70            0 :             struct_expr.get_struct_base ().get_base ().get_locus (),
      71              :             "incompatible types for base struct reference");
      72            0 :           return;
      73              :         }
      74              : 
      75              :       struct_def = static_cast<TyTy::ADTType *> (base_unify);
      76              :     }
      77              : 
      78              :   // figure out the variant
      79         1324 :   if (struct_path_resolved->is_enum ())
      80              :     {
      81              :       // lookup variant id
      82           89 :       HirId variant_id;
      83           89 :       bool ok = context->lookup_variant_definition (
      84           89 :         struct_expr.get_struct_name ().get_mappings ().get_hirid (),
      85              :         &variant_id);
      86           89 :       if (!ok)
      87              :         {
      88            1 :           rich_location r (line_table, struct_expr.get_locus ());
      89            1 :           r.add_range (struct_expr.get_struct_name ().get_locus ());
      90            1 :           rust_error_at (
      91            1 :             struct_expr.get_struct_name ().get_locus (), ErrorCode::E0574,
      92              :             "expected a struct, variant or union type, found enum %qs",
      93            1 :             struct_path_resolved->get_name ().c_str ());
      94            1 :           return;
      95            1 :         }
      96              : 
      97           88 :       ok = struct_path_resolved->lookup_variant_by_id (variant_id, &variant);
      98           88 :       rust_assert (ok);
      99              :     }
     100              :   else
     101              :     {
     102         1235 :       rust_assert (struct_path_resolved->number_of_variants () == 1);
     103         1235 :       variant = struct_path_resolved->get_variants ().at (0);
     104              :     }
     105              : 
     106         1323 :   std::vector<TyTy::StructFieldType *> infered_fields;
     107         1323 :   bool ok = true;
     108              : 
     109         3605 :   for (auto &field : struct_expr.get_fields ())
     110              :     {
     111         2282 :       resolved_field_value_expr = nullptr;
     112              : 
     113         2282 :       switch (field->get_kind ())
     114              :         {
     115          216 :         case HIR::StructExprField::StructExprFieldKind::IDENTIFIER:
     116          216 :           ok = visit (
     117          216 :             static_cast<HIR::StructExprFieldIdentifier &> (*field.get ()));
     118          216 :           break;
     119              : 
     120         2022 :         case HIR::StructExprField::StructExprFieldKind::IDENTIFIER_VALUE:
     121         2022 :           ok = visit (
     122         2022 :             static_cast<HIR::StructExprFieldIdentifierValue &> (*field.get ()));
     123         2022 :           break;
     124              : 
     125           44 :         case HIR::StructExprField::StructExprFieldKind::INDEX_VALUE:
     126           44 :           ok = visit (
     127           44 :             static_cast<HIR::StructExprFieldIndexValue &> (*field.get ()));
     128           44 :           break;
     129              :         }
     130              : 
     131         2282 :       if (ok)
     132         2277 :         context->insert_type (field->get_mappings (),
     133              :                               resolved_field_value_expr);
     134              :     }
     135              : 
     136              :   // something failed setting up the fields and error's emitted
     137         1323 :   if (!ok)
     138              :     return;
     139              : 
     140              :   // check the arguments are all assigned and fix up the ordering
     141         2636 :   std::vector<std::string> missing_field_names;
     142         4555 :   for (auto &field : variant->get_fields ())
     143              :     {
     144         3236 :       auto it = fields_assigned.find (field->get_name ());
     145         3236 :       if (it == fields_assigned.end ())
     146              :         {
     147          962 :           missing_field_names.push_back (field->get_name ());
     148              :         }
     149              :     }
     150         1319 :   if (!missing_field_names.empty ())
     151              :     {
     152          209 :       if (struct_def->is_union ())
     153              :         {
     154          151 :           if (fields_assigned.size () != 1 || struct_expr.has_struct_base ())
     155              :             {
     156            0 :               rust_error_at (
     157              :                 struct_expr.get_locus (),
     158              :                 "union must have exactly one field variant assigned");
     159            2 :               return;
     160              :             }
     161              :         }
     162           58 :       else if (!struct_expr.has_struct_base ())
     163              :         {
     164            2 :           Error missing_fields_error
     165              :             = make_missing_field_error (struct_expr.get_locus (),
     166              :                                         missing_field_names,
     167            2 :                                         struct_path_ty->get_name ());
     168              :           // We might want to return or handle these in the future emit for now.
     169            2 :           missing_fields_error.emit ();
     170            2 :           return;
     171            2 :         }
     172              :       else
     173              :         {
     174              :           // we have a struct base to assign the missing fields from.
     175              :           // the missing fields can be implicit FieldAccessExprs for the value
     176           56 :           std::set<std::string> missing_fields;
     177          714 :           for (auto &field : variant->get_fields ())
     178              :             {
     179          658 :               auto it = fields_assigned.find (field->get_name ());
     180          658 :               if (it == fields_assigned.end ())
     181          616 :                 missing_fields.insert (field->get_name ());
     182              :             }
     183              : 
     184              :           // we can generate FieldAccessExpr or TupleAccessExpr for the
     185              :           // values of the missing fields.
     186          672 :           for (auto &missing : missing_fields)
     187              :             {
     188          616 :               HIR::Expr *receiver
     189          616 :                 = struct_expr.get_struct_base ().get_base ().clone_expr_impl ();
     190              : 
     191          616 :               HIR::StructExprField *implicit_field = nullptr;
     192              : 
     193          616 :               AST::AttrVec outer_attribs;
     194          616 :               auto crate_num = mappings.get_current_crate ();
     195          616 :               Analysis::NodeMapping mapping (crate_num,
     196          616 :                                              struct_expr.get_struct_base ()
     197          616 :                                                .get_base ()
     198          616 :                                                .get_mappings ()
     199              :                                                .get_nodeid (),
     200          616 :                                              mappings.get_next_hir_id (
     201              :                                                crate_num),
     202          616 :                                              UNKNOWN_LOCAL_DEFID);
     203              : 
     204          616 :               HIR::Expr *field_value = new HIR::FieldAccessExpr (
     205         1848 :                 mapping, std::unique_ptr<HIR::Expr> (receiver), missing,
     206              :                 std::move (outer_attribs),
     207         1848 :                 struct_expr.get_struct_base ().get_base ().get_locus ());
     208              : 
     209          616 :               implicit_field = new HIR::StructExprFieldIdentifierValue (
     210         1848 :                 mapping, missing, std::unique_ptr<HIR::Expr> (field_value),
     211         1232 :                 struct_expr.get_struct_base ().get_base ().get_locus ());
     212              : 
     213          616 :               size_t field_index;
     214          616 :               bool ok = variant->lookup_field (missing, nullptr, &field_index);
     215          616 :               rust_assert (ok);
     216              : 
     217          616 :               adtFieldIndexToField[field_index] = implicit_field;
     218          616 :               struct_expr.get_fields ().emplace_back (implicit_field);
     219          616 :             }
     220           56 :         }
     221              :     }
     222              : 
     223         1317 :   if (struct_def->is_union ())
     224              :     {
     225              :       // There is exactly one field in this constructor, we need to
     226              :       // figure out the field index to make sure we initialize the
     227              :       // right union field.
     228          277 :       for (size_t i = 0; i < adtFieldIndexToField.size (); i++)
     229              :         {
     230          277 :           if (adtFieldIndexToField[i])
     231              :             {
     232          158 :               struct_expr.union_index = i;
     233          158 :               break;
     234              :             }
     235              :         }
     236          158 :       rust_assert (struct_expr.union_index != -1);
     237              :     }
     238              :   else
     239              :     {
     240              :       // everything is ok, now we need to ensure all field values are ordered
     241              :       // correctly. The GIMPLE backend uses a simple algorithm that assumes each
     242              :       // assigned field in the constructor is in the same order as the field in
     243              :       // the type
     244         3888 :       for (auto &field : struct_expr.get_fields ())
     245         2729 :         field.release ();
     246              : 
     247         1159 :       std::vector<std::unique_ptr<HIR::StructExprField> > ordered_fields;
     248         1159 :       ordered_fields.reserve (adtFieldIndexToField.size ());
     249              : 
     250         3888 :       for (size_t i = 0; i < adtFieldIndexToField.size (); i++)
     251         2729 :         ordered_fields.emplace_back (adtFieldIndexToField[i]);
     252              : 
     253         1159 :       struct_expr.set_fields_as_owner (std::move (ordered_fields));
     254         1159 :     }
     255              : 
     256         1317 :   resolved = struct_def;
     257         1323 : }
     258              : 
     259              : bool
     260         2022 : TypeCheckStructExpr::visit (HIR::StructExprFieldIdentifierValue &field)
     261              : {
     262         2022 :   size_t field_index;
     263         2022 :   TyTy::StructFieldType *field_type;
     264         2022 :   bool ok = variant->lookup_field (field.field_name.as_string (), &field_type,
     265              :                                    &field_index);
     266         2022 :   if (!ok)
     267              :     {
     268            1 :       rich_location r (line_table, parent.get_locus ());
     269            1 :       r.add_range (field.get_locus ());
     270            1 :       rust_error_at (r, ErrorCode::E0560, "unknown field %qs",
     271            1 :                      field.field_name.as_string ().c_str ());
     272            1 :       return false;
     273            1 :     }
     274              : 
     275         2021 :   auto it = adtFieldIndexToField.find (field_index);
     276         2021 :   if (it != adtFieldIndexToField.end ())
     277              :     {
     278            1 :       rich_location repeat_location (line_table, field.get_locus ());
     279            1 :       auto prev_field_locus = it->second->get_locus ();
     280            1 :       repeat_location.add_range (prev_field_locus);
     281              : 
     282            1 :       rust_error_at (repeat_location, ErrorCode::E0062,
     283              :                      "field %qs specified more than once",
     284            1 :                      field.field_name.as_string ().c_str ());
     285            1 :       return false;
     286            1 :     }
     287              : 
     288         2020 :   TyTy::BaseType *value = TypeCheckExpr::Resolve (field.get_value ());
     289         2020 :   location_t value_locus = field.get_value ().get_locus ();
     290              : 
     291         2020 :   HirId coercion_site_id = field.get_mappings ().get_hirid ();
     292         2020 :   resolved_field_value_expr
     293         4040 :     = coercion_site (coercion_site_id,
     294              :                      TyTy::TyWithLocation (field_type->get_field_type (),
     295         2020 :                                            field_type->get_locus ()),
     296         2020 :                      TyTy::TyWithLocation (value, value_locus),
     297              :                      field.get_locus ());
     298         2020 :   if (resolved_field_value_expr != nullptr)
     299              :     {
     300         2020 :       fields_assigned.insert (field.field_name.as_string ());
     301         2020 :       adtFieldIndexToField[field_index] = &field;
     302              :     }
     303              : 
     304              :   return true;
     305              : }
     306              : 
     307              : bool
     308           44 : TypeCheckStructExpr::visit (HIR::StructExprFieldIndexValue &field)
     309              : {
     310           44 :   std::string field_name (std::to_string (field.get_tuple_index ()));
     311              : 
     312           44 :   size_t field_index;
     313           44 :   TyTy::StructFieldType *field_type;
     314           44 :   bool ok = variant->lookup_field (field_name, &field_type, &field_index);
     315           44 :   if (!ok)
     316              :     {
     317            2 :       rich_location r (line_table, parent.get_locus ());
     318            2 :       r.add_range (field.get_locus ());
     319            2 :       rust_error_at (r, ErrorCode::E0560, "unknown field %qs",
     320              :                      field_name.c_str ());
     321            2 :       return false;
     322            2 :     }
     323              : 
     324           42 :   auto it = adtFieldIndexToField.find (field_index);
     325           42 :   if (it != adtFieldIndexToField.end ())
     326              :     {
     327            0 :       rich_location repeat_location (line_table, field.get_locus ());
     328            0 :       auto prev_field_locus = it->second->get_locus ();
     329            0 :       repeat_location.add_range (prev_field_locus);
     330              : 
     331            0 :       rust_error_at (repeat_location, ErrorCode::E0062,
     332              :                      "field %qs specified more than once", field_name.c_str ());
     333            0 :       return false;
     334            0 :     }
     335              : 
     336           42 :   TyTy::BaseType *value = TypeCheckExpr::Resolve (field.get_value ());
     337           42 :   location_t value_locus = field.get_value ().get_locus ();
     338              : 
     339           42 :   HirId coercion_site_id = field.get_mappings ().get_hirid ();
     340           42 :   resolved_field_value_expr
     341           84 :     = coercion_site (coercion_site_id,
     342              :                      TyTy::TyWithLocation (field_type->get_field_type (),
     343           42 :                                            field_type->get_locus ()),
     344           42 :                      TyTy::TyWithLocation (value, value_locus),
     345              :                      field.get_locus ());
     346           42 :   if (resolved_field_value_expr != nullptr)
     347              :     {
     348           42 :       fields_assigned.insert (field_name);
     349           42 :       adtFieldIndexToField[field_index] = &field;
     350              :     }
     351              : 
     352              :   return true;
     353           44 : }
     354              : 
     355              : bool
     356          216 : TypeCheckStructExpr::visit (HIR::StructExprFieldIdentifier &field)
     357              : {
     358          216 :   size_t field_index;
     359          216 :   TyTy::StructFieldType *field_type;
     360          216 :   bool ok = variant->lookup_field (field.get_field_name ().as_string (),
     361              :                                    &field_type, &field_index);
     362          216 :   if (!ok)
     363              :     {
     364            1 :       rust_error_at (field.get_locus (), "unknown field");
     365            1 :       return false;
     366              :     }
     367              : 
     368          215 :   auto it = adtFieldIndexToField.find (field_index);
     369          215 :   if (it != adtFieldIndexToField.end ())
     370              :     {
     371            0 :       rich_location repeat_location (line_table, field.get_locus ());
     372            0 :       auto prev_field_locus = it->second->get_locus ();
     373            0 :       repeat_location.add_range (prev_field_locus);
     374              : 
     375            0 :       rust_error_at (repeat_location, ErrorCode::E0062,
     376              :                      "field %qs specified more than once",
     377            0 :                      field.get_field_name ().as_string ().c_str ());
     378            0 :       return false;
     379            0 :     }
     380              : 
     381              :   // we can make the field look like a path expr to take advantage of existing
     382              :   // code
     383          215 :   Analysis::NodeMapping mappings_copy1 = field.get_mappings ();
     384          215 :   Analysis::NodeMapping mappings_copy2 = field.get_mappings ();
     385              : 
     386          430 :   HIR::PathIdentSegment ident_seg (field.get_field_name ().as_string ());
     387          215 :   HIR::PathExprSegment seg (mappings_copy1, ident_seg, field.get_locus (),
     388          430 :                             HIR::GenericArgs::create_empty ());
     389          215 :   HIR::PathInExpression expr (mappings_copy2, {seg}, field.get_locus (), false,
     390          430 :                               {});
     391          215 :   TyTy::BaseType *value = TypeCheckExpr::Resolve (expr);
     392          215 :   location_t value_locus = expr.get_locus ();
     393              : 
     394          215 :   HirId coercion_site_id = field.get_mappings ().get_hirid ();
     395          215 :   resolved_field_value_expr
     396          430 :     = coercion_site (coercion_site_id,
     397              :                      TyTy::TyWithLocation (field_type->get_field_type (),
     398          215 :                                            field_type->get_locus ()),
     399          215 :                      TyTy::TyWithLocation (value, value_locus),
     400              :                      field.get_locus ());
     401          215 :   if (resolved_field_value_expr != nullptr)
     402              : 
     403              :     {
     404          215 :       fields_assigned.insert (field.get_field_name ().as_string ());
     405          215 :       adtFieldIndexToField[field_index] = &field;
     406              :     }
     407              : 
     408          215 :   return true;
     409          430 : }
     410              : 
     411              : Error
     412            3 : TypeCheckStructExpr::make_missing_field_error (
     413              :   location_t locus, const std::vector<std::string> &missing_field_names,
     414              :   const std::string &struct_name)
     415              : {
     416              :   // Message plurality depends on size
     417            3 :   if (missing_field_names.size () == 1)
     418              :     {
     419            1 :       return Error (locus, ErrorCode::E0063,
     420              :                     "missing field %qs in initializer of %qs",
     421            1 :                     missing_field_names[0].c_str (), struct_name.c_str ());
     422              :     }
     423              :   // Make comma separated string for display
     424            2 :   std::stringstream display_field_names;
     425            2 :   size_t field_count = missing_field_names.size ();
     426            3 :   for (size_t i = 0; i + 2 < field_count; ++i)
     427              :     {
     428            1 :       const auto &field_name = missing_field_names[i];
     429            1 :       display_field_names << rust_open_quote () << field_name
     430            2 :                           << rust_close_quote () << ", ";
     431              :     }
     432            2 :   display_field_names << rust_open_quote ()
     433            2 :                       << missing_field_names[field_count - 2]
     434            4 :                       << rust_close_quote ();
     435            2 :   display_field_names << " and ";
     436            2 :   display_field_names << rust_open_quote ()
     437            2 :                       << missing_field_names[field_count - 1]
     438            4 :                       << rust_close_quote ();
     439              : 
     440            2 :   return Error (locus, ErrorCode::E0063,
     441              :                 "missing fields %s in initializer of %qs",
     442            2 :                 display_field_names.str ().c_str (), struct_name.c_str ());
     443            2 : }
     444              : 
     445              : } // namespace Resolver
     446              : } // namespace Rust
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.