diff --git a/toolchain/check/context.h b/toolchain/check/context.h index c6197cc4c88b..878f9652237f 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -227,14 +227,24 @@ class Context { // complete, or `false` if it could not be completed. A complete type has // known object and value representations. // - // If the type is not complete, `diagnoser` is invoked to diagnose the issue. - // The builder it returns will be annotated to describe the reason why the - // type is not complete. + // If the type is not complete, `diagnoser` is invoked to diagnose the issue, + // if a `diagnoser` is provided. The builder it returns will be annotated to + // describe the reason why the type is not complete. auto TryToCompleteType( SemIR::TypeId type_id, std::optionalDiagnosticBuilder>> diagnoser = std::nullopt) -> bool; + // Returns the type `type_id` as a complete type, or produces an incomplete + // type error and returns an error type. This is a convenience wrapper around + // TryToCompleteType. + auto AsCompleteType(SemIR::TypeId type_id, + llvm::function_refDiagnosticBuilder> diagnoser) + -> SemIR::TypeId { + return TryToCompleteType(type_id, diagnoser) ? type_id + : SemIR::TypeId::Error; + } + // Gets a builtin type. The returned type will be complete. auto GetBuiltinType(SemIR::BuiltinKind kind) -> SemIR::TypeId; diff --git a/toolchain/check/handle_binding_pattern.cpp b/toolchain/check/handle_binding_pattern.cpp index 39770f181f61..27fde6128b87 100644 --- a/toolchain/check/handle_binding_pattern.cpp +++ b/toolchain/check/handle_binding_pattern.cpp @@ -67,18 +67,16 @@ auto HandleBindingPattern(Context& context, Parse::NodeId parse_node) -> bool { case Parse::NodeKind::VariableIntroducer: { // A `var` declaration at class scope introduces a field. auto enclosing_class_decl = context.GetCurrentScopeAs(); - if (!context.TryToCompleteType(cast_type_id, [&] { - CARBON_DIAGNOSTIC(IncompleteTypeInVarDecl, Error, - "{0} has incomplete type `{1}`.", - llvm::StringLiteral, std::string); - return context.emitter().Build( - type_node_copy, IncompleteTypeInVarDecl, - enclosing_class_decl ? llvm::StringLiteral("Field") - : llvm::StringLiteral("Variable"), - context.sem_ir().StringifyType(cast_type_id)); - })) { - cast_type_id = SemIR::TypeId::Error; - } + cast_type_id = context.AsCompleteType(cast_type_id, [&] { + CARBON_DIAGNOSTIC(IncompleteTypeInVarDecl, Error, + "{0} has incomplete type `{1}`.", llvm::StringLiteral, + std::string); + return context.emitter().Build( + type_node_copy, IncompleteTypeInVarDecl, + enclosing_class_decl ? llvm::StringLiteral("Field") + : llvm::StringLiteral("Variable"), + context.sem_ir().StringifyType(cast_type_id)); + }); SemIR::InstId value_id = SemIR::InstId::Invalid; SemIR::TypeId value_type_id = cast_type_id; if (context_parse_node_kind == Parse::NodeKind::ReturnedModifier) { @@ -126,16 +124,14 @@ auto HandleBindingPattern(Context& context, Parse::NodeId parse_node) -> bool { break; case Parse::NodeKind::LetIntroducer: - if (!context.TryToCompleteType(cast_type_id, [&] { - CARBON_DIAGNOSTIC(IncompleteTypeInLetDecl, Error, - "`let` binding has incomplete type `{0}`.", - std::string); - return context.emitter().Build( - type_node_copy, IncompleteTypeInLetDecl, - context.sem_ir().StringifyType(cast_type_id)); - })) { - cast_type_id = SemIR::TypeId::Error; - } + cast_type_id = context.AsCompleteType(cast_type_id, [&] { + CARBON_DIAGNOSTIC(IncompleteTypeInLetDecl, Error, + "`let` binding has incomplete type `{0}`.", + std::string); + return context.emitter().Build( + type_node_copy, IncompleteTypeInLetDecl, + context.sem_ir().StringifyType(cast_type_id)); + }); // Create the instruction, but don't add it to a block until after we've // formed its initializer. // TODO: For general pattern parsing, we'll need to create a block to hold diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index a4b0f3e6001b..40eaff043b27 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -214,15 +214,13 @@ auto HandleBaseDecl(Context& context, Parse::NodeId parse_node) -> bool { } auto base_type_id = ExprAsType(context, parse_node, base_type_expr_id); - if (!context.TryToCompleteType(base_type_id, [&] { - CARBON_DIAGNOSTIC(IncompleteTypeInBaseDecl, Error, - "Base `{0}` is an incomplete type.", std::string); - return context.emitter().Build( - parse_node, IncompleteTypeInBaseDecl, - context.sem_ir().StringifyType(base_type_id)); - })) { - base_type_id = SemIR::TypeId::Error; - } + base_type_id = context.AsCompleteType(base_type_id, [&] { + CARBON_DIAGNOSTIC(IncompleteTypeInBaseDecl, Error, + "Base `{0}` is an incomplete type.", std::string); + return context.emitter().Build( + parse_node, IncompleteTypeInBaseDecl, + context.sem_ir().StringifyType(base_type_id)); + }); if (base_type_id != SemIR::TypeId::Error) { // For now, we treat all types that aren't introduced by a `class` diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 0879d18f054e..d80a708b09cb 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -62,17 +62,16 @@ static auto BuildFunctionDecl(Context& context, bool is_definition) auto return_node_copy = return_node; return_type_id = context.insts().Get(return_storage_id).type_id(); - if (!context.TryToCompleteType(return_type_id, [&] { - CARBON_DIAGNOSTIC(IncompleteTypeInFunctionReturnType, Error, - "Function returns incomplete type `{0}`.", - std::string); - return context.emitter().Build( - return_node_copy, IncompleteTypeInFunctionReturnType, - context.sem_ir().StringifyType(return_type_id)); - })) { - return_type_id = SemIR::TypeId::Error; - } else if (!SemIR::GetInitRepr(context.sem_ir(), return_type_id) - .has_return_slot()) { + return_type_id = context.AsCompleteType(return_type_id, [&] { + CARBON_DIAGNOSTIC(IncompleteTypeInFunctionReturnType, Error, + "Function returns incomplete type `{0}`.", std::string); + return context.emitter().Build( + return_node_copy, IncompleteTypeInFunctionReturnType, + context.sem_ir().StringifyType(return_type_id)); + }); + + if (!SemIR::GetInitRepr(context.sem_ir(), return_type_id) + .has_return_slot()) { // The function only has a return slot if it uses in-place initialization. } else { return_slot_id = return_storage_id;