Compound member access syntax. (#1233)

Implement initial support for `A.(B)` syntax, per #989. Specifically, this supports:

* `object.(Type.member)` for instance members,
* `object.(Interface.member)` for instance and non-instance members,
* `object.(Type.(Interface.member))` for instance members,
* `Type.(Interface.member)` for non-instance members.

Three new AST nodes are introduced:

* `CompoundFieldAccessExpression` represents the `A.(B)` syntax.
* `MemberName` is a `Value` that represents the result of evaluating an expression such as `Type.member` or `Interface.member` or `Type.(Interface.member)`.
* `TypeOfMemberName` is the type of a `MemberName` value.

In order to handle members of classes and interfaces which have corresponding declarations and may need substitution into their types, and members of structs which don't have declarations but also don't need substitution, a class `Member` is introduced that can refer to either of these kinds of member.

Co-authored-by: Geoff Romer <gromer@google.com>
Co-authored-by: Jon Meow <jperkins@google.com>
This commit is contained in:
Richard Smith
2022-05-13 16:52:41 -07:00
committed by GitHub
co-authored by Geoff Romer Jon Meow
parent 20728dbd3a
commit 235cb88a8e
28 changed files with 940 additions and 41 deletions
+46 -6
View File
@@ -354,6 +354,25 @@ void Value::Print(llvm::raw_ostream& out) const {
case Value::Kind::ParameterizedEntityName:
out << *GetName(cast<ParameterizedEntityName>(*this).declaration());
break;
case Value::Kind::MemberName: {
const auto& member_name = cast<MemberName>(*this);
if (member_name.base_type().has_value()) {
out << *member_name.base_type().value();
}
if (member_name.base_type().has_value() &&
member_name.interface().has_value()) {
out << "(";
}
if (member_name.interface().has_value()) {
out << *member_name.interface().value();
}
out << "." << member_name.name();
if (member_name.base_type().has_value() &&
member_name.interface().has_value()) {
out << ")";
}
break;
}
case Value::Kind::ChoiceType:
out << "choice " << cast<ChoiceType>(*this).name();
break;
@@ -388,9 +407,13 @@ void Value::Print(llvm::raw_ostream& out) const {
<< ")";
break;
case Value::Kind::TypeOfParameterizedEntityName:
out << "typeof(" << cast<TypeOfParameterizedEntityName>(*this).name()
<< ")";
out << "parameterized entity name "
<< cast<TypeOfParameterizedEntityName>(*this).name();
break;
case Value::Kind::TypeOfMemberName: {
out << "member name " << cast<TypeOfMemberName>(*this).member().name();
break;
}
case Value::Kind::StaticArrayType: {
const auto& array_type = cast<StaticArrayType>(*this);
out << "[" << array_type.element_type() << "; " << array_type.size()
@@ -522,10 +545,6 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool {
case Value::Kind::TypeOfChoiceType:
return TypeEqual(&cast<TypeOfChoiceType>(*t1).choice_type(),
&cast<TypeOfChoiceType>(*t2).choice_type());
case Value::Kind::TypeOfParameterizedEntityName: {
return ValueEqual(&cast<TypeOfParameterizedEntityName>(*t1).name(),
&cast<TypeOfParameterizedEntityName>(*t2).name());
}
case Value::Kind::StaticArrayType: {
const auto& array1 = cast<StaticArrayType>(*t1);
const auto& array2 = cast<StaticArrayType>(*t2);
@@ -546,6 +565,9 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool {
case Value::Kind::BindingPlaceholderValue:
case Value::Kind::ContinuationValue:
case Value::Kind::ParameterizedEntityName:
case Value::Kind::MemberName:
case Value::Kind::TypeOfParameterizedEntityName:
case Value::Kind::TypeOfMemberName:
CARBON_FATAL() << "TypeEqual used to compare non-type values\n"
<< *t1 << "\n"
<< *t2;
@@ -645,6 +667,7 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2) -> bool {
case Value::Kind::TypeOfInterfaceType:
case Value::Kind::TypeOfChoiceType:
case Value::Kind::TypeOfParameterizedEntityName:
case Value::Kind::TypeOfMemberName:
case Value::Kind::StaticArrayType:
return TypeEqual(v1, v2);
case Value::Kind::NominalClassValue:
@@ -654,6 +677,7 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2) -> bool {
case Value::Kind::ContinuationValue:
case Value::Kind::PointerValue:
case Value::Kind::LValue:
case Value::Kind::MemberName:
// TODO: support pointer comparisons once we have a clearer distinction
// between pointers and lvalues.
CARBON_FATAL() << "ValueEqual does not support this kind of value: "
@@ -703,6 +727,22 @@ auto FindMember(const std::string& name,
return std::nullopt;
}
auto Member::name() const -> std::string {
if (const Declaration* decl = member_.dyn_cast<const Declaration*>()) {
return GetName(*decl).value();
} else {
return member_.get<const NamedValue*>()->name;
}
}
auto Member::type() const -> const Value& {
if (const Declaration* decl = member_.dyn_cast<const Declaration*>()) {
return decl->static_type();
} else {
return *member_.get<const NamedValue*>()->value;
}
}
void ImplBinding::Print(llvm::raw_ostream& out) const {
out << "impl binding " << *type_var_ << " as " << *iface_;
}