// Copyright (C) 2020-2023 Free Software Foundation, Inc. // This file is part of GCC. // GCC is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 3, or (at your option) any later // version. // GCC is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // You should have received a copy of the GNU General Public License // along with GCC; see the file COPYING3. If not see // . #include "rust-hir-type-check.h" #include "rust-hir-full.h" #include "rust-hir-type-check-expr.h" #include "rust-hir-type-check-struct-field.h" namespace Rust { namespace Resolver { TypeCheckStructExpr::TypeCheckStructExpr (HIR::Expr *e) : TypeCheckBase (), resolved (new TyTy::ErrorType (e->get_mappings ().get_hirid ())), struct_path_resolved (nullptr), variant (&TyTy::VariantDef::get_error_node ()) {} TyTy::BaseType * TypeCheckStructExpr::Resolve (HIR::StructExprStructFields *expr) { TypeCheckStructExpr resolver (expr); resolver.resolve (*expr); return resolver.resolved; } void TypeCheckStructExpr::resolve (HIR::StructExprStructFields &struct_expr) { TyTy::BaseType *struct_path_ty = TypeCheckExpr::Resolve (&struct_expr.get_struct_name ()); if (struct_path_ty->get_kind () != TyTy::TypeKind::ADT) { rust_error_at (struct_expr.get_struct_name ().get_locus (), "expected an ADT type for constructor"); return; } struct_path_resolved = static_cast (struct_path_ty); TyTy::ADTType *struct_def = struct_path_resolved; if (struct_expr.has_struct_base ()) { TyTy::BaseType *base_resolved = TypeCheckExpr::Resolve (struct_expr.struct_base->base_struct.get ()); TyTy::BaseType *base_unify = unify_site ( struct_expr.struct_base->base_struct->get_mappings ().get_hirid (), TyTy::TyWithLocation (struct_path_resolved), TyTy::TyWithLocation (base_resolved), struct_expr.struct_base->base_struct->get_locus ()); if (base_unify->get_kind () != struct_path_ty->get_kind ()) { rust_fatal_error (struct_expr.struct_base->base_struct->get_locus (), "incompatible types for base struct reference"); return; } struct_def = static_cast (base_unify); } // figure out the variant if (struct_path_resolved->is_enum ()) { // lookup variant id HirId variant_id; bool ok = context->lookup_variant_definition ( struct_expr.get_struct_name ().get_mappings ().get_hirid (), &variant_id); rust_assert (ok); ok = struct_path_resolved->lookup_variant_by_id (variant_id, &variant); rust_assert (ok); } else { rust_assert (struct_path_resolved->number_of_variants () == 1); variant = struct_path_resolved->get_variants ().at (0); } std::vector infered_fields; bool ok = true; for (auto &field : struct_expr.get_fields ()) { resolved_field_value_expr = nullptr; switch (field->get_kind ()) { case HIR::StructExprField::StructExprFieldKind::IDENTIFIER: visit (static_cast (*field.get ())); break; case HIR::StructExprField::StructExprFieldKind::IDENTIFIER_VALUE: visit ( static_cast (*field.get ())); break; case HIR::StructExprField::StructExprFieldKind::INDEX_VALUE: visit (static_cast (*field.get ())); break; } if (resolved_field_value_expr == nullptr) { rust_fatal_error (field->get_locus (), "failed to resolve type for field"); ok = false; break; } context->insert_type (field->get_mappings (), resolved_field_value_expr); } // something failed setting up the fields if (!ok) { rust_error_at (struct_expr.get_locus (), "constructor type resolution failure"); return; } // check the arguments are all assigned and fix up the ordering if (fields_assigned.size () != variant->num_fields ()) { if (struct_def->is_union ()) { if (fields_assigned.size () != 1 || struct_expr.has_struct_base ()) { rust_error_at ( struct_expr.get_locus (), "union must have exactly one field variant assigned"); return; } } else if (!struct_expr.has_struct_base ()) { rust_error_at (struct_expr.get_locus (), "constructor is missing fields"); return; } else { // we have a struct base to assign the missing fields from. // the missing fields can be implicit FieldAccessExprs for the value std::set missing_fields; for (auto &field : variant->get_fields ()) { auto it = fields_assigned.find (field->get_name ()); if (it == fields_assigned.end ()) missing_fields.insert (field->get_name ()); } // we can generate FieldAccessExpr or TupleAccessExpr for the // values of the missing fields. for (auto &missing : missing_fields) { HIR::Expr *receiver = struct_expr.struct_base->base_struct->clone_expr_impl (); HIR::StructExprField *implicit_field = nullptr; AST::AttrVec outer_attribs; auto crate_num = mappings->get_current_crate (); Analysis::NodeMapping mapping ( crate_num, struct_expr.struct_base->base_struct->get_mappings () .get_nodeid (), mappings->get_next_hir_id (crate_num), UNKNOWN_LOCAL_DEFID); HIR::Expr *field_value = new HIR::FieldAccessExpr ( mapping, std::unique_ptr (receiver), missing, std::move (outer_attribs), struct_expr.struct_base->base_struct->get_locus ()); implicit_field = new HIR::StructExprFieldIdentifierValue ( mapping, missing, std::unique_ptr (field_value), struct_expr.struct_base->base_struct->get_locus ()); size_t field_index; bool ok = variant->lookup_field (missing, nullptr, &field_index); rust_assert (ok); adtFieldIndexToField[field_index] = implicit_field; struct_expr.get_fields ().push_back ( std::unique_ptr (implicit_field)); } } } if (struct_def->is_union ()) { // There is exactly one field in this constructor, we need to // figure out the field index to make sure we initialize the // right union field. for (size_t i = 0; i < adtFieldIndexToField.size (); i++) { if (adtFieldIndexToField[i]) { struct_expr.union_index = i; break; } } rust_assert (struct_expr.union_index != -1); } else { // everything is ok, now we need to ensure all field values are ordered // correctly. The GIMPLE backend uses a simple algorithm that assumes each // assigned field in the constructor is in the same order as the field in // the type for (auto &field : struct_expr.get_fields ()) field.release (); std::vector > ordered_fields; for (size_t i = 0; i < adtFieldIndexToField.size (); i++) { ordered_fields.push_back ( std::unique_ptr (adtFieldIndexToField[i])); } struct_expr.set_fields_as_owner (std::move (ordered_fields)); } resolved = struct_def; } void TypeCheckStructExpr::visit (HIR::StructExprFieldIdentifierValue &field) { auto it = fields_assigned.find (field.field_name); if (it != fields_assigned.end ()) { rust_fatal_error (field.get_locus (), "used more than once"); return; } size_t field_index; TyTy::StructFieldType *field_type; bool ok = variant->lookup_field (field.field_name, &field_type, &field_index); if (!ok) { rust_error_at (field.get_locus (), "unknown field"); return; } TyTy::BaseType *value = TypeCheckExpr::Resolve (field.get_value ()); Location value_locus = field.get_value ()->get_locus (); HirId coercion_site_id = field.get_mappings ().get_hirid (); resolved_field_value_expr = coercion_site (coercion_site_id, TyTy::TyWithLocation (field_type->get_field_type (), field_type->get_locus ()), TyTy::TyWithLocation (value, value_locus), field.get_locus ()); if (resolved_field_value_expr != nullptr) { fields_assigned.insert (field.field_name); adtFieldIndexToField[field_index] = &field; } } void TypeCheckStructExpr::visit (HIR::StructExprFieldIndexValue &field) { std::string field_name (std::to_string (field.get_tuple_index ())); auto it = fields_assigned.find (field_name); if (it != fields_assigned.end ()) { rust_fatal_error (field.get_locus (), "used more than once"); return; } size_t field_index; TyTy::StructFieldType *field_type; bool ok = variant->lookup_field (field_name, &field_type, &field_index); if (!ok) { rust_error_at (field.get_locus (), "unknown field"); return; } TyTy::BaseType *value = TypeCheckExpr::Resolve (field.get_value ()); Location value_locus = field.get_value ()->get_locus (); HirId coercion_site_id = field.get_mappings ().get_hirid (); resolved_field_value_expr = coercion_site (coercion_site_id, TyTy::TyWithLocation (field_type->get_field_type (), field_type->get_locus ()), TyTy::TyWithLocation (value, value_locus), field.get_locus ()); if (resolved_field_value_expr != nullptr) { fields_assigned.insert (field_name); adtFieldIndexToField[field_index] = &field; } } void TypeCheckStructExpr::visit (HIR::StructExprFieldIdentifier &field) { auto it = fields_assigned.find (field.get_field_name ()); if (it != fields_assigned.end ()) { rust_fatal_error (field.get_locus (), "used more than once"); return; } size_t field_index; TyTy::StructFieldType *field_type; bool ok = variant->lookup_field (field.get_field_name (), &field_type, &field_index); if (!ok) { rust_error_at (field.get_locus (), "unknown field"); return; } // we can make the field look like a path expr to take advantage of existing // code Analysis::NodeMapping mappings_copy1 = field.get_mappings (); Analysis::NodeMapping mappings_copy2 = field.get_mappings (); HIR::PathIdentSegment ident_seg (field.get_field_name ()); HIR::PathExprSegment seg (mappings_copy1, ident_seg, field.get_locus (), HIR::GenericArgs::create_empty ()); HIR::PathInExpression expr (mappings_copy2, {seg}, field.get_locus (), false, {}); TyTy::BaseType *value = TypeCheckExpr::Resolve (&expr); Location value_locus = expr.get_locus (); HirId coercion_site_id = field.get_mappings ().get_hirid (); resolved_field_value_expr = coercion_site (coercion_site_id, TyTy::TyWithLocation (field_type->get_field_type (), field_type->get_locus ()), TyTy::TyWithLocation (value, value_locus), field.get_locus ()); if (resolved_field_value_expr != nullptr) { fields_assigned.insert (field.get_field_name ()); adtFieldIndexToField[field_index] = &field; } } } // namespace Resolver } // namespace Rust