瀏覽代碼

Suppress Print intrinsic output in the fuzzer. (#2784)

Related to #2780, but mostly an issue when running over the committed corpus since we use Print a lot.
Jon Ross-Perkins 3 年之前
父節點
當前提交
62ee4a5c7a

+ 5 - 2
explorer/fuzzing/fuzzer_util.cpp

@@ -86,8 +86,11 @@ auto ParseAndExecute(const Fuzzing::CompilationUnit& compilation_unit)
   AddPrelude(*prelude_path, &arena, &ast.declarations,
              &ast.num_prelude_declarations);
   TraceStream trace_stream;
-  CARBON_ASSIGN_OR_RETURN(ast, AnalyzeProgram(&arena, ast, &trace_stream));
-  return ExecProgram(&arena, ast, &trace_stream);
+
+  // Use llvm::nulls() to suppress output from the Print intrinsic.
+  CARBON_ASSIGN_OR_RETURN(
+      ast, AnalyzeProgram(&arena, ast, &trace_stream, &llvm::nulls()));
+  return ExecProgram(&arena, ast, &trace_stream, &llvm::nulls());
 }
 
 }  // namespace Carbon

+ 7 - 4
explorer/interpreter/exec_program.cpp

@@ -19,7 +19,8 @@
 namespace Carbon {
 
 auto AnalyzeProgram(Nonnull<Arena*> arena, AST ast,
-                    Nonnull<TraceStream*> trace_stream) -> ErrorOr<AST> {
+                    Nonnull<TraceStream*> trace_stream,
+                    Nonnull<llvm::raw_ostream*> print_stream) -> ErrorOr<AST> {
   if (trace_stream->is_enabled()) {
     *trace_stream << "********** source program **********\n";
     for (int i = ast.num_prelude_declarations;
@@ -46,7 +47,8 @@ auto AnalyzeProgram(Nonnull<Arena*> arena, AST ast,
   if (trace_stream->is_enabled()) {
     *trace_stream << "********** type checking **********\n";
   }
-  CARBON_RETURN_IF_ERROR(TypeChecker(arena, trace_stream).TypeCheck(ast));
+  CARBON_RETURN_IF_ERROR(
+      TypeChecker(arena, trace_stream, print_stream).TypeCheck(ast));
 
   if (trace_stream->is_enabled()) {
     *trace_stream << "********** resolving unformed variables **********\n";
@@ -64,11 +66,12 @@ auto AnalyzeProgram(Nonnull<Arena*> arena, AST ast,
 }
 
 auto ExecProgram(Nonnull<Arena*> arena, AST ast,
-                 Nonnull<TraceStream*> trace_stream) -> ErrorOr<int> {
+                 Nonnull<TraceStream*> trace_stream,
+                 Nonnull<llvm::raw_ostream*> print_stream) -> ErrorOr<int> {
   if (trace_stream->is_enabled()) {
     *trace_stream << "********** starting execution **********\n";
   }
-  return InterpProgram(ast, arena, trace_stream);
+  return InterpProgram(ast, arena, trace_stream, print_stream);
 }
 
 }  // namespace Carbon

+ 4 - 2
explorer/interpreter/exec_program.h

@@ -17,11 +17,13 @@ namespace Carbon {
 
 // Perform semantic analysis on the AST.
 auto AnalyzeProgram(Nonnull<Arena*> arena, AST ast,
-                    Nonnull<TraceStream*> trace_stream) -> ErrorOr<AST>;
+                    Nonnull<TraceStream*> trace_stream,
+                    Nonnull<llvm::raw_ostream*> print_stream) -> ErrorOr<AST>;
 
 // Run the program's `Main` function.
 auto ExecProgram(Nonnull<Arena*> arena, AST ast,
-                 Nonnull<TraceStream*> trace_stream) -> ErrorOr<int>;
+                 Nonnull<TraceStream*> trace_stream,
+                 Nonnull<llvm::raw_ostream*> print_stream) -> ErrorOr<int>;
 
 }  // namespace Carbon
 

+ 18 - 9
explorer/interpreter/interpreter.cpp

@@ -59,11 +59,13 @@ class Interpreter {
   // traces if `trace` is true. `phase` indicates whether it executes at
   // compile time or run time.
   Interpreter(Phase phase, Nonnull<Arena*> arena,
-              Nonnull<TraceStream*> trace_stream)
+              Nonnull<TraceStream*> trace_stream,
+              Nonnull<llvm::raw_ostream*> print_stream)
       : arena_(arena),
         heap_(arena),
         todo_(MakeTodo(phase, &heap_)),
         trace_stream_(trace_stream),
+        print_stream_(print_stream),
         phase_(phase) {}
 
   // Runs all the steps of `action`.
@@ -178,6 +180,10 @@ class Interpreter {
   ActionStack todo_;
 
   Nonnull<TraceStream*> trace_stream_;
+
+  // The stream for the Print intrinsic.
+  Nonnull<llvm::raw_ostream*> print_stream_;
+
   Phase phase_;
 };
 
@@ -1561,17 +1567,17 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
               intrinsic.source_loc(), format_string, num_format_args));
           switch (num_format_args) {
             case 0:
-              llvm::outs() << llvm::formatv(format_string);
+              *print_stream_ << llvm::formatv(format_string);
               break;
             case 1:
-              llvm::outs() << llvm::formatv(format_string,
-                                            cast<IntValue>(*args[1]).value());
+              *print_stream_ << llvm::formatv(format_string,
+                                              cast<IntValue>(*args[1]).value());
               break;
             default:
               CARBON_FATAL() << "Too many format args: " << num_format_args;
           }
           // Implicit newline; currently no way to disable it.
-          llvm::outs() << "\n";
+          *print_stream_ << "\n";
           return todo_.FinishAction(TupleValue::Empty());
         }
         case IntrinsicExpression::Intrinsic::Assert: {
@@ -2438,8 +2444,9 @@ auto Interpreter::RunAllSteps(std::unique_ptr<Action> action)
 }
 
 auto InterpProgram(const AST& ast, Nonnull<Arena*> arena,
-                   Nonnull<TraceStream*> trace_stream) -> ErrorOr<int> {
-  Interpreter interpreter(Phase::RunTime, arena, trace_stream);
+                   Nonnull<TraceStream*> trace_stream,
+                   Nonnull<llvm::raw_ostream*> print_stream) -> ErrorOr<int> {
+  Interpreter interpreter(Phase::RunTime, arena, trace_stream, print_stream);
   if (trace_stream->is_enabled()) {
     *trace_stream << "********** initializing globals **********\n";
   }
@@ -2460,9 +2467,11 @@ auto InterpProgram(const AST& ast, Nonnull<Arena*> arena,
 }
 
 auto InterpExp(Nonnull<const Expression*> e, Nonnull<Arena*> arena,
-               Nonnull<TraceStream*> trace_stream)
+               Nonnull<TraceStream*> trace_stream,
+               Nonnull<llvm::raw_ostream*> print_stream)
     -> ErrorOr<Nonnull<const Value*>> {
-  Interpreter interpreter(Phase::CompileTime, arena, trace_stream);
+  Interpreter interpreter(Phase::CompileTime, arena, trace_stream,
+                          print_stream);
   CARBON_RETURN_IF_ERROR(
       interpreter.RunAllSteps(std::make_unique<ExpressionAction>(e)));
   return interpreter.result();

+ 4 - 2
explorer/interpreter/interpreter.h

@@ -25,13 +25,15 @@ namespace Carbon {
 // Interprets the program defined by `ast`, allocating values on `arena` and
 // printing traces if `trace` is true.
 auto InterpProgram(const AST& ast, Nonnull<Arena*> arena,
-                   Nonnull<TraceStream*> trace_stream) -> ErrorOr<int>;
+                   Nonnull<TraceStream*> trace_stream,
+                   Nonnull<llvm::raw_ostream*> print_stream) -> ErrorOr<int>;
 
 // Interprets `e` at compile-time, allocating values on `arena` and
 // printing traces if `trace` is true. The caller must ensure that all the
 // code this evaluates has been typechecked.
 auto InterpExp(Nonnull<const Expression*> e, Nonnull<Arena*> arena,
-               Nonnull<TraceStream*> trace_stream)
+               Nonnull<TraceStream*> trace_stream,
+               Nonnull<llvm::raw_ostream*> print_stream)
     -> ErrorOr<Nonnull<const Value*>>;
 
 // Attempts to match `v` against the pattern `p`, returning whether matching

