Reject abstract types in var function parameters (#6499)

### Description
Fixes an issue where the toolchain accepted abstract types in function
parameters declared with `var`.

### Changes
- Implemented a check for abstract types for function parameters with
`var` binding pattern in `HandleAnyBindingPattern`.
- Added a test case to
`toolchain/check/testdata/class/fail_abstract.carbon`.

**Note:** I did not use `AsConcreteType` like used in `case
FullPatternStack::Kind::NameBindingDecl`. Using it enforces type
completion, thus causing valid signatures such as `fn F[var self: Self]`
to fail.

Also the pre-commit checks fail due to a diagnostic name collision with
`toolchain/check/type_completion.cpp`. Should I add a function that just
checks if the type is abstract to share the diagnostic?

Fixes #6402
This commit is contained in:
Özgür
2025-12-15 20:17:34 +00:00
committed by GitHub
parent 25f63140e6
commit 2a3d0b71bb
5 changed files with 157 additions and 26 deletions
+34 -10
View File
@@ -158,6 +158,15 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
return binding.pattern_id;
};
auto abstract_diagnoser = [&] {
CARBON_DIAGNOSTIC(AbstractTypeInVarPattern, Error,
"binding pattern has abstract type {0} in `var` "
"pattern",
SemIR::TypeId);
return context.emitter().Build(type_node, AbstractTypeInVarPattern,
cast_type_id);
};
// A `self` binding can only appear in an implicit parameter list.
if (name_id == SemIR::NameId::SelfValue &&
!context.node_stack().PeekIs(Parse::NodeKind::ImplicitParamListStart)) {
@@ -192,6 +201,29 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
break;
}
// Using `AsConcreteType` here causes `fn F[var self: Self]();`
// to fail since `Self` is an incomplete type.
if (node_kind == Parse::NodeKind::VarBindingPattern) {
auto [unqualified_type_id, qualifiers] =
context.types().GetUnqualifiedTypeAndQualifiers(cast_type_id);
if ((qualifiers & SemIR::TypeQualifiers::Partial) !=
SemIR::TypeQualifiers::Partial &&
context.types().Is<SemIR::ClassType>(unqualified_type_id)) {
auto class_type =
context.types().GetAs<SemIR::ClassType>(unqualified_type_id);
auto& class_info = context.classes().Get(class_type.class_id);
if (class_info.inheritance_kind ==
SemIR::Class::InheritanceKind::Abstract) {
auto builder = abstract_diagnoser();
auto direct_use = true;
NoteAbstractClass(context, class_type.class_id, direct_use,
builder);
builder.Emit();
cast_type_id = SemIR::ErrorInst::TypeId;
}
}
}
auto result_inst_id = make_binding_pattern();
// A binding pattern in a function signature is a `Call` parameter
@@ -228,16 +260,8 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
cast_type_inst_id);
};
if (node_kind == Parse::NodeKind::VarBindingPattern) {
cast_type_id = AsConcreteType(
context, cast_type_id, type_node, incomplete_diagnoser, [&] {
CARBON_DIAGNOSTIC(
AbstractTypeInVarPattern, Error,
"binding pattern has abstract type {0} in `var` "
"pattern",
SemIR::TypeId);
return context.emitter().Build(
type_node, AbstractTypeInVarPattern, cast_type_id);
});
cast_type_id = AsConcreteType(context, cast_type_id, type_node,
incomplete_diagnoser, abstract_diagnoser);
} else {
cast_type_id = AsCompleteType(context, cast_type_id, type_node,
incomplete_diagnoser);