Skip to content

Commit d0e8afc

Browse files
authored
Handle arrow operator (#3768)
This change implements the check behavior for the arrow operator. `ptr->Foo()` is rewritten as `(*ptr).Foo()` and `ptr->(X.y)` is rewritten as `(*ptr).(X.y)`
1 parent 4421a75 commit d0e8afc

17 files changed

+365
-89
lines changed

toolchain/check/BUILD

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ cc_library(
107107
":import",
108108
":interface",
109109
":member_access",
110+
":pointer_dereference",
110111
"//common:check",
111112
"//common:ostream",
112113
"//toolchain/base:pretty_stack_trace_function",
@@ -212,6 +213,21 @@ cc_library(
212213
],
213214
)
214215

216+
cc_library(
217+
name = "pointer_dereference",
218+
srcs = ["pointer_dereference.cpp"],
219+
hdrs = ["pointer_dereference.h"],
220+
deps = [
221+
":context",
222+
"//common:check",
223+
"//toolchain/parse:node_kind",
224+
"//toolchain/sem_ir:ids",
225+
"//toolchain/sem_ir:inst",
226+
"//toolchain/sem_ir:inst_kind",
227+
"@llvm-project//llvm:Support",
228+
],
229+
)
230+
215231
cc_library(
216232
name = "subst",
217233
srcs = ["subst.cpp"],

toolchain/check/handle_name.cpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
#include "toolchain/check/context.h"
66
#include "toolchain/check/member_access.h"
7+
#include "toolchain/check/pointer_dereference.h"
78
#include "toolchain/lex/token_kind.h"
8-
#include "toolchain/parse/typed_nodes.h"
99
#include "toolchain/sem_ir/inst.h"
1010
#include "toolchain/sem_ir/typed_insts.h"
1111

@@ -31,7 +31,36 @@ auto HandleMemberAccessExpr(Context& context, Parse::MemberAccessExprId node_id)
3131
auto HandlePointerMemberAccessExpr(Context& context,
3232
Parse::PointerMemberAccessExprId node_id)
3333
-> bool {
34-
return context.TODO(node_id, "HandlePointerMemberAccessExpr");
34+
auto diagnose_not_pointer = [&context,
35+
&node_id](SemIR::TypeId not_pointer_type_id) {
36+
CARBON_DIAGNOSTIC(ArrowOperatorOfNonPointer, Error,
37+
"Cannot apply `->` operator to non-pointer type `{0}`.",
38+
SemIR::TypeId);
39+
40+
auto builder = context.emitter().Build(
41+
TokenOnly(node_id), ArrowOperatorOfNonPointer, not_pointer_type_id);
42+
builder.Emit();
43+
};
44+
45+
if (context.node_stack().PeekIs<Parse::NodeKind::ParenExpr>()) {
46+
auto member_expr_id = context.node_stack().PopExpr();
47+
auto base_id = context.node_stack().PopExpr();
48+
auto deref_base_id = PerformPointerDereference(context, node_id, base_id,
49+
diagnose_not_pointer);
50+
auto member_id = PerformCompoundMemberAccess(context, node_id,
51+
deref_base_id, member_expr_id);
52+
context.node_stack().Push(node_id, member_id);
53+
} else {
54+
SemIR::NameId name_id = context.node_stack().PopName();
55+
auto base_id = context.node_stack().PopExpr();
56+
auto deref_base_id = PerformPointerDereference(context, node_id, base_id,
57+
diagnose_not_pointer);
58+
auto member_id =
59+
PerformMemberAccess(context, node_id, deref_base_id, name_id);
60+
context.node_stack().Push(node_id, member_id);
61+
}
62+
63+
return true;
3564
}
3665

3766
static auto GetIdentifierAsName(Context& context, Parse::NodeId node_id)

toolchain/check/handle_operator.cpp

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#include "toolchain/check/context.h"
66
#include "toolchain/check/convert.h"
7+
#include "toolchain/check/pointer_dereference.h"
8+
#include "toolchain/diagnostics/diagnostic_emitter.h"
79

810
namespace Carbon::Check {
911

@@ -281,30 +283,31 @@ auto HandlePrefixOperatorPlusPlus(Context& context,
281283

282284
auto HandlePrefixOperatorStar(Context& context,
283285
Parse::PrefixOperatorStarId node_id) -> bool {
284-
auto value_id = context.node_stack().PopExpr();
285-
value_id = ConvertToValueExpr(context, value_id);
286-
auto type_id =
287-
context.GetUnqualifiedType(context.insts().Get(value_id).type_id());
288-
auto result_type_id = SemIR::TypeId::Error;
289-
if (auto pointer_type =
290-
context.types().TryGetAs<SemIR::PointerType>(type_id)) {
291-
result_type_id = pointer_type->pointee_id;
292-
} else if (type_id != SemIR::TypeId::Error) {
293-
CARBON_DIAGNOSTIC(DerefOfNonPointer, Error,
294-
"Cannot dereference operand of non-pointer type `{0}`.",
295-
SemIR::TypeId);
296-
auto builder =
297-
context.emitter().Build(TokenOnly(node_id), DerefOfNonPointer, type_id);
298-
// TODO: Check for any facet here, rather than only a type.
299-
if (type_id == SemIR::TypeId::TypeType) {
300-
CARBON_DIAGNOSTIC(
301-
DerefOfType, Note,
302-
"To form a pointer type, write the `*` after the pointee type.");
303-
builder.Note(TokenOnly(node_id), DerefOfType);
304-
}
305-
builder.Emit();
306-
}
307-
context.AddInstAndPush({node_id, SemIR::Deref{result_type_id, value_id}});
286+
auto base_id = context.node_stack().PopExpr();
287+
288+
auto deref_base_id = PerformPointerDereference(
289+
context, node_id, base_id,
290+
[&context, &node_id](SemIR::TypeId not_pointer_type_id) {
291+
CARBON_DIAGNOSTIC(
292+
DerefOfNonPointer, Error,
293+
"Cannot dereference operand of non-pointer type `{0}`.",
294+
SemIR::TypeId);
295+
296+
auto builder = context.emitter().Build(
297+
TokenOnly(node_id), DerefOfNonPointer, not_pointer_type_id);
298+
299+
// TODO: Check for any facet here, rather than only a type.
300+
if (not_pointer_type_id == SemIR::TypeId::TypeType) {
301+
CARBON_DIAGNOSTIC(
302+
DerefOfType, Note,
303+
"To form a pointer type, write the `*` after the pointee type.");
304+
builder.Note(TokenOnly(node_id), DerefOfType);
305+
}
306+
307+
builder.Emit();
308+
});
309+
310+
context.node_stack().Push(node_id, deref_base_id);
308311
return true;
309312
}
310313

toolchain/check/member_access.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ static auto PerformImplLookup(Context& context, SemIR::ConstantId type_const_id,
195195
// impl lookup if necessary. If the scope is invalid, assume an error has
196196
// already been diagnosed, and return BuiltinError.
197197
static auto LookupMemberNameInScope(Context& context,
198-
Parse::MemberAccessExprId node_id,
198+
Parse::AnyMemberAccessExprId node_id,
199199
SemIR::InstId /*base_id*/,
200200
SemIR::NameId name_id,
201201
SemIR::ConstantId name_scope_const_id,
@@ -231,7 +231,7 @@ static auto LookupMemberNameInScope(Context& context,
231231
// field, forms a class member access. If the found member is an instance
232232
// method, forms a bound method. Otherwise, the member is returned unchanged.
233233
static auto PerformInstanceBinding(Context& context,
234-
Parse::MemberAccessExprId node_id,
234+
Parse::AnyMemberAccessExprId node_id,
235235
SemIR::InstId base_id,
236236
SemIR::InstId member_id) -> SemIR::InstId {
237237
auto member_type_id = context.insts().Get(member_id).type_id();
@@ -295,7 +295,7 @@ static auto PerformInstanceBinding(Context& context,
295295
return member_id;
296296
}
297297

298-
auto PerformMemberAccess(Context& context, Parse::MemberAccessExprId node_id,
298+
auto PerformMemberAccess(Context& context, Parse::AnyMemberAccessExprId node_id,
299299
SemIR::InstId base_id, SemIR::NameId name_id)
300300
-> SemIR::InstId {
301301
// If the base is a name scope, such as a class or namespace, perform lookup
@@ -371,7 +371,7 @@ auto PerformMemberAccess(Context& context, Parse::MemberAccessExprId node_id,
371371
}
372372

373373
auto PerformCompoundMemberAccess(Context& context,
374-
Parse::MemberAccessExprId node_id,
374+
Parse::AnyMemberAccessExprId node_id,
375375
SemIR::InstId base_id,
376376
SemIR::InstId member_expr_id)
377377
-> SemIR::InstId {

toolchain/check/member_access.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ namespace Carbon::Check {
1212

1313
// Creates SemIR to perform a member access with base expression `base_id` and
1414
// member name `name_id`. Returns the result of the access.
15-
auto PerformMemberAccess(Context& context, Parse::MemberAccessExprId node_id,
15+
auto PerformMemberAccess(Context& context, Parse::AnyMemberAccessExprId node_id,
1616
SemIR::InstId base_id, SemIR::NameId name_id)
1717
-> SemIR::InstId;
1818

1919
// Creates SemIR to perform a compound member access with base expression
2020
// `base_id` and member name expression `member_expr_id`. Returns the result of
2121
// the access.
2222
auto PerformCompoundMemberAccess(Context& context,
23-
Parse::MemberAccessExprId node_id,
23+
Parse::AnyMemberAccessExprId node_id,
2424
SemIR::InstId base_id,
2525
SemIR::InstId member_expr_id) -> SemIR::InstId;
2626

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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 "llvm/ADT/STLFunctionalExtras.h"
6+
#include "toolchain/check/context.h"
7+
#include "toolchain/check/convert.h"
8+
#include "toolchain/parse/node_ids.h"
9+
#include "toolchain/sem_ir/ids.h"
10+
11+
namespace Carbon::Check {
12+
13+
auto PerformPointerDereference(
14+
Context& context, Parse::AnyPointerDeferenceExprId node_id,
15+
SemIR::InstId base_id,
16+
llvm::function_ref<auto(SemIR::TypeId not_pointer_type_id)->void>
17+
diagnose_not_pointer) -> SemIR::InstId {
18+
base_id = ConvertToValueExpr(context, base_id);
19+
auto type_id =
20+
context.GetUnqualifiedType(context.insts().Get(base_id).type_id());
21+
auto result_type_id = SemIR::TypeId::Error;
22+
if (auto pointer_type =
23+
context.types().TryGetAs<SemIR::PointerType>(type_id)) {
24+
result_type_id = pointer_type->pointee_id;
25+
} else if (type_id != SemIR::TypeId::Error) {
26+
diagnose_not_pointer(type_id);
27+
}
28+
return context.AddInst({node_id, SemIR::Deref{result_type_id, base_id}});
29+
}
30+
31+
} // namespace Carbon::Check
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
#ifndef CARBON_TOOLCHAIN_CHECK_POINTER_DEREFERENCE_H_
6+
#define CARBON_TOOLCHAIN_CHECK_POINTER_DEREFERENCE_H_
7+
8+
#include "llvm/ADT/STLFunctionalExtras.h"
9+
#include "toolchain/check/context.h"
10+
#include "toolchain/parse/node_ids.h"
11+
#include "toolchain/sem_ir/ids.h"
12+
13+
namespace Carbon::Check {
14+
15+
// Creates SemIR to perform a pointer dereference with base expression
16+
// `base_id`. Returns the result of the access.
17+
auto PerformPointerDereference(
18+
Context& context, Parse::AnyPointerDeferenceExprId node_id,
19+
SemIR::InstId base_i,
20+
llvm::function_ref<auto(SemIR::TypeId not_pointer_type_id)->void>
21+
diagnose_not_pointer) -> SemIR::InstId;
22+
23+
} // namespace Carbon::Check
24+
25+
#endif // CARBON_TOOLCHAIN_CHECK_POINTER_DEREFERENCE_H_

0 commit comments

Comments
 (0)