+ 38 - 48
explorer/interpreter/type_checker.cpp

@@ -756,7 +756,7 @@ auto TypeChecker::ImplicitlyConvert(std::string_view context,
                               ImplicitlyConvert(context, impl_scope, source,
                                                 arena_->New<TypeType>()));
       CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> converted_value,
-                              InterpExp(source_as_type, arena_, trace_stream_));
+                              InterpExp(source_as_type));
       CARBON_ASSIGN_OR_RETURN(
           Nonnull<const ConstraintType*> destination_constraint,
           ConvertToConstraintType(source->source_loc(), "implicit conversion",
@@ -1363,7 +1363,7 @@ auto TypeChecker::ArgumentDeduction::Finish(
 
     // Evaluate the argument to get the value.
     CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> value,
-                            InterpExp(arg, type_checker.arena_, trace_stream_));
+                            type_checker.InterpExp(arg));
     if (trace_stream_->is_enabled()) {
       *trace_stream_ << "evaluated generic parameter " << *binding << " as "
                      << *value << "\n";
@@ -2717,9 +2717,8 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
               ExpectExactType(index.offset().source_loc(), "tuple index",
                               arena_->New<IntType>(),
                               &index.offset().static_type(), impl_scope));
-          CARBON_ASSIGN_OR_RETURN(
-              auto offset_value,
-              InterpExp(&index.offset(), arena_, trace_stream_));
+          CARBON_ASSIGN_OR_RETURN(auto offset_value,
+                                  InterpExp(&index.offset()));
           int i = cast<IntValue>(*offset_value).value();
           if (i < 0 || i >= static_cast<int>(tuple_type.elements().size())) {
             return ProgramError(e->source_loc())
@@ -2936,9 +2935,8 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
           // TODO: Per the language rules, we are supposed to also perform
           // lookup into `type` and report an ambiguity if the name is found in
           // both places.
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const Value*> type,
-              InterpExp(&access.object(), arena_, trace_stream_));
+          CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> type,
+                                  InterpExp(&access.object()));
           CARBON_ASSIGN_OR_RETURN(
               ConstraintLookupResult result,
               LookupInConstraint(e->source_loc(), "member access", &object_type,
@@ -2987,9 +2985,8 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
         case Value::Kind::TypeType: {
           // This is member access into an unconstrained type. Evaluate it and
           // perform lookup in the result.
-          CARBON_ASSIGN_OR_RETURN(
-              Nonnull<const Value*> type,
-              InterpExp(&access.object(), arena_, trace_stream_));
+          CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> type,
+                                  InterpExp(&access.object()));
           CARBON_RETURN_IF_ERROR(
               ExpectCompleteType(access.source_loc(), "member access", type));
           switch (type->kind()) {
@@ -3119,7 +3116,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
       // Evaluate the member name expression to determine which member we're
       // accessing.
       CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> member_name_value,
-                              InterpExp(&access.path(), arena_, trace_stream_));
+                              InterpExp(&access.path()));
       const auto& member_name = cast<MemberName>(*member_name_value);
       access.set_member(&member_name);
       bool is_instance_member = IsInstanceMember(&member_name.member());
@@ -3130,8 +3127,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
         if (IsTypeOfType(&access.object().static_type())) {
           // This is `Type.(member_name)`, where `member_name` doesn't specify
           // a type. This access doesn't perform instance binding.
-          CARBON_ASSIGN_OR_RETURN(
-              base_type, InterpExp(&access.object(), arena_, trace_stream_));
+          CARBON_ASSIGN_OR_RETURN(base_type, InterpExp(&access.object()));
           has_instance = false;
         } else {
           // This is `value.(member_name)`, where `member_name` doesn't specify
@@ -3382,12 +3378,10 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
           // `&` between type-of-types performs constraint combination.
           // TODO: Should this be done via an intrinsic?
           if (IsTypeOfType(ts[0]) && IsTypeOfType(ts[1])) {
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const Value*> lhs,
-                InterpExp(op.arguments()[0], arena_, trace_stream_));
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const Value*> rhs,
-                InterpExp(op.arguments()[1], arena_, trace_stream_));
+            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> lhs,
+                                    InterpExp(op.arguments()[0]));
+            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> rhs,
+                                    InterpExp(op.arguments()[1]));
             CARBON_ASSIGN_OR_RETURN(
                 Nonnull<const ConstraintType*> lhs_constraint,
                 ConvertToConstraintType(op.arguments()[0]->source_loc(),
@@ -3568,9 +3562,8 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
               dyn_cast<SimpleMemberAccessExpression>(&call.function());
           if (member_access &&
               isa<TypeType>(member_access->object().static_type())) {
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const Value*> type,
-                InterpExp(&member_access->object(), arena_, trace_stream_));
+            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> type,
+                                    InterpExp(&member_access->object()));
             if (isa<ChoiceType>(type)) {
               return ProgramError(e->source_loc())
                      << "alternative `" << *type << "."
@@ -3885,9 +3878,8 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
             CARBON_ASSIGN_OR_RETURN(
                 Nonnull<const Value*> type,
                 TypeCheckTypeExp(&impls_clause.type(), inner_impl_scope));
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const Value*> constraint,
-                InterpExp(&impls_clause.constraint(), arena_, trace_stream_));
+            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> constraint,
+                                    InterpExp(&impls_clause.constraint()));
             CARBON_ASSIGN_OR_RETURN(
                 Nonnull<const ConstraintType*> constraint_type,
                 ConvertToConstraintType(impls_clause.source_loc(),
@@ -3904,12 +3896,10 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
           }
           case WhereClauseKind::EqualsWhereClause: {
             const auto& equals_clause = cast<EqualsWhereClause>(*clause);
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const Value*> lhs,
-                InterpExp(&equals_clause.lhs(), arena_, trace_stream_));
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const Value*> rhs,
-                InterpExp(&equals_clause.rhs(), arena_, trace_stream_));
+            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> lhs,
+                                    InterpExp(&equals_clause.lhs()));
+            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> rhs,
+                                    InterpExp(&equals_clause.rhs()));
             if (!ValueEqual(lhs, rhs, std::nullopt)) {
               builder.AddEqualityConstraint({.values = {lhs, rhs}});
             }
