Merge pull request #23922 from atrick/comment-noescape

diff --git a/cmake/modules/SwiftComponents.cmake b/cmake/modules/SwiftComponents.cmake
index a637e31..cae4d32 100644
--- a/cmake/modules/SwiftComponents.cmake
+++ b/cmake/modules/SwiftComponents.cmake
@@ -84,6 +84,7 @@
 else()
   list(REMOVE_ITEM _SWIFT_DEFAULT_COMPONENTS "sourcekit-xpc-service")
 endif()
+list(REMOVE_ITEM _SWIFT_DEFAULT_COMPONENTS "stdlib-experimental")
 
 macro(swift_configure_components)
   # Set the SWIFT_INSTALL_COMPONENTS variable to the default value if it is not passed in via -D
diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp
index 81c5283..748dde7 100644
--- a/lib/AST/DeclContext.cpp
+++ b/lib/AST/DeclContext.cpp
@@ -309,7 +309,8 @@
 /// domains, this ensures that only sufficiently-conservative access patterns
 /// are used.
 ResilienceExpansion DeclContext::getResilienceExpansion() const {
-  for (const auto *dc = this; dc->isLocalContext(); dc = dc->getParent()) {
+  for (const auto *dc = getLocalContext(); dc && dc->isLocalContext();
+       dc = dc->getParent()) {
     // Default argument initializer contexts have their resilience expansion
     // set when they're type checked.
     if (isa<DefaultArgumentInitializer>(dc)) {
diff --git a/lib/SILGen/SILGenPattern.cpp b/lib/SILGen/SILGenPattern.cpp
index 19448f5..ed0ea22 100644
--- a/lib/SILGen/SILGenPattern.cpp
+++ b/lib/SILGen/SILGenPattern.cpp
@@ -2497,11 +2497,48 @@
   PatternMatchEmission &Emission;
 };
 
+namespace {
+
+struct UnexpectedEnumCaseInfo {
+  CanType subjectTy;
+  ManagedValue metatype;
+  ManagedValue rawValue;
+  NullablePtr<const EnumDecl> singleObjCEnum;
+
+  UnexpectedEnumCaseInfo(CanType subjectTy, ManagedValue metatype,
+                         ManagedValue rawValue, const EnumDecl *singleObjCEnum)
+      : subjectTy(subjectTy), metatype(metatype), rawValue(rawValue),
+        singleObjCEnum(singleObjCEnum) {
+    assert(isa<MetatypeInst>(metatype));
+    assert(bool(rawValue) && isa<UncheckedTrivialBitCastInst>(rawValue));
+    assert(singleObjCEnum->hasRawType());
+  }
+
+  UnexpectedEnumCaseInfo(CanType subjectTy, ManagedValue valueMetatype)
+      : subjectTy(subjectTy), metatype(valueMetatype), rawValue(),
+        singleObjCEnum() {
+    assert(isa<ValueMetatypeInst>(valueMetatype));
+  }
+
+  bool isSingleObjCEnum() const { return singleObjCEnum.isNonNull(); }
+
+  void cleanupInstsIfUnused() {
+    auto f = [](SILValue v) {
+      if (!v->use_empty())
+        return;
+      cast<SingleValueInstruction>(v)->eraseFromParent();
+    };
+    f(metatype.getValue());
+    if (rawValue)
+      f(rawValue.getValue());
+  }
+};
+
+} // end anonymous namespace
+
 static void emitDiagnoseOfUnexpectedEnumCaseValue(SILGenFunction &SGF,
                                                   SILLocation loc,
-                                                  ManagedValue value,
-                                                  Type subjectTy,
-                                                  const EnumDecl *enumDecl) {
+                                                  UnexpectedEnumCaseInfo ueci) {
   ASTContext &ctx = SGF.getASTContext();
   auto diagnoseFailure = ctx.getDiagnoseUnexpectedEnumCaseValue();
   if (!diagnoseFailure) {
@@ -2509,25 +2546,6 @@
     return;
   }
 
-  assert(enumDecl->isObjC());
-  assert(enumDecl->hasRawType());
-  assert(value.getType().isTrivial(SGF.F));
-
-  // Get the enum type as an Any.Type value.
-  SILType metatypeType = SGF.getLoweredType(
-      AbstractionPattern::getOpaque(),
-      MetatypeType::get(subjectTy));
-  SILValue metatype = SGF.B.createMetatype(loc, metatypeType);
-
-  // Bitcast the enum value to its raw type. (This is only safe for @objc
-  // enums.)
-  SILType loweredRawType = SGF.getLoweredType(enumDecl->getRawType());
-  assert(loweredRawType.isTrivial(SGF.F));
-  assert(loweredRawType.isObject());
-  auto rawValue = SGF.B.createUncheckedTrivialBitCast(loc, value,
-                                                      loweredRawType);
-  auto materializedRawValue = rawValue.materialize(SGF, loc);
-
   auto genericSig = diagnoseFailure->getGenericSignature();
   auto subs = SubstitutionMap::get(
       genericSig,
@@ -2537,10 +2555,10 @@
         assert(genericParam->getIndex() < 2);
         switch (genericParam->getIndex()) {
         case 0:
-          return subjectTy;
+          return ueci.subjectTy;
 
         case 1:
-          return enumDecl->getRawType();
+          return ueci.singleObjCEnum.get()->getRawType();
 
         default:
           llvm_unreachable("wrong generic signature for expected case value");
@@ -2548,16 +2566,14 @@
       },
       LookUpConformanceInSignature(*genericSig));
 
-  SGF.emitApplyOfLibraryIntrinsic(loc, diagnoseFailure, subs,
-                                  {ManagedValue::forUnmanaged(metatype),
-                                   materializedRawValue},
-                                  SGFContext());
+  SGF.emitApplyOfLibraryIntrinsic(
+      loc, diagnoseFailure, subs,
+      {ueci.metatype, ueci.rawValue.materialize(SGF, loc)}, SGFContext());
 }
 
 static void emitDiagnoseOfUnexpectedEnumCase(SILGenFunction &SGF,
                                              SILLocation loc,
-                                             ManagedValue value,
-                                             Type subjectTy) {
+                                             UnexpectedEnumCaseInfo ueci) {
   ASTContext &ctx = SGF.getASTContext();
   auto diagnoseFailure = ctx.getDiagnoseUnexpectedEnumCase();
   if (!diagnoseFailure) {
@@ -2565,21 +2581,14 @@
     return;
   }
 
-  // Get the switched-upon value's type.
-  SILType metatypeType = SGF.getLoweredType(
-      AbstractionPattern::getOpaque(),
-      MetatypeType::get(subjectTy)->getCanonicalType());
-  ManagedValue metatype = SGF.B.createValueMetatype(loc, metatypeType, value);
-
   auto diagnoseSignature = diagnoseFailure->getGenericSignature();
   auto genericArgsMap = SubstitutionMap::get(
       diagnoseSignature,
-      [&](SubstitutableType *type) -> Type { return subjectTy; },
+      [&](SubstitutableType *type) -> Type { return ueci.subjectTy; },
       LookUpConformanceInSignature(*diagnoseSignature));
 
   SGF.emitApplyOfLibraryIntrinsic(loc, diagnoseFailure, genericArgsMap,
-                                  metatype,
-                                  SGFContext());
+                                  ueci.metatype, SGFContext());
 }
 
 static void switchCaseStmtSuccessCallback(SILGenFunction &SGF,
@@ -2778,6 +2787,38 @@
     return {subjectMV.copy(*this, S), CastConsumptionKind::TakeAlways};
   }());
 
+  // If we need to diagnose an unexpected enum case or unexpected enum case
+  // value, we need access to a value metatype for the subject. Emit this state
+  // now before we emit the actual switch to ensure that the subject has not
+  // been consumed.
+  auto unexpectedEnumCaseInfo = ([&]() -> UnexpectedEnumCaseInfo {
+    SILLocation loc = RegularLocation::getAutoGeneratedLocation();
+    CanType canSubjectTy = subjectTy->getCanonicalType();
+    CanType metatypeType = MetatypeType::get(canSubjectTy)->getCanonicalType();
+    SILType loweredMetatypeType =
+        getLoweredType(AbstractionPattern::getOpaque(), metatypeType);
+    ManagedValue value = subject.getFinalManagedValue();
+
+    if (auto *singleEnumDecl = canSubjectTy->getEnumOrBoundGenericEnum()) {
+      if (singleEnumDecl->isObjC()) {
+        auto metatype = ManagedValue::forUnmanaged(
+            B.createMetatype(loc, loweredMetatypeType));
+
+        // Bitcast the enum value to its raw type. (This is only safe for @objc
+        // enums.)
+        SILType loweredRawType = getLoweredType(singleEnumDecl->getRawType());
+        assert(loweredRawType.isTrivial(F));
+        assert(loweredRawType.isObject());
+        auto rawValue =
+            B.createUncheckedTrivialBitCast(loc, value, loweredRawType);
+        return {canSubjectTy, metatype, rawValue, singleEnumDecl};
+      }
+    }
+
+    return {canSubjectTy,
+            B.createValueMetatype(loc, loweredMetatypeType, value)};
+  }());
+
   auto failure = [&](SILLocation location) {
     // If we fail to match anything, we trap. This can happen with a switch
     // over an @objc enum, which may contain any value of its underlying type,
@@ -2786,18 +2827,12 @@
     SWIFT_DEFER { B.createUnreachable(location); };
 
     // Special case: if it's a single @objc enum, we can print the raw value.
-    CanType ty = S->getSubjectExpr()->getType()->getCanonicalType();
-    if (auto *singleEnumDecl = ty->getEnumOrBoundGenericEnum()) {
-      if (singleEnumDecl->isObjC()) {
-        emitDiagnoseOfUnexpectedEnumCaseValue(*this, location,
-                                              subject.getFinalManagedValue(),
-                                              subjectTy, singleEnumDecl);
-        return;
-      }
+    if (unexpectedEnumCaseInfo.isSingleObjCEnum()) {
+      emitDiagnoseOfUnexpectedEnumCaseValue(*this, location,
+                                            unexpectedEnumCaseInfo);
+      return;
     }
-    emitDiagnoseOfUnexpectedEnumCase(*this, location,
-                                     subject.getFinalManagedValue(),
-                                     subjectTy);
+    emitDiagnoseOfUnexpectedEnumCase(*this, location, unexpectedEnumCaseInfo);
   };
 
   // Set up an initial clause matrix.
@@ -2823,6 +2858,10 @@
   } else {
     B.emitBlock(contBB);
   }
+
+  // Now that we have emitted everything, see if our unexpected enum case info
+  // metatypes were actually used. If not, delete them.
+  unexpectedEnumCaseInfo.cleanupInstsIfUnused();
 }
 
 void SILGenFunction::emitSwitchFallthrough(FallthroughStmt *S) {
diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp
index 1cfc7a3..10554cb 100644
--- a/lib/Sema/CSApply.cpp
+++ b/lib/Sema/CSApply.cpp
@@ -2863,10 +2863,8 @@
       auto tupleTy =
           TupleType::get(TupleTypeElt(paramTy, ctx.Id_dynamicMember), ctx);
 
-      auto loc = nameLoc;
-      Expr *index = TupleExpr::create(ctx, loc, argExpr, ctx.Id_dynamicMember,
-                                      loc, loc, /*hasTrailingClosure*/ false,
-                                      /*implicit*/ true);
+      Expr *index =
+          TupleExpr::createImplicit(ctx, argExpr, ctx.Id_dynamicMember);
       index->setType(tupleTy);
       cs.cacheType(index);
 
@@ -2874,7 +2872,7 @@
       return buildSubscript(
           base, index, ctx.Id_dynamicMember,
           /*trailingClosure*/ false, cs.getConstraintLocator(expr),
-          /*isImplicit*/ false, AccessSemantics::Ordinary, overload);
+          /*isImplicit*/ true, AccessSemantics::Ordinary, overload);
     }
 
     Type getTypeOfDynamicMemberIndex(const SelectedOverload &overload) {
diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp
index 10bc318..27c31b8 100644
--- a/lib/Sema/ResilienceDiagnostics.cpp
+++ b/lib/Sema/ResilienceDiagnostics.cpp
@@ -25,7 +25,8 @@
 
 std::pair<FragileFunctionKind, bool>
 TypeChecker::getFragileFunctionKind(const DeclContext *DC) {
-  for (; DC->isLocalContext(); DC = DC->getParent()) {
+  for (DC = DC->getLocalContext(); DC && DC->isLocalContext();
+       DC = DC->getParent()) {
     if (isa<DefaultArgumentInitializer>(DC)) {
       // Default argument generators of public functions cannot reference
       // @usableFromInline declarations; all other fragile function kinds
@@ -176,6 +177,11 @@
     diagName = accessor->getStorage()->getFullName();
   }
 
+  // Swift 5.0 did not check the underlying types of local typealiases.
+  // FIXME: Conditionalize this once we have a new language mode.
+  if (isa<TypeAliasDecl>(DC))
+    downgradeToWarning = DowngradeToWarning::Yes;
+
   auto diagID = diag::resilience_decl_unavailable;
   if (downgradeToWarning == DowngradeToWarning::Yes)
     diagID = diag::resilience_decl_unavailable_warn;
diff --git a/test/Generics/non_generic_derived_class.swift b/test/Generics/non_generic_derived_class.swift
index bb89992..1934011 100644
--- a/test/Generics/non_generic_derived_class.swift
+++ b/test/Generics/non_generic_derived_class.swift
@@ -9,3 +9,10 @@
 class Derived : Base<Int> {}
 
 var a = Derived.f(42)
+
+protocol SR9160_EmptyProtocol {}
+class SR9160_AbstractFoobar<Foo> {}
+// This used to cause the swift compiler to never finish compiling.
+final class SR9160_SomeFoobar: SR9160_AbstractFoobar<SR9160_SomeFoobar.Foo> {
+    enum Foo: SR9160_EmptyProtocol {}
+}
diff --git a/test/SILGen/enum.swift b/test/SILGen/enum.swift
index ac064f4..7f1fd19 100644
--- a/test/SILGen/enum.swift
+++ b/test/SILGen/enum.swift
@@ -249,8 +249,8 @@
 // CHECK: bb0(%0 : $Optional<SR7799>):
 // CHECK-NEXT:  debug_value %0 : $Optional<SR7799>, let, name "bar", argno 1
 // CHECK-NEXT:  switch_enum %0 : $Optional<SR7799>, case #Optional.some!enumelt.1: bb1, default bb4
-// CHECK: bb1(%3 : $SR7799):
-// CHECK-NEXT:  switch_enum %3 : $SR7799, case #SR7799.one!enumelt: bb2, case #SR7799.two!enumelt: bb3
+// CHECK: bb1([[PHI_ARG:%.*]] : $SR7799):
+// CHECK-NEXT:  switch_enum [[PHI_ARG]] : $SR7799, case #SR7799.one!enumelt: bb2, case #SR7799.two!enumelt: bb3
 func sr7799(bar: SR7799?) {
   switch bar {
   case .one: print("one")
diff --git a/test/SILGen/enum_resilience.swift b/test/SILGen/enum_resilience.swift
index 5b6e1cc..c27f773 100644
--- a/test/SILGen/enum_resilience.swift
+++ b/test/SILGen/enum_resilience.swift
@@ -12,6 +12,7 @@
 // CHECK-LABEL: sil hidden [ossa] @$s15enum_resilience15resilientSwitchyy0c1_A06MediumOF : $@convention(thin) (@in_guaranteed Medium) -> ()
 // CHECK:         [[BOX:%.*]] = alloc_stack $Medium
 // CHECK-NEXT:    copy_addr %0 to [initialization] [[BOX]]
+// CHECK-NEXT:    [[METATYPE:%.+]] = value_metatype $@thick Medium.Type, [[BOX]] : $*Medium
 // CHECK-NEXT:    switch_enum_addr [[BOX]] : $*Medium, case #Medium.Paper!enumelt: bb1, case #Medium.Canvas!enumelt: bb2, case #Medium.Pamphlet!enumelt.1: bb3, case #Medium.Postcard!enumelt.1: bb4, default bb5
 // CHECK:       bb1:
 // CHECK-NEXT:    dealloc_stack [[BOX]]
@@ -32,7 +33,6 @@
 // CHECK-NEXT:    dealloc_stack [[BOX]]
 // CHECK-NEXT:    br bb6
 // CHECK:       bb5:
-// CHECK-NEXT:    [[METATYPE:%.+]] = value_metatype $@thick Medium.Type, [[BOX]] : $*Medium
 // CHECK-NEXT:    // function_ref
 // CHECK-NEXT:    [[DIAGNOSE:%.+]] = function_ref @$ss27_diagnoseUnexpectedEnumCase
 // CHECK-NEXT:    = apply [[DIAGNOSE]]<Medium>([[METATYPE]]) : $@convention(thin) <τ_0_0> (@thick τ_0_0.Type) -> Never
diff --git a/test/SILGen/enum_resilience_testable.swift b/test/SILGen/enum_resilience_testable.swift
index c1b3a46..75260ea 100644
--- a/test/SILGen/enum_resilience_testable.swift
+++ b/test/SILGen/enum_resilience_testable.swift
@@ -17,6 +17,7 @@
 // CHECK-LABEL: sil hidden [ossa] @$s24enum_resilience_testable15resilientSwitchyy0d1_A06MediumOF : $@convention(thin) (@in_guaranteed Medium) -> ()
 // CHECK:         [[BOX:%.*]] = alloc_stack $Medium
 // CHECK-NEXT:    copy_addr %0 to [initialization] [[BOX]]
+// CHECK-NEXT:    [[METATYPE:%.+]] = value_metatype $@thick Medium.Type, [[BOX]] : $*Medium
 // CHECK-NEXT:    switch_enum_addr [[BOX]] : $*Medium, case #Medium.Paper!enumelt: bb1, case #Medium.Canvas!enumelt: bb2, case #Medium.Pamphlet!enumelt.1: bb3, case #Medium.Postcard!enumelt.1: bb4, default bb5
 // CHECK:       bb1:
 // CHECK-NEXT:    dealloc_stack [[BOX]]
@@ -37,7 +38,6 @@
 // CHECK-NEXT:    dealloc_stack [[BOX]]
 // CHECK-NEXT:    br bb6
 // CHECK:       bb5:
-// CHECK-NEXT:    [[METATYPE:%.+]] = value_metatype $@thick Medium.Type, [[BOX]] : $*Medium
 // CHECK-NEXT:    // function_ref
 // CHECK-NEXT:    [[DIAGNOSE:%.+]] = function_ref @$ss27_diagnoseUnexpectedEnumCase
 // CHECK-NEXT:    = apply [[DIAGNOSE]]<Medium>([[METATYPE]]) : $@convention(thin) <τ_0_0> (@thick τ_0_0.Type) -> Never
diff --git a/test/SILGen/switch.swift b/test/SILGen/switch.swift
index c5c14ff..6bf6335 100644
--- a/test/SILGen/switch.swift
+++ b/test/SILGen/switch.swift
@@ -1029,8 +1029,8 @@
   case let x?:
     return 0
 
-  // CHECK: [[SOMEBB]](%3 : $Int):
-  // CHECK-NEXT: debug_value %3 : $Int, let, name "x"
+  // CHECK: [[SOMEBB]]([[X:%.*]] : $Int):
+  // CHECK-NEXT: debug_value [[X]] : $Int, let, name "x"
   // CHECK: integer_literal $Builtin.IntLiteral, 0
 
   case .none:
@@ -1051,8 +1051,8 @@
   case let x?:
     return 0
 
-  // CHECK: [[SOMEBB]](%3 : $Int):
-  // CHECK-NEXT: debug_value %3 : $Int, let, name "x"
+  // CHECK: [[SOMEBB]]([[X:%.*]] : $Int):
+  // CHECK-NEXT: debug_value [[X]] : $Int, let, name "x"
   // CHECK: integer_literal $Builtin.IntLiteral, 0
 
   case nil:
diff --git a/test/SILGen/switch_objc.swift b/test/SILGen/switch_objc.swift
index 72fe068..914bc41 100644
--- a/test/SILGen/switch_objc.swift
+++ b/test/SILGen/switch_objc.swift
@@ -25,3 +25,30 @@
     return false
   }
 }
+
+@objc enum ObjCEnum : UInt8 {
+case first
+case second
+}
+
+// CHECK-LABEL: sil hidden [ossa] @$s11switch_objc44checkObjCEnumUnhandledCaseDiagnosticEmission1xyAA0dE0O_tF : $@convention(thin) (ObjCEnum) -> () {
+// CHECK: bb0([[ARG:%.*]] :
+// CHECK:   [[METATYPE:%.*]] = metatype $@thick ObjCEnum.Type
+// CHECK:   [[ARG_INT_REPR:%.*]] = unchecked_trivial_bit_cast [[ARG]]
+// CHECK:   switch_enum [[ARG]] : $ObjCEnum, {{.*}}, default [[DEFAULT_BB:bb[0-9]+]]
+//
+// CHECK: [[DEFAULT_BB]](
+// CHECK:   [[STACK_SLOT:%.*]] = alloc_stack $UInt8
+// CHECK:   store [[ARG_INT_REPR]] to [trivial] [[STACK_SLOT]]
+// CHECK:   [[DIAGNOSE_FUNC:%.*]] = function_ref @$ss32_diagnoseUnexpectedEnumCaseValue4type03rawE0s5NeverOxm_q_tr0_lF : $@convention(thin) <τ_0_0, τ_0_1> (@thick τ_0_0.Type, @in_guaranteed τ_0_1) -> Never
+// CHECK:   apply [[DIAGNOSE_FUNC]]<ObjCEnum, UInt8>([[METATYPE]], [[STACK_SLOT]])
+// CHECK:   unreachable
+// CHECK: } // end sil function '$s11switch_objc44checkObjCEnumUnhandledCaseDiagnosticEmission1xyAA0dE0O_tF'
+func checkObjCEnumUnhandledCaseDiagnosticEmission(x: ObjCEnum) {
+  switch x {
+  case .first:
+    break
+  case .second:
+    break
+  }
+}
diff --git a/test/attr/attr_inlinable_typealias.swift b/test/attr/attr_inlinable_typealias.swift
index 850ad8b..2c2488c 100644
--- a/test/attr/attr_inlinable_typealias.swift
+++ b/test/attr/attr_inlinable_typealias.swift
@@ -5,6 +5,7 @@
 
 internal typealias InternalAlias = Int
 // expected-note@-1 {{type alias 'InternalAlias' is not '@usableFromInline' or public}}
+// expected-note@-2 * {{type alias 'InternalAlias' is not '@usableFromInline' or public}}
 
 @usableFromInline typealias UsableFromInlineAlias = Int
 
@@ -21,3 +22,8 @@
 
   _ = PublicAlias.self
 }
+
+@inlinable public func localTypealiases() {
+  typealias LocalAlias = InternalAlias // expected-warning {{type alias 'InternalAlias' is internal and should not be referenced from an '@inlinable' function}}
+  typealias GenericAlias<T> = (T, InternalAlias) // expected-warning {{type alias 'InternalAlias' is internal and should not be referenced from an '@inlinable' function}}
+}
diff --git a/test/decl/protocol/conforms/circular_validation.swift b/test/decl/protocol/conforms/circular_validation.swift
index ecb234e..055f3ea 100644
--- a/test/decl/protocol/conforms/circular_validation.swift
+++ b/test/decl/protocol/conforms/circular_validation.swift
@@ -11,3 +11,8 @@
   static var x = 0 // expected-note {{candidate operates on a type, not an instance as required}}
   var x = S.x // expected-note {{candidate references itself}}
 }
+
+// FIXME: Lousy diagnostics on this case.
+protocol SR9224_Foo: SR9224_Foobar {} // expected-error 2 {{protocol 'SR9224_Foo' refines itself}}
+protocol SR9224_Bar: SR9224_Foobar {} // expected-error {{protocol 'SR9224_Bar' refines itself}} expected-note {{protocol 'SR9224_Bar' declared here}}
+typealias SR9224_Foobar = SR9224_Foo & SR9224_Bar
\ No newline at end of file
diff --git a/utils/build-script b/utils/build-script
index 33b93f8..2d0cbd0 100755
--- a/utils/build-script
+++ b/utils/build-script
@@ -904,35 +904,18 @@
         """Execute the invocation with the configured arguments."""
 
         # Convert to a build-script-impl invocation.
-        (impl_env, impl_args) = self.convert_to_impl_arguments()
+        (self.impl_env, self.impl_args) = self.convert_to_impl_arguments()
 
         # If using the legacy implementation, delegate all behavior to
         # `build-script-impl`.
         if self.args.legacy_impl:
             # Execute the underlying build script implementation.
-            shell.call_without_sleeping([build_script_impl] + impl_args,
-                                        env=impl_env, echo=True)
+            shell.call_without_sleeping([build_script_impl] + self.impl_args,
+                                        env=self.impl_env, echo=True)
             return
 
         # Otherwise, we compute and execute the individual actions ourselves.
 
-        def execute_one_impl_action(host=None, product_class=None, name=None):
-            if host is None:
-                assert (product_class is None and
-                        name == "merged-hosts-lipo"), "invalid action"
-                action_name = name
-            elif product_class is None:
-                assert name in ("package", "extractsymbols"), "invalid action"
-                action_name = "{}-{}".format(host.name, name)
-            else:
-                assert name is not None, "invalid action"
-                action_name = "{}-{}-{}".format(
-                    host.name, product_class.product_name(), name)
-            shell.call_without_sleeping(
-                [build_script_impl] + impl_args + [
-                    "--only-execute", action_name],
-                env=impl_env, echo=self.args.verbose_build)
-
         # Compute the list of hosts to operate on.
         all_host_names = [
             self.args.host_target] + self.args.cross_compile_hosts
@@ -965,17 +948,17 @@
                     " ".join(config.swift_benchmark_run_targets)))
 
             for product_class in impl_product_classes:
