Quellcode durchsuchen

Improve handling of incomplete signatures. (#7004)

Assert cleanly if we try to emit a definition or a call of a function
whose signature we were not able to emit exactly. This should make such
issues a lot easier to debug, as we were previously failing in quite
mysterious ways in this case.
Richard Smith vor 1 Monat
Ursprung
Commit
be283a0744

+ 23 - 3
toolchain/lower/file_context.cpp

@@ -307,6 +307,7 @@ class FileContext::FunctionTypeInfoBuilder {
     param_types_.clear();
     param_di_types_.clear();
     return_type_ = nullptr;
+    inexact_ = true;
     SetReturnByCopy(SemIR::TypeId::None);
     return false;
   }
@@ -443,6 +444,12 @@ class FileContext::FunctionTypeInfoBuilder {
   // If not null, the LLVM function's first parameter should have a `sret`
   // attribute with this type.
   llvm::Type* sret_type_ = nullptr;
+
+  // Whether we failed to form an exact description of the function type. This
+  // can happen if a parameter or return type is incomplete. In this case, we
+  // can still sometimes need to emit a declaration of the function, for example
+  // because it appears in a vtable, but we cannot emit a definition or a call.
+  bool inexact_ = false;
 };
 
 auto FileContext::FunctionTypeInfoBuilder::Build(
@@ -514,6 +521,8 @@ auto FileContext::FunctionTypeInfoBuilder::HandleReturnForm(
         case SemIR::InitRepr::None:
           return SetReturnByCopy(SemIR::TypeId::None);
         case SemIR::InitRepr::Dependent:
+          CARBON_FATAL("Lowering function return with dependent type: {0}",
+                       return_form_inst);
         case SemIR::InitRepr::Incomplete:
         case SemIR::InitRepr::Abstract:
           return Abort();
@@ -536,8 +545,10 @@ auto FileContext::FunctionTypeInfoBuilder::HandleReturnForm(
       switch (
           SemIR::ValueRepr::ForType(context_.sem_ir(), return_type_id).kind) {
         case SemIR::ValueRepr::Unknown:
-        case SemIR::ValueRepr::Dependent:
           return Abort();
+        case SemIR::ValueRepr::Dependent:
+          CARBON_FATAL("Lowering function return with dependent type: {0}",
+                       return_form_inst);
         case SemIR::ValueRepr::None:
           return SetReturnByCopy(SemIR::TypeId::None);
         case SemIR::ValueRepr::Copy:
@@ -612,6 +623,8 @@ auto FileContext::FunctionTypeInfoBuilder::HandleParameter(
         case SemIR::InitRepr::None:
           return IgnoreParam(index);
         case SemIR::InitRepr::Dependent:
+          CARBON_FATAL("Lowering function parameter with dependent type: {0}",
+                       param_pattern);
         case SemIR::InitRepr::Incomplete:
         case SemIR::InitRepr::Abstract:
           return Abort();
@@ -657,7 +670,8 @@ auto FileContext::FunctionTypeInfoBuilder::Finalize() -> FunctionTypeInfo {
           .lowered_param_indices = std::move(lowered_param_indices_),
           .unused_param_indices = std::move(unused_param_indices_),
           .param_name_ids = std::move(param_name_ids_),
-          .sret_type = sret_type_};
+          .sret_type = sret_type_,
+          .inexact = inexact_};
 }
 
 auto FileContext::FunctionTypeInfoBuilder::GetLoweredTypes(
@@ -726,6 +740,8 @@ auto FileContext::GetOrCreateLLVMFunction(
   if (auto* existing = llvm_module().getFunction(mangled_name)) {
     // We might have already lowered this function while lowering a different
     // file. That's OK.
+    // TODO: If the prior function was inexact and the new one is not, we should
+    // lower this new one and replace the existing function with it.
     // TODO: Check-fail or maybe diagnose if the two LLVM functions are not
     // produced by declarations of the same Carbon function. Name collisions
     // between non-private members of the same library should have been
@@ -809,7 +825,8 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id,
                std::move(function_type_info.lowered_param_indices),
            .unused_param_indices =
                std::move(function_type_info.unused_param_indices),
-           .llvm_function = llvm_function}};
+           .llvm_function = llvm_function,
+           .inexact = function_type_info.inexact}};
 }
 
 // Find the file and function ID describing the definition of a function.
@@ -891,6 +908,9 @@ auto FileContext::BuildFunctionBody(SemIR::FunctionId function_id,
   auto function_info = GetFunctionInfo(function_id, specific_id);
   CARBON_CHECK(function_info && function_info->llvm_function,
                "Attempting to define function that was not declared");
+  CARBON_CHECK(!function_info->inexact,
+               "Attempting to emit definition of inexact function: {0}",
+               *function_info->llvm_function);
 
   const auto& body_block_ids = definition_function.body_block_ids;
   CARBON_DCHECK(!body_block_ids.empty(),

+ 9 - 0
toolchain/lower/file_context.h

@@ -37,6 +37,11 @@ struct FunctionInfo {
 
   // The lowered function declaration.
   llvm::Function* llvm_function;
+
+  // Whether the function type information is inexact, because some component
+  // type was incomplete. If this is set, the function should not be used to
+  // emit a definition or a call.
+  bool inexact;
 };
 
 // Context and shared functionality for lowering within a SemIR file.
@@ -213,6 +218,10 @@ class FileContext {
     // When `return_param_id` is not `None`, the corresponding lowered parameter
     // should be given an `sret` attribute with this type.
     llvm::Type* sret_type = nullptr;
+
+    // Whether the function type information is inexact, because some component
+    // type was incomplete.
+    bool inexact;
   };
 
   class FunctionTypeInfoBuilder;

+ 3 - 0
toolchain/lower/handle_call.cpp

@@ -690,6 +690,9 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
       context.GetFileContext(callee.file)
           .GetOrCreateFunctionInfo(callee_function.function_id,
                                    callee_function.resolved_specific_id);
+  CARBON_CHECK(!function_info->inexact,
+               "Attempting to emit call to inexact function: {0}",
+               *function_info->llvm_function);
 
   // Lower args in the LLVM parameter order, rather than the SemIR parameter
   // order.