Explorar el Código

Heap allocation basics (#1323)

* initial implementation of new and delete

* remove requirement for empty heap from trace.carbon

* more tests

* fixed substitution for generic function types
Jeremy G. Siek hace 3 años
padre
commit
b759401328

+ 2 - 0
common/fuzzing/carbon.proto

@@ -83,6 +83,8 @@ message IntrinsicExpression {
   enum Intrinsic {
     UnknownIntrinsic = 0;
     Print = 1;
+    Alloc = 2;
+    Dealloc = 3;
   }
   optional Intrinsic intrinsic = 1;
   optional TupleLiteralExpression argument = 2;

+ 6 - 0
common/fuzzing/proto_to_carbon.cpp

@@ -286,6 +286,12 @@ static auto ExpressionToCarbon(const Fuzzing::Expression& expression,
         case Fuzzing::IntrinsicExpression::Print:
           out << "__intrinsic_print";
           break;
+        case Fuzzing::IntrinsicExpression::Alloc:
+          out << "__intrinsic_new";
+          break;
+        case Fuzzing::IntrinsicExpression::Dealloc:
+          out << "__intrinsic_delete";
+          break;
       }
       TupleLiteralExpressionToCarbon(intrinsic.argument(), out);
     } break;

+ 16 - 5
explorer/ast/expression.cpp

@@ -21,8 +21,10 @@ using llvm::isa;
 auto IntrinsicExpression::FindIntrinsic(std::string_view name,
                                         SourceLocation source_loc)
     -> ErrorOr<Intrinsic> {
-  static const auto& intrinsic_map =
-      *new std::map<std::string_view, Intrinsic>({{"print", Intrinsic::Print}});
+  static const auto& intrinsic_map = *new std::map<std::string_view, Intrinsic>(
+      {{"print", Intrinsic::Print},
+       {"new", Intrinsic::Alloc},
+       {"delete", Intrinsic::Dealloc}});
   name.remove_prefix(std::strlen("__intrinsic_"));
   auto it = intrinsic_map.find(name);
   if (it == intrinsic_map.end()) {
@@ -157,14 +159,23 @@ void Expression::Print(llvm::raw_ostream& out) const {
       out << "fn " << fn.parameter() << " -> " << fn.return_type();
       break;
     }
-    case ExpressionKind::IntrinsicExpression:
-      out << "intrinsic_expression(";
+    case ExpressionKind::IntrinsicExpression: {
+      const auto& iexp = cast<IntrinsicExpression>(*this);
+      out << "intrinsic_";
       switch (cast<IntrinsicExpression>(*this).intrinsic()) {
         case IntrinsicExpression::Intrinsic::Print:
           out << "print";
+          break;
+        case IntrinsicExpression::Intrinsic::Alloc:
+          out << "new";
+          break;
+        case IntrinsicExpression::Intrinsic::Dealloc:
+          out << "delete";
+          break;
       }
-      out << ")";
+      out << iexp.args();
       break;
+    }
     case ExpressionKind::IfExpression: {
       const auto& if_expr = cast<IfExpression>(*this);
       out << "if " << if_expr.condition() << " then "

+ 1 - 3
explorer/ast/expression.h

@@ -601,9 +601,7 @@ class ValueLiteral : public Expression {
 
 class IntrinsicExpression : public Expression {
  public:
-  enum class Intrinsic {
-    Print,
-  };
+  enum class Intrinsic { Print, Alloc, Dealloc };
 
   // Returns the enumerator corresponding to the intrinsic named `name`,
   // or raises a fatal compile error if there is no such enumerator.

+ 12 - 0
explorer/ast/impl_binding.h

@@ -67,10 +67,22 @@ class ImplBinding : public AstNode {
   }
   auto value_category() const -> ValueCategory { return ValueCategory::Let; }
 
+  // Return the original impl binding.
+  auto original() const -> Nonnull<const ImplBinding*> {
+    if (original_.has_value())
+      return *original_;
+    else
+      return this;
+  }
+
+  // Set the original impl binding.
+  void set_original(Nonnull<const ImplBinding*> orig) { original_ = orig; }
+
  private:
   Nonnull<const GenericBinding*> type_var_;
   Nonnull<const Value*> iface_;
   std::optional<Nonnull<const Value*>> static_type_;
+  std::optional<Nonnull<const ImplBinding*>> original_;
 };
 
 }  // namespace Carbon

+ 12 - 0
explorer/ast/pattern.h

@@ -56,6 +56,7 @@ class Pattern : public AstNode {
     CARBON_CHECK(static_type_.has_value());
     return **static_type_;
   }
+  auto has_static_type() const -> bool { return static_type_.has_value(); }
 
   // Sets the static type of this expression. Can only be called once, during
   // typechecking.
@@ -266,11 +267,22 @@ class GenericBinding : public Pattern {
     impl_binding_ = binding;
   }
 
+  // Return the original generic binding.
+  auto original() const -> Nonnull<const GenericBinding*> {
+    if (original_.has_value())
+      return *original_;
+    else
+      return this;
+  }
+  // Set the original generic binding.
+  void set_original(Nonnull<const GenericBinding*> orig) { original_ = orig; }
+
  private:
   std::string name_;
   Nonnull<Expression*> type_;
   std::optional<Nonnull<const Value*>> symbolic_identity_;
   std::optional<Nonnull<const ImplBinding*>> impl_binding_;
+  std::optional<Nonnull<const GenericBinding*>> original_;
 };
 
 // Converts paren_contents to a Pattern, interpreting the parentheses as

+ 11 - 0
explorer/data/prelude.carbon

@@ -39,3 +39,14 @@ impl forall [U1:! Type, U2:! Type, U3:! Type,
 fn Print(format_str: String) {
   __intrinsic_print(format_str);
 }
+
+class Heap {
+  fn New[T:! Type, me: Self](x : T) -> T* {
+    return __intrinsic_new(x);
+  }
+  fn Delete[T:! Type, me: Self](p : T*) {
+    __intrinsic_delete(p);
+  }
+}
+
+var heap: Heap = {};

+ 6 - 0
explorer/fuzzing/ast_to_proto.cpp

@@ -215,6 +215,12 @@ static auto ExpressionToProto(const Expression& expression)
         case IntrinsicExpression::Intrinsic::Print:
           intrinsic_proto->set_intrinsic(Fuzzing::IntrinsicExpression::Print);
           break;
+        case IntrinsicExpression::Intrinsic::Alloc:
+          intrinsic_proto->set_intrinsic(Fuzzing::IntrinsicExpression::Alloc);
+          break;
+        case IntrinsicExpression::Intrinsic::Dealloc:
+          intrinsic_proto->set_intrinsic(Fuzzing::IntrinsicExpression::Dealloc);
+          break;
       }
       *intrinsic_proto->mutable_argument() =
           TupleLiteralExpressionToProto(intrinsic.args());

+ 2 - 0
explorer/interpreter/heap.cpp

@@ -56,6 +56,8 @@ void Heap::Deallocate(AllocationId allocation) {
   }
 }
 
+void Heap::Deallocate(const Address& a) { Deallocate(a.allocation_); }
+
 void Heap::Print(llvm::raw_ostream& out) const {
   llvm::ListSeparator sep;
   for (size_t i = 0; i < values_.size(); ++i) {

+ 1 - 0
explorer/interpreter/heap.h

@@ -40,6 +40,7 @@ class Heap : public HeapAllocationInterface {
 
   // Marks this allocation, and all of its sub-objects, as dead.
   void Deallocate(AllocationId allocation) override;
+  void Deallocate(const Address& a);
 
   // Print all the values on the heap to the stream `out`.
   void Print(llvm::raw_ostream& out) const;

+ 28 - 4
explorer/interpreter/interpreter.cpp

@@ -661,20 +661,28 @@ auto Interpreter::CallFunction(const CallExpression& call,
                   call.source_loc()));
       RuntimeScope method_scope(&heap_);
       BindingMap generic_args;
+      // Bind the receiver to the `me` parameter.
       CARBON_CHECK(PatternMatch(&method.me_pattern().value(), m.receiver(),
                                 call.source_loc(), &method_scope, generic_args,
                                 trace_stream_, this->arena_));
+      // Bind the arguments to the parameters.
       CARBON_CHECK(PatternMatch(&method.param_pattern().value(), converted_args,
                                 call.source_loc(), &method_scope, generic_args,
                                 trace_stream_, this->arena_));
       // Bring the class type arguments into scope.
       for (const auto& [bind, val] : m.type_args()) {
-        method_scope.Initialize(bind, val);
+        method_scope.Initialize(bind->original(), val);
+      }
+      // Bring the deduced type arguments into scope.
+      for (const auto& [bind, val] : call.deduced_args()) {
+        method_scope.Initialize(bind->original(), val);
       }
-
       // Bring the impl witness tables into scope.
+      for (const auto& [impl_bind, witness] : witnesses) {
+        method_scope.Initialize(impl_bind->original(), witness);
+      }
       for (const auto& [impl_bind, witness] : m.witnesses()) {
-        method_scope.Initialize(impl_bind, witness);
+        method_scope.Initialize(impl_bind->original(), witness);
       }
       CARBON_CHECK(method.body().has_value())
           << "Calling a method that's missing a body";
@@ -1011,6 +1019,18 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
           llvm::outs() << cast<StringValue>(*args.elements()[0]).value();
           return todo_.FinishAction(TupleValue::Empty());
         }
+        case IntrinsicExpression::Intrinsic::Alloc: {
+          const auto& args = cast<TupleValue>(*act.results()[0]);
+          CARBON_CHECK(args.elements().size() == 1);
+          Address addr(heap_.AllocateValue(args.elements()[0]));
+          return todo_.FinishAction(arena_->New<PointerValue>(addr));
+        }
+        case IntrinsicExpression::Intrinsic::Dealloc: {
+          const auto& args = cast<TupleValue>(*act.results()[0]);
+          CARBON_CHECK(args.elements().size() == 1);
+          heap_.Deallocate(cast<PointerValue>(args.elements()[0])->address());
+          return todo_.FinishAction(TupleValue::Empty());
+        }
       }
     }
     case ExpressionKind::IntTypeLiteral: {
@@ -1419,7 +1439,11 @@ auto Interpreter::StepDeclaration() -> ErrorOr<Success> {
           return todo_.Spawn(
               std::make_unique<ExpressionAction>(&var_decl.initializer()));
         } else {
-          todo_.Initialize(&var_decl.binding(), act.results()[0]);
+          CARBON_ASSIGN_OR_RETURN(
+              Nonnull<const Value*> v,
+              Convert(act.results()[0], &var_decl.binding().static_type(),
+                      var_decl.source_loc()));
+          todo_.Initialize(&var_decl.binding(), v);
           return todo_.FinishAction();
         }
       } else {

+ 94 - 20
explorer/interpreter/type_checker.cpp

@@ -508,6 +508,12 @@ auto TypeChecker::ArgumentDeduction(
     -> ErrorOr<Success> {
   if (trace_stream_) {
     **trace_stream_ << "deducing " << *param << " from " << *arg << "\n";
+    **trace_stream_ << "bindings: ";
+    llvm::ListSeparator sep;
+    for (auto binding : bindings_to_deduce) {
+      **trace_stream_ << sep << *binding;
+    }
+    **trace_stream_ << "\n";
   }
   // Handle the case where we can't perform deduction, either because the
   // parameter is a primitive type or because the parameter and argument have
@@ -908,12 +914,58 @@ auto TypeChecker::Substitute(
     }
     case Value::Kind::FunctionType: {
       const auto& fn_type = cast<FunctionType>(*type);
-      auto param = Substitute(dict, &fn_type.parameters());
-      auto ret = Substitute(dict, &fn_type.return_type());
-      // TODO: Only remove the bindings that are in `dict`; we may still need
-      // to do deduction.
-      return arena_->New<FunctionType>(param, llvm::None, ret, llvm::None,
-                                       llvm::None);
+      std::map<Nonnull<const GenericBinding*>, Nonnull<const Value*>> new_dict(
+          dict);
+      // Create new generic parameters and generic bindings
+      // and add them to new_dict.
+      std::vector<FunctionType::GenericParameter> generic_parameters;
+      std::vector<Nonnull<const GenericBinding*>> deduced_bindings;
+      std::map<Nonnull<const GenericBinding*>, Nonnull<const GenericBinding*>>
+          bind_map;  // Map old generic bindings to new ones.
+      for (const FunctionType::GenericParameter& gp :
+           fn_type.generic_parameters()) {
+        Nonnull<const Value*> new_type =
+            Substitute(dict, &gp.binding->static_type());
+        Nonnull<GenericBinding*> new_gb = arena_->New<GenericBinding>(
+            gp.binding->source_loc(), gp.binding->name(),
+            (Expression*)&gp.binding->type());  // How to avoid the cast? -jsiek
+        new_gb->set_original(gp.binding->original());
+        new_gb->set_static_type(new_type);
+        FunctionType::GenericParameter new_gp = {.index = gp.index,
+                                                 .binding = new_gb};
+        generic_parameters.push_back(new_gp);
+        new_dict[gp.binding] = arena_->New<VariableType>(new_gp.binding);
+        bind_map[gp.binding] = new_gb;
+      }
+      for (Nonnull<const GenericBinding*> gb : fn_type.deduced_bindings()) {
+        Nonnull<const Value*> new_type = Substitute(dict, &gb->static_type());
+        Nonnull<GenericBinding*> new_gb = arena_->New<GenericBinding>(
+            gb->source_loc(), gb->name(),
+            (Expression*)&gb->type());  // How to avoid the cast? -jsiek
+        new_gb->set_original(gb->original());
+        new_gb->set_static_type(new_type);
+        deduced_bindings.push_back(new_gb);
+        new_dict[gb] = arena_->New<VariableType>(new_gb);
+        bind_map[gb] = new_gb;
+      }
+      // Apply substitution to impl bindings and update their
+      // `type_var` pointers to the new generic bindings.
+      std::vector<Nonnull<const ImplBinding*>> impl_bindings;
+      for (auto ib : fn_type.impl_bindings()) {
+        Nonnull<ImplBinding*> new_ib =
+            arena_->New<ImplBinding>(ib->source_loc(), bind_map[ib->type_var()],
+                                     Substitute(new_dict, ib->interface()));
+        new_ib->set_original(ib->original());
+        impl_bindings.push_back(new_ib);
+      }
+      // Apply substitution to parameter types
+      auto param = Substitute(new_dict, &fn_type.parameters());
+      // Apply substitution to return type
+      auto ret = Substitute(new_dict, &fn_type.return_type());
+      // Create the new FunctionType
+      Nonnull<const Value*> new_fn_type = arena_->New<FunctionType>(
+          param, generic_parameters, ret, deduced_bindings, impl_bindings);
+      return new_fn_type;
     }
     case Value::Kind::PointerType: {
       return arena_->New<PointerType>(
@@ -925,10 +977,6 @@ auto TypeChecker::Substitute(
           arena_->New<NominalClassType>(
               &class_type.declaration(),
               SubstituteIntoBindingMap(class_type.type_args()));
-      if (trace_stream_) {
-        **trace_stream_ << "substitution: " << class_type << " => "
-                        << *new_class_type << "\n";
-      }
       return new_class_type;
     }
     case Value::Kind::InterfaceType: {
@@ -936,10 +984,6 @@ auto TypeChecker::Substitute(
       Nonnull<const InterfaceType*> new_iface_type = arena_->New<InterfaceType>(
           &iface_type.declaration(),
           SubstituteIntoBindingMap(iface_type.args()));
-      if (trace_stream_) {
-        **trace_stream_ << "substitution: " << iface_type << " => "
-                        << *new_iface_type << "\n";
-      }
       return new_iface_type;
     }
     case Value::Kind::ConstraintType: {
@@ -1112,6 +1156,8 @@ auto TypeChecker::SatisfyImpls(
   for (Nonnull<const ImplBinding*> impl_binding : impl_bindings) {
     Nonnull<const Value*> interface =
         Substitute(deduced_type_args, impl_binding->interface());
+    CARBON_CHECK(deduced_type_args.find(impl_binding->type_var()) !=
+                 deduced_type_args.end());
     CARBON_ASSIGN_OR_RETURN(
         Nonnull<Expression*> impl,
         impl_scope.Resolve(interface,
@@ -1292,8 +1338,6 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
   if (trace_stream_) {
     **trace_stream_ << "checking " << ExpressionKindName(e->kind()) << " "
                     << *e;
-    **trace_stream_ << "\nconstants: ";
-    PrintConstants(**trace_stream_);
     **trace_stream_ << "\n";
   }
   if (e->is_type_checked()) {
@@ -1449,10 +1493,12 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
                 if (func_decl->is_method() && func_decl->me_pattern().kind() ==
                                                   PatternKind::AddrPattern) {
                   access.set_is_field_addr_me_method();
-                  CARBON_RETURN_IF_ERROR(
-                      ExpectType(e->source_loc(), "method access",
-                                 &func_decl->me_pattern().static_type(),
-                                 &access.object().static_type(), &impl_scope));
+                  Nonnull<const Value*> me_type =
+                      Substitute(t_class.type_args(),
+                                 &func_decl->me_pattern().static_type());
+                  CARBON_RETURN_IF_ERROR(ExpectType(
+                      e->source_loc(), "method access, receiver type", me_type,
+                      &access.object().static_type(), &impl_scope));
                   if (access.object().value_category() != ValueCategory::Var) {
                     return CompilationError(e->source_loc())
                            << "method " << access.member()
@@ -1863,6 +1909,12 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
       switch (call.function().static_type().kind()) {
         case Value::Kind::FunctionType: {
           const auto& fun_t = cast<FunctionType>(call.function().static_type());
+          if (trace_stream_) {
+            **trace_stream_
+                << "checking call to function of type " << fun_t
+                << "\nwith arguments of type: " << call.argument().static_type()
+                << "\n";
+          }
           CARBON_RETURN_IF_ERROR(DeduceCallBindings(
               call, &fun_t.parameters(), fun_t.generic_parameters(),
               fun_t.deduced_bindings(), fun_t.impl_bindings(), impl_scope));
@@ -1966,6 +2018,28 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
           e->set_static_type(TupleValue::Empty());
           e->set_value_category(ValueCategory::Let);
           return Success();
+        case IntrinsicExpression::Intrinsic::Alloc: {
+          if (intrinsic_exp.args().fields().size() != 1) {
+            return CompilationError(e->source_loc())
+                   << "__intrinsic_new takes 1 argument";
+          }
+          auto arg_type = &intrinsic_exp.args().fields()[0]->static_type();
+          e->set_static_type(arena_->New<PointerType>(arg_type));
+          e->set_value_category(ValueCategory::Let);
+          return Success();
+        }
+        case IntrinsicExpression::Intrinsic::Dealloc: {
+          if (intrinsic_exp.args().fields().size() != 1) {
+            return CompilationError(e->source_loc())
+                   << "__intrinsic_new takes 1 argument";
+          }
+          auto arg_type = &intrinsic_exp.args().fields()[0]->static_type();
+          CARBON_RETURN_IF_ERROR(
+              ExpectPointerType(e->source_loc(), "*", arg_type));
+          e->set_static_type(TupleValue::Empty());
+          e->set_value_category(ValueCategory::Let);
+          return Success();
+        }
       }
     }
     case ExpressionKind::IntTypeLiteral:

+ 47 - 13
explorer/interpreter/value.cpp

@@ -175,8 +175,11 @@ static auto SetFieldImpl(
       return arena->New<StructValue>(elements);
     }
     case Value::Kind::NominalClassValue: {
-      return SetFieldImpl(arena, &cast<NominalClassValue>(*value).inits(),
-                          path_begin, path_end, field_value, source_loc);
+      const NominalClassValue& object = cast<NominalClassValue>(*value);
+      CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> inits,
+                              SetFieldImpl(arena, &object.inits(), path_begin,
+                                           path_end, field_value, source_loc));
+      return arena->New<NominalClassValue>(&object.type(), inits);
     }
     case Value::Kind::TupleValue: {
       std::vector<Nonnull<const Value*>> elements =
@@ -280,13 +283,48 @@ void Value::Print(llvm::raw_ostream& out) const {
     case Value::Kind::BoolValue:
       out << (cast<BoolValue>(*this).value() ? "true" : "false");
       break;
-    case Value::Kind::FunctionValue:
-      out << "fun<" << cast<FunctionValue>(*this).declaration().name() << ">";
+    case Value::Kind::FunctionValue: {
+      const FunctionValue& fun = cast<FunctionValue>(*this);
+      out << "fun<" << fun.declaration().name() << ">";
+      if (!fun.type_args().empty()) {
+        out << "[";
+        llvm::ListSeparator sep;
+        for (const auto& [ty_var, ty_arg] : fun.type_args()) {
+          out << sep << *ty_var << "=" << *ty_arg;
+        }
+        out << "]";
+      }
+      if (!fun.witnesses().empty()) {
+        out << "{|";
+        llvm::ListSeparator sep;
+        for (const auto& [impl_bind, witness] : fun.witnesses()) {
+          out << sep << *witness;
+        }
+        out << "|}";
+      }
       break;
-    case Value::Kind::BoundMethodValue:
-      out << "bound_method<"
-          << cast<BoundMethodValue>(*this).declaration().name() << ">";
+    }
+    case Value::Kind::BoundMethodValue: {
+      const BoundMethodValue& method = cast<BoundMethodValue>(*this);
+      out << "bound_method<" << method.declaration().name() << ">";
+      if (!method.type_args().empty()) {
+        out << "[";
+        llvm::ListSeparator sep;
+        for (const auto& [ty_var, ty_arg] : method.type_args()) {
+          out << sep << *ty_var << "=" << *ty_arg;
+        }
+        out << "]";
+      }
+      if (!method.witnesses().empty()) {
+        out << "{|";
+        llvm::ListSeparator sep;
+        for (const auto& [impl_bind, witness] : method.witnesses()) {
+          out << sep << *witness;
+        }
+        out << "|}";
+      }
       break;
+    }
     case Value::Kind::PointerValue:
       out << "ptr<" << cast<PointerValue>(*this).address() << ">";
       break;
@@ -316,14 +354,10 @@ void Value::Print(llvm::raw_ostream& out) const {
       out << "fn ";
       if (!fn_type.deduced_bindings().empty()) {
         out << "[";
-        unsigned int i = 0;
+        llvm::ListSeparator sep;
         for (Nonnull<const GenericBinding*> deduced :
              fn_type.deduced_bindings()) {
-          if (i != 0) {
-            out << ", ";
-          }
-          out << deduced->name() << ":! " << deduced->type();
-          ++i;
+          out << sep << *deduced;
         }
         out << "]";
       }

+ 1 - 1
explorer/testdata/addr/fail-method-me-type.carbon

@@ -32,7 +32,7 @@ class Point {
 
 fn Main() -> i32 {
   var p: Point = Point.Origin();
-  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/addr/fail-method-me-type.carbon:[[@LINE+1]]: type error in method access: 'class Point' is not implicitly convertible to 'class Shape'
+  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/addr/fail-method-me-type.carbon:[[@LINE+1]]: type error in method access, receiver type: 'class Point' is not implicitly convertible to 'class Shape'
   var x: auto = p.GetSetX(42);
   if (p.x == 42) {
     return x;

+ 0 - 4
explorer/testdata/basic_syntax/trace.carbon

@@ -18,10 +18,6 @@
 // CHECK: ********** starting execution **********
 // CHECK: ********** initializing globals **********
 // CHECK: ********** calling main function **********
-// CHECK: {
-// CHECK: stack:
-// CHECK: memory:
-// CHECK: }
 // CHECK: --- step exp Main() .0. (<Main()>:0) --->
 // CHECK: result: 0
 

+ 45 - 0
explorer/testdata/generic_function/generic_method.carbon

@@ -0,0 +1,45 @@
+// 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: 0
+
+package ExplorerTest api;
+
+class Cell(T:! Type) {
+  fn Create(x: T) -> Cell(T) { return { .data = x }; }
+
+  fn Get[me: Self]() -> T {
+    return me.data;
+  }
+  fn Put[addr me: Self*](x: T) {
+    (*me).data = x;
+  }
+  fn CreateOther[me: Self, U:! Type](x: U) -> Cell(U) {
+    return {.data = x};
+  }
+  var data: T;
+}
+
+class Integer {
+  var int: i32;
+}
+
+impl Integer as ImplicitAs(Integer) {
+  fn Convert[me: Self]() -> Integer { return me; }
+}
+
+fn Main() -> i32 {
+  var i: Integer = {.int = 1};
+  var c: Cell(Integer) = Cell(Integer).Create(i);
+  i = {.int = 0};
+  c.Put(i);
+  var j: Integer = c.Get();
+  var d: Cell(Integer) = c.CreateOther(j);
+  return c.data.int + d.data.int;
+}

+ 49 - 0
explorer/testdata/impl/generic_method_impl.carbon

@@ -0,0 +1,49 @@
+// 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: 3
+
+package ExplorerTest api;
+
+class Cell(T:! Type) {
+  fn Create(x: T) -> Cell(T) { return { .data = x }; }
+
+  fn Get[me: Self]() -> T {
+    return me.data;
+  }
+  fn Put[addr me: Self*](x: T) {
+    (*me).data = x;
+  }
+  fn Update[addr me: Self*, U:! ImplicitAs(T)](x: U) {
+    (*me).data = x;
+  }
+  fn CreateOther[me: Self, U:! Type](x: U) -> Cell(U) {
+    return {.data = x};
+  }
+  var data: T;
+}
+
+class Integer {
+  var int: i32;
+}
+
+impl Integer as ImplicitAs(Integer) {
+  fn Convert[me: Self]() -> Integer { return me; }
+}
+
+fn Main() -> i32 {
+  var i: Integer = {.int = 1};
+  var c: Cell(Integer) = Cell(Integer).Create(i); // c contains 1
+  i = {.int = 2};
+  var j: Integer = c.Get(); // j == 1
+  c.Put(i);    // c contains 2
+  c.Update(j); // c contains 1
+  var d: Cell(Integer) = c.CreateOther(i); // d contains 2
+  return c.data.int + d.data.int;
+}

+ 21 - 0
explorer/testdata/pointer/aliasing.carbon

@@ -0,0 +1,21 @@
+// 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: 0
+
+package ExplorerTest api;
+
+// Demonstrate the aliasing behavior of pointers.
+
+fn Main() -> i32 {
+  var p: i32* = heap.New(5);
+  var q: i32* = p;
+  *q = 0;
+  return *p;
+}

+ 18 - 0
explorer/testdata/pointer/fail_use_after_free.carbon

@@ -0,0 +1,18 @@
+// 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: %{not} %{explorer} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{explorer} %s
+
+package ExplorerTest api;
+
+fn Main() -> i32 {
+  var p: i32* = heap.New(5);
+  heap.Delete(p);
+  // CHECK: RUNTIME ERROR: {{.*}}/explorer/testdata/pointer/fail_use_after_free.carbon:[[@LINE+1]]: undefined behavior: access to dead value 5
+  return *p;
+}

+ 26 - 0
explorer/testdata/pointer/heap_alloc_lifetime.carbon

@@ -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
+//
+// 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: 0
+
+package ExplorerTest api;
+
+// The lifetime of a heap-allocated object is until it is explicitely
+// deleted. Its lifetime is unrelated to the procedure call stack.
+
+fn AllocateInteger() -> i32* {
+  return heap.New(0);
+}
+
+fn Main() -> i32 {
+  var p: i32* = AllocateInteger();
+  var y: i32 = *p;
+  heap.Delete(p);
+  return y;
+}

+ 19 - 0
explorer/testdata/pointer/intrinsic_new.carbon

@@ -0,0 +1,19 @@
+// 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: 0
+
+package ExplorerTest api;
+
+fn Main() -> i32 {
+  var p: i32* = __intrinsic_new(0);
+  var y: i32 = *p;
+  __intrinsic_delete(p);
+  return y;
+}

+ 19 - 0
explorer/testdata/pointer/new_and_delete.carbon

@@ -0,0 +1,19 @@
+// 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: 0
+
+package ExplorerTest api;
+
+fn Main() -> i32 {
+  var p: i32* = heap.New(0);
+  var y: i32 = *p;
+  heap.Delete(p);
+  return y;
+}