Explorar el Código

Feature generic choice (#2042)

Implementation of generic choices!

I hope it is useful!
pmqtt hace 3 años
padre
commit
f957d4d87d

+ 17 - 0
explorer/ast/declaration.h

@@ -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 - 0
explorer/interpreter/interpreter.cpp

@@ -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
explorer/interpreter/resolve_names.cpp

@@ -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
explorer/interpreter/type_checker.cpp

@@ -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
explorer/interpreter/value.cpp

@@ -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
explorer/interpreter/value.h

@@ -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
explorer/syntax/parser.ypp

@@ -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,

+ 30 - 0
explorer/testdata/choice/generic_choice_multiple_template_arguments.carbon

@@ -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;
+}

+ 58 - 0
explorer/testdata/choice/generic_choice_nested_in_template_class.carbon

@@ -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;
+}

+ 30 - 0
explorer/testdata/choice/generic_choice_simple_assignment.carbon

@@ -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;
+}