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: 91.6 % 237 217
Test Date: 2025-06-21 16:26:05 Functions: 100.0 % 7 7
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

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

Generated by: LCOV version 2.1-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.