@@ -3945,8 +3935,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
             // type. This is the value we'll rewrite to when type-checking a
             // member access.
             CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> replacement_value,
-                                    InterpExp(&rewrite_clause.replacement(),
-                                              arena_, trace_stream_));
+                                    InterpExp(&rewrite_clause.replacement()));
             Nonnull<const Value*> replacement_type =
                 &rewrite_clause.replacement().static_type();
 
@@ -3964,9 +3953,8 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
                 Nonnull<Expression*> converted_expression,
                 ImplicitlyConvert("rewrite constraint", inner_impl_scope,
                                   replacement_literal, constraint_type));
-            CARBON_ASSIGN_OR_RETURN(
-                Nonnull<const Value*> converted_value,
-                InterpExp(converted_expression, arena_, trace_stream_));
+            CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> converted_value,
+                                    InterpExp(converted_expression));
 
             // Add the rewrite constraint.
             builder.AddRewriteConstraint(
@@ -3998,9 +3986,8 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
           array_literal.size_expression().source_loc(), "array size",
           arena_->New<IntType>(),
           &array_literal.size_expression().static_type(), impl_scope));
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> size_value,
-          InterpExp(&array_literal.size_expression(), arena_, trace_stream_));
+      CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> size_value,
+                              InterpExp(&array_literal.size_expression()));
       if (cast<IntValue>(size_value)->value() < 0) {
         return ProgramError(array_literal.size_expression().source_loc())
                << "Array size cannot be negative";
@@ -4072,7 +4059,7 @@ auto TypeChecker::TypeCheckTypeExp(Nonnull<Expression*> type_expression,
       ImplicitlyConvert("type expression", impl_scope, type_expression,
                         arena_->New<TypeType>()));
   CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> type,
-                          InterpExp(type_expression, arena_, trace_stream_));
+                          InterpExp(type_expression));
   CARBON_CHECK(IsType(type))
       << "type expression did not produce a type, got " << *type;
   if (concrete) {
@@ -4180,8 +4167,7 @@ auto TypeChecker::TypeCheckPattern(
             auto* converted,
             ImplicitlyConvert("type of name binding", impl_scope, literal,
                               arena_->New<TypeType>()));
-        CARBON_ASSIGN_OR_RETURN(type,
-                                InterpExp(converted, arena_, trace_stream_));
+        CARBON_ASSIGN_OR_RETURN(type, InterpExp(converted));
       }
       CARBON_CHECK(IsType(type))
           << "conversion to type succeeded but didn't produce a type, got "
