Skip to content

Commit 9077141

Browse files
authored
Add builtins to form and detect null MaybeUnformed(T*) values. (#6208)
In preparation for modeling `Optional(T*)` as a null pointer value. With this PR, pointers remain non-nullable, but `MaybeUnformed(T*)` has a particular unformed state that has the same representation as a C++ null pointer, which is accessible and detectable via builtins.
1 parent 5714f4d commit 9077141

File tree

9 files changed

+585
-2
lines changed

9 files changed

+585
-2
lines changed

toolchain/check/eval.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1693,7 +1693,9 @@ static auto MakeConstantForBuiltinCall(EvalContext& eval_context,
16931693
case SemIR::BuiltinFunctionKind::IntOrAssign:
16941694
case SemIR::BuiltinFunctionKind::IntXorAssign:
16951695
case SemIR::BuiltinFunctionKind::IntLeftShiftAssign:
1696-
case SemIR::BuiltinFunctionKind::IntRightShiftAssign: {
1696+
case SemIR::BuiltinFunctionKind::IntRightShiftAssign:
1697+
case SemIR::BuiltinFunctionKind::PointerMakeNull:
1698+
case SemIR::BuiltinFunctionKind::PointerIsNull: {
16971699
// These are runtime-only builtins.
16981700
// TODO: Consider tracking this on the `BuiltinFunctionKind`.
16991701
return SemIR::ConstantId::NotConstant;

toolchain/check/testdata/builtins/pointer/is_null.carbon

Lines changed: 275 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
//
5+
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon
6+
//
7+
// AUTOUPDATE
8+
// TIP: To test this file alone, run:
9+
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/pointer/make_null.carbon
10+
// TIP: To dump output, run:
11+
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/pointer/make_null.carbon
12+
13+
// --- call_exact.carbon
14+
15+
library "[[@TEST_NAME]]";
16+
17+
class C {}
18+
19+
fn MakeUnformed(t: type) -> type = "maybe_unformed.make_type";
20+
fn MakeNullEmptyStruct() -> MakeUnformed({}*) = "pointer.make_null";
21+
fn MakeNullC() -> MakeUnformed(C*) = "pointer.make_null";
22+
23+
//@dump-sem-ir-begin
24+
let s: MakeUnformed({}*) = MakeNullEmptyStruct();
25+
let c: MakeUnformed(C*) = MakeNullC();
26+
//@dump-sem-ir-end
27+
28+
// --- call_generic.carbon
29+
30+
library "[[@TEST_NAME]]";
31+
32+
fn MakeUnformed(t: type) -> type = "maybe_unformed.make_type";
33+
fn MakeNull(T:! type) -> MakeUnformed(T*) = "pointer.make_null";
34+
35+
class C {}
36+
37+
//@dump-sem-ir-begin
38+
let s: MakeUnformed({}*) = MakeNull({});
39+
let c: MakeUnformed(C*) = MakeNull(C);
40+
//@dump-sem-ir-end
41+
42+
// --- fail_bad_decl.carbon
43+
44+
library "[[@TEST_NAME]]";
45+
46+
fn MakeUnformed(t: type) -> type = "maybe_unformed.make_type";
47+
48+
// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "pointer.make_null" [InvalidBuiltinSignature]
49+
// CHECK:STDERR: fn NoRetType() = "pointer.make_null";
50+
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
51+
// CHECK:STDERR:
52+
fn NoRetType() = "pointer.make_null";
53+
54+
// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "pointer.make_null" [InvalidBuiltinSignature]
55+
// CHECK:STDERR: fn NoUnformed() -> {}* = "pointer.make_null";
56+
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~
57+
// CHECK:STDERR:
58+
fn NoUnformed() -> {}* = "pointer.make_null";
59+
60+
// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "pointer.make_null" [InvalidBuiltinSignature]
61+
// CHECK:STDERR: fn NotPointer() -> MakeUnformed({}) = "pointer.make_null";
62+
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
63+
// CHECK:STDERR:
64+
fn NotPointer() -> MakeUnformed({}) = "pointer.make_null";
65+
66+
// CHECK:STDOUT: --- call_exact.carbon
67+
// CHECK:STDOUT:
68+
// CHECK:STDOUT: constants {
69+
// CHECK:STDOUT: %C: type = class_type @C [concrete]
70+
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
71+
// CHECK:STDOUT: %MakeUnformed.type: type = fn_type @MakeUnformed [concrete]
72+
// CHECK:STDOUT: %MakeUnformed: %MakeUnformed.type = struct_value () [concrete]
73+
// CHECK:STDOUT: %ptr.c28: type = ptr_type %empty_struct_type [concrete]
74+
// CHECK:STDOUT: %.b2d: type = maybe_unformed_type %ptr.c28 [concrete]
75+
// CHECK:STDOUT: %pattern_type.b42: type = pattern_type %.b2d [concrete]
76+
// CHECK:STDOUT: %MakeNullEmptyStruct.type: type = fn_type @MakeNullEmptyStruct [concrete]
77+
// CHECK:STDOUT: %MakeNullEmptyStruct: %MakeNullEmptyStruct.type = struct_value () [concrete]
78+
// CHECK:STDOUT: %ptr.019: type = ptr_type %C [concrete]
79+
// CHECK:STDOUT: %.273: type = maybe_unformed_type %ptr.019 [concrete]
80+
// CHECK:STDOUT: %pattern_type.ad6: type = pattern_type %.273 [concrete]
81+
// CHECK:STDOUT: %MakeNullC.type: type = fn_type @MakeNullC [concrete]
82+
// CHECK:STDOUT: %MakeNullC: %MakeNullC.type = struct_value () [concrete]
83+
// CHECK:STDOUT: }
84+
// CHECK:STDOUT:
85+
// CHECK:STDOUT: file {
86+
// CHECK:STDOUT: name_binding_decl {
87+
// CHECK:STDOUT: %s.patt: %pattern_type.b42 = binding_pattern s [concrete]
88+
// CHECK:STDOUT: }
89+
// CHECK:STDOUT: %.loc11_24.1: type = splice_block %.loc11_24.3 [concrete = constants.%.b2d] {
90+
// CHECK:STDOUT: %MakeUnformed.ref.loc11: %MakeUnformed.type = name_ref MakeUnformed, %MakeUnformed.decl [concrete = constants.%MakeUnformed]
91+
// CHECK:STDOUT: %.loc11_22: %empty_struct_type = struct_literal ()
92+
// CHECK:STDOUT: %.loc11_23: type = converted %.loc11_22, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
93+
// CHECK:STDOUT: %ptr.loc11: type = ptr_type %.loc11_23 [concrete = constants.%ptr.c28]
94+
// CHECK:STDOUT: %MakeUnformed.call.loc11: init type = call %MakeUnformed.ref.loc11(%ptr.loc11) [concrete = constants.%.b2d]
95+
// CHECK:STDOUT: %.loc11_24.2: type = value_of_initializer %MakeUnformed.call.loc11 [concrete = constants.%.b2d]
96+
// CHECK:STDOUT: %.loc11_24.3: type = converted %MakeUnformed.call.loc11, %.loc11_24.2 [concrete = constants.%.b2d]
97+
// CHECK:STDOUT: }
98+
// CHECK:STDOUT: %.loc11_48.1: ref %.b2d = temporary @__global_init.%.loc11, @__global_init.%MakeNullEmptyStruct.call
99+
// CHECK:STDOUT: %.loc11_48.2: %.b2d = bind_value %.loc11_48.1
100+
// CHECK:STDOUT: %s: %.b2d = bind_name s, %.loc11_48.2
101+
// CHECK:STDOUT: name_binding_decl {
102+
// CHECK:STDOUT: %c.patt: %pattern_type.ad6 = binding_pattern c [concrete]
103+
// CHECK:STDOUT: }
104+
// CHECK:STDOUT: %.loc12_23.1: type = splice_block %.loc12_23.3 [concrete = constants.%.273] {
105+
// CHECK:STDOUT: %MakeUnformed.ref.loc12: %MakeUnformed.type = name_ref MakeUnformed, %MakeUnformed.decl [concrete = constants.%MakeUnformed]
106+
// CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C]
107+
// CHECK:STDOUT: %ptr.loc12: type = ptr_type %C.ref [concrete = constants.%ptr.019]
108+
// CHECK:STDOUT: %MakeUnformed.call.loc12: init type = call %MakeUnformed.ref.loc12(%ptr.loc12) [concrete = constants.%.273]
109+
// CHECK:STDOUT: %.loc12_23.2: type = value_of_initializer %MakeUnformed.call.loc12 [concrete = constants.%.273]
110+
// CHECK:STDOUT: %.loc12_23.3: type = converted %MakeUnformed.call.loc12, %.loc12_23.2 [concrete = constants.%.273]
111+
// CHECK:STDOUT: }
112+
// CHECK:STDOUT: %.loc12_37.1: ref %.273 = temporary @__global_init.%.loc12, @__global_init.%MakeNullC.call
113+
// CHECK:STDOUT: %.loc12_37.2: %.273 = bind_value %.loc12_37.1
114+
// CHECK:STDOUT: %c: %.273 = bind_name c, %.loc12_37.2
115+
// CHECK:STDOUT: }
116+
// CHECK:STDOUT:
117+
// CHECK:STDOUT: fn @__global_init() {
118+
// CHECK:STDOUT: !entry:
119+
// CHECK:STDOUT: %MakeNullEmptyStruct.ref: %MakeNullEmptyStruct.type = name_ref MakeNullEmptyStruct, file.%MakeNullEmptyStruct.decl [concrete = constants.%MakeNullEmptyStruct]
120+
// CHECK:STDOUT: %.loc11: ref %.b2d = temporary_storage
121+
// CHECK:STDOUT: %MakeNullEmptyStruct.call: init %.b2d = call %MakeNullEmptyStruct.ref() to %.loc11
122+
// CHECK:STDOUT: %MakeNullC.ref: %MakeNullC.type = name_ref MakeNullC, file.%MakeNullC.decl [concrete = constants.%MakeNullC]
123+
// CHECK:STDOUT: %.loc12: ref %.273 = temporary_storage
124+
// CHECK:STDOUT: %MakeNullC.call: init %.273 = call %MakeNullC.ref() to %.loc12
125+
// CHECK:STDOUT: <elided>
126+
// CHECK:STDOUT: }
127+
// CHECK:STDOUT:
128+
// CHECK:STDOUT: --- call_generic.carbon
129+
// CHECK:STDOUT:
130+
// CHECK:STDOUT: constants {
131+
// CHECK:STDOUT: %MakeUnformed.type: type = fn_type @MakeUnformed [concrete]
132+
// CHECK:STDOUT: %MakeUnformed: %MakeUnformed.type = struct_value () [concrete]
133+
// CHECK:STDOUT: %MakeNull.type: type = fn_type @MakeNull [concrete]
134+
// CHECK:STDOUT: %MakeNull: %MakeNull.type = struct_value () [concrete]
135+
// CHECK:STDOUT: %C: type = class_type @C [concrete]
136+
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
137+
// CHECK:STDOUT: %ptr.c28: type = ptr_type %empty_struct_type [concrete]
138+
// CHECK:STDOUT: %.b2d: type = maybe_unformed_type %ptr.c28 [concrete]
139+
// CHECK:STDOUT: %pattern_type.b42: type = pattern_type %.b2d [concrete]
140+
// CHECK:STDOUT: %MakeNull.specific_fn.7cb: <specific function> = specific_function %MakeNull, @MakeNull(%empty_struct_type) [concrete]
141+
// CHECK:STDOUT: %ptr.019: type = ptr_type %C [concrete]
142+
// CHECK:STDOUT: %.273: type = maybe_unformed_type %ptr.019 [concrete]
143+
// CHECK:STDOUT: %pattern_type.ad6: type = pattern_type %.273 [concrete]
144+
// CHECK:STDOUT: %MakeNull.specific_fn.22e: <specific function> = specific_function %MakeNull, @MakeNull(%C) [concrete]
145+
// CHECK:STDOUT: }
146+
// CHECK:STDOUT:
147+
// CHECK:STDOUT: file {
148+
// CHECK:STDOUT: name_binding_decl {
149+
// CHECK:STDOUT: %s.patt: %pattern_type.b42 = binding_pattern s [concrete]
150+
// CHECK:STDOUT: }
151+
// CHECK:STDOUT: %.loc10_24.1: type = splice_block %.loc10_24.3 [concrete = constants.%.b2d] {
152+
// CHECK:STDOUT: %MakeUnformed.ref.loc10: %MakeUnformed.type = name_ref MakeUnformed, %MakeUnformed.decl [concrete = constants.%MakeUnformed]
153+
// CHECK:STDOUT: %.loc10_22: %empty_struct_type = struct_literal ()
154+
// CHECK:STDOUT: %.loc10_23: type = converted %.loc10_22, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
155+
// CHECK:STDOUT: %ptr.loc10: type = ptr_type %.loc10_23 [concrete = constants.%ptr.c28]
156+
// CHECK:STDOUT: %MakeUnformed.call.loc10: init type = call %MakeUnformed.ref.loc10(%ptr.loc10) [concrete = constants.%.b2d]
157+
// CHECK:STDOUT: %.loc10_24.2: type = value_of_initializer %MakeUnformed.call.loc10 [concrete = constants.%.b2d]
158+
// CHECK:STDOUT: %.loc10_24.3: type = converted %MakeUnformed.call.loc10, %.loc10_24.2 [concrete = constants.%.b2d]
159+
// CHECK:STDOUT: }
160+
// CHECK:STDOUT: %.loc10_39.1: ref %.b2d = temporary @__global_init.%.loc10_39.2, @__global_init.%MakeNull.call.loc10
161+
// CHECK:STDOUT: %.loc10_39.2: %.b2d = bind_value %.loc10_39.1
162+
// CHECK:STDOUT: %s: %.b2d = bind_name s, %.loc10_39.2
163+
// CHECK:STDOUT: name_binding_decl {
164+
// CHECK:STDOUT: %c.patt: %pattern_type.ad6 = binding_pattern c [concrete]
165+
// CHECK:STDOUT: }
166+
// CHECK:STDOUT: %.loc11_23.1: type = splice_block %.loc11_23.3 [concrete = constants.%.273] {
167+
// CHECK:STDOUT: %MakeUnformed.ref.loc11: %MakeUnformed.type = name_ref MakeUnformed, %MakeUnformed.decl [concrete = constants.%MakeUnformed]
168+
// CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C]
169+
// CHECK:STDOUT: %ptr.loc11: type = ptr_type %C.ref [concrete = constants.%ptr.019]
170+
// CHECK:STDOUT: %MakeUnformed.call.loc11: init type = call %MakeUnformed.ref.loc11(%ptr.loc11) [concrete = constants.%.273]
171+
// CHECK:STDOUT: %.loc11_23.2: type = value_of_initializer %MakeUnformed.call.loc11 [concrete = constants.%.273]
172+
// CHECK:STDOUT: %.loc11_23.3: type = converted %MakeUnformed.call.loc11, %.loc11_23.2 [concrete = constants.%.273]
173+
// CHECK:STDOUT: }
174+
// CHECK:STDOUT: %.loc11_37.1: ref %.273 = temporary @__global_init.%.loc11, @__global_init.%MakeNull.call.loc11
175+
// CHECK:STDOUT: %.loc11_37.2: %.273 = bind_value %.loc11_37.1
176+
// CHECK:STDOUT: %c: %.273 = bind_name c, %.loc11_37.2
177+
// CHECK:STDOUT: }
178+
// CHECK:STDOUT:
179+
// CHECK:STDOUT: fn @__global_init() {
180+
// CHECK:STDOUT: !entry:
181+
// CHECK:STDOUT: %MakeNull.ref.loc10: %MakeNull.type = name_ref MakeNull, file.%MakeNull.decl [concrete = constants.%MakeNull]
182+
// CHECK:STDOUT: %.loc10_38: %empty_struct_type = struct_literal ()
183+
// CHECK:STDOUT: %.loc10_39.1: type = converted %.loc10_38, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
184+
// CHECK:STDOUT: %MakeNull.specific_fn.loc10: <specific function> = specific_function %MakeNull.ref.loc10, @MakeNull(constants.%empty_struct_type) [concrete = constants.%MakeNull.specific_fn.7cb]
185+
// CHECK:STDOUT: %.loc10_39.2: ref %.b2d = temporary_storage
186+
// CHECK:STDOUT: %MakeNull.call.loc10: init %.b2d = call %MakeNull.specific_fn.loc10() to %.loc10_39.2
187+
// CHECK:STDOUT: %MakeNull.ref.loc11: %MakeNull.type = name_ref MakeNull, file.%MakeNull.decl [concrete = constants.%MakeNull]
188+
// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C]
189+
// CHECK:STDOUT: %MakeNull.specific_fn.loc11: <specific function> = specific_function %MakeNull.ref.loc11, @MakeNull(constants.%C) [concrete = constants.%MakeNull.specific_fn.22e]
190+
// CHECK:STDOUT: %.loc11: ref %.273 = temporary_storage
191+
// CHECK:STDOUT: %MakeNull.call.loc11: init %.273 = call %MakeNull.specific_fn.loc11() to %.loc11
192+
// CHECK:STDOUT: <elided>
193+
// CHECK:STDOUT: }
194+
// CHECK:STDOUT:

toolchain/lower/handle_call.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,26 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id,
469469
CARBON_FATAL("Missing constant value for call to comptime-only function");
470470
}
471471

472+
case SemIR::BuiltinFunctionKind::PointerMakeNull: {
473+
// MaybeUnformed(T*) has an in-place initializing representation, so an
474+
// out parameter will be passed.
475+
context.builder().CreateStore(
476+
llvm::ConstantPointerNull::get(
477+
llvm::PointerType::get(context.llvm_context(),
478+
/*AddressSpace=*/0)),
479+
context.GetValue(arg_ids[0]));
480+
context.SetLocal(inst_id,
481+
llvm::PoisonValue::get(context.GetTypeOfInst(inst_id)));
482+
return;
483+
}
484+
485+
case SemIR::BuiltinFunctionKind::PointerIsNull: {
486+
auto* ptr = context.builder().CreateLoad(
487+
context.GetTypeOfInst(arg_ids[0]), context.GetValue(arg_ids[0]));
488+
context.SetLocal(inst_id, context.builder().CreateIsNull(ptr));
489+
return;
490+
}
491+
472492
case SemIR::BuiltinFunctionKind::TypeDestroy:
473493
// TODO: Destroy aggregate members.
474494
return;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
//
5+
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/bool.carbon
6+
//
7+
// AUTOUPDATE
8+
// TIP: To test this file alone, run:
9+
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/builtins/pointer.carbon
10+
// TIP: To dump output, run:
11+
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/builtins/pointer.carbon
12+
13+
fn MakeUnformed(t: type) -> type = "maybe_unformed.make_type";
14+
fn MakeNull(T:! type) -> MakeUnformed(T*) = "pointer.make_null";
15+
fn IsNull[T:! type](p: MakeUnformed(T*)) -> bool = "pointer.is_null";
16+
17+
class C {}
18+
19+
fn Null() -> MakeUnformed(C*) {
20+
return MakeNull(C);
21+
}
22+
23+
fn Check(p: MakeUnformed(C*)) -> bool {
24+
return IsNull(p);
25+
}
26+
27+
// CHECK:STDOUT: ; ModuleID = 'pointer.carbon'
28+
// CHECK:STDOUT: source_filename = "pointer.carbon"
29+
// CHECK:STDOUT:
30+
// CHECK:STDOUT: define void @_CNull.Main(ptr sret(ptr) %return) !dbg !4 {
31+
// CHECK:STDOUT: entry:
32+
// CHECK:STDOUT: store ptr null, ptr %return, align 8, !dbg !7
33+
// CHECK:STDOUT: ret void, !dbg !8
34+
// CHECK:STDOUT: }
35+
// CHECK:STDOUT:
36+
// CHECK:STDOUT: define i1 @_CCheck.Main(ptr %p) !dbg !9 {
37+
// CHECK:STDOUT: entry:
38+
// CHECK:STDOUT: %IsNull.call = load ptr, ptr %p, align 8, !dbg !10
39+
// CHECK:STDOUT: %IsNull.call1 = icmp eq ptr %IsNull.call, null, !dbg !10
40+
// CHECK:STDOUT: ret i1 %IsNull.call1, !dbg !11
41+
// CHECK:STDOUT: }
42+
// CHECK:STDOUT:
43+
// CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
44+
// CHECK:STDOUT: !llvm.dbg.cu = !{!2}
45+
// CHECK:STDOUT:
46+
// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5}
47+
// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3}
48+
// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
49+
// CHECK:STDOUT: !3 = !DIFile(filename: "pointer.carbon", directory: "")
50+
// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Null", linkageName: "_CNull.Main", scope: null, file: !3, line: 19, type: !5, spFlags: DISPFlagDefinition, unit: !2)
51+
// CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
52+
// CHECK:STDOUT: !6 = !{}
53+
// CHECK:STDOUT: !7 = !DILocation(line: 20, column: 10, scope: !4)
54+
// CHECK:STDOUT: !8 = !DILocation(line: 20, column: 3, scope: !4)
55+
// CHECK:STDOUT: !9 = distinct !DISubprogram(name: "Check", linkageName: "_CCheck.Main", scope: null, file: !3, line: 23, type: !5, spFlags: DISPFlagDefinition, unit: !2)
56+
// CHECK:STDOUT: !10 = !DILocation(line: 24, column: 10, scope: !9)
57+
// CHECK:STDOUT: !11 = !DILocation(line: 24, column: 3, scope: !9)

toolchain/sem_ir/builtin_function_kind.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,22 @@ struct PointerTo {
8080
}
8181
};
8282

83+
// Constraint that a type is MaybeUnformed<T>.
84+
template <typename T>
85+
struct MaybeUnformed {
86+
static auto Check(const File& sem_ir, ValidateState& state, TypeId type_id)
87+
-> bool {
88+
auto maybe_unformed =
89+
sem_ir.types().TryGetAs<SemIR::MaybeUnformedType>(type_id);
90+
if (!maybe_unformed) {
91+
return false;
92+
}
93+
return Check<T>(
94+
sem_ir, state,
95+
sem_ir.types().GetTypeIdForTypeInstId(maybe_unformed->inner_id));
96+
}
97+
};
98+
8399
// Constraint that a type is `()`, used as the return type of builtin functions
84100
// with no return value.
85101
struct NoReturn {
@@ -605,6 +621,18 @@ constexpr BuiltinInfo BoolEq = {"bool.eq",
605621
constexpr BuiltinInfo BoolNeq = {"bool.neq",
606622
ValidateSignature<auto(Bool, Bool)->Bool>};
607623

624+
// "pointer.make_null": returns the representation of a null pointer value. This
625+
// is an unformed state for the pointer type.
626+
constexpr BuiltinInfo PointerMakeNull = {
627+
"pointer.make_null",
628+
ValidateSignature<auto()->MaybeUnformed<PointerTo<AnyType>>>};
629+
630+
// "pointer.is_null": determines whether the given pointer representation is a
631+
// null pointer value.
632+
constexpr BuiltinInfo PointerIsNull = {
633+
"pointer.is_null",
634+
ValidateSignature<auto(MaybeUnformed<PointerTo<AnyType>>)->Bool>};
635+
608636
// "type.and": facet type combination.
609637
constexpr BuiltinInfo TypeAnd = {"type.and",
610638
ValidateSignature<auto(Type, Type)->Type>};

toolchain/sem_ir/builtin_function_kind.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(FloatGreaterEq)
123123
CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(BoolEq)
124124
CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(BoolNeq)
125125

126+
// Pointers.
127+
CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(PointerMakeNull)
128+
CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(PointerIsNull)
129+
126130
// Facet type combination.
127131
CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(TypeAnd)
128132

toolchain/sem_ir/type_iterator.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ class TypeIterator {
4040
// The iterator will visit things in the reverse order that they are added.
4141
auto Add(InstId inst_id) -> void {
4242
auto type_id = sem_ir_->insts().Get(inst_id).type_id();
43-
CARBON_CHECK(sem_ir_->types().IsFacetType(type_id));
43+
CARBON_CHECK(sem_ir_->types().IsFacetType(type_id),
44+
"Type {0} of type inst is not a facet type",
45+
sem_ir_->types().GetAsInst(type_id).kind());
4446
PushInstId(inst_id);
4547
}
4648

toolchain/sem_ir/typed_insts.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,7 @@ struct MaybeUnformedType {
12011201
.ir_name = "maybe_unformed_type",
12021202
.is_type = InstIsType::Always,
12031203
.constant_kind = InstConstantKind::WheneverPossible,
1204+
.deduce_through = true,
12041205
});
12051206

12061207
TypeId type_id;

0 commit comments

Comments
 (0)