From d827f58a5f3b598e616a770e9b76abded956c1de Mon Sep 17 00:00:00 2001 From: Ravi Kandhadai Date: Tue, 14 Jul 2020 19:36:02 -0700 Subject: [PATCH] [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"). --- lib/Sema/ConstantnessSemaDiagnostics.cpp | 25 ++++++++---- test/SILOptimizer/OSLogMandatoryOptTest.swift | 38 +++++++++++++++++++ .../Sema/diag_constantness_check_os_log.swift | 29 ++++++++++++++ 3 files changed, 84 insertions(+), 8 deletions(-) diff --git a/lib/Sema/ConstantnessSemaDiagnostics.cpp b/lib/Sema/ConstantnessSemaDiagnostics.cpp index 49f9334697c55..369be3feda885 100644 --- a/lib/Sema/ConstantnessSemaDiagnostics.cpp +++ b/lib/Sema/ConstantnessSemaDiagnostics.cpp @@ -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 @@ -162,12 +169,6 @@ static Expr *checkConstantness(Expr *expr) { if (!isa(expr)) return expr; - if (NominalTypeDecl *nominal = - expr->getType()->getNominalOrBoundGenericNominal()) { - if (nominal->getName() == nominal->getASTContext().Id_OSLogMessage) - return expr; - } - ApplyExpr *apply = cast(expr); ValueDecl *calledValue = apply->getCalledValue(); if (!calledValue) @@ -179,10 +180,18 @@ static Expr *checkConstantness(Expr *expr) { continue; } + AbstractFunctionDecl *callee = dyn_cast(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(calledValue); - if (!callee || !hasConstantEvaluableAttr(callee)) + if (!hasConstantEvaluableAttr(callee)) return expr; expressionsToCheck.push_back(apply->getArg()); } diff --git a/test/SILOptimizer/OSLogMandatoryOptTest.swift b/test/SILOptimizer/OSLogMandatoryOptTest.swift index 3ea45c0a316a7..1bfca455b15a2 100644 --- a/test/SILOptimizer/OSLogMandatoryOptTest.swift +++ b/test/SILOptimizer/OSLogMandatoryOptTest.swift @@ -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_ + } +} diff --git a/test/Sema/diag_constantness_check_os_log.swift b/test/Sema/diag_constantness_check_os_log.swift index 57bc1ee002173..dc29825573e06 100644 --- a/test/Sema/diag_constantness_check_os_log.swift +++ b/test/Sema/diag_constantness_check_os_log.swift @@ -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}} +}