Skip to content

[Sema] Don't check availability in assigned properties initializers for -check-api-availaiblity-only mode #39847

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 26, 2021
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
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsFrontend.def
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ ERROR(error_mode_cannot_emit_module_semantic_info,none,
ERROR(cannot_emit_ir_skipping_function_bodies,none,
"the -experimental-skip-*-function-bodies* flags do not support "
"emitting IR", ())
ERROR(cannot_emit_ir_checking_api_availability_only,none,
"the flag -check-api-availability-only does not support "
"emitting IR", ())

WARNING(emit_reference_dependencies_without_primary_file,none,
"ignoring -emit-reference-dependencies (requires -primary-file)", ())
Expand Down
3 changes: 2 additions & 1 deletion include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -5890,7 +5890,8 @@ bool hasExplicitResult(ClosureExpr *closure);
/// Emit diagnostics for syntactic restrictions within a given solution
/// application target.
void performSyntacticDiagnosticsForTarget(
const SolutionApplicationTarget &target, bool isExprStmt);
const SolutionApplicationTarget &target,
bool isExprStmt,bool disableExprAvailabiltyChecking = false);

/// Given a member of a protocol, check whether `Self` type of that
/// protocol is contextually bound to some concrete type via same-type
Expand Down
20 changes: 13 additions & 7 deletions lib/Frontend/ArgsToFrontendOptionsConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,19 @@ bool ArgsToFrontendOptionsConverter::convert(
if (checkUnusedSupplementaryOutputPaths())
return true;

if (FrontendOptions::doesActionGenerateIR(Opts.RequestedAction) &&
(Args.hasArg(OPT_experimental_skip_non_inlinable_function_bodies) ||
Args.hasArg(OPT_experimental_skip_all_function_bodies) ||
Args.hasArg(
OPT_experimental_skip_non_inlinable_function_bodies_without_types))) {
Diags.diagnose(SourceLoc(), diag::cannot_emit_ir_skipping_function_bodies);
return true;
if (FrontendOptions::doesActionGenerateIR(Opts.RequestedAction)) {
if (Args.hasArg(OPT_experimental_skip_non_inlinable_function_bodies) ||
Args.hasArg(OPT_experimental_skip_all_function_bodies) ||
Args.hasArg(
OPT_experimental_skip_non_inlinable_function_bodies_without_types)) {
Diags.diagnose(SourceLoc(), diag::cannot_emit_ir_skipping_function_bodies);
return true;
}

if (Args.hasArg(OPT_check_api_availability_only)) {
Diags.diagnose(SourceLoc(), diag::cannot_emit_ir_checking_api_availability_only);
return true;
}
}

if (const Arg *A = Args.getLastArg(OPT_module_abi_name))
Expand Down
5 changes: 3 additions & 2 deletions lib/Sema/MiscDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4732,7 +4732,8 @@ void swift::checkPatternBindingDeclAsyncUsage(PatternBindingDecl *decl) {
/// Emit diagnostics for syntactic restrictions on a given expression.
void swift::performSyntacticExprDiagnostics(const Expr *E,
const DeclContext *DC,
bool isExprStmt) {
bool isExprStmt,
bool disableExprAvailabiltyChecking) {
auto &ctx = DC->getASTContext();
TypeChecker::diagnoseSelfAssignment(E);
diagSyntacticUseRestrictions(E, DC, isExprStmt);
Expand All @@ -4744,7 +4745,7 @@ void swift::performSyntacticExprDiagnostics(const Expr *E,
diagnoseComparisonWithNaN(E, DC);
if (!ctx.isSwiftVersionAtLeast(5))
diagnoseDeprecatedWritableKeyPath(E, DC);
if (!ctx.LangOpts.DisableAvailabilityChecking)
if (!ctx.LangOpts.DisableAvailabilityChecking && !disableExprAvailabiltyChecking)
diagnoseExprAvailability(E, const_cast<DeclContext*>(DC));
if (ctx.LangOpts.EnableObjCInterop)
diagDeprecatedObjCSelectors(DC, E);
Expand Down
5 changes: 3 additions & 2 deletions lib/Sema/MiscDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ namespace swift {
class ValueDecl;

/// Emit diagnostics for syntactic restrictions on a given expression.
void performSyntacticExprDiagnostics(const Expr *E, const DeclContext *DC,
bool isExprStmt);
void performSyntacticExprDiagnostics(
const Expr *E, const DeclContext *DC,
bool isExprStmt, bool disableExprAvailabiltyChecking = false);

/// Emit diagnostics for a given statement.
void performStmtDiagnostics(const Stmt *S, DeclContext *DC);
Expand Down
31 changes: 16 additions & 15 deletions lib/Sema/TypeCheckAccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1640,19 +1640,6 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
// "name: TheType" form, we can get better results by diagnosing the TypeRepr.
UNINTERESTING(Var)

static bool shouldCheck(Decl *D) {
if (D && D->getASTContext().LangOpts.CheckAPIAvailabilityOnly) {
// Skip whole decl if not API-public.
if (auto valueDecl = dyn_cast<const ValueDecl>(D)) {
AccessScope scope =
valueDecl->getFormalAccessScope(/*useDC*/nullptr,
/*treatUsableFromInlineAsPublic*/true);
if (!scope.isPublic())
return false;
}
}
return true;
}

/// \see visitPatternBindingDecl
void checkNamedPattern(const NamedPattern *NP,
Expand Down Expand Up @@ -1699,7 +1686,7 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
}

void visitPatternBindingDecl(PatternBindingDecl *PBD) {
if (!shouldCheck(PBD->getAnchoringVarDecl(0)))
if (!shouldCheckAvailability(PBD->getAnchoringVarDecl(0)))
return;

llvm::DenseSet<const VarDecl *> seenVars;
Expand Down Expand Up @@ -1969,8 +1956,22 @@ void swift::checkAccessControl(Decl *D) {
if (where.isImplicit())
return;

if (!DeclAvailabilityChecker::shouldCheck(D))
if (!shouldCheckAvailability(D))
return;

DeclAvailabilityChecker(where).visit(D);
}

bool swift::shouldCheckAvailability(const Decl *D) {
if (D && D->getASTContext().LangOpts.CheckAPIAvailabilityOnly) {
// Skip whole decl if not API-public.
if (auto valueDecl = dyn_cast<const ValueDecl>(D)) {
AccessScope scope =
valueDecl->getFormalAccessScope(/*useDC*/nullptr,
/*treatUsableFromInlineAsPublic*/true);
if (!scope.isPublic())
return false;
}
}
return true;
}
4 changes: 4 additions & 0 deletions lib/Sema/TypeCheckAvailability.h
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,10 @@ bool diagnoseExplicitUnavailability(
/// Check if \p decl has a introduction version required by -require-explicit-availability
void checkExplicitAvailability(Decl *decl);

/// Check if \p D needs to be checked for correct availability depending on the
/// flag -check-api-availability-only.
bool shouldCheckAvailability(const Decl *D);

} // namespace swift

#endif // SWIFT_SEMA_TYPE_CHECK_AVAILABILITY_H
Expand Down
24 changes: 20 additions & 4 deletions lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "MiscDiagnostics.h"
#include "TypeChecker.h"
#include "TypeCheckAvailability.h"
#include "swift/AST/ASTVisitor.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/DiagnosticSuppression.h"
Expand Down Expand Up @@ -272,12 +273,14 @@ class FunctionSyntacticDiagnosticWalker : public ASTWalker {
} // end anonymous namespace

void constraints::performSyntacticDiagnosticsForTarget(
const SolutionApplicationTarget &target, bool isExprStmt) {
const SolutionApplicationTarget &target,
bool isExprStmt, bool disableExprAvailabiltyChecking) {
auto *dc = target.getDeclContext();
switch (target.kind) {
case SolutionApplicationTarget::Kind::expression: {
// First emit diagnostics for the main expression.
performSyntacticExprDiagnostics(target.getAsExpr(), dc, isExprStmt);
performSyntacticExprDiagnostics(target.getAsExpr(), dc,
isExprStmt, disableExprAvailabiltyChecking);

// If this is a for-in statement, we also need to check the where clause if
// present.
Expand Down Expand Up @@ -400,7 +403,9 @@ TypeChecker::typeCheckExpression(
// expression now.
if (!cs.shouldSuppressDiagnostics()) {
bool isExprStmt = options.contains(TypeCheckExprFlags::IsExprStmt);
performSyntacticDiagnosticsForTarget(*resultTarget, isExprStmt);
performSyntacticDiagnosticsForTarget(
*resultTarget, isExprStmt,
options.contains(TypeCheckExprFlags::DisableExprAvailabilityChecking));
}

resultTarget->setExpr(result);
Expand Down Expand Up @@ -428,8 +433,19 @@ bool TypeChecker::typeCheckBinding(
initializer, DC, patternType, pattern,
/*bindPatternVarsOneWay=*/false);

auto options = TypeCheckExprOptions();
if (DC->getASTContext().LangOpts.CheckAPIAvailabilityOnly &&
PBD && !DC->getAsDecl()) {
// Skip checking the initializer for non-public decls when
// checking the API only.
auto VD = PBD->getAnchoringVarDecl(0);
if (!swift::shouldCheckAvailability(VD)) {
options |= TypeCheckExprFlags::DisableExprAvailabilityChecking;
}
}

// Type-check the initializer.
auto resultTarget = typeCheckExpression(target);
auto resultTarget = typeCheckExpression(target, options);

if (resultTarget) {
initializer = resultTarget->getAsExpr();
Expand Down
3 changes: 3 additions & 0 deletions lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ enum class TypeCheckExprFlags {
/// unchecked. This is used by source tooling functionalities such as code
/// completion.
LeaveClosureBodyUnchecked = 0x04,

/// Don't type check expressions for correct availability.
DisableExprAvailabilityChecking = 0x08,
};

using TypeCheckExprOptions = OptionSet<TypeCheckExprFlags>;
Expand Down
7 changes: 7 additions & 0 deletions test/Sema/api-availability-only-ok.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
@available(macOS 11.0, *)
public protocol NewProto {}

@available(macOS 11.0, *)
public struct NewStruct {}

@available(macOS 11.0, *)
public func newFunc() {}

Expand Down Expand Up @@ -58,6 +61,10 @@ public struct Struct {
private var privateVar: NewProto
fileprivate var fileprivateVar: NewProto

internal var internalAssigned = NewStruct()
private var privateAssigned = NewStruct()
fileprivate var fileprivateAssigned = NewStruct()

@available(macOS 11.0, *)
public typealias PubTA = NewProto
private typealias PrivateTA = NewProto
Expand Down
28 changes: 26 additions & 2 deletions test/Sema/api-availability-only.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@

// RUN: %target-typecheck-verify-swift -module-name MyModule -target %target-cpu-apple-macosx10.15 -check-api-availability-only -enable-library-evolution

// RUN: not %target-build-swift -emit-executable %s -g -o %t -emit-module -Xfrontend -check-api-availability-only 2>&1 | %FileCheck %s
// CHECK: the flag -check-api-availability-only does not support emitting IR

// REQUIRES: OS=macosx

@available(macOS 11.0, *)
public protocol NewProto {}

@available(macOS 11.0, *)
public struct NewStruct {
public init() {}
}

@available(macOS 11.0, *)
public func newFunc() {}

Expand All @@ -23,14 +31,25 @@ public func apiFunc(s : NewProto) { // expected-error {{'NewProto' is only avail
newFunc()
}

// expected-note @+1 3 {{add @available attribute to enclosing}}
// expected-note @+1 6 {{add @available attribute to enclosing}}
@inlinable func inlinable(s : NewProto) { // expected-error {{'NewProto' is only available in macOS 11.0 or newer}}

// expected-note @+1 {{add 'if #available' version check}}
let _: NewProto // expected-error {{'NewProto' is only available in macOS 11.0 or newer}}

// expected-note @+1 {{add 'if #available' version check}}
newFunc() // expected-error {{'newFunc()' is only available in macOS 11.0 or newer}}

// expected-note @+1 {{add 'if #available' version check}}
let _ = NewStruct() // expected-error {{'NewStruct' is only available in macOS 11.0 or newer}}

// expected-note @+2 {{add 'if #available' version check}}
// expected-warning @+1 {{initialization of immutable value 'a' was never used}}
let a = NewStruct() // expected-error {{'NewStruct' is only available in macOS 11.0 or newer}}

// expected-note @+2 {{add 'if #available' version check}}
// expected-warning @+1 {{result of 'NewStruct' initializer is unused}}
NewStruct() // expected-error {{'NewStruct' is only available in macOS 11.0 or newer}}
}

// expected-note @+1 {{add @available attribute to enclosing}}
Expand All @@ -54,13 +73,18 @@ fileprivate func fileprivateFunc(s : NewProto) {
newFunc()
}

// expected-note @+1 7 {{add @available attribute to enclosing struct}}
// expected-note @+1 8 {{add @available attribute to enclosing struct}}
public struct Struct {
public var publicVar: NewProto // expected-error {{'NewProto' is only available in macOS 11.0 or newer}}
internal var internalVar: NewProto
private var privateVar: NewProto
fileprivate var fileprivateVar: NewProto

public var publicAssigned = NewStruct() // expected-error {{'NewStruct' is only available in macOS 11.0 or newer}}
internal var internalAssigned = NewStruct()
private var privateAssigned = NewStruct()
fileprivate var fileprivateAssigned = NewStruct()

// expected-note @+1 {{add @available attribute to enclosing}}
public typealias PubTA = NewProto // expected-error {{'NewProto' is only available in macOS 11.0 or newer}}
private typealias PrivateTA = NewProto
Expand Down