Feature generic choice (#2042)

Implementation of generic choices!

I hope it is useful!
This commit is contained in:
pmqtt
2022-08-24 11:30:04 -07:00
committed by GitHub
parent 499188bfcf
commit f957d4d87d
10 changed files with 225 additions and 17 deletions
+17
View File
@@ -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.
+3
View File
@@ -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;
}
+7 -1
View File
@@ -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()
+59 -7
View File
@@ -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();
+2 -1
View File
@@ -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;
}
+17 -6
View File
@@ -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.
+2 -2
View File
@@ -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;
}