// 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 "toolchain/check/context.h" #include "toolchain/check/generic.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/member_access.h" #include "toolchain/check/name_component.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/name_ref.h" #include "toolchain/check/pointer_dereference.h" #include "toolchain/check/type.h" #include "toolchain/lex/token_kind.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::MemberAccessExprId node_id) -> bool { auto node_kind = context.node_stack().PeekNodeKind(); if (node_kind == Parse::NodeKind::ParenExpr) { auto member_expr_id = context.node_stack().PopExpr(); auto base_id = context.node_stack().PopExpr(); auto member_id = PerformCompoundMemberAccess(context, node_id, base_id, member_expr_id); context.node_stack().Push(node_id, member_id); } else if (node_kind == Parse::NodeKind::IntLiteral) { auto index_inst_id = context.node_stack().PopExpr(); auto tuple_inst_id = context.node_stack().PopExpr(); auto tuple_value_inst_id = PerformTupleAccess(context, node_id, tuple_inst_id, index_inst_id); context.node_stack().Push(node_id, tuple_value_inst_id); } else { SemIR::NameId name_id = context.node_stack().PopName(); auto base_id = context.node_stack().PopExpr(); auto member_id = PerformMemberAccess(context, node_id, base_id, name_id); context.node_stack().Push(node_id, member_id); } return true; } auto HandleParseNode(Context& context, Parse::PointerMemberAccessExprId node_id) -> bool { auto diagnose_not_pointer = [&context, &node_id](SemIR::TypeId not_pointer_type_id) { // TODO: Pass in the expression we're trying to dereference to produce a // better diagnostic. CARBON_DIAGNOSTIC(ArrowOperatorOfNonPointer, Error, "cannot apply `->` operator to non-pointer type {0}", SemIR::TypeId); auto builder = context.emitter().Build(LocIdForDiagnostics::TokenOnly(node_id), ArrowOperatorOfNonPointer, not_pointer_type_id); builder.Emit(); }; auto node_kind = context.node_stack().PeekNodeKind(); if (node_kind == 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 if (node_kind == Parse::NodeKind::IntLiteral) { auto index_inst_id = context.node_stack().PopExpr(); auto tuple_pointer_inst_id = context.node_stack().PopExpr(); auto tuple_inst_id = PerformPointerDereference( context, node_id, tuple_pointer_inst_id, diagnose_not_pointer); auto tuple_value_inst_id = PerformTupleAccess(context, node_id, tuple_inst_id, index_inst_id); context.node_stack().Push(node_id, tuple_value_inst_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; } // Returns the `NameId` for an identifier node. static auto GetIdentifierAsNameId( Context& context, Parse::NodeIdOneOf node_id) -> SemIR::NameId { CARBON_CHECK(!context.parse_tree().node_has_error(node_id), "TODO: Support checking error parse nodes"); auto token = context.parse_tree().node_token(node_id); return SemIR::NameId::ForIdentifier(context.tokens().GetIdentifier(token)); } // Handle a name that is used as an expression by performing unqualified name // lookup. static auto HandleNameAsExpr(Context& context, Parse::NodeId node_id, SemIR::NameId name_id) -> SemIR::InstId { auto result = LookupUnqualifiedName(context, node_id, name_id); return BuildNameRef(context, node_id, name_id, result.scope_result.target_inst_id(), result.specific_id); } auto HandleParseNode(Context& context, Parse::IdentifierNameNotBeforeSignatureId node_id) -> bool { // The parent is responsible for binding the name. context.node_stack().Push(node_id, GetIdentifierAsNameId(context, node_id)); return true; } auto HandleParseNode(Context& context, Parse::IdentifierNameMaybeBeforeSignatureId node_id) -> bool { // Push a pattern block stack entry to handle the parameter pattern. context.pattern_block_stack().Push(); context.full_pattern_stack().PushParameterizedDecl(); // The parent is responsible for binding the name. context.node_stack().Push(node_id, GetIdentifierAsNameId(context, node_id)); return true; } auto HandleParseNode(Context& context, Parse::IdentifierNameExprId node_id) -> bool { auto name_id = GetIdentifierAsNameId(context, node_id); context.node_stack().Push(node_id, HandleNameAsExpr(context, node_id, name_id)); return true; } auto HandleParseNode(Context& context, Parse::BaseNameId node_id) -> bool { context.node_stack().Push(node_id, SemIR::NameId::Base); return true; } auto HandleParseNode(Context& context, Parse::SelfTypeNameId node_id) -> bool { context.node_stack().Push(node_id, SemIR::NameId::SelfType); return true; } auto HandleParseNode(Context& context, Parse::SelfTypeNameExprId node_id) -> bool { context.node_stack().Push( node_id, HandleNameAsExpr(context, node_id, SemIR::NameId::SelfType)); return true; } auto HandleParseNode(Context& context, Parse::SelfValueNameId node_id) -> bool { context.node_stack().Push(node_id, SemIR::NameId::SelfValue); return true; } auto HandleParseNode(Context& context, Parse::SelfValueNameExprId node_id) -> bool { context.node_stack().Push( node_id, HandleNameAsExpr(context, node_id, SemIR::NameId::SelfValue)); return true; } // Common logic for name qualifiers. static auto ApplyNameQualifier(Context& context) -> bool { context.decl_name_stack().ApplyNameQualifier(PopNameComponent(context)); return true; } auto HandleParseNode(Context& context, Parse::IdentifierNameQualifierWithParamsId /*node_id*/) -> bool { return ApplyNameQualifier(context); } auto HandleParseNode(Context& context, Parse::IdentifierNameQualifierWithoutParamsId /*node_id*/) -> bool { return ApplyNameQualifier(context); } auto HandleParseNode(Context& context, Parse::DesignatorExprId node_id) -> bool { SemIR::NameId name_id = context.node_stack().PopName(); if (name_id == SemIR::NameId::SelfType) { // Look up `.Self`. SemIR::InstId period_self_id = HandleNameAsExpr(context, node_id, SemIR::NameId::PeriodSelf); context.node_stack().Push(node_id, period_self_id); } else { // Otherwise this is `.Member`, so look up `.Self` and then `Member` in // `.Self`. SemIR::InstId period_self_id = SemIR::InstId::None; { // TODO: Instead of annotating the diagnostic, should change // `HandleNameAsExpr` to optionally allow us to produce the diagnostic // instead so we can generate a "name `.Self` implicitly referenced by // designated expression, but not found" diagnostic instead of adding a // note to the current "name `.Self` not found" message. Diagnostics::AnnotationScope annotate_diagnostics( &context.emitter(), [&](auto& builder) { CARBON_DIAGNOSTIC( NoPeriodSelfForDesignator, Note, "designator may only be used when `.Self` is in scope"); builder.Note(SemIR::LocId::None, NoPeriodSelfForDesignator); }); period_self_id = HandleNameAsExpr(context, node_id, SemIR::NameId::PeriodSelf); } auto member_id = PerformMemberAccess(context, node_id, period_self_id, name_id); context.node_stack().Push(node_id, member_id); } return true; } auto HandleParseNode(Context& context, Parse::PackageExprId node_id) -> bool { AddInstAndPush( context, node_id, {.type_id = GetSingletonType(context, SemIR::NamespaceType::TypeInstId), .name_id = SemIR::NameId::PackageKeyword, .value_id = SemIR::Namespace::PackageInstId}); return true; } auto HandleParseNode(Context& context, Parse::CoreNameExprId node_id) -> bool { // TODO: Unqualified lookup will never find anything; perform lookup directly // into file scope. context.node_stack().Push( node_id, HandleNameAsExpr(context, node_id, SemIR::NameId::Core)); return true; } auto HandleParseNode(Context& context, Parse::CppNameExprId node_id) -> bool { // TODO: Unqualified lookup will never find anything; perform lookup directly // into file scope. context.node_stack().Push( node_id, HandleNameAsExpr(context, node_id, SemIR::NameId::Cpp)); return true; } } // namespace Carbon::Check