Skip to content

Commit

Permalink
Handle arrow operator (carbon-language#3768)
Browse files Browse the repository at this point in the history
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)`
  • Loading branch information
CJ-Johnson authored Mar 18, 2024
1 parent 4421a75 commit d0e8afc
Show file tree
Hide file tree
Showing 17 changed files with 365 additions and 89 deletions.
16 changes: 16 additions & 0 deletions toolchain/check/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ cc_library(
":import",
":interface",
":member_access",
":pointer_dereference",
"//common:check",
"//common:ostream",
"//toolchain/base:pretty_stack_trace_function",
Expand Down Expand Up @@ -212,6 +213,21 @@ cc_library(
],
)

cc_library(
name = "pointer_dereference",
srcs = ["pointer_dereference.cpp"],
hdrs = ["pointer_dereference.h"],
deps = [
":context",
"//common:check",
"//toolchain/parse:node_kind",
"//toolchain/sem_ir:ids",
"//toolchain/sem_ir:inst",
"//toolchain/sem_ir:inst_kind",
"@llvm-project//llvm:Support",
],
)

cc_library(
name = "subst",
srcs = ["subst.cpp"],
Expand Down
33 changes: 31 additions & 2 deletions toolchain/check/handle_name.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

#include "toolchain/check/context.h"
#include "toolchain/check/member_access.h"
#include "toolchain/check/pointer_dereference.h"
#include "toolchain/lex/token_kind.h"
#include "toolchain/parse/typed_nodes.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/typed_insts.h"

Expand All @@ -31,7 +31,36 @@ auto HandleMemberAccessExpr(Context& context, Parse::MemberAccessExprId node_id)
auto HandlePointerMemberAccessExpr(Context& context,
Parse::PointerMemberAccessExprId node_id)
-> bool {
return context.TODO(node_id, "HandlePointerMemberAccessExpr");
auto diagnose_not_pointer = [&context,
&node_id](SemIR::TypeId not_pointer_type_id) {
CARBON_DIAGNOSTIC(ArrowOperatorOfNonPointer, Error,
"Cannot apply `->` operator to non-pointer type `{0}`.",
SemIR::TypeId);

auto builder = context.emitter().Build(
TokenOnly(node_id), ArrowOperatorOfNonPointer, not_pointer_type_id);
builder.Emit();
};

if (context.node_stack().PeekIs<Parse::NodeKind::ParenExpr>()) {
auto member_expr_id = context.node_stack().PopExpr();
auto base_id = context.node_stack().PopExpr();
auto deref_base_id = PerformPointerDereference(context, node_id, base_id,
diagnose_not_pointer);
auto member_id = PerformCompoundMemberAccess(context, node_id,
deref_base_id, member_expr_id);
context.node_stack().Push(node_id, member_id);
} else {
SemIR::NameId name_id = context.node_stack().PopName();
auto base_id = context.node_stack().PopExpr();
auto deref_base_id = PerformPointerDereference(context, node_id, base_id,
diagnose_not_pointer);
auto member_id =
PerformMemberAccess(context, node_id, deref_base_id, name_id);
context.node_stack().Push(node_id, member_id);
}

return true;
}

static auto GetIdentifierAsName(Context& context, Parse::NodeId node_id)
Expand Down
51 changes: 27 additions & 24 deletions toolchain/check/handle_operator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/check/pointer_dereference.h"
#include "toolchain/diagnostics/diagnostic_emitter.h"

namespace Carbon::Check {

Expand Down Expand Up @@ -281,30 +283,31 @@ auto HandlePrefixOperatorPlusPlus(Context& context,

auto HandlePrefixOperatorStar(Context& context,
Parse::PrefixOperatorStarId node_id) -> bool {
auto value_id = context.node_stack().PopExpr();
value_id = ConvertToValueExpr(context, value_id);
auto type_id =
context.GetUnqualifiedType(context.insts().Get(value_id).type_id());
auto result_type_id = SemIR::TypeId::Error;
if (auto pointer_type =
context.types().TryGetAs<SemIR::PointerType>(type_id)) {
result_type_id = pointer_type->pointee_id;
} else if (type_id != SemIR::TypeId::Error) {
CARBON_DIAGNOSTIC(DerefOfNonPointer, Error,
"Cannot dereference operand of non-pointer type `{0}`.",
SemIR::TypeId);
auto builder =
context.emitter().Build(TokenOnly(node_id), DerefOfNonPointer, type_id);
// TODO: Check for any facet here, rather than only a type.
if (type_id == SemIR::TypeId::TypeType) {
CARBON_DIAGNOSTIC(
DerefOfType, Note,
"To form a pointer type, write the `*` after the pointee type.");
builder.Note(TokenOnly(node_id), DerefOfType);
}
builder.Emit();
}
context.AddInstAndPush({node_id, SemIR::Deref{result_type_id, value_id}});
auto base_id = context.node_stack().PopExpr();

auto deref_base_id = PerformPointerDereference(
context, node_id, base_id,
[&context, &node_id](SemIR::TypeId not_pointer_type_id) {
CARBON_DIAGNOSTIC(
DerefOfNonPointer, Error,
"Cannot dereference operand of non-pointer type `{0}`.",
SemIR::TypeId);

auto builder = context.emitter().Build(
TokenOnly(node_id), DerefOfNonPointer, not_pointer_type_id);

// TODO: Check for any facet here, rather than only a type.
if (not_pointer_type_id == SemIR::TypeId::TypeType) {
CARBON_DIAGNOSTIC(
DerefOfType, Note,
"To form a pointer type, write the `*` after the pointee type.");
builder.Note(TokenOnly(node_id), DerefOfType);
}

builder.Emit();
});

context.node_stack().Push(node_id, deref_base_id);
return true;
}

Expand Down
8 changes: 4 additions & 4 deletions toolchain/check/member_access.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ static auto PerformImplLookup(Context& context, SemIR::ConstantId type_const_id,
// impl lookup if necessary. If the scope is invalid, assume an error has
// already been diagnosed, and return BuiltinError.
static auto LookupMemberNameInScope(Context& context,
Parse::MemberAccessExprId node_id,
Parse::AnyMemberAccessExprId node_id,
SemIR::InstId /*base_id*/,
SemIR::NameId name_id,
SemIR::ConstantId name_scope_const_id,
Expand Down Expand Up @@ -231,7 +231,7 @@ static auto LookupMemberNameInScope(Context& context,
// field, forms a class member access. If the found member is an instance
// method, forms a bound method. Otherwise, the member is returned unchanged.
static auto PerformInstanceBinding(Context& context,
Parse::MemberAccessExprId node_id,
Parse::AnyMemberAccessExprId node_id,
SemIR::InstId base_id,
SemIR::InstId member_id) -> SemIR::InstId {
auto member_type_id = context.insts().Get(member_id).type_id();
Expand Down Expand Up @@ -295,7 +295,7 @@ static auto PerformInstanceBinding(Context& context,
return member_id;
}

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

auto PerformCompoundMemberAccess(Context& context,
Parse::MemberAccessExprId node_id,
Parse::AnyMemberAccessExprId node_id,
SemIR::InstId base_id,
SemIR::InstId member_expr_id)
-> SemIR::InstId {
Expand Down
4 changes: 2 additions & 2 deletions toolchain/check/member_access.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ namespace Carbon::Check {

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

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

Expand Down
31 changes: 31 additions & 0 deletions toolchain/check/pointer_dereference.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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

#include "llvm/ADT/STLFunctionalExtras.h"
#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/parse/node_ids.h"
#include "toolchain/sem_ir/ids.h"

namespace Carbon::Check {

auto PerformPointerDereference(
Context& context, Parse::AnyPointerDeferenceExprId node_id,
SemIR::InstId base_id,
llvm::function_ref<auto(SemIR::TypeId not_pointer_type_id)->void>
diagnose_not_pointer) -> SemIR::InstId {
base_id = ConvertToValueExpr(context, base_id);
auto type_id =
context.GetUnqualifiedType(context.insts().Get(base_id).type_id());
auto result_type_id = SemIR::TypeId::Error;
if (auto pointer_type =
context.types().TryGetAs<SemIR::PointerType>(type_id)) {
result_type_id = pointer_type->pointee_id;
} else if (type_id != SemIR::TypeId::Error) {
diagnose_not_pointer(type_id);
}
return context.AddInst({node_id, SemIR::Deref{result_type_id, base_id}});
}

} // namespace Carbon::Check
25 changes: 25 additions & 0 deletions toolchain/check/pointer_dereference.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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

#ifndef CARBON_TOOLCHAIN_CHECK_POINTER_DEREFERENCE_H_
#define CARBON_TOOLCHAIN_CHECK_POINTER_DEREFERENCE_H_

#include "llvm/ADT/STLFunctionalExtras.h"
#include "toolchain/check/context.h"
#include "toolchain/parse/node_ids.h"
#include "toolchain/sem_ir/ids.h"

namespace Carbon::Check {

// Creates SemIR to perform a pointer dereference with base expression
// `base_id`. Returns the result of the access.
auto PerformPointerDereference(
Context& context, Parse::AnyPointerDeferenceExprId node_id,
SemIR::InstId base_i,
llvm::function_ref<auto(SemIR::TypeId not_pointer_type_id)->void>
diagnose_not_pointer) -> SemIR::InstId;

} // namespace Carbon::Check

#endif // CARBON_TOOLCHAIN_CHECK_POINTER_DEREFERENCE_H_
Loading

0 comments on commit d0e8afc

Please sign in to comment.