From 5bcdcd1fa62dd352be08ffd64f134cbe7fd699af Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 13 Jan 2026 09:18:22 -0500 Subject: [PATCH] FrontendTool: Stop if SILGen emits diagnostics even when lazy type checking is off We've had several bugs lately where SILGen produces a diagnostic, but the resulting invalid SIL causes crashes further down in the mandatory pipeline. To prevent this from happening, just stop after SILGen if diagnostics were emitted, even if lazy type checking is disabled, because the same rationale applies in either case. --- lib/FrontendTool/FrontendTool.cpp | 10 ++-- .../exclusivity_static_diagnostics.swift | 46 +++++++++++++++++++ test/SILGen/noimplicitcopy_diagnostic.swift | 13 ++++++ test/SILGen/polymorphic_inout_aliasing.swift | 19 +------- .../exclusivity_static_diagnostics.swift | 38 --------------- .../moveonly_checker_addressonly_fail.swift | 2 + .../polymorphic_inout_aliasing.swift | 15 ++++++ ...nState-recomputeExitFromEntry-bab42e.swift | 2 +- 8 files changed, 83 insertions(+), 62 deletions(-) create mode 100644 test/SILGen/exclusivity_static_diagnostics.swift create mode 100644 test/SILGen/noimplicitcopy_diagnostic.swift create mode 100644 test/SILOptimizer/polymorphic_inout_aliasing.swift rename validation-test/{compiler_crashers => compiler_crashers_fixed}/BlockPartitionState-recomputeExitFromEntry-bab42e.swift (84%) diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index cfe3cdf0762..f4fd502bf0c 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -2053,11 +2053,11 @@ static bool performCompileStepsPostSILGen( return writeSIL(*SM, PSPs, Instance, Invocation.getSILOptions()); } - // In lazy typechecking mode, SILGen may have triggered requests which - // resulted in errors. We don't want to proceed with optimization or - // serialization if there were errors since the SIL may be incomplete or - // invalid. - if (Context.TypeCheckerOpts.EnableLazyTypecheck && Context.hadError()) + // SILGen emits certain diagnostics of its own, and it can also trigger + // requests which result in errors. We don't want to proceed with + // optimization or serialization if there were errors since the SIL + // may be incomplete or invalid. + if (!Context.LangOpts.AllowModuleWithCompilerErrors && Context.hadError()) return true; if (Action == FrontendOptions::ActionType::EmitSIBGen) { diff --git a/test/SILGen/exclusivity_static_diagnostics.swift b/test/SILGen/exclusivity_static_diagnostics.swift new file mode 100644 index 00000000000..6a5425d78c5 --- /dev/null +++ b/test/SILGen/exclusivity_static_diagnostics.swift @@ -0,0 +1,46 @@ +// RUN: %target-swift-frontend -enforce-exclusivity=checked -swift-version 4 -emit-sil -primary-file %s -o /dev/null -verify + +import Swift + +func takesTwoInouts(_ p1: inout T, _ p2: inout T) { } + +func simpleInoutDiagnostic() { + var i = 7 + + // FIXME: This diagnostic should be removed if static enforcement is + // turned on by default. + // expected-error@+2{{inout arguments are not allowed to alias each other}} + // expected-note@+1{{previous aliasing argument}} + takesTwoInouts(&i, &i) +} + +func inoutOnInoutParameter(p: inout Int) { + // expected-error@+2{{inout arguments are not allowed to alias each other}} + // expected-note@+1{{previous aliasing argument}} + takesTwoInouts(&p, &p) +} + +class SomeClass { } + +struct StructWithMutatingMethodThatTakesSelfInout { + var f = SomeClass() + mutating func mutate(_ other: inout StructWithMutatingMethodThatTakesSelfInout) { } + mutating func mutate(_ other: inout SomeClass) { } + + mutating func callMutatingMethodThatTakesSelfInout() { + // expected-error@+2{{inout arguments are not allowed to alias each other}} + // expected-note@+1{{previous aliasing argument}} + mutate(&self) + } + + mutating func callMutatingMethodThatTakesSelfStoredPropInout() { + mutate(&self.f) + } +} + +func violationWithGenericType(_ p: T) { + var local = p + // expected-error@+2{{inout arguments are not allowed to alias each other}} + // expected-note@+1{{previous aliasing argument}} + takesTwoInouts(&local, &local) +} \ No newline at end of file diff --git a/test/SILGen/noimplicitcopy_diagnostic.swift b/test/SILGen/noimplicitcopy_diagnostic.swift new file mode 100644 index 00000000000..b125e11f75e --- /dev/null +++ b/test/SILGen/noimplicitcopy_diagnostic.swift @@ -0,0 +1,13 @@ +// RUN: %target-swift-frontend -enable-experimental-move-only -verify %s -emit-silgen + +func useValue(_ x: T) {} +func consumeValue(_ x: __owned T) {} + +struct GenericAggregate { + var value: T +} + +func test1(_ x: T) { + @_noImplicitCopy let x2 = x // expected-error {{'@_noImplicitCopy' can not be used on a generic or existential typed binding or a nominal type containing such typed things}} + _ = x2 +} \ No newline at end of file diff --git a/test/SILGen/polymorphic_inout_aliasing.swift b/test/SILGen/polymorphic_inout_aliasing.swift index e83e9479b88..0c7841927e0 100644 --- a/test/SILGen/polymorphic_inout_aliasing.swift +++ b/test/SILGen/polymorphic_inout_aliasing.swift @@ -1,31 +1,14 @@ -// RUN: %target-swift-emit-sil -verify %s -// RUN: %target-swift-frontend -emit-sil -verify %s +// RUN: %target-swift-emit-silgen -verify %s struct Block {} class Story { - final var finalStored = [Block]() - var overridableStored = [Block]() var computed: [Block] { get { return [] } set {} } func test() { - // expected-error@+2 {{overlapping accesses to 'finalStored', but modification requires exclusive access; consider calling MutableCollection.swapAt(_:_:)}} - // expected-note@+1 {{conflicting access is here}} - swap(&self.finalStored[0], &self.finalStored[1]) - swap(&self.overridableStored[0], &self.overridableStored[1]) swap(&self.computed[0], &self.computed[1]) // expected-error{{invalid aliasing}} expected-note{{concurrent writeback}} } } - -protocol Storied { - var protocolRequirement: [Block] { get set } -} - -func testProtocol(x: inout T) { - // expected-error@+2 {{overlapping accesses to 'x', but modification requires exclusive access; consider calling MutableCollection.swapAt(_:_:)}} - // expected-note@+1 {{conflicting access is here}} - swap(&x.protocolRequirement[0], &x.protocolRequirement[1]) -} diff --git a/test/SILOptimizer/exclusivity_static_diagnostics.swift b/test/SILOptimizer/exclusivity_static_diagnostics.swift index 0fc1f2c9ee4..db343c88f1c 100644 --- a/test/SILOptimizer/exclusivity_static_diagnostics.swift +++ b/test/SILOptimizer/exclusivity_static_diagnostics.swift @@ -4,26 +4,6 @@ import Swift func takesTwoInouts(_ p1: inout T, _ p2: inout T) { } -func simpleInoutDiagnostic() { - var i = 7 - - // FIXME: This diagnostic should be removed if static enforcement is - // turned on by default. - // expected-error@+4{{inout arguments are not allowed to alias each other}} - // expected-note@+3{{previous aliasing argument}} - // expected-error@+2{{overlapping accesses to 'i', but modification requires exclusive access; consider copying to a local variable}} - // expected-note@+1{{conflicting access is here}} - takesTwoInouts(&i, &i) -} - -func inoutOnInoutParameter(p: inout Int) { - // expected-error@+4{{inout arguments are not allowed to alias each other}} - // expected-note@+3{{previous aliasing argument}} - // expected-error@+2{{overlapping accesses to 'p', but modification requires exclusive access; consider copying to a local variable}} - // expected-note@+1{{conflicting access is here}} - takesTwoInouts(&p, &p) -} - func swapNoSuppression(_ i: Int, _ j: Int) { var a: [Int] = [1, 2, 3] @@ -39,14 +19,6 @@ struct StructWithMutatingMethodThatTakesSelfInout { mutating func mutate(_ other: inout StructWithMutatingMethodThatTakesSelfInout) { } mutating func mutate(_ other: inout SomeClass) { } - mutating func callMutatingMethodThatTakesSelfInout() { - // expected-error@+4{{inout arguments are not allowed to alias each other}} - // expected-note@+3{{previous aliasing argument}} - // expected-error@+2{{overlapping accesses to 'self', but modification requires exclusive access; consider copying to a local variable}} - // expected-note@+1{{conflicting access is here}} - mutate(&self) - } - mutating func callMutatingMethodThatTakesSelfStoredPropInout() { // expected-error@+2{{overlapping accesses to 'self', but modification requires exclusive access; consider copying to a local variable}} // expected-note@+1{{conflicting access is here}} @@ -81,16 +53,6 @@ class ClassWithFinalStoredProp { } } -func violationWithGenericType(_ p: T) { - var local = p - // expected-error@+4{{inout arguments are not allowed to alias each other}} - // expected-note@+3{{previous aliasing argument}} - // expected-error@+2{{overlapping accesses to 'local', but modification requires exclusive access; consider copying to a local variable}} - // expected-note@+1{{conflicting access is here}} - takesTwoInouts(&local, &local) -} - - // Helper. struct StructWithTwoStoredProp { var f1: Int = 7 diff --git a/test/SILOptimizer/moveonly_checker_addressonly_fail.swift b/test/SILOptimizer/moveonly_checker_addressonly_fail.swift index 4fb68356ce2..02c71386a0e 100644 --- a/test/SILOptimizer/moveonly_checker_addressonly_fail.swift +++ b/test/SILOptimizer/moveonly_checker_addressonly_fail.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend -enable-experimental-move-only -verify %s -emit-sil +// REQUIRES: rdar168066916 + func useValue(_ x: T) {} func consumeValue(_ x: __owned T) {} diff --git a/test/SILOptimizer/polymorphic_inout_aliasing.swift b/test/SILOptimizer/polymorphic_inout_aliasing.swift new file mode 100644 index 00000000000..70ce1d8df71 --- /dev/null +++ b/test/SILOptimizer/polymorphic_inout_aliasing.swift @@ -0,0 +1,15 @@ +// RUN: %target-swift-emit-sil -verify %s + +struct Block {} + +class Story { + final var finalStored = [Block]() + var overridableStored = [Block]() + + func test() { + // expected-error@+2 {{overlapping accesses to 'finalStored', but modification requires exclusive access; consider calling MutableCollection.swapAt(_:_:)}} + // expected-note@+1 {{conflicting access is here}} + swap(&self.finalStored[0], &self.finalStored[1]) + swap(&self.overridableStored[0], &self.overridableStored[1]) + } +} diff --git a/validation-test/compiler_crashers/BlockPartitionState-recomputeExitFromEntry-bab42e.swift b/validation-test/compiler_crashers_fixed/BlockPartitionState-recomputeExitFromEntry-bab42e.swift similarity index 84% rename from validation-test/compiler_crashers/BlockPartitionState-recomputeExitFromEntry-bab42e.swift rename to validation-test/compiler_crashers_fixed/BlockPartitionState-recomputeExitFromEntry-bab42e.swift index 2023bd686d4..59e3772ad97 100644 --- a/validation-test/compiler_crashers/BlockPartitionState-recomputeExitFromEntry-bab42e.swift +++ b/validation-test/compiler_crashers_fixed/BlockPartitionState-recomputeExitFromEntry-bab42e.swift @@ -1,5 +1,5 @@ // {"kind":"emit-sil","languageMode":6,"signature":"swift::regionanalysisimpl::BlockPartitionState::recomputeExitFromEntry(swift::regionanalysisimpl::PartitionOpTranslator&)","signatureAssert":"Assertion failed: (p.isTrackingElement(op.getOpArg1()) && \"Require PartitionOp's argument should already be tracked\"), function apply"} -// RUN: not --crash %target-swift-frontend -emit-sil -swift-version 6 %s +// RUN: not %target-swift-frontend -emit-sil -swift-version 6 %s func a(d: b) { var e = 0 let d: @convention(c) () -> Void = {