diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def
index a39ce494c7b8..ede81a3e2334 100644
--- a/toolchain/diagnostics/diagnostic_kind.def
+++ b/toolchain/diagnostics/diagnostic_kind.def
@@ -98,6 +98,8 @@ CARBON_DIAGNOSTIC_KIND(SemanticsTodo)
CARBON_DIAGNOSTIC_KIND(AddressOfEphemeralReference)
CARBON_DIAGNOSTIC_KIND(AddressOfNonReference)
+CARBON_DIAGNOSTIC_KIND(DereferenceOfNonPointer)
+CARBON_DIAGNOSTIC_KIND(DereferenceOfType)
CARBON_DIAGNOSTIC_KIND(NameNotFound)
CARBON_DIAGNOSTIC_KIND(NameDeclarationDuplicate)
CARBON_DIAGNOSTIC_KIND(NameDeclarationPrevious)
diff --git a/toolchain/lowering/lowering_handle.cpp b/toolchain/lowering/lowering_handle.cpp
index ebea588284d7..1e68bd89745c 100644
--- a/toolchain/lowering/lowering_handle.cpp
+++ b/toolchain/lowering/lowering_handle.cpp
@@ -142,6 +142,12 @@ auto LoweringHandleCall(LoweringFunctionContext& context,
}
}
+auto LoweringHandleDereference(LoweringFunctionContext& context,
+ SemanticsNodeId node_id, SemanticsNode node)
+ -> void {
+ context.SetLocal(node_id, context.GetLocal(node.GetAsDereference()));
+}
+
auto LoweringHandleFunctionDeclaration(LoweringFunctionContext& /*context*/,
SemanticsNodeId /*node_id*/,
SemanticsNode node) -> void {
diff --git a/toolchain/lowering/testdata/pointer/basic.carbon b/toolchain/lowering/testdata/pointer/basic.carbon
index e5565ff378f0..d3c94a24572d 100644
--- a/toolchain/lowering/testdata/pointer/basic.carbon
+++ b/toolchain/lowering/testdata/pointer/basic.carbon
@@ -4,23 +4,27 @@
//
// AUTOUPDATE
-fn G(p: i32*);
+fn G(p: i32*) -> i32 {
+ // TODO: The LLVM IR generated here is wrong, missing a load.
+ return *p;
+}
-fn F() {
+fn F() -> i32 {
var n: i32 = 0;
- G(&n);
+ return G(&n);
}
// CHECK:STDOUT: ; ModuleID = 'basic.carbon'
// CHECK:STDOUT: source_filename = "basic.carbon"
// CHECK:STDOUT:
-// CHECK:STDOUT: declare void @G(ptr)
+// CHECK:STDOUT: define i32 @G(ptr %p) {
+// CHECK:STDOUT: ret ptr %p
+// CHECK:STDOUT: }
// CHECK:STDOUT:
-// CHECK:STDOUT: define void @F() {
+// CHECK:STDOUT: define i32 @F() {
// CHECK:STDOUT: %var = alloca i32, align 4
// CHECK:STDOUT: store i32 0, ptr %var, align 4
// CHECK:STDOUT: %1 = load ptr, ptr %var, align 8
-// CHECK:STDOUT: call void @G(ptr %1)
-// CHECK:STDOUT: %TupleLiteralValue = alloca {}, align 8
-// CHECK:STDOUT: ret void
+// CHECK:STDOUT: %G = call i32 @G(ptr %1)
+// CHECK:STDOUT: ret i32 %G
// CHECK:STDOUT: }
diff --git a/toolchain/semantics/semantics_context.cpp b/toolchain/semantics/semantics_context.cpp
index 9dc191e9748c..1779d0696c47 100644
--- a/toolchain/semantics/semantics_context.cpp
+++ b/toolchain/semantics/semantics_context.cpp
@@ -360,7 +360,11 @@ auto SemanticsContext::ImplicitAsImpl(SemanticsNodeId value_id,
auto value = semantics_ir_->GetNode(value_id);
auto value_type_id = value.type_id();
if (value_type_id == SemanticsTypeId::Error) {
- return ImplicitAsKind::Identical;
+ // Although the source type is invalid, this still changes the value.
+ if (output_value_id != nullptr) {
+ *output_value_id = SemanticsNodeId::BuiltinError;
+ }
+ return ImplicitAsKind::Compatible;
}
if (as_type_id == SemanticsTypeId::Error) {
@@ -603,7 +607,7 @@ auto SemanticsContext::GetPointerType(ParseTree::Node parse_node,
auto SemanticsContext::GetUnqualifiedType(SemanticsTypeId type_id)
-> SemanticsTypeId {
SemanticsNode type_node =
- semantics_ir_->GetNode(semantics_ir_->GetType(type_id));
+ semantics_ir_->GetNode(semantics_ir_->GetTypeAllowBuiltinTypes(type_id));
if (type_node.kind() == SemanticsNodeKind::ConstType)
return type_node.GetAsConstType();
return type_id;
diff --git a/toolchain/semantics/semantics_handle_operator.cpp b/toolchain/semantics/semantics_handle_operator.cpp
index bb7cfa385e17..31626334f615 100644
--- a/toolchain/semantics/semantics_handle_operator.cpp
+++ b/toolchain/semantics/semantics_handle_operator.cpp
@@ -150,8 +150,36 @@ auto SemanticsHandlePrefixOperator(SemanticsContext& context,
value_id));
return true;
- case TokenKind::Star:
- return context.TODO(parse_node, llvm::formatv("Handle {0}", token_kind));
+ case TokenKind::Star: {
+ auto type_id = context.GetUnqualifiedType(
+ context.semantics_ir().GetNode(value_id).type_id());
+ auto type_node = context.semantics_ir().GetNode(
+ context.semantics_ir().GetTypeAllowBuiltinTypes(type_id));
+ auto result_type_id = SemanticsTypeId::Error;
+ if (type_node.kind() == SemanticsNodeKind::PointerType) {
+ result_type_id = type_node.GetAsPointerType();
+ } else {
+ CARBON_DIAGNOSTIC(
+ DereferenceOfNonPointer, Error,
+ "Cannot dereference operand of non-pointer type `{0}`.",
+ std::string);
+ auto builder = context.emitter().Build(
+ parse_node, DereferenceOfNonPointer,
+ context.semantics_ir().StringifyType(type_id));
+ // TODO: Check for any facet here, rather than only a type.
+ if (type_id == SemanticsTypeId::TypeType) {
+ CARBON_DIAGNOSTIC(
+ DereferenceOfType, Note,
+ "To form a pointer type, write the `*` after the pointee type.");
+ builder.Note(parse_node, DereferenceOfType);
+ }
+ builder.Emit();
+ }
+ context.AddNodeAndPush(parse_node,
+ SemanticsNode::Dereference::Make(
+ parse_node, result_type_id, value_id));
+ return true;
+ }
default:
return context.TODO(parse_node, llvm::formatv("Handle {0}", token_kind));
diff --git a/toolchain/semantics/semantics_ir.cpp b/toolchain/semantics/semantics_ir.cpp
index cfe333f97c1e..2301160002e2 100644
--- a/toolchain/semantics/semantics_ir.cpp
+++ b/toolchain/semantics/semantics_ir.cpp
@@ -227,6 +227,7 @@ static auto GetTypePrecedence(SemanticsNodeKind kind) -> int {
case SemanticsNodeKind::BranchIf:
case SemanticsNodeKind::BranchWithArg:
case SemanticsNodeKind::Call:
+ case SemanticsNodeKind::Dereference:
case SemanticsNodeKind::FunctionDeclaration:
case SemanticsNodeKind::Index:
case SemanticsNodeKind::IntegerLiteral:
@@ -368,6 +369,7 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string {
case SemanticsNodeKind::BranchWithArg:
case SemanticsNodeKind::Builtin:
case SemanticsNodeKind::Call:
+ case SemanticsNodeKind::Dereference:
case SemanticsNodeKind::CrossReference:
case SemanticsNodeKind::FunctionDeclaration:
case SemanticsNodeKind::Index:
@@ -481,6 +483,7 @@ auto GetSemanticsExpressionCategory(const SemanticsIR& semantics_ir,
// struct/tuple value construction.
return SemanticsExpressionCategory::Value;
+ case SemanticsNodeKind::Dereference:
case SemanticsNodeKind::VarStorage:
return SemanticsExpressionCategory::DurableReference;
}
diff --git a/toolchain/semantics/semantics_node.h b/toolchain/semantics/semantics_node.h
index 98d6bcf33108..a21305920a26 100644
--- a/toolchain/semantics/semantics_node.h
+++ b/toolchain/semantics/semantics_node.h
@@ -379,6 +379,9 @@ class SemanticsNode {
using FactoryBase::Get;
};
+ using Dereference =
+ Factory;
+
using FunctionDeclaration =
FactoryNoType;
diff --git a/toolchain/semantics/semantics_node_kind.def b/toolchain/semantics/semantics_node_kind.def
index 5f1387bd5251..c1a33457b6f4 100644
--- a/toolchain/semantics/semantics_node_kind.def
+++ b/toolchain/semantics/semantics_node_kind.def
@@ -39,6 +39,7 @@ CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND(BranchWithArg, Terminator)
CARBON_SEMANTICS_NODE_KIND(Builtin)
CARBON_SEMANTICS_NODE_KIND(Call)
CARBON_SEMANTICS_NODE_KIND(ConstType)
+CARBON_SEMANTICS_NODE_KIND(Dereference)
CARBON_SEMANTICS_NODE_KIND(FunctionDeclaration)
CARBON_SEMANTICS_NODE_KIND(Index)
CARBON_SEMANTICS_NODE_KIND(IntegerLiteral)
diff --git a/toolchain/semantics/testdata/operators/fail_type_mismatch.carbon b/toolchain/semantics/testdata/operators/fail_type_mismatch.carbon
index eefcb156d66d..0986566d4337 100644
--- a/toolchain/semantics/testdata/operators/fail_type_mismatch.carbon
+++ b/toolchain/semantics/testdata/operators/fail_type_mismatch.carbon
@@ -35,7 +35,7 @@ fn Main() -> i32 {
// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int0, type: type0},
// CHECK:STDOUT: {kind: RealLiteral, arg0: real0, type: type1},
// CHECK:STDOUT: {kind: BinaryOperatorAdd, arg0: nodeError, arg1: node+2, type: typeError},
-// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+3},
+// CHECK:STDOUT: {kind: ReturnExpression, arg0: nodeError},
// CHECK:STDOUT: ]
// CHECK:STDOUT: node_blocks: [
// CHECK:STDOUT: [
diff --git a/toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon b/toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon
index 74a4c1027512..8f67cb86d4ac 100644
--- a/toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon
+++ b/toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon
@@ -39,8 +39,8 @@ fn Main() -> i32 {
// CHECK:STDOUT: {kind: RealLiteral, arg0: real0, type: type1},
// CHECK:STDOUT: {kind: BinaryOperatorAdd, arg0: nodeError, arg1: node+2, type: typeError},
// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int1, type: type0},
-// CHECK:STDOUT: {kind: BinaryOperatorAdd, arg0: node+3, arg1: node+4, type: typeError},
-// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+5},
+// CHECK:STDOUT: {kind: BinaryOperatorAdd, arg0: nodeError, arg1: node+4, type: typeError},
+// CHECK:STDOUT: {kind: ReturnExpression, arg0: nodeError},
// CHECK:STDOUT: ]
// CHECK:STDOUT: node_blocks: [
// CHECK:STDOUT: [
diff --git a/toolchain/semantics/testdata/pointer/address_of_deref.carbon b/toolchain/semantics/testdata/pointer/address_of_deref.carbon
new file mode 100644
index 000000000000..1e6a0f30cdb5
--- /dev/null
+++ b/toolchain/semantics/testdata/pointer/address_of_deref.carbon
@@ -0,0 +1,62 @@
+// 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
+
+fn F() -> i32 {
+ var n: i32 = 0;
+ return *&*&n;
+}
+
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT: {name: str0, param_refs: block0, return_type: type0, body: {block2}}},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT: 0,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT: F,
+// CHECK:STDOUT: n,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT: nodeIntegerType,
+// CHECK:STDOUT: node+5,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: type_blocks: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0},
+// CHECK:STDOUT: {kind: VarStorage, type: type0},
+// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+1, type: type0},
+// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int0, type: type0},
+// CHECK:STDOUT: {kind: Assign, arg0: node+1, arg1: node+3},
+// CHECK:STDOUT: {kind: PointerType, arg0: type0, type: typeTypeType},
+// CHECK:STDOUT: {kind: AddressOf, arg0: node+1, type: type1},
+// CHECK:STDOUT: {kind: Dereference, arg0: node+6, type: type0},
+// CHECK:STDOUT: {kind: AddressOf, arg0: node+7, type: type1},
+// CHECK:STDOUT: {kind: Dereference, arg0: node+8, type: type0},
+// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+9},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: node_blocks: [
+// CHECK:STDOUT: [
+// CHECK:STDOUT: ],
+// CHECK:STDOUT: [
+// CHECK:STDOUT: node+0,
+// CHECK:STDOUT: ],
+// CHECK:STDOUT: [
+// CHECK:STDOUT: node+1,
+// CHECK:STDOUT: node+2,
+// CHECK:STDOUT: node+3,
+// CHECK:STDOUT: node+4,
+// CHECK:STDOUT: node+5,
+// CHECK:STDOUT: node+6,
+// CHECK:STDOUT: node+7,
+// CHECK:STDOUT: node+8,
+// CHECK:STDOUT: node+9,
+// CHECK:STDOUT: node+10,
+// CHECK:STDOUT: ],
+// CHECK:STDOUT: ]
diff --git a/toolchain/semantics/testdata/pointer/basic.carbon b/toolchain/semantics/testdata/pointer/basic.carbon
index 837c18503103..e2f48282866b 100644
--- a/toolchain/semantics/testdata/pointer/basic.carbon
+++ b/toolchain/semantics/testdata/pointer/basic.carbon
@@ -8,10 +8,7 @@ fn F() -> i32 {
var n: i32 = 0;
var p: i32* = &n;
- // TODO: Dereference.
- // return *p;
-
- return 0;
+ return *p;
}
// CHECK:STDOUT: cross_reference_irs_size: 1
@@ -20,7 +17,6 @@ fn F() -> i32 {
// CHECK:STDOUT: ]
// CHECK:STDOUT: integer_literals: [
// CHECK:STDOUT: 0,
-// CHECK:STDOUT: 0,
// CHECK:STDOUT: ]
// CHECK:STDOUT: real_literals: [
// CHECK:STDOUT: ]
@@ -46,7 +42,7 @@ fn F() -> i32 {
// CHECK:STDOUT: {kind: BindName, arg0: str2, arg1: node+6, type: type1},
// CHECK:STDOUT: {kind: AddressOf, arg0: node+1, type: type1},
// CHECK:STDOUT: {kind: Assign, arg0: node+6, arg1: node+8},
-// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int1, type: type0},
+// CHECK:STDOUT: {kind: Dereference, arg0: node+6, type: type0},
// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+10},
// CHECK:STDOUT: ]
// CHECK:STDOUT: node_blocks: [
diff --git a/toolchain/semantics/testdata/pointer/fail_dereference_not_pointer.carbon b/toolchain/semantics/testdata/pointer/fail_dereference_not_pointer.carbon
new file mode 100644
index 000000000000..fdb25ec39acf
--- /dev/null
+++ b/toolchain/semantics/testdata/pointer/fail_dereference_not_pointer.carbon
@@ -0,0 +1,79 @@
+// 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
+
+fn Deref(n: i32) {
+ // CHECK:STDERR: fail_dereference_not_pointer.carbon:[[@LINE+3]]:3: Cannot dereference operand of non-pointer type `i32`.
+ // CHECK:STDERR: *n;
+ // CHECK:STDERR: ^
+ *n;
+ // CHECK:STDERR: fail_dereference_not_pointer.carbon:[[@LINE+3]]:3: Cannot dereference operand of non-pointer type `() as type`.
+ // CHECK:STDERR: *();
+ // CHECK:STDERR: ^
+ *();
+ // CHECK:STDERR: fail_dereference_not_pointer.carbon:[[@LINE+3]]:3: Cannot dereference operand of non-pointer type `{} as type`.
+ // CHECK:STDERR: *{};
+ // CHECK:STDERR: ^
+ *{};
+}
+
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT: {name: str0, param_refs: block2, body: {block4}}},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT: Deref,
+// CHECK:STDOUT: n,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT: nodeIntegerType,
+// CHECK:STDOUT: node+4,
+// CHECK:STDOUT: node+7,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: type_blocks: [
+// CHECK:STDOUT: [
+// CHECK:STDOUT: ],
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT: {kind: VarStorage, type: type0},
+// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+0, type: type0},
+// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0},
+// CHECK:STDOUT: {kind: Dereference, arg0: node+0, type: typeError},
+// CHECK:STDOUT: {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
+// CHECK:STDOUT: {kind: TupleValue, arg0: block0, type: type1},
+// CHECK:STDOUT: {kind: Dereference, arg0: node+5, type: typeError},
+// CHECK:STDOUT: {kind: StructType, arg0: block0, type: typeTypeType},
+// CHECK:STDOUT: {kind: StructValue, arg0: block0, type: type2},
+// CHECK:STDOUT: {kind: Dereference, arg0: node+8, type: typeError},
+// CHECK:STDOUT: {kind: Return},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: node_blocks: [
+// CHECK:STDOUT: [
+// CHECK:STDOUT: ],
+// CHECK:STDOUT: [
+// CHECK:STDOUT: node+0,
+// CHECK:STDOUT: node+1,
+// CHECK:STDOUT: ],
+// CHECK:STDOUT: [
+// CHECK:STDOUT: node+1,
+// CHECK:STDOUT: ],
+// CHECK:STDOUT: [
+// CHECK:STDOUT: node+2,
+// CHECK:STDOUT: ],
+// CHECK:STDOUT: [
+// CHECK:STDOUT: node+3,
+// CHECK:STDOUT: node+4,
+// CHECK:STDOUT: node+5,
+// CHECK:STDOUT: node+6,
+// CHECK:STDOUT: node+7,
+// CHECK:STDOUT: node+8,
+// CHECK:STDOUT: node+9,
+// CHECK:STDOUT: node+10,
+// CHECK:STDOUT: ],
+// CHECK:STDOUT: ]
diff --git a/toolchain/semantics/testdata/pointer/fail_dereference_type.carbon b/toolchain/semantics/testdata/pointer/fail_dereference_type.carbon
new file mode 100644
index 000000000000..e0ea3aa9783c
--- /dev/null
+++ b/toolchain/semantics/testdata/pointer/fail_dereference_type.carbon
@@ -0,0 +1,45 @@
+// 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
+
+// CHECK:STDERR: fail_dereference_type.carbon:[[@LINE+9]]:8: Parentheses are required around this unary `*` operator.
+// CHECK:STDERR: var p: *i32;
+// CHECK:STDERR: ^
+// CHECK:STDERR: fail_dereference_type.carbon:[[@LINE+6]]:8: Cannot dereference operand of non-pointer type `type`.
+// CHECK:STDERR: var p: *i32;
+// CHECK:STDERR: ^
+// CHECK:STDERR: fail_dereference_type.carbon:[[@LINE+3]]:8: To form a pointer type, write the `*` after the pointee type.
+// CHECK:STDERR: var p: *i32;
+// CHECK:STDERR: ^
+var p: *i32;
+
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT: p,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: type_blocks: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT: {kind: Dereference, arg0: nodeIntegerType, type: typeError},
+// CHECK:STDOUT: {kind: VarStorage, type: typeError},
+// CHECK:STDOUT: {kind: BindName, arg0: str0, arg1: node+1, type: typeError},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: node_blocks: [
+// CHECK:STDOUT: [
+// CHECK:STDOUT: ],
+// CHECK:STDOUT: [
+// CHECK:STDOUT: node+0,
+// CHECK:STDOUT: node+1,
+// CHECK:STDOUT: node+2,
+// CHECK:STDOUT: ],
+// CHECK:STDOUT: ]
diff --git a/toolchain/semantics/testdata/pointer/nested_const.carbon b/toolchain/semantics/testdata/pointer/nested_const.carbon
new file mode 100644
index 000000000000..46738c346b5c
--- /dev/null
+++ b/toolchain/semantics/testdata/pointer/nested_const.carbon
@@ -0,0 +1,72 @@
+// 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
+
+// TODO: The `const` in the return type should not be necessary.
+fn F(p: const (const (const i32*)*)) -> const i32 {
+ return **p;
+}
+
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: functions: [
+// CHECK:STDOUT: {name: str0, param_refs: block2, return_type: type1, body: {block4}}},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT: F,
+// CHECK:STDOUT: p,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: types: [
+// CHECK:STDOUT: nodeIntegerType,
+// CHECK:STDOUT: node+0,
+// CHECK:STDOUT: node+1,
+// CHECK:STDOUT: node+2,
+// CHECK:STDOUT: node+3,
+// CHECK:STDOUT: node+4,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: type_blocks: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT: {kind: ConstType, arg0: type0, type: typeTypeType},
+// CHECK:STDOUT: {kind: PointerType, arg0: type1, type: typeTypeType},
+// CHECK:STDOUT: {kind: ConstType, arg0: type2, type: typeTypeType},
+// CHECK:STDOUT: {kind: PointerType, arg0: type3, type: typeTypeType},
+// CHECK:STDOUT: {kind: ConstType, arg0: type4, type: typeTypeType},
+// CHECK:STDOUT: {kind: VarStorage, type: type5},
+// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+5, type: type5},
+// CHECK:STDOUT: {kind: ConstType, arg0: type0, type: typeTypeType},
+// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0},
+// CHECK:STDOUT: {kind: Dereference, arg0: node+5, type: type3},
+// CHECK:STDOUT: {kind: Dereference, arg0: node+9, type: type1},
+// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+10},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: node_blocks: [
+// CHECK:STDOUT: [
+// CHECK:STDOUT: ],
+// CHECK:STDOUT: [
+// CHECK:STDOUT: node+0,
+// CHECK:STDOUT: node+1,
+// CHECK:STDOUT: node+2,
+// CHECK:STDOUT: node+3,
+// CHECK:STDOUT: node+4,
+// CHECK:STDOUT: node+5,
+// CHECK:STDOUT: node+6,
+// CHECK:STDOUT: ],
+// CHECK:STDOUT: [
+// CHECK:STDOUT: node+6,
+// CHECK:STDOUT: ],
+// CHECK:STDOUT: [
+// CHECK:STDOUT: node+7,
+// CHECK:STDOUT: node+8,
+// CHECK:STDOUT: ],
+// CHECK:STDOUT: [
+// CHECK:STDOUT: node+9,
+// CHECK:STDOUT: node+10,
+// CHECK:STDOUT: node+11,
+// CHECK:STDOUT: ],
+// CHECK:STDOUT: ]