@@ -4306,7 +4292,7 @@ auto TypeChecker::TypeCheckPattern(
       p->set_static_type(&expression.static_type());
       // TODO: Per proposal #2188, we should form an `==` comparison here.
       CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> expr_value,
-                              InterpExp(&expression, arena_, trace_stream_));
+                              InterpExp(&expression));
       p->set_value(expr_value);
       return Success();
     }
@@ -5910,7 +5896,7 @@ auto TypeChecker::DeclareAliasDeclaration(Nonnull<AliasDeclaration*> alias,
   if (alias->target().static_type().kind() !=
       Value::Kind::TypeOfNamespaceName) {
     CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> target,
-                            InterpExp(&alias->target(), arena_, trace_stream_));
+                            InterpExp(&alias->target()));
     alias->set_constant_value(target);
   }
   return Success();
@@ -6091,9 +6077,8 @@ auto TypeChecker::DeclareDeclaration(Nonnull<Declaration*> d,
       auto& mix_decl = cast<MixDeclaration>(*d);
       CARBON_RETURN_IF_ERROR(
           TypeCheckExp(&mix_decl.mixin(), *scope_info.innermost_scope));
-      CARBON_ASSIGN_OR_RETURN(
-          Nonnull<const Value*> mixin,
-          InterpExp(&mix_decl.mixin(), arena_, trace_stream_));
+      CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> mixin,
+                              InterpExp(&mix_decl.mixin()));
       if (const auto* mixin_value = dyn_cast<MixinPseudoType>(mixin)) {
         mix_decl.set_mixin_value(mixin_value);
       } else {
@@ -6333,4 +6318,9 @@ auto TypeChecker::InstantiateImplDeclaration(
   return std::pair{impl, arena_->New<Bindings>(std::move(new_bindings))};
 }
 
+auto TypeChecker::InterpExp(Nonnull<const Expression*> e)
+    -> ErrorOr<Nonnull<const Value*>> {
+  return Carbon::InterpExp(e, arena_, trace_stream_, print_stream_);
+}
+
 }  // namespace Carbon

