Skip to content

Commit 49b0984

Browse files
committedJul 15, 2020
[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/65215054>
1 parent 29a3d08 commit 49b0984

File tree

3 files changed

+84
-8
lines changed

3 files changed

+84
-8
lines changed
 

‎lib/Sema/ConstantnessSemaDiagnostics.cpp

+17-8
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ static bool hasConstantEvaluableAttr(ValueDecl *decl) {
8888
return hasSemanticsAttr(decl, semantics::CONSTANT_EVALUABLE);
8989
}
9090

91+
/// Return true iff the \p decl is annotated with oslog.message.init semantics
92+
/// attribute.
93+
static bool isOSLogMessageInitializer(ValueDecl *decl) {
94+
return hasSemanticsAttr(decl, semantics::OSLOG_MESSAGE_INIT_STRING_LITERAL) ||
95+
hasSemanticsAttr(decl, semantics::OSLOG_MESSAGE_INIT_INTERPOLATION);
96+
}
97+
9198
/// Check whether \p expr is a compile-time constant. It must either be a
9299
/// literal_expr, which does not include array and dictionary literal, or a
93100
/// closure expression, which is considered a compile-time constant of a
@@ -158,12 +165,6 @@ static Expr *checkConstantness(Expr *expr) {
158165
if (!isa<ApplyExpr>(expr))
159166
return expr;
160167

161-
if (NominalTypeDecl *nominal =
162-
expr->getType()->getNominalOrBoundGenericNominal()) {
163-
if (nominal->getName() == nominal->getASTContext().Id_OSLogMessage)
164-
return expr;
165-
}
166-
167168
ApplyExpr *apply = cast<ApplyExpr>(expr);
168169
ValueDecl *calledValue = apply->getCalledValue();
169170
if (!calledValue)
@@ -175,10 +176,18 @@ static Expr *checkConstantness(Expr *expr) {
175176
continue;
176177
}
177178

179+
AbstractFunctionDecl *callee = dyn_cast<AbstractFunctionDecl>(calledValue);
180+
if (!callee)
181+
return expr;
182+
183+
// If this is an application of OSLogMessage initializer, fail the check
184+
// as this type must be created from string interpolations.
185+
if (isOSLogMessageInitializer(callee))
186+
return expr;
187+
178188
// If this is a constant_evaluable function, check whether the arguments are
179189
// constants.
180-
AbstractFunctionDecl *callee = dyn_cast<AbstractFunctionDecl>(calledValue);
181-
if (!callee || !hasConstantEvaluableAttr(callee))
190+
if (!hasConstantEvaluableAttr(callee))
182191
return expr;
183192
expressionsToCheck.push_back(apply->getArg());
184193
}

‎test/SILOptimizer/OSLogMandatoryOptTest.swift

+38
Original file line numberDiff line numberDiff line change
@@ -585,3 +585,41 @@ extension TestProtocolSelfTypeCapture {
585585
}
586586
}
587587

588+
// Test that SwiftUI's preview transformations work with the logging APIs.
589+
590+
// A function similar to the one used by SwiftUI preview to wrap string
591+
// literals.
592+
@_semantics("constant_evaluable")
593+
@_transparent
594+
public func __designTimeStringStub(
595+
_ key: String,
596+
fallback: OSLogMessage
597+
) -> OSLogMessage {
598+
fallback
599+
}
600+
601+
// CHECK-LABEL: @${{.*}}testSwiftUIPreviewWrappingyy
602+
func testSwiftUIPreviewWrapping() {
603+
_osLogTestHelper(__designTimeStringStub("key", fallback: "percent: %"))
604+
// CHECK: string_literal utf8 "percent: %%"
605+
// CHECK-NOT: OSLogMessage
606+
// CHECK-NOT: OSLogInterpolation
607+
// CHECK-LABEL: end sil function '${{.*}}testSwiftUIPreviewWrappingyy
608+
}
609+
610+
611+
func functionTakingClosure(_ x: () -> Void) { }
612+
613+
func testWrappingWithinClosures(x: Int) {
614+
functionTakingClosure {
615+
_osLogTestHelper(
616+
__designTimeStringStub(
617+
"key",
618+
fallback: "escaping of percent: %"))
619+
// CHECK-LABEL: @${{.*}}testWrappingWithinClosures1xySi_tFyyXEfU_
620+
// CHECK: string_literal utf8 "escaping of percent: %%"
621+
// CHECK-NOT: OSLogMessage
622+
// CHECK-NOT: OSLogInterpolation
623+
// CHECK-LABEL: end sil function '${{.*}}testWrappingWithinClosures1xySi_tFyyXEfU_
624+
}
625+
}

‎test/Sema/diag_constantness_check_os_log.swift

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

0 commit comments

Comments
 (0)
Please sign in to comment.