diff --git a/xls/dslx/frontend/ast.cc b/xls/dslx/frontend/ast.cc index 36523a9c5f..d28d08fd27 100644 --- a/xls/dslx/frontend/ast.cc +++ b/xls/dslx/frontend/ast.cc @@ -405,15 +405,7 @@ AnyNameDef TypeDefinitionGetNameDef(const TypeDefinition& td) { } AstNode* TypeDefinitionToAstNode(const TypeDefinition& td) { - return absl::visit(Visitor{ - [](TypeAlias* n) -> AstNode* { return n; }, - [](StructDef* n) -> AstNode* { return n; }, - [](ProcDef* n) -> AstNode* { return n; }, - [](EnumDef* n) -> AstNode* { return n; }, - [](ColonRef* n) -> AstNode* { return n; }, - [](UseTreeEntry* n) -> AstNode* { return n; }, - }, - td); + return absl::visit(Visitor{[](auto* n) -> AstNode* { return n; }}, td); } absl::StatusOr ToTypeDefinition(AstNode* node) { diff --git a/xls/dslx/frontend/ast_utils.cc b/xls/dslx/frontend/ast_utils.cc index 4676d42c18..9f01abc0bd 100644 --- a/xls/dslx/frontend/ast_utils.cc +++ b/xls/dslx/frontend/ast_utils.cc @@ -201,12 +201,7 @@ absl::StatusOr ResolveLocalStructDef(TypeDefinition td) { return ResolveLocalStructDef(&n->type_annotation(), td); }, [&](StructDef* n) -> absl::StatusOr { return n; }, - [&](ProcDef* n) -> absl::StatusOr { return error(n); }, - [&](EnumDef* n) -> absl::StatusOr { return error(n); }, - [&](ColonRef* n) -> absl::StatusOr { return error(n); }, - [&](UseTreeEntry* n) -> absl::StatusOr { - return error(n); - }, + [&](auto* n) -> absl::StatusOr { return error(n); }, }, td); } diff --git a/xls/dslx/frontend/bindings.h b/xls/dslx/frontend/bindings.h index 353cafbac5..cb14944d78 100644 --- a/xls/dslx/frontend/bindings.h +++ b/xls/dslx/frontend/bindings.h @@ -231,6 +231,16 @@ class Bindings { if (!bn) { return false; } + // A `NameDef` is a type definition if its definer is a + // `GenericTypeAnnotation`. + if (std::holds_alternative(*bn)) { + auto nd = std::get(*bn); + if (nd->definer() != nullptr) { + if (auto* ta = dynamic_cast(nd->definer())) { + return ta->IsAnnotation(); + } + } + } return std::holds_alternative(*bn) || std::holds_alternative(*bn) || std::holds_alternative(*bn) || diff --git a/xls/dslx/type_system_v2/import_utils.cc b/xls/dslx/type_system_v2/import_utils.cc index 6c5432076f..a0c0a6a824 100644 --- a/xls/dslx/type_system_v2/import_utils.cc +++ b/xls/dslx/type_system_v2/import_utils.cc @@ -44,8 +44,9 @@ namespace { // can be obtained by calling `GetStructOrProcRef` or `GetEnumDef`. class TypeRefUnwrapper : public AstNodeVisitorWithDefault { public: - explicit TypeRefUnwrapper(const ImportData& import_data) - : import_data_(import_data) {} + explicit TypeRefUnwrapper(const ImportData& import_data, + bool include_generic = false) + : import_data_(import_data), include_generic_(include_generic) {} absl::Status HandleColonRef(const ColonRef* colon_ref) override { XLS_ASSIGN_OR_RETURN(std::optional import_module, @@ -101,6 +102,12 @@ class TypeRefUnwrapper : public AstNodeVisitorWithDefault { return ToAstNode(annotation->type_ref()->type_definition())->Accept(this); } + absl::Status HandleGenericTypeAnnotation( + const GenericTypeAnnotation* annotation) override { + is_generic_ = true; + return absl::OkStatus(); + } + absl::Status HandleProcDef(const ProcDef* def) override { type_def_ = const_cast(def); return absl::OkStatus(); @@ -128,16 +135,21 @@ class TypeRefUnwrapper : public AstNodeVisitorWithDefault { } std::optional GetStructOrProcRef() { - if (!type_def_.has_value() || - (!std::holds_alternative(*type_def_) && - !std::holds_alternative(*type_def_))) { + if (!(include_generic_ && is_generic_) && + (!type_def_.has_value() || + (!std::holds_alternative(*type_def_) && + !std::holds_alternative(*type_def_)))) { return std::nullopt; } return StructOrProcRef{ - .def = absl::down_cast(ToAstNode(*type_def_)), + .def = type_def_.has_value() + ? absl::down_cast(ToAstNode(*type_def_)) + : nullptr, .parametrics = parametrics_, .instantiator = instantiator_, - .type_ref_type_annotation = type_ref_type_annotation_}; + .type_ref_type_annotation = type_ref_type_annotation_, + .is_generic = is_generic_, + }; } std::optional GetEnumDef() { @@ -148,23 +160,26 @@ class TypeRefUnwrapper : public AstNodeVisitorWithDefault { private: const ImportData& import_data_; + bool include_generic_; // These fields get populated as we visit nodes. std::vector parametrics_; std::optional type_def_; std::optional type_ref_type_annotation_; std::optional instantiator_; + bool is_generic_ = false; }; } // namespace absl::StatusOr> GetStructOrProcRef( - const TypeAnnotation* annotation, const ImportData& import_data) { + const TypeAnnotation* annotation, const ImportData& import_data, + bool include_generic) { if (!annotation->IsAnnotation() && !annotation->IsAnnotation()) { return std::nullopt; } - TypeRefUnwrapper unwrapper(import_data); + TypeRefUnwrapper unwrapper(import_data, include_generic); XLS_RETURN_IF_ERROR(annotation->Accept(&unwrapper)); return unwrapper.GetStructOrProcRef(); } diff --git a/xls/dslx/type_system_v2/import_utils.h b/xls/dslx/type_system_v2/import_utils.h index 20125e6867..f08a728eb2 100644 --- a/xls/dslx/type_system_v2/import_utils.h +++ b/xls/dslx/type_system_v2/import_utils.h @@ -31,8 +31,12 @@ namespace xls::dslx { // Resolves the definition and parametrics for the struct or proc type referred // to by `annotation`. +// If `include_generic` is true, annotations that resolve to +// `GenericTypeAnnotation` will return a `StructOrProcRef`, but with +// `.def=nullptr` and `.is_generic=true`. absl::StatusOr> GetStructOrProcRef( - const TypeAnnotation* annotation, const ImportData& import_data); + const TypeAnnotation* annotation, const ImportData& import_data, + bool include_generic = false); // Variant that takes a `ColonRef`. This will only yield a struct ref if the // `ColonRef` itself refers to an actual struct or alias of one. It will yield diff --git a/xls/dslx/type_system_v2/populate_table_visitor.cc b/xls/dslx/type_system_v2/populate_table_visitor.cc index bf3d49144c..c1ca13760b 100644 --- a/xls/dslx/type_system_v2/populate_table_visitor.cc +++ b/xls/dslx/type_system_v2/populate_table_visitor.cc @@ -2278,8 +2278,14 @@ class PopulateInferenceTableVisitor : public PopulateTableVisitor, // creates additional pitfalls, like erroneously naming two different // arguments the same thing. XLS_RETURN_IF_ERROR(node->struct_ref()->Accept(this)); - XLS_ASSIGN_OR_RETURN(std::optional struct_or_proc_ref, - GetStructOrProcRef(node->struct_ref(), import_data_)); + const NameRef* type_variable = *table_.GetTypeVariable(node); + if (source.has_value()) { + XLS_RETURN_IF_ERROR(table_.SetTypeVariable(*source, type_variable)); + } + XLS_ASSIGN_OR_RETURN( + std::optional struct_or_proc_ref, + GetStructOrProcRef(node->struct_ref(), import_data_, true)); + if (!struct_or_proc_ref.has_value()) { return TypeInferenceErrorStatusForAnnotation( node->span(), node->struct_ref(), @@ -2289,33 +2295,38 @@ class PopulateInferenceTableVisitor : public PopulateTableVisitor, file_table_); } - const StructDefBase* struct_def = struct_or_proc_ref->def; + absl::flat_hash_map formal_member_map; + bool is_generic = false; + bool allow_missing_members = false; + if (struct_or_proc_ref->is_generic) { + // If the struct ref is a `GenericTypeAnnotation`, then we are + // instantiating a struct that is typed as a generic parametric. In this + // case, we don't try to resolve the struct def and we just create + // annotations based on the instantiation members. + is_generic = true; + } else { + const StructDefBase* struct_def = struct_or_proc_ref->def; - const NameRef* type_variable = *table_.GetTypeVariable(node); - if (source.has_value()) { - XLS_RETURN_IF_ERROR(table_.SetTypeVariable(*source, type_variable)); + XLS_RETURN_IF_ERROR(table_.SetTypeAnnotation( + node, CreateStructOrProcAnnotation( + module_, const_cast(struct_def), + struct_or_proc_ref->parametrics, node))); + const StructDef* concrete_struct_def = + dynamic_cast(struct_def); + allow_missing_members = + in_fuzz_test_domain_ || (concrete_struct_def != nullptr && + concrete_struct_def->is_domain_struct()); + XLS_RETURN_IF_ERROR(ValidateStructInstanceMemberNames( + *node, *struct_def, allow_missing_members)); + formal_member_map.reserve(struct_def->members().size()); + for (const StructMemberNode* formal_member : struct_def->members()) { + formal_member_map.emplace(formal_member->name(), formal_member); + } } - XLS_RETURN_IF_ERROR(table_.SetTypeAnnotation( - node, CreateStructOrProcAnnotation( - module_, const_cast(struct_def), - struct_or_proc_ref->parametrics, node))); - const StructDef* concrete_struct_def = - dynamic_cast(struct_def); - bool allow_missing_members = - in_fuzz_test_domain_ || (concrete_struct_def != nullptr && - concrete_struct_def->is_domain_struct()); - XLS_RETURN_IF_ERROR(ValidateStructInstanceMemberNames( - *node, *struct_def, allow_missing_members)); - absl::flat_hash_map formal_member_map; - for (const StructMemberNode* formal_member : struct_def->members()) { - formal_member_map.emplace(formal_member->name(), formal_member); - } const TypeAnnotation* struct_variable_type = module_.Make(type_variable); for (const auto& [name, actual_member] : node->members()) { - const StructMemberNode* formal_member = formal_member_map.at(name); - if (allow_missing_members) { // In a fuzz test domain, the actual member expression represents a // domain (e.g. `u32:0..10` which has type `u32[10]`) rather than a @@ -2326,20 +2337,29 @@ class PopulateInferenceTableVisitor : public PopulateTableVisitor, DefineAndSetTypeVariable(actual_member, "actual_member_domain")); XLS_RETURN_IF_ERROR(actual_member->Accept(this)); } else { - // In the context of instance initialization only, we require the RHS of - // proc state assignment to be T rather than State. - const TypeAnnotation* formal_member_type = - module_.Make( - formal_member->name_def()->span(), struct_variable_type, - formal_member->name(), - /*use_wrapped_type_if_proc_state=*/false); - - table_.SetAnnotationFlag(formal_member_type, - TypeInferenceFlag::kFormalMemberType); + const TypeAnnotation* member_type = nullptr; + if (is_generic) { + // For generic types, construct the member type based on the actual + // member. + member_type = module_.Make( + actual_member->span(), struct_variable_type, name, + /*use_wrapped_type_if_proc_state=*/false); + } else { + const StructMemberNode* formal_member = formal_member_map.at(name); + // In the context of instance initialization only, we require the RHS + // of proc state assignment to be T rather than State. + member_type = module_.Make( + formal_member->name_def()->span(), struct_variable_type, + formal_member->name(), + /*use_wrapped_type_if_proc_state=*/false); + + table_.SetAnnotationFlag(member_type, + TypeInferenceFlag::kFormalMemberType); + } XLS_RETURN_IF_ERROR(DefineAndSetTypeVariable( - actual_member, "actual_member", formal_member_type)); + actual_member, "actual_member", member_type)); XLS_RETURN_IF_ERROR( - table_.SetTypeAnnotation(actual_member, formal_member_type)); + table_.SetTypeAnnotation(actual_member, member_type)); XLS_RETURN_IF_ERROR(actual_member->Accept(this)); } } diff --git a/xls/dslx/type_system_v2/type_annotation_utils.h b/xls/dslx/type_system_v2/type_annotation_utils.h index 5a6748af52..65afcdde40 100644 --- a/xls/dslx/type_system_v2/type_annotation_utils.h +++ b/xls/dslx/type_system_v2/type_annotation_utils.h @@ -42,6 +42,7 @@ struct StructOrProcRef { std::vector parametrics; std::optional instantiator; std::optional type_ref_type_annotation; + bool is_generic; }; // The signedness and bit count extracted from a `TypeAnnotation`. The diff --git a/xls/dslx/type_system_v2/typecheck_module_v2_generics_test.cc b/xls/dslx/type_system_v2/typecheck_module_v2_generics_test.cc index 27f72da194..b539155912 100644 --- a/xls/dslx/type_system_v2/typecheck_module_v2_generics_test.cc +++ b/xls/dslx/type_system_v2/typecheck_module_v2_generics_test.cc @@ -640,7 +640,7 @@ const A = add_wrapper(u16:1, 2); TypecheckFails(HasTypeMismatch("uN[16]", "uN[32]"))); } -TEST(TypecheckV2GenericTest, ComparisonAsParametricArgument) { +TEST(TypecheckV2GenericsTest, ComparisonAsParametricArgument) { EXPECT_THAT(R"( fn foo(a: xN[S][32]) -> xN[S][32] { a } const Y = foo<{2 > 1}>(s32:5); @@ -650,7 +650,7 @@ const Y = foo<{2 > 1}>(s32:5); HasNodeWithType("1", "uN[2]")))); } -TEST(TypecheckV2GenericTest, ComparisonAsParametricArgumentWithConflictFails) { +TEST(TypecheckV2GenericsTest, ComparisonAsParametricArgumentWithConflictFails) { EXPECT_THAT(R"( fn foo(a: xN[S][32]) -> xN[S][32] { a } const Y = foo<{2 > 1}>(u32:5); @@ -658,7 +658,7 @@ const Y = foo<{2 > 1}>(u32:5); TypecheckFails(HasSignednessMismatch("xN[1][32]", "u32"))); } -TEST(TypecheckV2GenericTest, ComparisonAndSumAsParametricArguments) { +TEST(TypecheckV2GenericsTest, ComparisonAndSumAsParametricArguments) { XLS_ASSERT_OK_AND_ASSIGN(TypecheckResult result, TypecheckV2(R"( const X = u32:1; fn foo(a: xN[S][N]) -> xN[S][N] { a } @@ -669,7 +669,7 @@ const Y = foo<{X == 1}, {X + 3}>(s4:3); EXPECT_THAT(type_info_string, HasSubstr("node: `Y`, type: sN[4]")); } -TEST(TypecheckV2GenericTest, +TEST(TypecheckV2GenericsTest, ComparisonAndSumParametricArgumentsWithConflictFails) { EXPECT_THAT(R"( const X = u32:1; @@ -679,7 +679,7 @@ const Y = foo<{X == 1}, {X + 4}>(s4:3); TypecheckFails(HasSizeMismatch("xN[1][5]", "s4"))); } -TEST(TypecheckV2GenericTest, +TEST(TypecheckV2GenericsTest, InferenceOfParametricUsingAnnotationWithInvocation) { // This is based on a similar pattern that occurs in std.x, with the inference // of the implicit parametrics for `assert_eq` being the potential trouble @@ -696,7 +696,7 @@ fn foo() { TypecheckSucceeds(HasNodeWithType("y", "uN[34]"))); } -TEST(TypecheckV2GenericTest, MultiTermSumOfParametricCalls) { +TEST(TypecheckV2GenericsTest, MultiTermSumOfParametricCalls) { EXPECT_THAT( R"( fn foo(a: uN[N]) -> u32 { a as u32 } @@ -709,7 +709,7 @@ const Y = foo(u32:1) + foo(u32:2) + foo(u32:3) + foo(u32:4) + foo(u32:5) + TypecheckSucceeds(HasNodeWithType("Y", "uN[32]"))); } -TEST(TypecheckV2GenericTest, ParametricDefaultWithTypeBasedOnOtherParametric) { +TEST(TypecheckV2GenericsTest, ParametricDefaultWithTypeBasedOnOtherParametric) { EXPECT_THAT(R"( fn p(x: bits[X]) -> bits[X] { x } const X = p(u1:0); @@ -717,7 +717,7 @@ const X = p(u1:0); TypecheckSucceeds(HasNodeWithType("X", "uN[1]"))); } -TEST(TypecheckV2GenericTest, UnassignedReturnValueIgnoredParametric) { +TEST(TypecheckV2GenericsTest, UnassignedReturnValueIgnoredParametric) { EXPECT_THAT( R"( fn ignored() -> uN[N] { zero!() } @@ -730,7 +730,7 @@ fn main() -> u32 { TypecheckSucceeds(HasNodeWithType("ignored()", "uN[31]"))); } -TEST(TypecheckV2GenericTest, ParametricLet) { +TEST(TypecheckV2GenericsTest, ParametricLet) { EXPECT_THAT(R"( fn f() -> uN[N] { let x = uN[N]:0; @@ -744,7 +744,7 @@ const Y = f<16>(); HasNodeWithType("Y", "uN[16]")))); } -TEST(TypecheckV2GenericTest, ParametricContextStackingViaDefault) { +TEST(TypecheckV2GenericsTest, ParametricContextStackingViaDefault) { EXPECT_THAT( R"( fn g(x: uN[A]) -> u32 { 32 } @@ -754,7 +754,7 @@ const X = f(u32:5); TypecheckSucceeds(HasNodeWithType("X", "uN[32]"))); } -TEST(TypecheckV2GenericTest, ParametricValuesDefinedMultipleTimesInTypeAlias) { +TEST(TypecheckV2GenericsTest, ParametricValuesDefinedMultipleTimesInTypeAlias) { EXPECT_THAT(R"( struct S { x: bits[X], @@ -771,7 +771,7 @@ fn f() -> uN[4] { "times for annotation: `S<3>`"))); } -TEST(TypecheckV2GenericTest, ParametricValuesNeverDefinedInTypeAlias) { +TEST(TypecheckV2GenericsTest, ParametricValuesNeverDefinedInTypeAlias) { EXPECT_THAT( R"( struct S { @@ -787,7 +787,7 @@ fn f() -> MyS { "Could not infer parametric(s) for instance of struct S: X"))); } -TEST(TypecheckV2GenericTest, ImportedConstantSizeAsParametricValue) { +TEST(TypecheckV2GenericsTest, ImportedConstantSizeAsParametricValue) { constexpr std::string_view kImported = R"( pub const SOME_CONSTANT = u32:8; )"; @@ -804,7 +804,7 @@ const X = foo(uN[imported::SOME_CONSTANT]:0); IsOkAndHolds(HasTypeInfo(HasNodeWithType("X", "uN[8]")))); } -TEST(TypecheckV2GenericTest, ImportTypeAliasWithParametrics) { +TEST(TypecheckV2GenericsTest, ImportTypeAliasWithParametrics) { constexpr std::string_view kImported = R"( pub struct S { a: uN[N] @@ -824,7 +824,7 @@ fn get_a(s: imported::S32) -> u32 { XLS_EXPECT_OK(TypecheckV2(kProgram, "main", &import_data)); } -TEST(TypecheckV2GenericTest, RangeAsArgumentParametric) { +TEST(TypecheckV2GenericsTest, RangeAsArgumentParametric) { EXPECT_THAT( R"( pub fn pass_back(input: u32[N]) -> u32[N] { @@ -841,7 +841,7 @@ fn test() { HasNodeWithType("pass_back(0..4)", "uN[32][4]")))); } -TEST(TypecheckV2GenericTest, InferParametricWithRange) { +TEST(TypecheckV2GenericsTest, InferParametricWithRange) { EXPECT_THAT(R"( pub fn infer_parametric(true_indices: u32[M]) -> bool[N] { for (i, x): (u32, bool[N]) in true_indices { @@ -862,7 +862,7 @@ fn test() { HasNodeWithType("a", "uN[32][4]")))); } -TEST(TypecheckV2GenericTest, UnusedDefinitionParametrics) { +TEST(TypecheckV2GenericsTest, UnusedDefinitionParametrics) { XLS_ASSERT_OK_AND_ASSIGN(TypecheckResult result, TypecheckV2(R"( fn f() { let a: uN[A] = 0; @@ -877,7 +877,7 @@ fn g() { "Definition of `a` (type `uN[2]`) is not used in function `f`"); } -TEST(TypecheckV2GenericTest, ParametricTypeRolloverOk) { +TEST(TypecheckV2GenericsTest, ParametricTypeRolloverOk) { XLS_ASSERT_OK_AND_ASSIGN(TypecheckResult result, TypecheckV2(R"( fn p() -> u32 { uN[N - M + u32:2]:1 as u32 @@ -890,12 +890,12 @@ fn main() -> u32 { EXPECT_EQ(result.tm.warnings.warnings().size(), 0); } -TEST(TypecheckV2GenericTest, XnAnnotationWithNonBoolLiteralSignednessFails) { +TEST(TypecheckV2GenericsTest, XnAnnotationWithNonBoolLiteralSignednessFails) { EXPECT_THAT("const Y = xN[2][32]:1;", TypecheckFails(HasSizeMismatch("bool", "u2"))); } -TEST(TypecheckV2GenericTest, XnAnnotationWithNonBoolConstantSignednessFails) { +TEST(TypecheckV2GenericsTest, XnAnnotationWithNonBoolConstantSignednessFails) { EXPECT_THAT(R"( const X = u32:2; const Y = xN[X][32]:1; @@ -903,7 +903,7 @@ const Y = xN[X][32]:1; TypecheckFails(HasSizeMismatch("bool", "u32"))); } -TEST(TypecheckV2GenericTest, ConcatOfBitsAsImplicitParametric) { +TEST(TypecheckV2GenericsTest, ConcatOfBitsAsImplicitParametric) { EXPECT_THAT(R"( fn f(a: uN[A]) -> uN[A] { a } const X = f(u16:0 ++ u32:0); @@ -911,7 +911,7 @@ const X = f(u16:0 ++ u32:0); TypecheckSucceeds(HasNodeWithType("X", "uN[48]"))); } -TEST(TypecheckV2GenericTest, ConcatOfArrayAsImplicitParametric) { +TEST(TypecheckV2GenericsTest, ConcatOfArrayAsImplicitParametric) { EXPECT_THAT(R"( fn f(a: u16[A]) -> u16[A] { a } const X = f([u16:0, 1, 2] ++ [u16:20]); @@ -919,7 +919,7 @@ const X = f([u16:0, 1, 2] ++ [u16:20]); TypecheckSucceeds(HasNodeWithType("X", "uN[16][4]"))); } -TEST(TypecheckV2GenericTest, +TEST(TypecheckV2GenericsTest, ParametricFunctionTakingIntegerOfImplicitParameterizedSize) { EXPECT_THAT(R"( fn foo(a: uN[N]) -> uN[N] { a } @@ -931,7 +931,7 @@ const Y = foo(u11:5); HasNodeWithType("const Y = foo(u11:5);", "uN[11]")))); } -TEST(TypecheckV2GenericTest, +TEST(TypecheckV2GenericsTest, ParametricFunctionTakingIntegerOfImplicitParameterizedSignedness) { EXPECT_THAT(R"( fn foo(a: xN[S][32]) -> xN[S][32] { a } @@ -943,7 +943,7 @@ const Y = foo(s32:5); HasNodeWithType("const Y = foo(s32:5);", "sN[32]")))); } -TEST(TypecheckV2GenericTest, +TEST(TypecheckV2GenericsTest, ParametricFunctionTakingIntegerOfImplicitParameterizedSignednessAndSize) { EXPECT_THAT(R"( fn foo(a: xN[S][N]) -> xN[S][N] { a } @@ -955,7 +955,7 @@ const Y = foo(s11:5); HasNodeWithType("const Y = foo(s11:5);", "sN[11]")))); } -TEST(TypecheckV2GenericTest, +TEST(TypecheckV2GenericsTest, ParametricFunctionWithDefaultImplicitlyOverriddenFails) { EXPECT_THAT(R"( fn foo(a: uN[N]) -> uN[N] { a } @@ -964,7 +964,7 @@ const X = foo<11>(u20:5); TypecheckFails(HasSizeMismatch("u20", "uN[12]"))); } -TEST(TypecheckV2GenericTest, +TEST(TypecheckV2GenericsTest, ParametricFunctionTakingIntegerOfImplicitSignednessAndSizeWithSum) { EXPECT_THAT(R"( const X = u32:3; @@ -976,7 +976,7 @@ const Z = foo(X + Y + X + 50); HasNodeWithType("const Z = foo(X + Y + X + 50);", "uN[32]"))); } -TEST(TypecheckV2GenericTest, ParametricFunctionTakingArrayOfImplicitSize) { +TEST(TypecheckV2GenericsTest, ParametricFunctionTakingArrayOfImplicitSize) { EXPECT_THAT( R"( fn foo(a: u32[N]) -> u32[N] { a } @@ -992,7 +992,7 @@ const Y = foo([4, 5, 6, 7]); HasNodeWithType("7", "uN[32]")))); } -TEST(TypecheckV2GenericTest, ParametricFunctionImplicitParameterPropagation) { +TEST(TypecheckV2GenericsTest, ParametricFunctionImplicitParameterPropagation) { EXPECT_THAT(R"( fn bar(a: uN[A], b: uN[B]) -> uN[A] { a + 1 } fn foo(a: uN[A], b: uN[B]) -> uN[B] { bar(b, a) } @@ -1002,7 +1002,7 @@ const X = foo(u23:4, u17:5); HasNodeWithType("const X = foo(u23:4, u17:5);", "uN[17]"))); } -TEST(TypecheckV2GenericTest, +TEST(TypecheckV2GenericsTest, ParametricFunctionImplicitParameterExplicitPropagation) { EXPECT_THAT(R"( fn bar(a: uN[A], b: uN[B]) -> uN[A] { a + 1 } @@ -1013,7 +1013,7 @@ const X = foo(u23:4, u17:5); HasNodeWithType("const X = foo(u23:4, u17:5);", "uN[17]"))); } -TEST(TypecheckV2GenericTest, ParametricFunctionImplicitInvocationNesting) { +TEST(TypecheckV2GenericsTest, ParametricFunctionImplicitInvocationNesting) { EXPECT_THAT(R"( fn foo(a: uN[N]) -> uN[N] { a + 1 } const X = foo(foo(u24:4) + foo(u24:5)); @@ -1022,7 +1022,7 @@ const X = foo(foo(u24:4) + foo(u24:5)); "const X = foo(foo(u24:4) + foo(u24:5));", "uN[24]"))); } -TEST(TypecheckV2GenericTest, +TEST(TypecheckV2GenericsTest, ParametricFunctionImplicitInvocationNestingWithExplicitOuter) { EXPECT_THAT(R"( fn foo(a: uN[N]) -> uN[N] { a + 1 } @@ -1033,7 +1033,7 @@ const X = foo<24>(foo(u24:4 + foo(u24:6)) + foo(u24:5)); "uN[24]"))); } -TEST(TypecheckV2GenericTest, +TEST(TypecheckV2GenericsTest, ParametricFunctionImplicitInvocationNestingWithExplicitInner) { EXPECT_THAT(R"( fn foo(a: uN[N]) -> uN[N] { a + 1 } @@ -1043,7 +1043,7 @@ const X = foo(foo<24>(4) + foo<24>(5)); "const X = foo(foo<24>(4) + foo<24>(5));", "uN[24]"))); } -TEST(TypecheckV2GenericTest, +TEST(TypecheckV2GenericsTest, ParametricFunctionCallUsingGlobalConstantInImplicitParametricArgument) { EXPECT_THAT(R"( fn foo(a: uN[N]) -> uN[N] { a } @@ -1053,7 +1053,7 @@ const Z = foo(X); TypecheckSucceeds(HasNodeWithType("const Z = foo(X);", "uN[3]"))); } -TEST(TypecheckV2GenericTest, +TEST(TypecheckV2GenericsTest, ParametricFunctionCallWithImplicitParameterFollowedByTypePropagation) { EXPECT_THAT(R"( fn foo(a: uN[N]) -> uN[N] { a } @@ -1063,7 +1063,7 @@ const Z = Y + 1; TypecheckSucceeds(HasNodeWithType("const Z = Y + 1;", "uN[15]"))); } -TEST(TypecheckV2GenericTest, ImplicitParametricBindingRollover) { +TEST(TypecheckV2GenericsTest, ImplicitParametricBindingRollover) { XLS_ASSERT_OK_AND_ASSIGN(TypecheckResult result, TypecheckV2(R"( fn bar(n: u32) -> u32 { n - (u32:1 << 31) - u32:1 } fn foo(n: u32) -> u32 { bar(n) } @@ -1082,8 +1082,7 @@ fn main() { HasSubstr("in bar\nin foo\nfrom fake.x:6:27-6:30"))); } -// TODO(erinzmoore): It should be possible to instantiate a generic type. -TEST(TypecheckV2GenericTest, DISABLED_InstantiateGenericTypeAsStruct) { +TEST(TypecheckV2GenericsTest, InstantiateGenericTypeAsStruct) { EXPECT_THAT( R"( #![feature(generics)] @@ -1102,5 +1101,63 @@ const_assert!(RES == S{x: 5}); TypecheckSucceeds(HasNodeWithType("RES", "S { x: uN[32] }"))); } +TEST(TypecheckV2GenericsTest, InstantiateGenericTypeAsStructWithTypeAlias) { + EXPECT_THAT( + R"( +#![feature(generics)] + +struct S { + x: u32 +} + +fn main() -> T { + type MyAlias = T; + MyAlias { x: u32:5 } +} + +const RES = main(); +const_assert!(RES == S{x: 5}); +)", + TypecheckSucceeds(HasNodeWithType("RES", "S { x: uN[32] }"))); +} + +TEST(TypecheckV2GenericsTest, + InstantiateGenericTypeAsStructIncorrectMemberName) { + EXPECT_THAT( + R"( +#![feature(generics)] + +struct S { + x: u32 +} + +fn main() -> T { + T { y: u32:5 } +} + +const RES = main(); +)", + TypecheckFails(HasSubstr("No member `y` in struct `S`"))); +} + +TEST(TypecheckV2GenericsTest, + InstantiateGenericTypeAsStructIncorrectMemberType) { + EXPECT_THAT( + R"( +#![feature(generics)] + +struct S { + x: u1 +} + +fn main() -> T { + T { x: u32:5 } +} + +const RES = main(); +)", + TypecheckFails(HasTypeMismatch("uN[32]", "uN[1]"))); +} + } // namespace } // namespace xls::dslx