@@ -132,52 +132,63 @@ func evaluateIfConfig(
132132 }
133133
134134 // Evaluate the left-hand side.
135- let ( lhsActive, lhssyntaxErrorsAllowed , lhsDiagnostics) = evaluateIfConfig (
135+ let ( lhsActive, lhsSyntaxErrorsAllowed , lhsDiagnostics) = evaluateIfConfig (
136136 condition: binOp. leftOperand,
137137 configuration: configuration,
138138 outermostCondition: false
139139 )
140140
141- // Short-circuit evaluation if we know the answer and the left-hand side
142- // was syntaxErrorsAllowed.
143- if lhssyntaxErrorsAllowed {
144- switch ( lhsActive , op . operator . text ) {
145- case ( true , " || " ) :
146- return (
147- active : true ,
148- syntaxErrorsAllowed : lhssyntaxErrorsAllowed ,
149- diagnostics : extraDiagnostics + lhsDiagnostics
150- )
151- case ( false , " && " ) :
152- return (
153- active : false ,
154- syntaxErrorsAllowed : lhssyntaxErrorsAllowed ,
155- diagnostics : extraDiagnostics + lhsDiagnostics
156- )
157- default :
158- break
159- }
141+ // Short-circuit evaluation if we know the answer. We still recurse into
142+ // the right-hand side, but with a dummy configuration that won't have
143+ // side effects, so we only get validation-related errors.
144+ let shortCircuitResult : Bool ?
145+ switch ( lhsActive , op . operator . text ) {
146+ case ( true , " || " ) : shortCircuitResult = true
147+ case ( false , " && " ) : shortCircuitResult = false
148+ default : shortCircuitResult = nil
149+ }
150+
151+ // If we are supposed to short-circuit and the left-hand side of this
152+ // operator with inactive &&, stop now: we shouldn't evaluate the right-
153+ // hand side at all.
154+ if let isActive = shortCircuitResult , lhsSyntaxErrorsAllowed {
155+ return (
156+ active : isActive ,
157+ syntaxErrorsAllowed : lhsSyntaxErrorsAllowed ,
158+ diagnostics : extraDiagnostics + lhsDiagnostics
159+ )
160160 }
161161
162162 // Evaluate the right-hand side.
163- let ( rhsActive, rhssyntaxErrorsAllowed, rhsDiagnostics) = evaluateIfConfig (
164- condition: binOp. rightOperand,
165- configuration: configuration,
166- outermostCondition: false
167- )
163+ let rhsActive : Bool
164+ let rhsSyntaxErrorsAllowed : Bool
165+ let rhsDiagnostics : [ Diagnostic ]
166+ if shortCircuitResult != nil {
167+ ( rhsActive, rhsSyntaxErrorsAllowed, rhsDiagnostics) = evaluateIfConfig (
168+ condition: binOp. rightOperand,
169+ configuration: CanImportSuppressingBuildConfiguration ( other: configuration) ,
170+ outermostCondition: false
171+ )
172+ } else {
173+ ( rhsActive, rhsSyntaxErrorsAllowed, rhsDiagnostics) = evaluateIfConfig (
174+ condition: binOp. rightOperand,
175+ configuration: configuration,
176+ outermostCondition: false
177+ )
178+ }
168179
169180 switch op. operator. text {
170181 case " || " :
171182 return (
172183 active: lhsActive || rhsActive,
173- syntaxErrorsAllowed: lhssyntaxErrorsAllowed && rhssyntaxErrorsAllowed ,
184+ syntaxErrorsAllowed: lhsSyntaxErrorsAllowed && rhsSyntaxErrorsAllowed ,
174185 diagnostics: extraDiagnostics + lhsDiagnostics + rhsDiagnostics
175186 )
176187
177188 case " && " :
178189 return (
179190 active: lhsActive && rhsActive,
180- syntaxErrorsAllowed: lhssyntaxErrorsAllowed || rhssyntaxErrorsAllowed ,
191+ syntaxErrorsAllowed: lhsSyntaxErrorsAllowed || rhsSyntaxErrorsAllowed ,
181192 diagnostics: extraDiagnostics + lhsDiagnostics + rhsDiagnostics
182193 )
183194
@@ -674,3 +685,55 @@ extension ExprSyntaxProtocol {
674685 return false
675686 }
676687}
688+
689+ /// Build configuration adaptor that suppresses calls to canImport, which
690+ /// can have side effects. This is somewhat of a hack for the compiler.
691+ private struct CanImportSuppressingBuildConfiguration < Other: BuildConfiguration > : BuildConfiguration {
692+ var other : Other
693+
694+ func isCustomConditionSet( name: String ) throws -> Bool {
695+ return try other. isCustomConditionSet ( name: name)
696+ }
697+
698+ func hasFeature( name: String ) throws -> Bool {
699+ return try other. hasFeature ( name: name)
700+ }
701+
702+ func hasAttribute( name: String ) throws -> Bool {
703+ return try other. hasAttribute ( name: name)
704+ }
705+
706+ func canImport( importPath: [ String ] , version: CanImportVersion ) throws -> Bool {
707+ return false
708+ }
709+
710+ func isActiveTargetOS( name: String ) throws -> Bool {
711+ return try other. isActiveTargetOS ( name: name)
712+ }
713+
714+ func isActiveTargetArchitecture( name: String ) throws -> Bool {
715+ return try other. isActiveTargetArchitecture ( name: name)
716+ }
717+
718+ func isActiveTargetEnvironment( name: String ) throws -> Bool {
719+ return try other. isActiveTargetEnvironment ( name: name)
720+ }
721+
722+ func isActiveTargetRuntime( name: String ) throws -> Bool {
723+ return try other. isActiveTargetRuntime ( name: name)
724+ }
725+
726+ func isActiveTargetPointerAuthentication( name: String ) throws -> Bool {
727+ return try other. isActiveTargetPointerAuthentication ( name: name)
728+ }
729+
730+ var targetPointerBitWidth : Int { return other. targetPointerBitWidth }
731+
732+ var targetAtomicBitWidths : [ Int ] { return other. targetAtomicBitWidths }
733+
734+ var endianness : Endianness { return other. endianness }
735+
736+ var languageVersion : VersionTuple { return other. languageVersion }
737+
738+ var compilerVersion : VersionTuple { return other. compilerVersion }
739+ }
0 commit comments