Rough support for calling a generic class name to form a class type. (#3939)

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
This commit is contained in:
Richard Smith
2024-05-07 23:49:47 +00:00
committed by GitHub
co-authored by Jon Ross-Perkins Carbon Infra Bot
parent 76471cf701
commit 20c0322bbe
8 changed files with 321 additions and 13 deletions
+23
View File
@@ -12,12 +12,35 @@
namespace Carbon::Check {
// Performs a call where the callee is the name of a generic class, such as
// `Vector(i32)`.
static auto PerformCallToGenericClass(Context& context, Parse::NodeId node_id,
SemIR::ClassId class_id,
llvm::ArrayRef<SemIR::InstId> arg_ids)
-> SemIR::InstId {
auto& class_info = context.classes().Get(class_id);
// Convert the arguments to match the parameters.
auto converted_args_id = ConvertCallArgs(
context, node_id, /*self_id=*/SemIR::InstId::Invalid, arg_ids,
/*return_storage_id=*/SemIR::InstId::Invalid, class_info.decl_id,
class_info.implicit_param_refs_id, class_info.param_refs_id);
return context.AddInst(
{node_id,
SemIR::ClassType{SemIR::TypeId::TypeType, class_id, converted_args_id}});
}
auto PerformCall(Context& context, Parse::NodeId node_id,
SemIR::InstId callee_id, llvm::ArrayRef<SemIR::InstId> arg_ids)
-> SemIR::InstId {
// Identify the function we're calling.
auto callee_function = GetCalleeFunction(context.sem_ir(), callee_id);
if (!callee_function.function_id.is_valid()) {
if (auto generic_class = context.types().TryGetAs<SemIR::GenericClassType>(
context.insts().Get(callee_id).type_id())) {
return PerformCallToGenericClass(context, node_id,
generic_class->class_id, arg_ids);
}
if (!callee_function.is_error) {
CARBON_DIAGNOSTIC(CallToNonCallable, Error,
"Value of type `{0}` is not callable.", SemIR::TypeId);
+3 -2
View File
@@ -1158,8 +1158,9 @@ auto ConvertCallArgs(Context& context, SemIR::LocId call_loc_id,
SemIR::InstId return_storage_id, SemIR::InstId callee_id,
SemIR::InstBlockId implicit_param_refs_id,
SemIR::InstBlockId param_refs_id) -> SemIR::InstBlockId {
auto implicit_param_refs = context.inst_blocks().Get(implicit_param_refs_id);
auto param_refs = context.inst_blocks().Get(param_refs_id);
auto implicit_param_refs =
context.inst_blocks().GetOrEmpty(implicit_param_refs_id);
auto param_refs = context.inst_blocks().GetOrEmpty(param_refs_id);
// If sizes mismatch, fail early.
if (arg_refs.size() != param_refs.size()) {
+236
View File
@@ -0,0 +1,236 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// AUTOUPDATE
// --- call.carbon
library "call" api;
class Class(T:! type, N:! i32) {}
var a: Class(i32*, 5);
// Requires an implicit conversion to type `type`.
var b: Class((), 0);
// --- fail_too_few.carbon
library "too_few" api;
class Class(T:! type, N:! i32) {}
// CHECK:STDERR: fail_too_few.carbon:[[@LINE+7]]:8: ERROR: 1 argument(s) passed to function expecting 2 argument(s).
// CHECK:STDERR: var a: Class(i32*);
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR: fail_too_few.carbon:[[@LINE-5]]:1: Calling function declared here.
// CHECK:STDERR: class Class(T:! type, N:! i32) {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
var a: Class(i32*);
// --- fail_too_many.carbon
library "too_many" api;
class Class(T:! type, N:! i32) {}
// CHECK:STDERR: fail_too_many.carbon:[[@LINE+7]]:8: ERROR: 3 argument(s) passed to function expecting 2 argument(s).
// CHECK:STDERR: var a: Class(i32*, 1, 2);
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR: fail_too_many.carbon:[[@LINE-5]]:1: Calling function declared here.
// CHECK:STDERR: class Class(T:! type, N:! i32) {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
var a: Class(i32*, 1, 2);
// --- fail_no_conversion.carbon
library "no_conversion" api;
class Class(T:! type, N:! i32) {}
// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+6]]:8: ERROR: Cannot implicitly convert from `i32` to `type`.
// CHECK:STDERR: var a: Class(5, i32*);
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE-5]]:1: Initializing parameter 1 of function declared here.
// CHECK:STDERR: class Class(T:! type, N:! i32) {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var a: Class(5, i32*);
// CHECK:STDOUT: --- call.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %T: type = bind_symbolic_name T 0 [symbolic]
// CHECK:STDOUT: %N: i32 = bind_symbolic_name N 1 [symbolic]
// CHECK:STDOUT: %Class.1: type = generic_class_type @Class [template]
// CHECK:STDOUT: %.1: type = tuple_type () [template]
// CHECK:STDOUT: %struct: Class = struct_value () [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class [template]
// CHECK:STDOUT: %.2: type = struct_type {} [template]
// CHECK:STDOUT: %.3: type = ptr_type i32 [template]
// CHECK:STDOUT: %.4: i32 = int_literal 5 [template]
// CHECK:STDOUT: %Class.3: type = class_type @Class, (file.%.loc6_17, file.%.loc6_20) [template]
// CHECK:STDOUT: %.5: type = ptr_type {} [template]
// CHECK:STDOUT: %.6: i32 = int_literal 0 [template]
// CHECK:STDOUT: %Class.4: type = class_type @Class, (file.%.loc9_13, file.%.loc9_18) [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .Core = %Core
// CHECK:STDOUT: .Class = %Class.decl
// CHECK:STDOUT: .a = %a
// CHECK:STDOUT: .b = %b
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core: <namespace> = namespace [template] {}
// CHECK:STDOUT: %Class.decl: Class = class_decl @Class [template = constants.%struct] {
// CHECK:STDOUT: %T.loc4_13.1: type = param T
// CHECK:STDOUT: %T.loc4_13.2: type = bind_symbolic_name T 0, %T.loc4_13.1 [symbolic = constants.%T]
// CHECK:STDOUT: %N.loc4_23.1: i32 = param N
// CHECK:STDOUT: %N.loc4_23.2: i32 = bind_symbolic_name N 1, %N.loc4_23.1 [symbolic = constants.%N]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Class.ref.loc6: Class = name_ref Class, %Class.decl [template = constants.%struct]
// CHECK:STDOUT: %.loc6_17: type = ptr_type i32 [template = constants.%.3]
// CHECK:STDOUT: %.loc6_20: i32 = int_literal 5 [template = constants.%.4]
// CHECK:STDOUT: %Class.loc6: type = class_type @Class, (%.loc6_17, %.loc6_20) [template = constants.%Class.3]
// CHECK:STDOUT: %a.var: ref Class = var a
// CHECK:STDOUT: %a: ref Class = bind_name a, %a.var
// CHECK:STDOUT: %Class.ref.loc9: Class = name_ref Class, %Class.decl [template = constants.%struct]
// CHECK:STDOUT: %.loc9_15: () = tuple_literal ()
// CHECK:STDOUT: %.loc9_18: i32 = int_literal 0 [template = constants.%.6]
// CHECK:STDOUT: %.loc9_13: type = converted %.loc9_15, constants.%.1 [template = constants.%.1]
// CHECK:STDOUT: %Class.loc9: type = class_type @Class, (%.loc9_13, %.loc9_18) [template = constants.%Class.4]
// CHECK:STDOUT: %b.var: ref Class = var b
// CHECK:STDOUT: %b: ref Class = bind_name b, %b.var
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @Class {
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%Class.2
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_too_few.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %T: type = bind_symbolic_name T 0 [symbolic]
// CHECK:STDOUT: %N: i32 = bind_symbolic_name N 1 [symbolic]
// CHECK:STDOUT: %Class.1: type = generic_class_type @Class [template]
// CHECK:STDOUT: %.1: type = tuple_type () [template]
// CHECK:STDOUT: %struct: Class = struct_value () [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class [template]
// CHECK:STDOUT: %.2: type = struct_type {} [template]
// CHECK:STDOUT: %.3: type = ptr_type i32 [template]
// CHECK:STDOUT: %.4: type = ptr_type {} [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .Core = %Core
// CHECK:STDOUT: .Class = %Class.decl
// CHECK:STDOUT: .a = %a
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core: <namespace> = namespace [template] {}
// CHECK:STDOUT: %Class.decl: Class = class_decl @Class [template = constants.%struct] {
// CHECK:STDOUT: %T.loc4_13.1: type = param T
// CHECK:STDOUT: %T.loc4_13.2: type = bind_symbolic_name T 0, %T.loc4_13.1 [symbolic = constants.%T]
// CHECK:STDOUT: %N.loc4_23.1: i32 = param N
// CHECK:STDOUT: %N.loc4_23.2: i32 = bind_symbolic_name N 1, %N.loc4_23.1 [symbolic = constants.%N]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Class.ref: Class = name_ref Class, %Class.decl [template = constants.%struct]
// CHECK:STDOUT: %.loc13: type = ptr_type i32 [template = constants.%.3]
// CHECK:STDOUT: %Class: type = class_type @Class [template = constants.%Class.2]
// CHECK:STDOUT: %a.var: ref Class = var a
// CHECK:STDOUT: %a: ref Class = bind_name a, %a.var
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @Class {
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%Class.2
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_too_many.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %T: type = bind_symbolic_name T 0 [symbolic]
// CHECK:STDOUT: %N: i32 = bind_symbolic_name N 1 [symbolic]
// CHECK:STDOUT: %Class.1: type = generic_class_type @Class [template]
// CHECK:STDOUT: %.1: type = tuple_type () [template]
// CHECK:STDOUT: %struct: Class = struct_value () [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class [template]
// CHECK:STDOUT: %.2: type = struct_type {} [template]
// CHECK:STDOUT: %.3: type = ptr_type i32 [template]
// CHECK:STDOUT: %.4: i32 = int_literal 1 [template]
// CHECK:STDOUT: %.5: i32 = int_literal 2 [template]
// CHECK:STDOUT: %.6: type = ptr_type {} [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .Core = %Core
// CHECK:STDOUT: .Class = %Class.decl
// CHECK:STDOUT: .a = %a
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core: <namespace> = namespace [template] {}
// CHECK:STDOUT: %Class.decl: Class = class_decl @Class [template = constants.%struct] {
// CHECK:STDOUT: %T.loc4_13.1: type = param T
// CHECK:STDOUT: %T.loc4_13.2: type = bind_symbolic_name T 0, %T.loc4_13.1 [symbolic = constants.%T]
// CHECK:STDOUT: %N.loc4_23.1: i32 = param N
// CHECK:STDOUT: %N.loc4_23.2: i32 = bind_symbolic_name N 1, %N.loc4_23.1 [symbolic = constants.%N]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Class.ref: Class = name_ref Class, %Class.decl [template = constants.%struct]
// CHECK:STDOUT: %.loc13_17: type = ptr_type i32 [template = constants.%.3]
// CHECK:STDOUT: %.loc13_20: i32 = int_literal 1 [template = constants.%.4]
// CHECK:STDOUT: %.loc13_23: i32 = int_literal 2 [template = constants.%.5]
// CHECK:STDOUT: %Class: type = class_type @Class [template = constants.%Class.2]
// CHECK:STDOUT: %a.var: ref Class = var a
// CHECK:STDOUT: %a: ref Class = bind_name a, %a.var
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @Class {
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%Class.2
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_no_conversion.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %T: type = bind_symbolic_name T 0 [symbolic]
// CHECK:STDOUT: %N: i32 = bind_symbolic_name N 1 [symbolic]
// CHECK:STDOUT: %Class.1: type = generic_class_type @Class [template]
// CHECK:STDOUT: %.1: type = tuple_type () [template]
// CHECK:STDOUT: %struct: Class = struct_value () [template]
// CHECK:STDOUT: %Class.2: type = class_type @Class [template]
// CHECK:STDOUT: %.2: type = struct_type {} [template]
// CHECK:STDOUT: %.3: i32 = int_literal 5 [template]
// CHECK:STDOUT: %.4: type = ptr_type i32 [template]
// CHECK:STDOUT: %.5: type = ptr_type {} [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .Core = %Core
// CHECK:STDOUT: .Class = %Class.decl
// CHECK:STDOUT: .a = %a
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core: <namespace> = namespace [template] {}
// CHECK:STDOUT: %Class.decl: Class = class_decl @Class [template = constants.%struct] {
// CHECK:STDOUT: %T.loc4_13.1: type = param T
// CHECK:STDOUT: %T.loc4_13.2: type = bind_symbolic_name T 0, %T.loc4_13.1 [symbolic = constants.%T]
// CHECK:STDOUT: %N.loc4_23.1: i32 = param N
// CHECK:STDOUT: %N.loc4_23.2: i32 = bind_symbolic_name N 1, %N.loc4_23.1 [symbolic = constants.%N]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Class.ref: Class = name_ref Class, %Class.decl [template = constants.%struct]
// CHECK:STDOUT: %.loc12_14: i32 = int_literal 5 [template = constants.%.3]
// CHECK:STDOUT: %.loc12_20: type = ptr_type i32 [template = constants.%.4]
// CHECK:STDOUT: %Class: type = class_type @Class [template = constants.%Class.2]
// CHECK:STDOUT: %a.var: ref Class = var a
// CHECK:STDOUT: %a: ref Class = bind_name a, %a.var
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @Class {
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%Class.2
// CHECK:STDOUT: }
// CHECK:STDOUT:
+25 -7
View File
@@ -14,10 +14,21 @@ class Class(T:! type) {
// TODO: The following should work.
fn Run() -> i32 {
// CHECK:STDERR: fail_todo_use.carbon:[[@LINE+3]]:10: ERROR: Value of type `Class` is not callable.
// CHECK:STDERR: fail_todo_use.carbon:[[@LINE+4]]:23: ERROR: Cannot implicitly convert from `i32` to `T`.
// CHECK:STDERR: var v: Class(i32) = {.k = 0};
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR:
var v: Class(i32) = {.k = 0};
// CHECK:STDERR: fail_todo_use.carbon:[[@LINE+10]]:3: ERROR: Cannot implicitly convert from `T*` to `i32`.
// CHECK:STDERR: return v.Get();
// CHECK:STDERR: ^~~~~~~~~~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_todo_use.carbon:[[@LINE+6]]:10: ERROR: Cannot implicitly convert from `Class*` to `Class*`.
// CHECK:STDERR: return v.Get();
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR: fail_todo_use.carbon:[[@LINE-21]]:15: Initializing `addr self` parameter of method declared here.
// CHECK:STDERR: fn Get[addr self: Self*]() -> T* {
// CHECK:STDERR: ^~~~
return v.Get();
}
@@ -38,8 +49,10 @@ fn Run() -> i32 {
// CHECK:STDOUT: %.6: type = ptr_type {.k: T} [symbolic]
// CHECK:STDOUT: %Run: type = fn_type @Run [template]
// CHECK:STDOUT: %struct.3: Run = struct_value () [template]
// CHECK:STDOUT: %Class.3: type = class_type @Class, (i32) [template]
// CHECK:STDOUT: %.7: i32 = int_literal 0 [template]
// CHECK:STDOUT: %.8: type = struct_type {.k: i32} [template]
// CHECK:STDOUT: %.9: type = ptr_type Class [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
@@ -91,12 +104,17 @@ fn Run() -> i32 {
// CHECK:STDOUT: fn @Run() -> i32 {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Class.ref: Class = name_ref Class, file.%Class.decl [template = constants.%struct.1]
// CHECK:STDOUT: %v.var: ref <error> = var v
// CHECK:STDOUT: %v: ref <error> = bind_name v, %v.var
// CHECK:STDOUT: %.loc20_29: i32 = int_literal 0 [template = constants.%.7]
// CHECK:STDOUT: %.loc20_30: {.k: i32} = struct_literal (%.loc20_29)
// CHECK:STDOUT: %Class: type = class_type @Class, (i32) [template = constants.%Class.3]
// CHECK:STDOUT: %v.var: ref Class = var v
// CHECK:STDOUT: %v: ref Class = bind_name v, %v.var
// CHECK:STDOUT: %.loc21_29: i32 = int_literal 0 [template = constants.%.7]
// CHECK:STDOUT: %.loc21_30: {.k: i32} = struct_literal (%.loc21_29)
// CHECK:STDOUT: assign %v.var, <error>
// CHECK:STDOUT: %v.ref: ref <error> = name_ref v, %v
// CHECK:STDOUT: %v.ref: ref Class = name_ref v, %v
// CHECK:STDOUT: %Get.ref: Get = name_ref Get, @Class.%Get.decl [template = constants.%struct.2]
// CHECK:STDOUT: %.loc32_11: <bound method> = bound_method %v.ref, %Get.ref
// CHECK:STDOUT: %.loc32_10: Class* = addr_of %v.ref
// CHECK:STDOUT: %Get.call: init T* = call %.loc32_11(<invalid>) [template = <error>]
// CHECK:STDOUT: return <error>
// CHECK:STDOUT: }
// CHECK:STDOUT:
+8
View File
@@ -604,6 +604,14 @@ class Formatter {
FormatTrailingBlock(inst.decl_block_id);
}
auto FormatInstructionRHS(ClassType inst) -> void {
if (inst.args_id.is_valid()) {
FormatArgs(inst.class_id, inst.args_id);
} else {
FormatArgs(inst.class_id);
}
}
auto FormatInstructionRHS(ImplDecl inst) -> void {
FormatArgs(inst.impl_id);
FormatTrailingBlock(inst.decl_block_id);
+6
View File
@@ -437,6 +437,12 @@ class InstBlockStore : public BlockValueStore<InstBlockId> {
CARBON_CHECK(block_id != InstBlockId::Unreachable);
BlockValueStore<InstBlockId>::Set(block_id, content);
}
// Returns the contents of the specified block, or an empty array if the block
// is empty.
auto GetOrEmpty(InstBlockId block_id) -> llvm::ArrayRef<InstId> {
return block_id.is_valid() ? Get(block_id) : llvm::ArrayRef<InstId>();
}
};
} // namespace Carbon::SemIR
+18 -2
View File
@@ -35,7 +35,15 @@ static auto DefaultProfileArgFunction(llvm::FoldingSetNodeID& id,
static auto InstBlockProfileArgFunction(llvm::FoldingSetNodeID& id,
const File& sem_ir, int32_t arg)
-> void {
for (auto inst_id : sem_ir.inst_blocks().Get(InstBlockId(arg))) {
auto inst_block_id = InstBlockId(arg);
if (!inst_block_id.is_valid()) {
id.AddInteger(-1);
return;
}
auto inst_block = sem_ir.inst_blocks().Get(inst_block_id);
id.AddInteger(inst_block.size());
for (auto inst_id : inst_block) {
id.AddInteger(inst_id.index);
}
}
@@ -45,7 +53,15 @@ static auto InstBlockProfileArgFunction(llvm::FoldingSetNodeID& id,
static auto TypeBlockProfileArgFunction(llvm::FoldingSetNodeID& id,
const File& sem_ir, int32_t arg)
-> void {
for (auto type_id : sem_ir.type_blocks().Get(TypeBlockId(arg))) {
auto type_block_id = TypeBlockId(arg);
if (!type_block_id.is_valid()) {
id.AddInteger(-1);
return;
}
auto type_block = sem_ir.type_blocks().Get(type_block_id);
id.AddInteger(type_block.size());
for (auto type_id : type_block) {
id.AddInteger(type_id.index);
}
}
+2 -2
View File
@@ -402,11 +402,11 @@ struct ClassInit {
struct ClassType {
static constexpr auto Kind =
InstKind::ClassType.Define<Parse::AnyClassDeclId>("class_type");
InstKind::ClassType.Define<Parse::NodeId>("class_type");
TypeId type_id;
ClassId class_id;
// TODO: Once we support generic classes, include the class's arguments here.
InstBlockId args_id = InstBlockId::Invalid;
};
struct ConstType {