Skip to content

Commit 281f84d

Browse files
committed
[Compile Time Values] Rewrite the 'Diagnose Unknown Compile Time Values' diagnostic pass in Swift
1 parent 5753603 commit 281f84d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+437
-371
lines changed

SwiftCompilerSources/Sources/AST/Declarations.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ final public class AssociatedTypeDecl: TypeDecl {}
8383

8484
final public class ModuleDecl: TypeDecl {}
8585

86-
public class AbstractStorageDecl: ValueDecl {}
86+
public class AbstractStorageDecl: ValueDecl {
87+
final public var isConst: Bool { bridged.AbstractStorage_isConst() }
88+
}
8789

8890
public class VarDecl: AbstractStorageDecl {}
8991

SwiftCompilerSources/Sources/Optimizer/ModulePasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
swift_compiler_sources(Optimizer
10+
DiagnoseUnknownConstValues.swift
1011
MandatoryPerformanceOptimizations.swift
1112
ReadOnlyGlobalVariables.swift
1213
StackProtection.swift
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//===--------- DiagnoseUnknownConstValues.swift --------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SIL
14+
import AST
15+
16+
/// Performs mandatory diagnostic pass for emitting errors for '@const' values which the compiler was not able to
17+
/// simplify/interpret/reduce to a symbolic value.
18+
///
19+
let diagnoseUnknownConstValues = ModulePass(name: "diagnose-unknown-const-values") {
20+
(moduleContext: ModulePassContext) in
21+
var constExprEvaluator: ConstExpressionEvaluator = .init()
22+
defer { constExprEvaluator.deinitialize() }
23+
24+
// Verify all const globals to be initialized with compile-time known values
25+
verifyGlobals(moduleContext)
26+
27+
// Verify @const lets appearing as local variables
28+
verifyLocals(constExprEvaluator: &constExprEvaluator,
29+
moduleContext)
30+
31+
// For each function call, ensure arguments to @const parameters are all compile-time known values
32+
verifyCallArguments(constExprEvaluator: &constExprEvaluator,
33+
moduleContext)
34+
35+
// For each `@const` function, ensure it is fully evaluable/interpretable at compile time
36+
verifyFunctions(moduleContext)
37+
}
38+
39+
private func verifyGlobals(_ context: ModulePassContext) {
40+
for gv in context.globalVariables where gv.isConst {
41+
if gv.staticInitValue == nil {
42+
context.diagnosticEngine.diagnose(gv.varDecl?.location.sourceLoc,
43+
.require_const_initializer_for_const)
44+
}
45+
}
46+
}
47+
48+
private func verifyLocals(constExprEvaluator: inout ConstExpressionEvaluator,
49+
_ context: ModulePassContext) {
50+
for f in context.functions {
51+
for i in f.instructions {
52+
if let dbi = i as? DebugValueInst {
53+
verifyLocal(debugValueInst: dbi, constExprState: &constExprEvaluator, in: context)
54+
}
55+
}
56+
}
57+
}
58+
59+
private func verifyLocal(debugValueInst: DebugValueInst,
60+
constExprState: inout ConstExpressionEvaluator,
61+
in context: ModulePassContext) {
62+
guard let localVarDecl = debugValueInst.varDecl,
63+
!(localVarDecl is ParamDecl),
64+
localVarDecl.isConst else {
65+
return
66+
}
67+
68+
if !constExprState.isConstantValue(debugValueInst.operand.value) {
69+
context.diagnosticEngine.diagnose(debugValueInst.location.sourceLoc,
70+
.require_const_initializer_for_const)
71+
}
72+
}
73+
74+
private func verifyCallArguments(constExprEvaluator: inout ConstExpressionEvaluator,
75+
_ context: ModulePassContext) {
76+
for f in context.functions {
77+
for i in f.instructions {
78+
// TODO: Consider closures (partial_apply)
79+
if let apply = i as? FullApplySite {
80+
verifyCallArguments(apply: apply, constExprState: &constExprEvaluator, in: context)
81+
}
82+
}
83+
}
84+
}
85+
86+
private func verifyCallArguments(apply: FullApplySite,
87+
constExprState: inout ConstExpressionEvaluator,
88+
in context: ModulePassContext) {
89+
guard let calleeFn = apply.referencedFunction else {
90+
return
91+
}
92+
for (paramIdx, param) in calleeFn.convention.parameters.enumerated() where param.hasOption(.const) {
93+
let matchingOperand = apply.parameterOperands[paramIdx]
94+
if !constExprState.isConstantValue(matchingOperand.value) {
95+
context.diagnosticEngine.diagnose(apply.location.sourceLoc,
96+
.require_const_arg_for_parameter)
97+
}
98+
}
99+
}
100+
101+
private func verifyFunctions(_ context: ModulePassContext) {
102+
// TODO: Implement
103+
}

SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ let mandatoryPerformanceOptimizations = ModulePass(name: "mandatory-performance-
3535
if moduleContext.options.enableEmbeddedSwift {
3636
worklist.addAllNonGenericFunctions(of: moduleContext)
3737
} else {
38-
worklist.addAllPerformanceAnnotatedFunctions(of: moduleContext)
39-
worklist.addAllAnnotatedGlobalInitOnceFunctions(of: moduleContext)
38+
worklist.addAllMandatoryRequiredFunctions(of: moduleContext)
4039
}
4140

4241
optimizeFunctionsTopDown(using: &worklist, moduleContext)
@@ -47,15 +46,6 @@ let mandatoryPerformanceOptimizations = ModulePass(name: "mandatory-performance-
4746
}
4847
}
4948

50-
/// Performance the same set of top-down optimization as the above "mandatory-performance-optimizations"
51-
/// pass, but only on the initializers of '@const' globals.
52-
let constGlobalVariableFoldingPass = ModulePass(name: "const-global-variable-folding") {
53-
(moduleContext: ModulePassContext) in
54-
var worklist = FunctionWorklist()
55-
worklist.addAllConstGlobalInitOnceFunctions(of: moduleContext)
56-
optimizeFunctionsTopDown(using: &worklist, moduleContext)
57-
}
58-
5949
private func optimizeFunctionsTopDown(using worklist: inout FunctionWorklist,
6050
_ moduleContext: ModulePassContext) {
6151
while let f = worklist.pop() {
@@ -492,10 +482,29 @@ fileprivate struct FunctionWorklist {
492482
}
493483
return nil
494484
}
495-
496-
mutating func addAllPerformanceAnnotatedFunctions(of moduleContext: ModulePassContext) {
497-
for f in moduleContext.functions where f.performanceConstraints != .none {
498-
pushIfNotVisited(f)
485+
486+
mutating func addAllMandatoryRequiredFunctions(of moduleContext: ModulePassContext) {
487+
for f in moduleContext.functions {
488+
// Performance annotated functions
489+
if f.performanceConstraints != .none {
490+
pushIfNotVisited(f)
491+
}
492+
493+
// Annotated global init-once functions
494+
if f.isGlobalInitOnceFunction {
495+
if let global = f.getInitializedGlobal(),
496+
global.mustBeInitializedStatically {
497+
pushIfNotVisited(f)
498+
}
499+
}
500+
501+
// @const global init-once functions
502+
if f.isGlobalInitOnceFunction {
503+
if let global = f.getInitializedGlobal(),
504+
global.mustBeInitializedStatically {
505+
pushIfNotVisited(f)
506+
}
507+
}
499508
}
500509
}
501510

@@ -506,24 +515,6 @@ fileprivate struct FunctionWorklist {
506515
return
507516
}
508517

509-
mutating func addAllAnnotatedGlobalInitOnceFunctions(of moduleContext: ModulePassContext) {
510-
for f in moduleContext.functions where f.isGlobalInitOnceFunction {
511-
if let global = f.getInitializedGlobal(),
512-
global.mustBeInitializedStatically {
513-
pushIfNotVisited(f)
514-
}
515-
}
516-
}
517-
518-
mutating func addAllConstGlobalInitOnceFunctions(of moduleContext: ModulePassContext) {
519-
for f in moduleContext.functions where f.isGlobalInitOnceFunction {
520-
if let global = f.getInitializedGlobal(),
521-
global.isConst {
522-
pushIfNotVisited(f)
523-
}
524-
}
525-
}
526-
527518
mutating func addCallees(of function: Function) {
528519
for inst in function.instructions {
529520
switch inst {

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ private func registerForSILCombine<InstType: SILCombineSimplifiable>(
6464
private func registerSwiftPasses() {
6565
// Module passes
6666
registerPass(mandatoryPerformanceOptimizations, { mandatoryPerformanceOptimizations.run($0) })
67-
registerPass(constGlobalVariableFoldingPass, { constGlobalVariableFoldingPass.run($0)})
67+
registerPass(diagnoseUnknownConstValues, { diagnoseUnknownConstValues.run($0)})
6868
registerPass(readOnlyGlobalVariablesPass, { readOnlyGlobalVariablesPass.run($0) })
6969
registerPass(stackProtection, { stackProtection.run($0) })
7070

SwiftCompilerSources/Sources/SIL/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ add_swift_compiler_module(SIL
1616
ASTExtensions.swift
1717
BasicBlock.swift
1818
Builder.swift
19+
ConstExpressionEvaluator.swift
1920
DeclRef.swift
2021
Effects.swift
2122
ForwardingInstruction.swift
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===--- ConstExpressionEvaluator.swift - ---------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import AST
14+
import SILBridging
15+
16+
public struct ConstExpressionEvaluator {
17+
var bridged: BridgedConstExprFunctionState
18+
19+
public init(bridged: BridgedConstExprFunctionState) { self.bridged = bridged }
20+
public init() {
21+
self.bridged = BridgedConstExprFunctionState.create()
22+
}
23+
24+
mutating public func isConstantValue(_ value: Value) -> Bool {
25+
return bridged.isConstantValue(value.bridged)
26+
}
27+
28+
/// TODO: once we have move-only types, make this a real deinit.
29+
mutating public func deinitialize() {
30+
bridged.deinitialize()
31+
}
32+
}

SwiftCompilerSources/Sources/SIL/FunctionConvention.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,15 @@ public struct ParameterInfo : CustomStringConvertible {
163163
public let convention: ArgumentConvention
164164
public let options: UInt8
165165
public let hasLoweredAddresses: Bool
166+
167+
// Must be kept consistent with 'SILParameterInfo::Flag'
168+
public enum Flag : UInt8 {
169+
case notDifferentiable = 0x1
170+
case sending = 0x2
171+
case isolated = 0x4
172+
case implicitLeading = 0x8
173+
case const = 0x10
174+
};
166175

167176
public init(type: CanonicalType, convention: ArgumentConvention, options: UInt8, hasLoweredAddresses: Bool) {
168177
self.type = type
@@ -193,6 +202,10 @@ public struct ParameterInfo : CustomStringConvertible {
193202
public var description: String {
194203
"\(convention): \(type)"
195204
}
205+
206+
public func hasOption(_ flag: Flag) -> Bool {
207+
return options & flag.rawValue != 0
208+
}
196209
}
197210

198211
extension FunctionConvention {

SwiftCompilerSources/Sources/SIL/GlobalVariable.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,11 @@ final public class GlobalVariable : CustomStringConvertible, HasShortDescription
7272
}
7373

7474
public var isConst: Bool {
75-
return bridged.isConstVal()
75+
return bridged.isConstValue()
76+
}
77+
78+
public var sourceLocation: SourceLoc? {
79+
return SourceLoc(bridged: bridged.getSourceLocation())
7680
}
7781

7882
public static func ==(lhs: GlobalVariable, rhs: GlobalVariable) -> Bool {

include/swift/AST/ASTBridging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ struct BridgedDeclObj {
384384
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedSourceLoc Value_getNameLoc() const;
385385
BRIDGED_INLINE bool hasClangNode() const;
386386
BRIDGED_INLINE bool Value_isObjC() const;
387+
BRIDGED_INLINE bool AbstractStorage_isConst() const;
387388
BRIDGED_INLINE bool GenericType_isGenericAtAnyLevel() const;
388389
BRIDGED_INLINE bool NominalType_isGlobalActor() const;
389390
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE OptionalBridgedDeclObj NominalType_getValueTypeDestructor() const;

0 commit comments

Comments
 (0)