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
|