mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 21:10:12 +01:00
Parse and check support for compound member access. (#3790)
On the parsing side, we treat `a.(b)` as a member access whose second operand is a `ParenExpr` rather than a `MemberName`. A new node category is added for the union of `MemberName` and `ParenExpr` to support this. Checking is mostly reusing the same pieces we already have for simple member access. Compound member access is in most ways a simplified form of simple member access because it doesn't need to do any lookup.
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/check/member_access.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"
|
||||
|
||||
@@ -12,10 +13,18 @@ namespace Carbon::Check {
|
||||
|
||||
auto HandleMemberAccessExpr(Context& context, Parse::MemberAccessExprId node_id)
|
||||
-> bool {
|
||||
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);
|
||||
if (context.node_stack().PeekIs<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 {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -269,6 +269,10 @@ static auto PerformInstanceBinding(Context& context,
|
||||
context.GetBuiltinType(SemIR::BuiltinKind::FunctionType)) {
|
||||
// Find the named function and check whether it's an instance method.
|
||||
auto function_name_id = context.constant_values().Get(member_id);
|
||||
if (function_name_id == SemIR::ConstantId::Error) {
|
||||
return SemIR::InstId::BuiltinError;
|
||||
}
|
||||
|
||||
CARBON_CHECK(function_name_id.is_constant())
|
||||
<< "Non-constant value " << context.insts().Get(member_id)
|
||||
<< " of function type";
|
||||
@@ -366,4 +370,42 @@ auto PerformMemberAccess(Context& context, Parse::MemberAccessExprId node_id,
|
||||
return member_id;
|
||||
}
|
||||
|
||||
auto PerformCompoundMemberAccess(Context& context,
|
||||
Parse::MemberAccessExprId node_id,
|
||||
SemIR::InstId base_id,
|
||||
SemIR::InstId member_expr_id)
|
||||
-> SemIR::InstId {
|
||||
// Materialize a temporary for the base expression if necessary.
|
||||
base_id = ConvertToValueOrRefExpr(context, base_id);
|
||||
auto base_type_id = context.insts().Get(base_id).type_id();
|
||||
auto base_type_const_id = context.types().GetConstantId(base_type_id);
|
||||
|
||||
auto member_id = member_expr_id;
|
||||
auto member = context.insts().Get(member_id);
|
||||
|
||||
// If the member expression names an associated entity, impl lookup is always
|
||||
// performed using the type of the base expression.
|
||||
if (auto assoc_type = context.types().TryGetAs<SemIR::AssociatedEntityType>(
|
||||
member.type_id())) {
|
||||
member_id =
|
||||
PerformImplLookup(context, base_type_const_id, *assoc_type, member_id);
|
||||
}
|
||||
|
||||
// Perform instance binding if we found an instance member.
|
||||
member_id = PerformInstanceBinding(context, node_id, base_id, member_id);
|
||||
|
||||
// If we didn't perform impl lookup or instance binding, that's an error
|
||||
// because the base expression is not used for anything.
|
||||
if (member_id == member_expr_id) {
|
||||
CARBON_DIAGNOSTIC(CompoundMemberAccessDoesNotUseBase, Error,
|
||||
"Member name of type `{0}` in compound member access is "
|
||||
"not an instance member or an interface member.",
|
||||
SemIR::TypeId);
|
||||
context.emitter().Emit(node_id, CompoundMemberAccessDoesNotUseBase,
|
||||
member.type_id());
|
||||
}
|
||||
|
||||
return member_id;
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
@@ -16,6 +16,14 @@ auto PerformMemberAccess(Context& context, Parse::MemberAccessExprId 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,
|
||||
SemIR::InstId base_id,
|
||||
SemIR::InstId member_expr_id) -> SemIR::InstId;
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_CHECK_MEMBER_ACCESS_H_
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
// 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
|
||||
|
||||
base class Base {
|
||||
var a: i32;
|
||||
var b: i32;
|
||||
var c: i32;
|
||||
}
|
||||
|
||||
class Derived {
|
||||
extend base: Base;
|
||||
|
||||
var d: i32;
|
||||
var e: i32;
|
||||
}
|
||||
|
||||
fn AccessDerived(d: Derived) -> i32 {
|
||||
return d.(Derived.d);
|
||||
}
|
||||
|
||||
fn AccessBase(d: Derived) -> i32 {
|
||||
return d.(Base.b);
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- compound_field.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %Base: type = class_type @Base [template]
|
||||
// CHECK:STDOUT: %.1: type = unbound_element_type Base, i32 [template]
|
||||
// CHECK:STDOUT: %.2: type = struct_type {.a: i32, .b: i32, .c: i32} [template]
|
||||
// CHECK:STDOUT: %Derived: type = class_type @Derived [template]
|
||||
// CHECK:STDOUT: %.3: type = ptr_type {.a: i32, .b: i32, .c: i32} [template]
|
||||
// CHECK:STDOUT: %.4: type = unbound_element_type Derived, Base [template]
|
||||
// CHECK:STDOUT: %.5: type = unbound_element_type Derived, i32 [template]
|
||||
// CHECK:STDOUT: %.6: type = struct_type {.base: Base, .d: i32, .e: i32} [template]
|
||||
// CHECK:STDOUT: %.7: type = struct_type {.base: {.a: i32, .b: i32, .c: i32}*, .d: i32, .e: i32} [template]
|
||||
// CHECK:STDOUT: %.8: type = ptr_type {.base: {.a: i32, .b: i32, .c: i32}*, .d: i32, .e: i32} [template]
|
||||
// CHECK:STDOUT: %.9: type = ptr_type {.base: Base, .d: i32, .e: i32} [template]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace [template] {
|
||||
// CHECK:STDOUT: .Base = %Base.decl
|
||||
// CHECK:STDOUT: .Derived = %Derived.decl
|
||||
// CHECK:STDOUT: .AccessDerived = %AccessDerived
|
||||
// CHECK:STDOUT: .AccessBase = %AccessBase
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Base.decl: type = class_decl @Base [template = constants.%Base] {}
|
||||
// CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [template = constants.%Derived] {}
|
||||
// CHECK:STDOUT: %AccessDerived: <function> = fn_decl @AccessDerived [template] {
|
||||
// CHECK:STDOUT: %Derived.ref.loc20: type = name_ref Derived, %Derived.decl [template = constants.%Derived]
|
||||
// CHECK:STDOUT: %d.loc20_18.1: Derived = param d
|
||||
// CHECK:STDOUT: @AccessDerived.%d: Derived = bind_name d, %d.loc20_18.1
|
||||
// CHECK:STDOUT: %return.var.loc20: ref i32 = var <return slot>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %AccessBase: <function> = fn_decl @AccessBase [template] {
|
||||
// CHECK:STDOUT: %Derived.ref.loc24: type = name_ref Derived, %Derived.decl [template = constants.%Derived]
|
||||
// CHECK:STDOUT: %d.loc24_15.1: Derived = param d
|
||||
// CHECK:STDOUT: @AccessBase.%d: Derived = bind_name d, %d.loc24_15.1
|
||||
// CHECK:STDOUT: %return.var.loc24: ref i32 = var <return slot>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Base {
|
||||
// CHECK:STDOUT: %.loc8: <unbound element of class Base> = field_decl a, element0 [template]
|
||||
// CHECK:STDOUT: %.loc9: <unbound element of class Base> = field_decl b, element1 [template]
|
||||
// CHECK:STDOUT: %.loc10: <unbound element of class Base> = field_decl c, element2 [template]
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .Self = constants.%Base
|
||||
// CHECK:STDOUT: .a = %.loc8
|
||||
// CHECK:STDOUT: .b = %.loc9
|
||||
// CHECK:STDOUT: .c = %.loc10
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Derived {
|
||||
// CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [template = constants.%Base]
|
||||
// CHECK:STDOUT: %.loc14: <unbound element of class Derived> = base_decl Base, element0 [template]
|
||||
// CHECK:STDOUT: %.loc16: <unbound element of class Derived> = field_decl d, element1 [template]
|
||||
// CHECK:STDOUT: %.loc17: <unbound element of class Derived> = field_decl e, element2 [template]
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .Self = constants.%Derived
|
||||
// CHECK:STDOUT: .base = %.loc14
|
||||
// CHECK:STDOUT: .d = %.loc16
|
||||
// CHECK:STDOUT: .e = %.loc17
|
||||
// CHECK:STDOUT: extend name_scope1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @AccessDerived(%d: Derived) -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %d.ref.loc21_10: Derived = name_ref d, %d
|
||||
// CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [template = constants.%Derived]
|
||||
// CHECK:STDOUT: %d.ref.loc21_20: <unbound element of class Derived> = name_ref d, @Derived.%.loc16 [template = @Derived.%.loc16]
|
||||
// CHECK:STDOUT: %.loc21_11.1: ref i32 = class_element_access %d.ref.loc21_10, element1
|
||||
// CHECK:STDOUT: %.loc21_11.2: i32 = bind_value %.loc21_11.1
|
||||
// CHECK:STDOUT: return %.loc21_11.2
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @AccessBase(%d: Derived) -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %d.ref: Derived = name_ref d, %d
|
||||
// CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [template = constants.%Base]
|
||||
// CHECK:STDOUT: %b.ref: <unbound element of class Base> = name_ref b, @Base.%.loc9 [template = @Base.%.loc9]
|
||||
// CHECK:STDOUT: %.loc25_11.1: ref Base = class_element_access %d.ref, element0
|
||||
// CHECK:STDOUT: %.loc25_10: ref Base = converted %d.ref, %.loc25_11.1
|
||||
// CHECK:STDOUT: %.loc25_11.2: ref i32 = class_element_access %.loc25_10, element1
|
||||
// CHECK:STDOUT: %.loc25_11.3: i32 = bind_value %.loc25_11.2
|
||||
// CHECK:STDOUT: return %.loc25_11.3
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -0,0 +1,75 @@
|
||||
// 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
|
||||
|
||||
class A {
|
||||
var a: i32;
|
||||
}
|
||||
|
||||
class B {
|
||||
var b: i32;
|
||||
}
|
||||
|
||||
fn AccessBInA(a: A) -> i32 {
|
||||
// CHECK:STDERR: fail_compound_type_mismatch.carbon:[[@LINE+3]]:10: ERROR: Cannot implicitly convert from `A` to `B`.
|
||||
// CHECK:STDERR: return a.(B.b);
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
return a.(B.b);
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- fail_compound_type_mismatch.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %A: type = class_type @A [template]
|
||||
// CHECK:STDOUT: %.1: type = unbound_element_type A, i32 [template]
|
||||
// CHECK:STDOUT: %.2: type = struct_type {.a: i32} [template]
|
||||
// CHECK:STDOUT: %B: type = class_type @B [template]
|
||||
// CHECK:STDOUT: %.3: type = unbound_element_type B, i32 [template]
|
||||
// CHECK:STDOUT: %.4: type = struct_type {.b: i32} [template]
|
||||
// CHECK:STDOUT: %.5: type = ptr_type {.a: i32} [template]
|
||||
// CHECK:STDOUT: %.6: type = ptr_type {.b: i32} [template]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace [template] {
|
||||
// CHECK:STDOUT: .A = %A.decl
|
||||
// CHECK:STDOUT: .B = %B.decl
|
||||
// CHECK:STDOUT: .AccessBInA = %AccessBInA
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %A.decl: type = class_decl @A [template = constants.%A] {}
|
||||
// CHECK:STDOUT: %B.decl: type = class_decl @B [template = constants.%B] {}
|
||||
// CHECK:STDOUT: %AccessBInA: <function> = fn_decl @AccessBInA [template] {
|
||||
// CHECK:STDOUT: %A.ref: type = name_ref A, %A.decl [template = constants.%A]
|
||||
// CHECK:STDOUT: %a.loc15_15.1: A = param a
|
||||
// CHECK:STDOUT: @AccessBInA.%a: A = bind_name a, %a.loc15_15.1
|
||||
// CHECK:STDOUT: %return.var: ref i32 = var <return slot>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @A {
|
||||
// CHECK:STDOUT: %.loc8: <unbound element of class A> = field_decl a, element0 [template]
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .Self = constants.%A
|
||||
// CHECK:STDOUT: .a = %.loc8
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @B {
|
||||
// CHECK:STDOUT: %.loc12: <unbound element of class B> = field_decl b, element0 [template]
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .Self = constants.%B
|
||||
// CHECK:STDOUT: .b = %.loc12
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @AccessBInA(%a: A) -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %a.ref: A = name_ref a, %a
|
||||
// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [template = constants.%B]
|
||||
// CHECK:STDOUT: %b.ref: <unbound element of class B> = name_ref b, @B.%.loc12 [template = @B.%.loc12]
|
||||
// CHECK:STDOUT: %.loc19: i32 = class_element_access <error>, element0 [template = <error>]
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
// 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
|
||||
|
||||
interface Simple {
|
||||
fn F();
|
||||
fn G[self: Self]();
|
||||
}
|
||||
|
||||
impl i32 as Simple {
|
||||
fn F();
|
||||
fn G[self: i32]();
|
||||
}
|
||||
|
||||
fn NonInstanceCall(n: i32) {
|
||||
n.(Simple.F)();
|
||||
}
|
||||
|
||||
fn InstanceCall(n: i32) {
|
||||
n.(Simple.G)();
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- compound.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %.1: type = interface_type @Simple [template]
|
||||
// CHECK:STDOUT: %.2: type = assoc_entity_type @Simple, <function> [template]
|
||||
// CHECK:STDOUT: %.3: <associated <function> in Simple> = assoc_entity element0, @Simple.%F [template]
|
||||
// CHECK:STDOUT: %.4: <associated <function> in Simple> = assoc_entity element1, @Simple.%G [template]
|
||||
// CHECK:STDOUT: %.5: <witness> = interface_witness (@impl.%F, @impl.%G) [template]
|
||||
// CHECK:STDOUT: %.6: type = tuple_type () [template]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace [template] {
|
||||
// CHECK:STDOUT: .Simple = %Simple.decl
|
||||
// CHECK:STDOUT: .NonInstanceCall = %NonInstanceCall
|
||||
// CHECK:STDOUT: .InstanceCall = %InstanceCall
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Simple.decl: type = interface_decl @Simple [template = constants.%.1] {}
|
||||
// CHECK:STDOUT: impl_decl @impl {
|
||||
// CHECK:STDOUT: %Simple.ref: type = name_ref Simple, %Simple.decl [template = constants.%.1]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %NonInstanceCall: <function> = fn_decl @NonInstanceCall [template] {
|
||||
// CHECK:STDOUT: %n.loc17_20.1: i32 = param n
|
||||
// CHECK:STDOUT: @NonInstanceCall.%n: i32 = bind_name n, %n.loc17_20.1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %InstanceCall: <function> = fn_decl @InstanceCall [template] {
|
||||
// CHECK:STDOUT: %n.loc21_17.1: i32 = param n
|
||||
// CHECK:STDOUT: @InstanceCall.%n: i32 = bind_name n, %n.loc21_17.1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: interface @Simple {
|
||||
// CHECK:STDOUT: %Self: Simple = bind_symbolic_name Self [symbolic]
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F.1 [template] {}
|
||||
// CHECK:STDOUT: %.loc8: <associated <function> in Simple> = assoc_entity element0, %F [template = constants.%.3]
|
||||
// CHECK:STDOUT: %G: <function> = fn_decl @G.1 [template] {
|
||||
// CHECK:STDOUT: %Self.ref: Simple = name_ref Self, %Self [symbolic = %Self]
|
||||
// CHECK:STDOUT: %.loc9_14.1: type = facet_type_access %Self.ref [symbolic = %Self]
|
||||
// CHECK:STDOUT: %.loc9_14.2: type = converted %Self.ref, %.loc9_14.1 [symbolic = %Self]
|
||||
// CHECK:STDOUT: %self.loc9_8.1: Self = param self
|
||||
// CHECK:STDOUT: %self.loc9_8.2: Self = bind_name self, %self.loc9_8.1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.loc9_21: <associated <function> in Simple> = assoc_entity element1, %G [template = constants.%.4]
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .Self = %Self
|
||||
// CHECK:STDOUT: .F = %.loc8
|
||||
// CHECK:STDOUT: .G = %.loc9_21
|
||||
// CHECK:STDOUT: witness = (%F, %G)
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: impl @impl: i32 as Simple {
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F.2 [template] {}
|
||||
// CHECK:STDOUT: %G: <function> = fn_decl @G.2 [template] {
|
||||
// CHECK:STDOUT: %self.loc14_8.1: i32 = param self
|
||||
// CHECK:STDOUT: %self.loc14_8.2: i32 = bind_name self, %self.loc14_8.1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.1: <witness> = interface_witness (%F, %G) [template = constants.%.5]
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .F = %F
|
||||
// CHECK:STDOUT: .G = %G
|
||||
// CHECK:STDOUT: witness = %.1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F.1();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G.1[@Simple.%self.loc9_8.2: Self]();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F.2();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G.2[@impl.%self.loc14_8.2: i32]();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @NonInstanceCall(%n: i32) {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %n.ref: i32 = name_ref n, %n
|
||||
// CHECK:STDOUT: %Simple.ref: type = name_ref Simple, file.%Simple.decl [template = constants.%.1]
|
||||
// CHECK:STDOUT: %F.ref: <associated <function> in Simple> = name_ref F, @Simple.%.loc8 [template = constants.%.3]
|
||||
// CHECK:STDOUT: %.1: <function> = interface_witness_access @impl.%.1, element0 [template = @impl.%F]
|
||||
// CHECK:STDOUT: %.loc18: init () = call %.1()
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @InstanceCall(%n: i32) {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %n.ref: i32 = name_ref n, %n
|
||||
// CHECK:STDOUT: %Simple.ref: type = name_ref Simple, file.%Simple.decl [template = constants.%.1]
|
||||
// CHECK:STDOUT: %G.ref: <associated <function> in Simple> = name_ref G, @Simple.%.loc9_21 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.1: <function> = interface_witness_access @impl.%.1, element1 [template = @impl.%G]
|
||||
// CHECK:STDOUT: %.loc22_4: <bound method> = bound_method %n.ref, %.1
|
||||
// CHECK:STDOUT: %.loc22_15: init () = call %.loc22_4(%n.ref)
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -0,0 +1,87 @@
|
||||
// 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
|
||||
|
||||
interface Simple {
|
||||
fn G[self: Self]();
|
||||
}
|
||||
|
||||
impl i32 as Simple {
|
||||
// CHECK:STDERR: fail_call_invalid.carbon:[[@LINE+3]]:14: ERROR: Name `Undeclared` not found.
|
||||
// CHECK:STDERR: fn G[self: Undeclared]();
|
||||
// CHECK:STDERR: ^~~~~~~~~~
|
||||
fn G[self: Undeclared]();
|
||||
}
|
||||
|
||||
fn InstanceCall(n: i32) {
|
||||
n.(Simple.G)();
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- fail_call_invalid.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %.1: type = interface_type @Simple [template]
|
||||
// CHECK:STDOUT: %.2: type = assoc_entity_type @Simple, <function> [template]
|
||||
// CHECK:STDOUT: %.3: <associated <function> in Simple> = assoc_entity element0, @Simple.%G [template]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace [template] {
|
||||
// CHECK:STDOUT: .Simple = %Simple.decl
|
||||
// CHECK:STDOUT: .InstanceCall = %InstanceCall
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Simple.decl: type = interface_decl @Simple [template = constants.%.1] {}
|
||||
// CHECK:STDOUT: impl_decl @impl {
|
||||
// CHECK:STDOUT: %Simple.ref: type = name_ref Simple, %Simple.decl [template = constants.%.1]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %InstanceCall: <function> = fn_decl @InstanceCall [template] {
|
||||
// CHECK:STDOUT: %n.loc18_17.1: i32 = param n
|
||||
// CHECK:STDOUT: @InstanceCall.%n: i32 = bind_name n, %n.loc18_17.1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: interface @Simple {
|
||||
// CHECK:STDOUT: %Self: Simple = bind_symbolic_name Self [symbolic]
|
||||
// CHECK:STDOUT: %G: <function> = fn_decl @G.1 [template] {
|
||||
// CHECK:STDOUT: %Self.ref: Simple = name_ref Self, %Self [symbolic = %Self]
|
||||
// CHECK:STDOUT: %.loc8_14.1: type = facet_type_access %Self.ref [symbolic = %Self]
|
||||
// CHECK:STDOUT: %.loc8_14.2: type = converted %Self.ref, %.loc8_14.1 [symbolic = %Self]
|
||||
// CHECK:STDOUT: %self.loc8_8.1: Self = param self
|
||||
// CHECK:STDOUT: %self.loc8_8.2: Self = bind_name self, %self.loc8_8.1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.loc8_21: <associated <function> in Simple> = assoc_entity element0, %G [template = constants.%.3]
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .Self = %Self
|
||||
// CHECK:STDOUT: .G = %.loc8_21
|
||||
// CHECK:STDOUT: witness = (%G)
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: impl @impl: i32 as Simple {
|
||||
// CHECK:STDOUT: %G: <function> = fn_decl @G.2 [template] {
|
||||
// CHECK:STDOUT: %Undeclared.ref: <error> = name_ref Undeclared, <error> [template = <error>]
|
||||
// CHECK:STDOUT: %self.loc15_8.1: <error> = param self
|
||||
// CHECK:STDOUT: %self.loc15_8.2: <error> = bind_name self, %self.loc15_8.1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.1: <witness> = interface_witness (<error>) [template = <error>]
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .G = %G
|
||||
// CHECK:STDOUT: witness = %.1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G.1[@Simple.%self.loc8_8.2: Self]();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G.2[@impl.%self.loc15_8.2: <error>]();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @InstanceCall(%n: i32) {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %n.ref: i32 = name_ref n, %n
|
||||
// CHECK:STDOUT: %Simple.ref: type = name_ref Simple, file.%Simple.decl [template = constants.%.1]
|
||||
// CHECK:STDOUT: %G.ref: <associated <function> in Simple> = name_ref G, @Simple.%.loc8_21 [template = constants.%.3]
|
||||
// CHECK:STDOUT: %.1: <function> = interface_witness_access @impl.%.1, element0 [template = <error>]
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -0,0 +1,63 @@
|
||||
// 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 { return 0; }
|
||||
|
||||
fn Main() {
|
||||
var a: i32 = 3;
|
||||
// CHECK:STDERR: fail_redundant_compound_access.carbon:[[@LINE+3]]:7: ERROR: Member name of type `i32` in compound member access is not an instance member or an interface member.
|
||||
// CHECK:STDERR: a = a.(a);
|
||||
// CHECK:STDERR: ^~~~~
|
||||
a = a.(a);
|
||||
// CHECK:STDERR: fail_redundant_compound_access.carbon:[[@LINE+3]]:7: ERROR: Member name of type `<function>` in compound member access is not an instance member or an interface member.
|
||||
// CHECK:STDERR: a = a.(F)();
|
||||
// CHECK:STDERR: ^~~~~
|
||||
a = a.(F)();
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- fail_redundant_compound_access.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %.1: i32 = int_literal 0 [template]
|
||||
// CHECK:STDOUT: %.2: i32 = int_literal 3 [template]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace [template] {
|
||||
// CHECK:STDOUT: .F = %F
|
||||
// CHECK:STDOUT: .Main = %Main
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F [template] {
|
||||
// CHECK:STDOUT: %return.var: ref i32 = var <return slot>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Main: <function> = fn_decl @Main [template] {}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %.loc7: i32 = int_literal 0 [template = constants.%.1]
|
||||
// CHECK:STDOUT: return %.loc7
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Main() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %a.var: ref i32 = var a
|
||||
// CHECK:STDOUT: %a: ref i32 = bind_name a, %a.var
|
||||
// CHECK:STDOUT: %.loc10: i32 = int_literal 3 [template = constants.%.2]
|
||||
// CHECK:STDOUT: assign %a.var, %.loc10
|
||||
// CHECK:STDOUT: %a.ref.loc14_3: ref i32 = name_ref a, %a
|
||||
// CHECK:STDOUT: %a.ref.loc14_7: ref i32 = name_ref a, %a
|
||||
// CHECK:STDOUT: %a.ref.loc14_10: ref i32 = name_ref a, %a
|
||||
// CHECK:STDOUT: %.loc14: i32 = bind_value %a.ref.loc14_10
|
||||
// CHECK:STDOUT: assign %a.ref.loc14_3, %.loc14
|
||||
// CHECK:STDOUT: %a.ref.loc18_3: ref i32 = name_ref a, %a
|
||||
// CHECK:STDOUT: %a.ref.loc18_7: ref i32 = name_ref a, %a
|
||||
// CHECK:STDOUT: %F.ref: <function> = name_ref F, file.%F [template = file.%F]
|
||||
// CHECK:STDOUT: %.loc18: init i32 = call %F.ref()
|
||||
// CHECK:STDOUT: assign %a.ref.loc18_3, %.loc18
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -234,6 +234,7 @@ CARBON_DIAGNOSTIC_KIND(CopyOfUncopyableType)
|
||||
CARBON_DIAGNOSTIC_KIND(DerefOfNonPointer)
|
||||
CARBON_DIAGNOSTIC_KIND(DerefOfType)
|
||||
CARBON_DIAGNOSTIC_KIND(CompileTimeBindingInVarDecl)
|
||||
CARBON_DIAGNOSTIC_KIND(CompoundMemberAccessDoesNotUseBase)
|
||||
CARBON_DIAGNOSTIC_KIND(NameAmbiguousDueToExtend)
|
||||
CARBON_DIAGNOSTIC_KIND(NameNotFound)
|
||||
CARBON_DIAGNOSTIC_KIND(NameDeclDuplicate)
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// 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
|
||||
|
||||
interface I {
|
||||
fn F[self: Self]() -> i32;
|
||||
}
|
||||
|
||||
class A {
|
||||
var n: i32;
|
||||
}
|
||||
|
||||
impl A as I {
|
||||
fn F[self: A]() -> i32 {
|
||||
return self.n;
|
||||
}
|
||||
}
|
||||
|
||||
fn Call(a: A) -> i32 {
|
||||
return a.(I.F)();
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: ; ModuleID = 'impl.carbon'
|
||||
// CHECK:STDOUT: source_filename = "impl.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define i32 @F(ptr %self) {
|
||||
// CHECK:STDOUT: %n = getelementptr inbounds { i32 }, ptr %self, i32 0, i32 0
|
||||
// CHECK:STDOUT: %1 = load i32, ptr %n, align 4
|
||||
// CHECK:STDOUT: ret i32 %1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: define i32 @Call(ptr %a) {
|
||||
// CHECK:STDOUT: %F = call i32 @F(ptr %a)
|
||||
// CHECK:STDOUT: ret i32 %F
|
||||
// CHECK:STDOUT: }
|
||||
@@ -10,7 +10,7 @@ namespace Carbon::Parse {
|
||||
// TODO: This currently only supports identifiers on the rhs, but will in the
|
||||
// future need to handle things like `object.(Interface.member)` for qualifiers.
|
||||
static auto HandlePeriodOrArrow(Context& context, NodeKind node_kind,
|
||||
bool is_arrow) -> void {
|
||||
State paren_state, bool is_arrow) -> void {
|
||||
auto state = context.PopState();
|
||||
|
||||
// We're handling `.something` or `->something`.
|
||||
@@ -25,6 +25,12 @@ static auto HandlePeriodOrArrow(Context& context, NodeKind node_kind,
|
||||
NodeKind::BaseName)) {
|
||||
// OK, `.base`. This is allowed in any name context other than declaring a
|
||||
// new qualified name: `fn Namespace.base() {}`
|
||||
} else if (paren_state != State::Invalid &&
|
||||
context.PositionIs(Lex::TokenKind::OpenParen)) {
|
||||
state.state = paren_state;
|
||||
context.PushState(state);
|
||||
context.PushState(State::ParenExpr);
|
||||
return;
|
||||
} else {
|
||||
CARBON_DIAGNOSTIC(ExpectedIdentifierAfterDotOrArrow, Error,
|
||||
"Expected identifier after `{0}`.", llvm::StringLiteral);
|
||||
@@ -49,23 +55,37 @@ static auto HandlePeriodOrArrow(Context& context, NodeKind node_kind,
|
||||
}
|
||||
|
||||
auto HandlePeriodAsDecl(Context& context) -> void {
|
||||
HandlePeriodOrArrow(context, NodeKind::QualifiedName,
|
||||
HandlePeriodOrArrow(context, NodeKind::QualifiedName, State::Invalid,
|
||||
/*is_arrow=*/false);
|
||||
}
|
||||
|
||||
auto HandlePeriodAsExpr(Context& context) -> void {
|
||||
HandlePeriodOrArrow(context, NodeKind::MemberAccessExpr,
|
||||
State::CompoundMemberAccess,
|
||||
/*is_arrow=*/false);
|
||||
}
|
||||
|
||||
auto HandlePeriodAsStruct(Context& context) -> void {
|
||||
HandlePeriodOrArrow(context, NodeKind::StructFieldDesignator,
|
||||
HandlePeriodOrArrow(context, NodeKind::StructFieldDesignator, State::Invalid,
|
||||
/*is_arrow=*/false);
|
||||
}
|
||||
|
||||
auto HandleArrowExpr(Context& context) -> void {
|
||||
HandlePeriodOrArrow(context, NodeKind::PointerMemberAccessExpr,
|
||||
State::CompoundPointerMemberAccess,
|
||||
/*is_arrow=*/true);
|
||||
}
|
||||
|
||||
auto HandleCompoundMemberAccess(Context& context) -> void {
|
||||
auto state = context.PopState();
|
||||
context.AddNode(NodeKind::MemberAccessExpr, state.token, state.subtree_start,
|
||||
state.has_error);
|
||||
}
|
||||
|
||||
auto HandleCompoundPointerMemberAccess(Context& context) -> void {
|
||||
auto state = context.PopState();
|
||||
context.AddNode(NodeKind::PointerMemberAccessExpr, state.token,
|
||||
state.subtree_start, state.has_error);
|
||||
}
|
||||
|
||||
} // namespace Carbon::Parse
|
||||
|
||||
@@ -66,6 +66,8 @@ using AnyDeclId = NodeIdInCategory<NodeCategory::Decl>;
|
||||
using AnyExprId = NodeIdInCategory<NodeCategory::Expr>;
|
||||
using AnyImplAsId = NodeIdInCategory<NodeCategory::ImplAs>;
|
||||
using AnyMemberNameId = NodeIdInCategory<NodeCategory::MemberName>;
|
||||
using AnyMemberNameOrMemberExprId =
|
||||
NodeIdInCategory<NodeCategory::MemberName | NodeCategory::MemberExpr>;
|
||||
using AnyModifierId = NodeIdInCategory<NodeCategory::Modifier>;
|
||||
using AnyNameComponentId = NodeIdInCategory<NodeCategory::NameComponent>;
|
||||
using AnyPatternId = NodeIdInCategory<NodeCategory::Pattern>;
|
||||
|
||||
@@ -23,11 +23,12 @@ enum class NodeCategory : uint32_t {
|
||||
Decl = 1 << 0,
|
||||
Expr = 1 << 1,
|
||||
ImplAs = 1 << 2,
|
||||
MemberName = 1 << 3,
|
||||
Modifier = 1 << 4,
|
||||
NameComponent = 1 << 5,
|
||||
Pattern = 1 << 6,
|
||||
Statement = 1 << 7,
|
||||
MemberExpr = 1 << 3,
|
||||
MemberName = 1 << 4,
|
||||
Modifier = 1 << 5,
|
||||
NameComponent = 1 << 6,
|
||||
Pattern = 1 << 7,
|
||||
Statement = 1 << 8,
|
||||
None = 0,
|
||||
|
||||
LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/Statement)
|
||||
|
||||
@@ -385,13 +385,41 @@ CARBON_PARSE_STATE(DeclScopeLoop)
|
||||
//
|
||||
// . name
|
||||
// ^~~~~~
|
||||
// -> name
|
||||
// ^~~~~~~
|
||||
// . base (variant is not Decl)
|
||||
// ^~~~~~
|
||||
// -> base (variant is not Decl)
|
||||
// ^~~~~~~
|
||||
// . ??? (??? consumed if it is a keyword)
|
||||
// ^
|
||||
// -> ??? (??? consumed if it is a keyword)
|
||||
// ^~
|
||||
// (state done)
|
||||
//
|
||||
// . ??? (??? consumed if it is a keyword)
|
||||
// ^
|
||||
// (state done)
|
||||
// expr . ( ... )
|
||||
// ^
|
||||
// 1. ParenExpr
|
||||
// 2. CompoundMemberAccess
|
||||
//
|
||||
// expr -> ( ... )
|
||||
// ^~
|
||||
// 1. ParenExpr
|
||||
// 2. CompoundPointerMemberAccess
|
||||
CARBON_PARSE_STATE_VARIANTS3(Period, Decl, Expr, Struct)
|
||||
|
||||
// Handles a compound member access after we parse the name expression.
|
||||
//
|
||||
// expr . ( expr )
|
||||
// ^
|
||||
CARBON_PARSE_STATE(CompoundMemberAccess)
|
||||
|
||||
// Handles a compound pointer member access after we parse the name expression.
|
||||
//
|
||||
// expr -> ( expr )
|
||||
// ^
|
||||
CARBON_PARSE_STATE(CompoundPointerMemberAccess)
|
||||
|
||||
// Handles `->name` expressions. Identical to PeriodAsExpr except for the
|
||||
// leading token.
|
||||
//
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
// 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: Decide if we want to support this syntax. See #3763.
|
||||
|
||||
interface Interface {
|
||||
fn F();
|
||||
}
|
||||
|
||||
impl bool as Interface {
|
||||
fn F();
|
||||
}
|
||||
|
||||
// CHECK:STDERR: fail_out_of_line_member.carbon:[[@LINE+3]]:4: ERROR: `fn` introducer should be followed by a name.
|
||||
// CHECK:STDERR: fn (bool as Interface).F() {}
|
||||
// CHECK:STDERR: ^
|
||||
fn (bool as Interface).F() {}
|
||||
|
||||
class C {
|
||||
impl Self as Interface {
|
||||
fn F();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: The error recovery here is not very good. The `(` is treated as
|
||||
// starting the function parameter list.
|
||||
// CHECK:STDERR: fail_out_of_line_member.carbon:[[@LINE+9]]:6: ERROR: Expected identifier after `.`.
|
||||
// CHECK:STDERR: fn C.(Self as Interface).F() {}
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_out_of_line_member.carbon:[[@LINE+6]]:7: ERROR: Expected binding pattern.
|
||||
// CHECK:STDERR: fn C.(Self as Interface).F() {}
|
||||
// CHECK:STDERR: ^~~~
|
||||
// CHECK:STDERR: fail_out_of_line_member.carbon:[[@LINE+3]]:25: ERROR: `fn` declarations must either end with a `;` or have a `{ ... }` block for a definition.
|
||||
// CHECK:STDERR: fn C.(Self as Interface).F() {}
|
||||
// CHECK:STDERR: ^
|
||||
fn C.(Self as Interface).F() {}
|
||||
|
||||
// CHECK:STDOUT: - filename: fail_out_of_line_member.carbon
|
||||
// CHECK:STDOUT: parse_tree: [
|
||||
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
|
||||
// CHECK:STDOUT: {kind: 'InterfaceIntroducer', text: 'interface'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Interface'},
|
||||
// CHECK:STDOUT: {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'},
|
||||
// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 9},
|
||||
// CHECK:STDOUT: {kind: 'ImplIntroducer', text: 'impl'},
|
||||
// CHECK:STDOUT: {kind: 'BoolTypeLiteral', text: 'bool'},
|
||||
// CHECK:STDOUT: {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Interface'},
|
||||
// CHECK:STDOUT: {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'},
|
||||
// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'ImplDefinition', text: '}', subtree_size: 11},
|
||||
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'InvalidParse', text: '(', has_error: yes},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDecl', text: '}', has_error: yes, subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'C'},
|
||||
// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'ImplIntroducer', text: 'impl'},
|
||||
// CHECK:STDOUT: {kind: 'SelfTypeNameExpr', text: 'Self'},
|
||||
// CHECK:STDOUT: {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Interface'},
|
||||
// CHECK:STDOUT: {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'},
|
||||
// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'ImplDefinition', text: '}', subtree_size: 11},
|
||||
// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 15},
|
||||
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'C'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: '(', has_error: yes},
|
||||
// CHECK:STDOUT: {kind: 'QualifiedName', text: '.', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Self', has_error: yes},
|
||||
// CHECK:STDOUT: {kind: 'InvalidParse', text: 'Self', has_error: yes},
|
||||
// CHECK:STDOUT: {kind: 'BindingPattern', text: 'Self', has_error: yes, subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', has_error: yes, subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDecl', text: '}', has_error: yes, subtree_size: 10},
|
||||
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
|
||||
// CHECK:STDOUT: ]
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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() {
|
||||
a.(b);
|
||||
a->(b);
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: - filename: compound.carbon
|
||||
// CHECK:STDOUT: parse_tree: [
|
||||
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
|
||||
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'},
|
||||
// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'a'},
|
||||
// CHECK:STDOUT: {kind: 'ExprOpenParen', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'b'},
|
||||
// CHECK:STDOUT: {kind: 'ParenExpr', text: ')', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'MemberAccessExpr', text: '.', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 6},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'a'},
|
||||
// CHECK:STDOUT: {kind: 'ExprOpenParen', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'b'},
|
||||
// CHECK:STDOUT: {kind: 'ParenExpr', text: ')', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'PointerMemberAccessExpr', text: '->', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 6},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 18},
|
||||
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
|
||||
// CHECK:STDOUT: ]
|
||||
@@ -0,0 +1,68 @@
|
||||
// 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() {
|
||||
// CHECK:STDERR: fail_keyword.carbon:[[@LINE+3]]:5: ERROR: Expected identifier after `.`.
|
||||
// CHECK:STDERR: a.self;
|
||||
// CHECK:STDERR: ^~~~
|
||||
a.self;
|
||||
// CHECK:STDERR: fail_keyword.carbon:[[@LINE+3]]:5: ERROR: Expected identifier after `.`.
|
||||
// CHECK:STDERR: a.Self;
|
||||
// CHECK:STDERR: ^~~~
|
||||
a.Self;
|
||||
// CHECK:STDERR: fail_keyword.carbon:[[@LINE+3]]:5: ERROR: Expected identifier after `.`.
|
||||
// CHECK:STDERR: a.for;
|
||||
// CHECK:STDERR: ^~~
|
||||
a.for;
|
||||
// CHECK:STDERR: fail_keyword.carbon:[[@LINE+3]]:6: ERROR: Expected identifier after `->`.
|
||||
// CHECK:STDERR: p->self;
|
||||
// CHECK:STDERR: ^~~~
|
||||
p->self;
|
||||
// CHECK:STDERR: fail_keyword.carbon:[[@LINE+3]]:6: ERROR: Expected identifier after `->`.
|
||||
// CHECK:STDERR: p->Self;
|
||||
// CHECK:STDERR: ^~~~
|
||||
p->Self;
|
||||
// CHECK:STDERR: fail_keyword.carbon:[[@LINE+3]]:6: ERROR: Expected identifier after `->`.
|
||||
// CHECK:STDERR: p->while;
|
||||
// CHECK:STDERR: ^~~~~
|
||||
p->while;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: - filename: fail_keyword.carbon
|
||||
// CHECK:STDOUT: parse_tree: [
|
||||
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
|
||||
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'},
|
||||
// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'a'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'self', has_error: yes},
|
||||
// CHECK:STDOUT: {kind: 'MemberAccessExpr', text: '.', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'a'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Self', has_error: yes},
|
||||
// CHECK:STDOUT: {kind: 'MemberAccessExpr', text: '.', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'a'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'for', has_error: yes},
|
||||
// CHECK:STDOUT: {kind: 'MemberAccessExpr', text: '.', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'p'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'self', has_error: yes},
|
||||
// CHECK:STDOUT: {kind: 'PointerMemberAccessExpr', text: '->', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'p'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Self', has_error: yes},
|
||||
// CHECK:STDOUT: {kind: 'PointerMemberAccessExpr', text: '->', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'p'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'while', has_error: yes},
|
||||
// CHECK:STDOUT: {kind: 'PointerMemberAccessExpr', text: '->', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 30},
|
||||
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
|
||||
// CHECK:STDOUT: ]
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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() {
|
||||
a.base;
|
||||
p->base;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: - filename: keyword.carbon
|
||||
// CHECK:STDOUT: parse_tree: [
|
||||
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
|
||||
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'},
|
||||
// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'a'},
|
||||
// CHECK:STDOUT: {kind: 'BaseName', text: 'base'},
|
||||
// CHECK:STDOUT: {kind: 'MemberAccessExpr', text: '.', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'p'},
|
||||
// CHECK:STDOUT: {kind: 'BaseName', text: 'base'},
|
||||
// CHECK:STDOUT: {kind: 'PointerMemberAccessExpr', text: '->', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 14},
|
||||
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
|
||||
// CHECK:STDOUT: ]
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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(a: T) {
|
||||
a.x;
|
||||
a->x;
|
||||
a.x->y;
|
||||
a->x.y;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: - filename: simple.carbon
|
||||
// CHECK:STDOUT: parse_tree: [
|
||||
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
|
||||
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'},
|
||||
// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'a'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'T'},
|
||||
// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 8},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'a'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'x'},
|
||||
// CHECK:STDOUT: {kind: 'MemberAccessExpr', text: '.', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'a'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'x'},
|
||||
// CHECK:STDOUT: {kind: 'PointerMemberAccessExpr', text: '->', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'a'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'x'},
|
||||
// CHECK:STDOUT: {kind: 'MemberAccessExpr', text: '.', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'y'},
|
||||
// CHECK:STDOUT: {kind: 'PointerMemberAccessExpr', text: '->', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 6},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'a'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'x'},
|
||||
// CHECK:STDOUT: {kind: 'PointerMemberAccessExpr', text: '->', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'y'},
|
||||
// CHECK:STDOUT: {kind: 'MemberAccessExpr', text: '.', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', subtree_size: 6},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 29},
|
||||
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
|
||||
// CHECK:STDOUT: ]
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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_dot_paren.carbon:[[@LINE+3]]:10: ERROR: Expected identifier after `.`.
|
||||
// CHECK:STDERR: var x: {.(a) = 1};
|
||||
// CHECK:STDERR: ^
|
||||
var x: {.(a) = 1};
|
||||
|
||||
// CHECK:STDOUT: - filename: fail_dot_paren.carbon
|
||||
// CHECK:STDOUT: parse_tree: [
|
||||
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
|
||||
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'x'},
|
||||
// CHECK:STDOUT: {kind: 'StructLiteralOrStructTypeLiteralStart', text: '{'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierName', text: '(', has_error: yes},
|
||||
// CHECK:STDOUT: {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'IntLiteral', text: '1'},
|
||||
// CHECK:STDOUT: {kind: 'InvalidParse', text: '=', has_error: yes},
|
||||
// CHECK:STDOUT: {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 6},
|
||||
// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 8},
|
||||
// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 10},
|
||||
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
|
||||
// CHECK:STDOUT: ]
|
||||
@@ -620,7 +620,8 @@ using ExprOpenParen = LeafNode<NodeKind::ExprOpenParen>;
|
||||
|
||||
// A parenthesized expression: `(a)`.
|
||||
struct ParenExpr {
|
||||
static constexpr auto Kind = NodeKind::ParenExpr.Define(NodeCategory::Expr);
|
||||
static constexpr auto Kind =
|
||||
NodeKind::ParenExpr.Define(NodeCategory::Expr | NodeCategory::MemberExpr);
|
||||
|
||||
ExprOpenParenId left_paren;
|
||||
AnyExprId expr;
|
||||
@@ -656,22 +657,22 @@ struct CallExpr {
|
||||
CommaSeparatedList<AnyExprId, CallExprCommaId> arguments;
|
||||
};
|
||||
|
||||
// A simple member access expression: `a.b`.
|
||||
// A member access expression: `a.b` or `a.(b)`.
|
||||
struct MemberAccessExpr {
|
||||
static constexpr auto Kind =
|
||||
NodeKind::MemberAccessExpr.Define(NodeCategory::Expr);
|
||||
|
||||
AnyExprId lhs;
|
||||
AnyMemberNameId rhs;
|
||||
AnyMemberNameOrMemberExprId rhs;
|
||||
};
|
||||
|
||||
// A simple indirect member access expression: `a->b`.
|
||||
// An indirect member access expression: `a->b` or `a->(b)`.
|
||||
struct PointerMemberAccessExpr {
|
||||
static constexpr auto Kind =
|
||||
NodeKind::PointerMemberAccessExpr.Define(NodeCategory::Expr);
|
||||
|
||||
AnyExprId lhs;
|
||||
AnyMemberNameId rhs;
|
||||
AnyMemberNameOrMemberExprId rhs;
|
||||
};
|
||||
|
||||
// A prefix operator expression.
|
||||
|
||||
Reference in New Issue
Block a user