]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/rust/typecheck/rust-hir-type-check-struct.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / rust / typecheck / rust-hir-type-check-struct.cc
1 // Copyright (C) 2020-2024 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-full.h"
21 #include "rust-hir-type-check-expr.h"
22 #include "rust-hir-type-check-struct-field.h"
23
24 namespace Rust {
25 namespace Resolver {
26
27 TypeCheckStructExpr::TypeCheckStructExpr (HIR::Expr *e)
28 : TypeCheckBase (),
29 resolved (new TyTy::ErrorType (e->get_mappings ().get_hirid ())),
30 struct_path_resolved (nullptr),
31 variant (&TyTy::VariantDef::get_error_node ())
32 {}
33
34 TyTy::BaseType *
35 TypeCheckStructExpr::Resolve (HIR::StructExprStructFields *expr)
36 {
37 TypeCheckStructExpr resolver (expr);
38 resolver.resolve (*expr);
39 return resolver.resolved;
40 }
41
42 void
43 TypeCheckStructExpr::resolve (HIR::StructExprStructFields &struct_expr)
44 {
45 TyTy::BaseType *struct_path_ty
46 = TypeCheckExpr::Resolve (&struct_expr.get_struct_name ());
47 if (struct_path_ty->get_kind () != TyTy::TypeKind::ADT)
48 {
49 rust_error_at (struct_expr.get_struct_name ().get_locus (),
50 "expected an ADT type for constructor");
51 return;
52 }
53
54 struct_path_resolved = static_cast<TyTy::ADTType *> (struct_path_ty);
55 TyTy::ADTType *struct_def = struct_path_resolved;
56 if (struct_expr.has_struct_base ())
57 {
58 TyTy::BaseType *base_resolved
59 = TypeCheckExpr::Resolve (struct_expr.struct_base->base_struct.get ());
60 TyTy::BaseType *base_unify = unify_site (
61 struct_expr.struct_base->base_struct->get_mappings ().get_hirid (),
62 TyTy::TyWithLocation (struct_path_resolved),
63 TyTy::TyWithLocation (base_resolved),
64 struct_expr.struct_base->base_struct->get_locus ());
65
66 if (base_unify->get_kind () != struct_path_ty->get_kind ())
67 {
68 rust_fatal_error (struct_expr.struct_base->base_struct->get_locus (),
69 "incompatible types for base struct reference");
70 return;
71 }
72
73 struct_def = static_cast<TyTy::ADTType *> (base_unify);
74 }
75
76 // figure out the variant
77 if (struct_path_resolved->is_enum ())
78 {
79 // lookup variant id
80 HirId variant_id;
81 bool ok = context->lookup_variant_definition (
82 struct_expr.get_struct_name ().get_mappings ().get_hirid (),
83 &variant_id);
84 rust_assert (ok);
85
86 ok = struct_path_resolved->lookup_variant_by_id (variant_id, &variant);
87 rust_assert (ok);
88 }
89 else
90 {
91 rust_assert (struct_path_resolved->number_of_variants () == 1);
92 variant = struct_path_resolved->get_variants ().at (0);
93 }
94
95 std::vector<TyTy::StructFieldType *> infered_fields;
96 bool ok = true;
97
98 for (auto &field : struct_expr.get_fields ())
99 {
100 resolved_field_value_expr = nullptr;
101
102 switch (field->get_kind ())
103 {
104 case HIR::StructExprField::StructExprFieldKind::IDENTIFIER:
105 visit (static_cast<HIR::StructExprFieldIdentifier &> (*field.get ()));
106 break;
107
108 case HIR::StructExprField::StructExprFieldKind::IDENTIFIER_VALUE:
109 visit (
110 static_cast<HIR::StructExprFieldIdentifierValue &> (*field.get ()));
111 break;
112
113 case HIR::StructExprField::StructExprFieldKind::INDEX_VALUE:
114 visit (static_cast<HIR::StructExprFieldIndexValue &> (*field.get ()));
115 break;
116 }
117
118 if (resolved_field_value_expr == nullptr)
119 {
120 rust_fatal_error (field->get_locus (),
121 "failed to resolve type for field");
122 ok = false;
123 break;
124 }
125
126 context->insert_type (field->get_mappings (), resolved_field_value_expr);
127 }
128
129 // something failed setting up the fields
130 if (!ok)
131 {
132 rust_error_at (struct_expr.get_locus (),
133 "constructor type resolution failure");
134 return;
135 }
136
137 // check the arguments are all assigned and fix up the ordering
138 if (fields_assigned.size () != variant->num_fields ())
139 {
140 if (struct_def->is_union ())
141 {
142 if (fields_assigned.size () != 1 || struct_expr.has_struct_base ())
143 {
144 rust_error_at (
145 struct_expr.get_locus (),
146 "union must have exactly one field variant assigned");
147 return;
148 }
149 }
150 else if (!struct_expr.has_struct_base ())
151 {
152 rust_error_at (struct_expr.get_locus (),
153 "constructor is missing fields");
154 return;
155 }
156 else
157 {
158 // we have a struct base to assign the missing fields from.
159 // the missing fields can be implicit FieldAccessExprs for the value
160 std::set<std::string> missing_fields;
161 for (auto &field : variant->get_fields ())
162 {
163 auto it = fields_assigned.find (field->get_name ());
164 if (it == fields_assigned.end ())
165 missing_fields.insert (field->get_name ());
166 }
167
168 // we can generate FieldAccessExpr or TupleAccessExpr for the
169 // values of the missing fields.
170 for (auto &missing : missing_fields)
171 {
172 HIR::Expr *receiver
173 = struct_expr.struct_base->base_struct->clone_expr_impl ();
174
175 HIR::StructExprField *implicit_field = nullptr;
176
177 AST::AttrVec outer_attribs;
178 auto crate_num = mappings->get_current_crate ();
179 Analysis::NodeMapping mapping (
180 crate_num,
181 struct_expr.struct_base->base_struct->get_mappings ()
182 .get_nodeid (),
183 mappings->get_next_hir_id (crate_num), UNKNOWN_LOCAL_DEFID);
184
185 HIR::Expr *field_value = new HIR::FieldAccessExpr (
186 mapping, std::unique_ptr<HIR::Expr> (receiver), missing,
187 std::move (outer_attribs),
188 struct_expr.struct_base->base_struct->get_locus ());
189
190 implicit_field = new HIR::StructExprFieldIdentifierValue (
191 mapping, missing, std::unique_ptr<HIR::Expr> (field_value),
192 struct_expr.struct_base->base_struct->get_locus ());
193
194 size_t field_index;
195 bool ok = variant->lookup_field (missing, nullptr, &field_index);
196 rust_assert (ok);
197
198 adtFieldIndexToField[field_index] = implicit_field;
199 struct_expr.get_fields ().push_back (
200 std::unique_ptr<HIR::StructExprField> (implicit_field));
201 }
202 }
203 }
204
205 if (struct_def->is_union ())
206 {
207 // There is exactly one field in this constructor, we need to
208 // figure out the field index to make sure we initialize the
209 // right union field.
210 for (size_t i = 0; i < adtFieldIndexToField.size (); i++)
211 {
212 if (adtFieldIndexToField[i])
213 {
214 struct_expr.union_index = i;
215 break;
216 }
217 }
218 rust_assert (struct_expr.union_index != -1);
219 }
220 else
221 {
222 // everything is ok, now we need to ensure all field values are ordered
223 // correctly. The GIMPLE backend uses a simple algorithm that assumes each
224 // assigned field in the constructor is in the same order as the field in
225 // the type
226 for (auto &field : struct_expr.get_fields ())
227 field.release ();
228
229 std::vector<std::unique_ptr<HIR::StructExprField> > ordered_fields;
230 for (size_t i = 0; i < adtFieldIndexToField.size (); i++)
231 {
232 ordered_fields.push_back (
233 std::unique_ptr<HIR::StructExprField> (adtFieldIndexToField[i]));
234 }
235 struct_expr.set_fields_as_owner (std::move (ordered_fields));
236 }
237
238 resolved = struct_def;
239 }
240
241 void
242 TypeCheckStructExpr::visit (HIR::StructExprFieldIdentifierValue &field)
243 {
244 auto it = fields_assigned.find (field.field_name);
245 if (it != fields_assigned.end ())
246 {
247 rust_fatal_error (field.get_locus (), "used more than once");
248 return;
249 }
250
251 size_t field_index;
252 TyTy::StructFieldType *field_type;
253 bool ok = variant->lookup_field (field.field_name, &field_type, &field_index);
254 if (!ok)
255 {
256 rust_error_at (field.get_locus (), "unknown field");
257 return;
258 }
259
260 TyTy::BaseType *value = TypeCheckExpr::Resolve (field.get_value ());
261 Location value_locus = field.get_value ()->get_locus ();
262
263 HirId coercion_site_id = field.get_mappings ().get_hirid ();
264 resolved_field_value_expr
265 = coercion_site (coercion_site_id,
266 TyTy::TyWithLocation (field_type->get_field_type (),
267 field_type->get_locus ()),
268 TyTy::TyWithLocation (value, value_locus),
269 field.get_locus ());
270 if (resolved_field_value_expr != nullptr)
271 {
272 fields_assigned.insert (field.field_name);
273 adtFieldIndexToField[field_index] = &field;
274 }
275 }
276
277 void
278 TypeCheckStructExpr::visit (HIR::StructExprFieldIndexValue &field)
279 {
280 std::string field_name (std::to_string (field.get_tuple_index ()));
281 auto it = fields_assigned.find (field_name);
282 if (it != fields_assigned.end ())
283 {
284 rust_fatal_error (field.get_locus (), "used more than once");
285 return;
286 }
287
288 size_t field_index;
289 TyTy::StructFieldType *field_type;
290 bool ok = variant->lookup_field (field_name, &field_type, &field_index);
291 if (!ok)
292 {
293 rust_error_at (field.get_locus (), "unknown field");
294 return;
295 }
296
297 TyTy::BaseType *value = TypeCheckExpr::Resolve (field.get_value ());
298 Location value_locus = field.get_value ()->get_locus ();
299
300 HirId coercion_site_id = field.get_mappings ().get_hirid ();
301 resolved_field_value_expr
302 = coercion_site (coercion_site_id,
303 TyTy::TyWithLocation (field_type->get_field_type (),
304 field_type->get_locus ()),
305 TyTy::TyWithLocation (value, value_locus),
306 field.get_locus ());
307 if (resolved_field_value_expr != nullptr)
308 {
309 fields_assigned.insert (field_name);
310 adtFieldIndexToField[field_index] = &field;
311 }
312 }
313
314 void
315 TypeCheckStructExpr::visit (HIR::StructExprFieldIdentifier &field)
316 {
317 auto it = fields_assigned.find (field.get_field_name ());
318 if (it != fields_assigned.end ())
319 {
320 rust_fatal_error (field.get_locus (), "used more than once");
321 return;
322 }
323
324 size_t field_index;
325 TyTy::StructFieldType *field_type;
326 bool ok = variant->lookup_field (field.get_field_name (), &field_type,
327 &field_index);
328 if (!ok)
329 {
330 rust_error_at (field.get_locus (), "unknown field");
331 return;
332 }
333
334 // we can make the field look like a path expr to take advantage of existing
335 // code
336 Analysis::NodeMapping mappings_copy1 = field.get_mappings ();
337 Analysis::NodeMapping mappings_copy2 = field.get_mappings ();
338
339 HIR::PathIdentSegment ident_seg (field.get_field_name ());
340 HIR::PathExprSegment seg (mappings_copy1, ident_seg, field.get_locus (),
341 HIR::GenericArgs::create_empty ());
342 HIR::PathInExpression expr (mappings_copy2, {seg}, field.get_locus (), false,
343 {});
344 TyTy::BaseType *value = TypeCheckExpr::Resolve (&expr);
345 Location value_locus = expr.get_locus ();
346
347 HirId coercion_site_id = field.get_mappings ().get_hirid ();
348 resolved_field_value_expr
349 = coercion_site (coercion_site_id,
350 TyTy::TyWithLocation (field_type->get_field_type (),
351 field_type->get_locus ()),
352 TyTy::TyWithLocation (value, value_locus),
353 field.get_locus ());
354 if (resolved_field_value_expr != nullptr)
355
356 {
357 fields_assigned.insert (field.get_field_name ());
358 adtFieldIndexToField[field_index] = &field;
359 }
360 }
361
362 } // namespace Resolver
363 } // namespace Rust