-                execute_one_impl_action(host_target, product_class, "build")
+                self._execute_build_action(host_target, product_class)
 
         # Test...
         for host_target in all_hosts:
             for product_class in impl_product_classes:
-                execute_one_impl_action(host_target, product_class, "test")
+                self._execute_test_action(host_target, product_class)
 
         # Install...
         for host_target in all_hosts:
             for product_class in impl_product_classes:
-                execute_one_impl_action(host_target, product_class, "install")
+                self._execute_install_action(host_target, product_class)
 
         # Non-build-script-impl products...
         # Note: currently only supports building for the host.
@@ -996,14 +979,46 @@
 
         # Extract symbols...
         for host_target in all_hosts:
-            execute_one_impl_action(host_target, name="extractsymbols")
+            self._execute_extract_symbols_action(host_target)
 
         # Package...
         for host_target in all_hosts:
-            execute_one_impl_action(host_target, name="package")
+            self._execute_package_action(host_target)
 
         # Lipo...
-        execute_one_impl_action(name="merged-hosts-lipo")
+        self._execute_merged_host_lipo_action()
+
+    def _execute_build_action(self, host_target, product_class):
+        action_name = "{}-{}-build".format(host_target.name,
+                                           product_class.product_name())
+        self._execute_action(action_name)
+
+    def _execute_test_action(self, host_target, product_class):
+        action_name = "{}-{}-test".format(host_target.name,
+                                          product_class.product_name())
+        self._execute_action(action_name)
+
+    def _execute_install_action(self, host_target, product_class):
+        action_name = "{}-{}-install".format(host_target.name,
+                                             product_class.product_name())
+        self._execute_action(action_name)
+
+    def _execute_extract_symbols_action(self, host_target):
+        action_name = "{}-extractsymbols".format(host_target.name)
+        self._execute_action(action_name)
+
+    def _execute_package_action(self, host_target):
+        action_name = "{}-package".format(host_target.name)
+        self._execute_action(action_name)
+
+    def _execute_merged_host_lipo_action(self):
+        self._execute_action("merged-hosts-lipo")
+
+    def _execute_action(self, action_name):
+        shell.call_without_sleeping(
+            [build_script_impl] + self.impl_args +
+            ["--only-execute", action_name],
+            env=self.impl_env, echo=self.args.verbose_build)
 
 
 # Provide a short delay so accidentally invoked clean builds can be canceled.