mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:00:12 +01:00
Feature generic choice (#2042)
Implementation of generic choices! I hope it is useful!
This commit is contained in:
@@ -262,9 +262,11 @@ class ChoiceDeclaration : public Declaration {
|
||||
using ImplementsCarbonValueNode = void;
|
||||
|
||||
ChoiceDeclaration(SourceLocation source_loc, std::string name,
|
||||
std::optional<Nonnull<TuplePattern*>> type_params,
|
||||
std::vector<Nonnull<AlternativeSignature*>> alternatives)
|
||||
: Declaration(AstNodeKind::ChoiceDeclaration, source_loc),
|
||||
name_(std::move(name)),
|
||||
type_params_(type_params),
|
||||
alternatives_(std::move(alternatives)) {}
|
||||
|
||||
static auto classof(const AstNode* node) -> bool {
|
||||
@@ -272,6 +274,14 @@ class ChoiceDeclaration : public Declaration {
|
||||
}
|
||||
|
||||
auto name() const -> const std::string& { return name_; }
|
||||
|
||||
auto type_params() const -> std::optional<Nonnull<const TuplePattern*>> {
|
||||
return type_params_;
|
||||
}
|
||||
auto type_params() -> std::optional<Nonnull<TuplePattern*>> {
|
||||
return type_params_;
|
||||
}
|
||||
|
||||
auto alternatives() const
|
||||
-> llvm::ArrayRef<Nonnull<const AlternativeSignature*>> {
|
||||
return alternatives_;
|
||||
@@ -280,11 +290,18 @@ class ChoiceDeclaration : public Declaration {
|
||||
return alternatives_;
|
||||
}
|
||||
|
||||
void set_members(const std::vector<NamedValue>& members) {
|
||||
members_ = members;
|
||||
}
|
||||
auto members() const -> std::vector<NamedValue> { return members_; }
|
||||
|
||||
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::optional<Nonnull<TuplePattern*>> type_params_;
|
||||
std::vector<Nonnull<AlternativeSignature*>> alternatives_;
|
||||
std::vector<NamedValue> members_;
|
||||
};
|
||||
|
||||
// Global variable definition implements the Declaration concept.
|
||||
|
||||
@@ -838,6 +838,9 @@ auto Interpreter::CallFunction(const CallExpression& call,
|
||||
case DeclarationKind::InterfaceDeclaration:
|
||||
return todo_.FinishAction(arena_->New<InterfaceType>(
|
||||
&cast<InterfaceDeclaration>(decl), bindings));
|
||||
case DeclarationKind::ChoiceDeclaration:
|
||||
return todo_.FinishAction(arena_->New<ChoiceType>(
|
||||
&cast<ChoiceDeclaration>(decl), bindings));
|
||||
default:
|
||||
CARBON_FATAL() << "unknown kind of ParameterizedEntityName " << decl;
|
||||
}
|
||||
|
||||
@@ -562,14 +562,20 @@ static auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope,
|
||||
}
|
||||
case DeclarationKind::ChoiceDeclaration: {
|
||||
auto& choice = cast<ChoiceDeclaration>(declaration);
|
||||
StaticScope choice_scope;
|
||||
choice_scope.AddParent(&enclosing_scope);
|
||||
enclosing_scope.MarkDeclared(choice.name());
|
||||
if (choice.type_params().has_value()) {
|
||||
CARBON_RETURN_IF_ERROR(
|
||||
ResolveNames(**choice.type_params(), choice_scope));
|
||||
}
|
||||
// Alternative names are never used unqualified, so we don't need to
|
||||
// add the alternatives to a scope, or introduce a new scope; we only
|
||||
// need to check for duplicates.
|
||||
std::set<std::string_view> alternative_names;
|
||||
for (Nonnull<AlternativeSignature*> alternative : choice.alternatives()) {
|
||||
CARBON_RETURN_IF_ERROR(
|
||||
ResolveNames(alternative->signature(), enclosing_scope));
|
||||
ResolveNames(alternative->signature(), choice_scope));
|
||||
if (!alternative_names.insert(alternative->name()).second) {
|
||||
return CompilationError(alternative->source_loc())
|
||||
<< "Duplicate name `" << alternative->name()
|
||||
|
||||
@@ -1744,9 +1744,15 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
<< " does not have an alternative named "
|
||||
<< access.member_name();
|
||||
}
|
||||
Nonnull<const Value*> type =
|
||||
arena_->New<FunctionType>(*parameter_types, llvm::None,
|
||||
&choice, llvm::None, llvm::None);
|
||||
Nonnull<const Value*> substituted_parameter_type =
|
||||
*parameter_types;
|
||||
if (choice.IsParameterized()) {
|
||||
substituted_parameter_type =
|
||||
Substitute(choice.type_args(), *parameter_types);
|
||||
}
|
||||
Nonnull<const Value*> type = arena_->New<FunctionType>(
|
||||
substituted_parameter_type, llvm::None, &choice, llvm::None,
|
||||
llvm::None);
|
||||
// TODO: Should there be a Declaration corresponding to each
|
||||
// choice type alternative?
|
||||
access.set_member(Member(arena_->New<NamedValue>(
|
||||
@@ -2278,12 +2284,22 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
call.set_value_category(ValueCategory::Let);
|
||||
break;
|
||||
}
|
||||
case DeclarationKind::ChoiceDeclaration: {
|
||||
Nonnull<ChoiceType*> ct = arena_->New<ChoiceType>(
|
||||
cast<ChoiceDeclaration>(&decl), bindings);
|
||||
Nonnull<TypeOfChoiceType*> inst_choice_type =
|
||||
arena_->New<TypeOfChoiceType>(ct);
|
||||
call.set_static_type(inst_choice_type);
|
||||
call.set_value_category(ValueCategory::Let);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
CARBON_FATAL()
|
||||
<< "unknown type of ParameterizedEntityName for " << decl;
|
||||
}
|
||||
return Success();
|
||||
}
|
||||
case Value::Kind::TypeOfChoiceType:
|
||||
default: {
|
||||
return CompilationError(e->source_loc())
|
||||
<< "in call `" << *e
|
||||
@@ -2852,9 +2868,15 @@ auto TypeChecker::TypeCheckPattern(
|
||||
<< "'" << alternative.alternative_name()
|
||||
<< "' is not an alternative of " << choice_type;
|
||||
}
|
||||
CARBON_RETURN_IF_ERROR(TypeCheckPattern(&alternative.arguments(),
|
||||
*parameter_types, impl_scope,
|
||||
enclosing_value_category));
|
||||
|
||||
Nonnull<const Value*> substituted_parameter_type = *parameter_types;
|
||||
if (choice_type.IsParameterized()) {
|
||||
substituted_parameter_type =
|
||||
Substitute(choice_type.type_args(), *parameter_types);
|
||||
}
|
||||
CARBON_RETURN_IF_ERROR(
|
||||
TypeCheckPattern(&alternative.arguments(), substituted_parameter_type,
|
||||
impl_scope, enclosing_value_category));
|
||||
alternative.set_static_type(&choice_type);
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
Nonnull<const Value*> alternative_value,
|
||||
@@ -3802,6 +3824,23 @@ auto TypeChecker::TypeCheckImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
|
||||
auto TypeChecker::DeclareChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
|
||||
const ScopeInfo& scope_info)
|
||||
-> ErrorOr<Success> {
|
||||
ImplScope choice_scope;
|
||||
choice_scope.AddParent(scope_info.innermost_scope);
|
||||
std::vector<Nonnull<const GenericBinding*>> bindings = scope_info.bindings;
|
||||
if (choice->type_params().has_value()) {
|
||||
Nonnull<TuplePattern*> type_params = *choice->type_params();
|
||||
CARBON_RETURN_IF_ERROR(TypeCheckPattern(type_params, std::nullopt,
|
||||
choice_scope, ValueCategory::Let));
|
||||
CollectGenericBindingsInPattern(type_params, bindings);
|
||||
if (trace_stream_) {
|
||||
**trace_stream_ << choice_scope;
|
||||
}
|
||||
}
|
||||
BindingMap generic_args;
|
||||
for (auto* binding : bindings) {
|
||||
generic_args[binding] = *binding->symbolic_identity();
|
||||
}
|
||||
|
||||
std::vector<NamedValue> alternatives;
|
||||
for (Nonnull<AlternativeSignature*> alternative : choice->alternatives()) {
|
||||
CARBON_ASSIGN_OR_RETURN(auto signature,
|
||||
@@ -3809,7 +3848,20 @@ auto TypeChecker::DeclareChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
|
||||
*scope_info.innermost_scope));
|
||||
alternatives.push_back({.name = alternative->name(), .value = signature});
|
||||
}
|
||||
auto ct = arena_->New<ChoiceType>(choice->name(), std::move(alternatives));
|
||||
choice->set_members(alternatives);
|
||||
if (choice->type_params().has_value()) {
|
||||
Nonnull<ParameterizedEntityName*> param_name =
|
||||
arena_->New<ParameterizedEntityName>(choice, *choice->type_params());
|
||||
SetConstantValue(choice, param_name);
|
||||
choice->set_static_type(
|
||||
arena_->New<TypeOfParameterizedEntityName>(param_name));
|
||||
return Success();
|
||||
}
|
||||
|
||||
auto ct = arena_->New<ChoiceType>(
|
||||
choice,
|
||||
arena_->New<Bindings>(std::move(generic_args), Bindings::NoWitnesses));
|
||||
|
||||
SetConstantValue(choice, ct);
|
||||
choice->set_static_type(arena_->New<TypeOfChoiceType>(ct));
|
||||
return Success();
|
||||
|
||||
@@ -937,7 +937,8 @@ auto ConstraintType::VisitEqualValues(
|
||||
|
||||
auto ChoiceType::FindAlternative(std::string_view name) const
|
||||
-> std::optional<Nonnull<const Value*>> {
|
||||
for (const NamedValue& alternative : alternatives_) {
|
||||
std::vector<NamedValue> alternatives = declaration_->members();
|
||||
for (const NamedValue& alternative : alternatives) {
|
||||
if (alternative.name == name) {
|
||||
return alternative.value;
|
||||
}
|
||||
|
||||
@@ -835,25 +835,36 @@ class SymbolicWitness : public Witness {
|
||||
// A choice type.
|
||||
class ChoiceType : public Value {
|
||||
public:
|
||||
ChoiceType(std::string name, std::vector<NamedValue> alternatives)
|
||||
ChoiceType(Nonnull<const ChoiceDeclaration*> declaration,
|
||||
Nonnull<const Bindings*> bindings)
|
||||
: Value(Kind::ChoiceType),
|
||||
name_(std::move(name)),
|
||||
alternatives_(std::move(alternatives)) {}
|
||||
declaration_(declaration),
|
||||
bindings_(bindings) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->kind() == Kind::ChoiceType;
|
||||
}
|
||||
|
||||
auto name() const -> const std::string& { return name_; }
|
||||
auto name() const -> const std::string& { return declaration_->name(); }
|
||||
|
||||
// Returns the parameter types of the alternative with the given name,
|
||||
// or nullopt if no such alternative is present.
|
||||
auto FindAlternative(std::string_view name) const
|
||||
-> std::optional<Nonnull<const Value*>>;
|
||||
|
||||
auto bindings() const -> const Bindings& { return *bindings_; }
|
||||
|
||||
auto type_args() const -> const BindingMap& { return bindings_->args(); }
|
||||
|
||||
auto declaration() const -> const ChoiceDeclaration& { return *declaration_; }
|
||||
|
||||
auto IsParameterized() const -> bool {
|
||||
return declaration_->type_params().has_value();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::vector<NamedValue> alternatives_;
|
||||
Nonnull<const ChoiceDeclaration*> declaration_;
|
||||
Nonnull<const Bindings*> bindings_;
|
||||
};
|
||||
|
||||
// A continuation type.
|
||||
|
||||
@@ -1115,8 +1115,8 @@ declaration:
|
||||
context.source_loc(), $3,
|
||||
arena->New<SelfDeclaration>(context.source_loc()), $1, $4, $5, $7);
|
||||
}
|
||||
| CHOICE identifier LEFT_CURLY_BRACE alternative_list RIGHT_CURLY_BRACE
|
||||
{ $$ = arena->New<ChoiceDeclaration>(context.source_loc(), $2, $4); }
|
||||
| CHOICE identifier type_params LEFT_CURLY_BRACE alternative_list RIGHT_CURLY_BRACE
|
||||
{ $$ = arena->New<ChoiceDeclaration>(context.source_loc(), $2, $3, $5); }
|
||||
| VAR variable_declaration SEMICOLON
|
||||
{
|
||||
$$ = arena->New<VariableDeclaration>(context.source_loc(), $2,
|
||||
|
||||
@@ -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
|
||||
//
|
||||
// RUN: %{explorer} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
// CHECK: result: 22
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
|
||||
choice MyOptionalElement(ZZ:! Type, YY:! Type) {
|
||||
None(YY),
|
||||
Element(ZZ)
|
||||
}
|
||||
|
||||
|
||||
fn Main() -> i32 {
|
||||
var f: MyOptionalElement(String,i32);
|
||||
f = MyOptionalElement(String,i32).None(22);
|
||||
match(f) {
|
||||
case MyOptionalElement(String,i32).None(var x: i32) => {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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
|
||||
//
|
||||
// RUN: %{explorer} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
// CHECK: H 22
|
||||
// CHECK: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
|
||||
choice MyOptionalElement(T:! Type) {
|
||||
None(),
|
||||
Element(T)
|
||||
}
|
||||
|
||||
class MyOptional(T:! Type){
|
||||
fn CreateEmpty() -> MyOptional(T){
|
||||
return { .element = MyOptionalElement(T).None() };
|
||||
}
|
||||
fn Create ( value: T ) -> MyOptional(T){
|
||||
return { .element = MyOptionalElement(T).Element(value) };
|
||||
}
|
||||
|
||||
fn has_value[me: Self] () -> bool{
|
||||
var x: MyOptionalElement(T) = me.element;
|
||||
match(x){
|
||||
case MyOptionalElement(T).None() => { return false; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn get[me: Self] () -> T {
|
||||
var y: T;
|
||||
var x: MyOptionalElement(T) = me.element;
|
||||
match(x){
|
||||
case MyOptionalElement(T).Element( var x: T ) =>{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
var element: MyOptionalElement(T);
|
||||
}
|
||||
|
||||
|
||||
fn Main() -> i32 {
|
||||
var f: MyOptional(i32) = MyOptional(i32).Create(22);
|
||||
var x: i32 = f.get();
|
||||
Print("H {0}",x);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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
|
||||
//
|
||||
// RUN: %{explorer} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
// CHECK: result: 22
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
|
||||
choice MyOptionalElement(ZZ:! Type) {
|
||||
None(ZZ),
|
||||
Element(ZZ)
|
||||
}
|
||||
|
||||
|
||||
fn Main() -> i32 {
|
||||
var f: MyOptionalElement(i32);
|
||||
f = MyOptionalElement(i32).None(22);
|
||||
match(f) {
|
||||
case MyOptionalElement(i32).None(var x: i32) => {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user