Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions lib/Sema/ConstantnessSemaDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ static bool hasConstantEvaluableAttr(ValueDecl *decl) {
return hasSemanticsAttr(decl, semantics::CONSTANT_EVALUABLE);
}

/// Return true iff the \p decl is annotated with oslog.message.init semantics
/// attribute.
static bool isOSLogMessageInitializer(ValueDecl *decl) {
return hasSemanticsAttr(decl, semantics::OSLOG_MESSAGE_INIT_STRING_LITERAL) ||
hasSemanticsAttr(decl, semantics::OSLOG_MESSAGE_INIT_INTERPOLATION);
}

/// Check whether \p expr is a compile-time constant. It must either be a
/// literal_expr, which does not include array and dictionary literal, or a
/// closure expression, which is considered a compile-time constant of a
Expand Down Expand Up @@ -162,12 +169,6 @@ static Expr *checkConstantness(Expr *expr) {
if (!isa<ApplyExpr>(expr))
return expr;

if (NominalTypeDecl *nominal =
expr->getType()->getNominalOrBoundGenericNominal()) {
if (nominal->getName() == nominal->getASTContext().Id_OSLogMessage)
return expr;
}

ApplyExpr *apply = cast<ApplyExpr>(expr);
ValueDecl *calledValue = apply->getCalledValue();
if (!calledValue)
Expand All @@ -179,10 +180,18 @@ static Expr *checkConstantness(Expr *expr) {
continue;
}

AbstractFunctionDecl *callee = dyn_cast<AbstractFunctionDecl>(calledValue);
if (!callee)
return expr;

// If this is an application of OSLogMessage initializer, fail the check
// as this type must be created from string interpolations.
if (isOSLogMessageInitializer(callee))
return expr;

// If this is a constant_evaluable function, check whether the arguments are
// constants.
AbstractFunctionDecl *callee = dyn_cast<AbstractFunctionDecl>(calledValue);
if (!callee || !hasConstantEvaluableAttr(callee))
if (!hasConstantEvaluableAttr(callee))
return expr;
expressionsToCheck.push_back(apply->getArg());
}
Expand Down
38 changes: 38 additions & 0 deletions test/SILOptimizer/OSLogMandatoryOptTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -635,3 +635,41 @@ extension TestProtocolSelfTypeCapture {
}
}

// Test that SwiftUI's preview transformations work with the logging APIs.

// A function similar to the one used by SwiftUI preview to wrap string
// literals.
@_semantics("constant_evaluable")
@_transparent
public func __designTimeStringStub(
_ key: String,
fallback: OSLogMessage
) -> OSLogMessage {
fallback
}

// CHECK-LABEL: @${{.*}}testSwiftUIPreviewWrappingyy
func testSwiftUIPreviewWrapping() {
_osLogTestHelper(__designTimeStringStub("key", fallback: "percent: %"))
// CHECK: string_literal utf8 "percent: %%"
// CHECK-NOT: OSLogMessage
// CHECK-NOT: OSLogInterpolation
// CHECK-LABEL: end sil function '${{.*}}testSwiftUIPreviewWrappingyy
}


func functionTakingClosure(_ x: () -> Void) { }

func testWrappingWithinClosures(x: Int) {
functionTakingClosure {
_osLogTestHelper(
__designTimeStringStub(
"key",
fallback: "escaping of percent: %"))
// CHECK-LABEL: @${{.*}}testWrappingWithinClosures1xySi_tFyyXEfU_
// CHECK: string_literal utf8 "escaping of percent: %%"
// CHECK-NOT: OSLogMessage
// CHECK-NOT: OSLogInterpolation
// CHECK-LABEL: end sil function '${{.*}}testWrappingWithinClosures1xySi_tFyyXEfU_
}
}
29 changes: 29 additions & 0 deletions test/Sema/diag_constantness_check_os_log.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,32 @@ func testNonConstantLogObjectLevel(
osLogWithLevel(level, log: log, message)
// expected-error@-1 {{argument must be a string interpolation}}
}

// Test that log messages can be wrapped in constant_evaluable functions.

// A function similar to the one used by SwiftUI preview to wrap string
// literals.
@_semantics("constant_evaluable")
public func __designTimeStringStub(
_ key: String,
fallback: OSLogMessage
) -> OSLogMessage {
fallback
}

func testSwiftUIPreviewWrapping() {
// This should not produce any diagnostics.
_osLogTestHelper(__designTimeStringStub("key", fallback: "A literal message"))
}

public func nonConstantFunction(
_ key: String,
fallback: OSLogMessage
) -> OSLogMessage {
fallback
}

func testLogMessageWrappingDiagnostics() {
_osLogTestHelper(nonConstantFunction("key", fallback: "A literal message"))
// expected-error@-1{{argument must be a string interpolation}}
}