Skip to content

Commit d827f58

Browse files
committed
[Sema][os_log] Allow wrapping os_log strings within constant evaluable functions.
As of now, the sema checks for os_log allow only string interpolations to be passed to the log calls. E.g. logger.log(foo("message")) would not be allowed. This PR relaxes this requirement and allows it as long as foo is annotated as @_semantics("constant_evaluable"). <rdar://problem/65842243>
1 parent 63f79b6 commit d827f58

File tree

3 files changed

+84
-8
lines changed

3 files changed

+84
-8
lines changed

lib/Sema/ConstantnessSemaDiagnostics.cpp

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ static bool hasConstantEvaluableAttr(ValueDecl *decl) {
9292
return hasSemanticsAttr(decl, semantics::CONSTANT_EVALUABLE);
9393
}
9494

95+
/// Return true iff the \p decl is annotated with oslog.message.init semantics
96+
/// attribute.
97+
static bool isOSLogMessageInitializer(ValueDecl *decl) {
98+
return hasSemanticsAttr(decl, semantics::OSLOG_MESSAGE_INIT_STRING_LITERAL) ||
99+
hasSemanticsAttr(decl, semantics::OSLOG_MESSAGE_INIT_INTERPOLATION);
100+
}
101+
95102
/// Check whether \p expr is a compile-time constant. It must either be a
96103
/// literal_expr, which does not include array and dictionary literal, or a
97104
/// closure expression, which is considered a compile-time constant of a
@@ -162,12 +169,6 @@ static Expr *checkConstantness(Expr *expr) {
162169
if (!isa<ApplyExpr>(expr))
163170
return expr;
164171

165-
if (NominalTypeDecl *nominal =
166-
expr->getType()->getNominalOrBoundGenericNominal()) {
167-
if (nominal->getName() == nominal->getASTContext().Id_OSLogMessage)
168-
return expr;
169-
}
170-
171172
ApplyExpr *apply = cast<ApplyExpr>(expr);
172173
ValueDecl *calledValue = apply->getCalledValue();
173174
if (!calledValue)
@@ -179,10 +180,18 @@ static Expr *checkConstantness(Expr *expr) {
179180
continue;
180181
}
181182

183+
AbstractFunctionDecl *callee = dyn_cast<AbstractFunctionDecl>(calledValue);
184+
if (!callee)
185+
return expr;
186+
187+
// If this is an application of OSLogMessage initializer, fail the check
188+
// as this type must be created from string interpolations.
189+
if (isOSLogMessageInitializer(callee))
190+
return expr;
191+
182192
// If this is a constant_evaluable function, check whether the arguments are
183193
// constants.
184-
AbstractFunctionDecl *callee = dyn_cast<AbstractFunctionDecl>(calledValue);
185-
if (!callee || !hasConstantEvaluableAttr(callee))
194+
if (!hasConstantEvaluableAttr(callee))
186195
return expr;
187196
expressionsToCheck.push_back(apply->getArg());
188197
}

test/SILOptimizer/OSLogMandatoryOptTest.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,3 +635,41 @@ extension TestProtocolSelfTypeCapture {
635635
}
636636
}
637637

638+
// Test that SwiftUI's preview transformations work with the logging APIs.
639+
640+
// A function similar to the one used by SwiftUI preview to wrap string
641+
// literals.
642+
@_semantics("constant_evaluable")
643+
@_transparent
644+
public func __designTimeStringStub(
645+
_ key: String,
646+
fallback: OSLogMessage
647+
) -> OSLogMessage {
648+
fallback
649+
}
650+
651+
// CHECK-LABEL: @${{.*}}testSwiftUIPreviewWrappingyy
652+
func testSwiftUIPreviewWrapping() {
653+
_osLogTestHelper(__designTimeStringStub("key", fallback: "percent: %"))
654+
// CHECK: string_literal utf8 "percent: %%"
655+
// CHECK-NOT: OSLogMessage
656+
// CHECK-NOT: OSLogInterpolation
657+
// CHECK-LABEL: end sil function '${{.*}}testSwiftUIPreviewWrappingyy
658+
}
659+
660+
661+
func functionTakingClosure(_ x: () -> Void) { }
662+
663+
func testWrappingWithinClosures(x: Int) {
664+
functionTakingClosure {
665+
_osLogTestHelper(
666+
__designTimeStringStub(
667+
"key",
668+
fallback: "escaping of percent: %"))
669+
// CHECK-LABEL: @${{.*}}testWrappingWithinClosures1xySi_tFyyXEfU_
670+
// CHECK: string_literal utf8 "escaping of percent: %%"
671+
// CHECK-NOT: OSLogMessage
672+
// CHECK-NOT: OSLogInterpolation
673+
// CHECK-LABEL: end sil function '${{.*}}testWrappingWithinClosures1xySi_tFyyXEfU_
674+
}
675+
}

test/Sema/diag_constantness_check_os_log.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,32 @@ func testNonConstantLogObjectLevel(
155155
osLogWithLevel(level, log: log, message)
156156
// expected-error@-1 {{argument must be a string interpolation}}
157157
}
158+
159+
// Test that log messages can be wrapped in constant_evaluable functions.
160+
161+
// A function similar to the one used by SwiftUI preview to wrap string
162+
// literals.
163+
@_semantics("constant_evaluable")
164+
public func __designTimeStringStub(
165+
_ key: String,
166+
fallback: OSLogMessage
167+
) -> OSLogMessage {
168+
fallback
169+
}
170+
171+
func testSwiftUIPreviewWrapping() {
172+
// This should not produce any diagnostics.
173+
_osLogTestHelper(__designTimeStringStub("key", fallback: "A literal message"))
174+
}
175+
176+
public func nonConstantFunction(
177+
_ key: String,
178+
fallback: OSLogMessage
179+
) -> OSLogMessage {
180+
fallback
181+
}
182+
183+
func testLogMessageWrappingDiagnostics() {
184+
_osLogTestHelper(nonConstantFunction("key", fallback: "A literal message"))
185+
// expected-error@-1{{argument must be a string interpolation}}
186+
}

0 commit comments

Comments
 (0)