+ 16 - 5
explorer/interpreter/type_checker.h

@@ -38,8 +38,11 @@ using GlobalMembersMap =
 class TypeChecker {
  public:
   explicit TypeChecker(Nonnull<Arena*> arena,
-                       Nonnull<TraceStream*> trace_stream)
-      : arena_(arena), trace_stream_(trace_stream) {}
+                       Nonnull<TraceStream*> trace_stream,
+                       Nonnull<llvm::raw_ostream*> print_stream)
+      : arena_(arena),
+        trace_stream_(trace_stream),
+        print_stream_(print_stream) {}
 
   // Type-checks `ast` and sets properties such as `static_type`, as documented
   // on the individual nodes.
@@ -523,9 +526,14 @@ class TypeChecker {
 
   // Instantiate an impl with the given set of bindings, including one or more
   // template bindings.
-  ErrorOr<std::pair<Nonnull<ImplDeclaration*>, Nonnull<Bindings*>>>
-  InstantiateImplDeclaration(Nonnull<const ImplDeclaration*> pattern,
-                             Nonnull<const Bindings*> bindings) const;
+  auto InstantiateImplDeclaration(Nonnull<const ImplDeclaration*> pattern,
+                                  Nonnull<const Bindings*> bindings) const
+      -> ErrorOr<std::pair<Nonnull<ImplDeclaration*>, Nonnull<Bindings*>>>;
+
+  // Wraps the interpreter's InterpExp, forwarding TypeChecker members as
+  // arguments.
+  auto InterpExp(Nonnull<const Expression*> e)
+      -> ErrorOr<Nonnull<const Value*>>;
 
   Nonnull<Arena*> arena_;
   Builtins builtins_;
@@ -535,6 +543,9 @@ class TypeChecker {
 
   Nonnull<TraceStream*> trace_stream_;
 
+  // The stream for the Print intrinsic.
+  Nonnull<llvm::raw_ostream*> print_stream_;
+
   // The top-level ImplScope, containing `impl` declarations that should be
   // usable from any context. This is used when we want to try to refine a
   // symbolic witness into an impl witness during substitution.

+ 4 - 2
explorer/main.cpp

@@ -105,7 +105,8 @@ auto ExplorerMain(int argc, char** argv, void* static_for_main_addr,
   auto time_after_prelude = std::chrono::system_clock::now();
 
   // Semantically analyze the parsed program.
-  if (ErrorOr<AST> analyze_result = AnalyzeProgram(&arena, ast, &trace_stream);
+  if (ErrorOr<AST> analyze_result =
+          AnalyzeProgram(&arena, ast, &trace_stream, &llvm::outs());
       analyze_result.ok()) {
     ast = *std::move(analyze_result);
   } else {
@@ -117,7 +118,8 @@ auto ExplorerMain(int argc, char** argv, void* static_for_main_addr,
 
   // Run the program.
   auto ret = EXIT_SUCCESS;
-  if (ErrorOr<int> exec_result = ExecProgram(&arena, ast, &trace_stream);
+  if (ErrorOr<int> exec_result =
+          ExecProgram(&arena, ast, &trace_stream, &llvm::outs());
       exec_result.ok()) {
     // Print the return code to stdout.
     llvm::outs() << "result: " << *exec_result << "\n";