From 3e96e1f06b4d8e3ea95296f957989e4543b45309 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Wed, 24 Jun 2015 23:09:22 +0000 Subject: [PATCH] [Sema] Suppress warning about useless availability checks in playgrounds and immediate mode. We normally report a warning when a #available() check will always be true because of the minimum deployment target. These warnings are potentially annoying when the developer either cannot change the minimum deployment target from the default (as in playgrounds) or when doing so is burdensome (as for interpreted command-line scripts, which would require passing a target triple) -- so suppress them. The is a updated version of the reverted r29582, which didn't check for immediate mode properly. rdar://problem/21324005 Swift SVN r29646 --- include/swift/Subsystems.h | 6 +- lib/Frontend/Frontend.cpp | 3 + lib/Sema/TypeChecker.cpp | 60 +++++++++++-------- lib/Sema/TypeChecker.h | 19 +++++- .../availability_versions_playgrounds.swift | 36 +++++++++++ 5 files changed, 96 insertions(+), 28 deletions(-) create mode 100644 test/Sema/availability_versions_playgrounds.swift diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index 13f78c79e34c4..1d3ec5b271611 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -146,7 +146,11 @@ namespace swift { /// If set, dumps wall time taken to check each function body to /// llvm::errs(). - DebugTimeFunctionBodies = 1 << 1 + DebugTimeFunctionBodies = 1 << 1, + + /// Indicates that the type checker is checking code that will be + /// immediately executed. + ForImmediateMode = 1 << 2 }; /// Once parsing and name-binding are complete, this walks the AST to resolve diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 750d16f96b53e..4bd4d71634818 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -406,6 +406,9 @@ void CompilerInstance::performSema() { if (Invocation.getFrontendOptions().DebugTimeFunctionBodies) { TypeCheckOptions |= TypeCheckingFlags::DebugTimeFunctionBodies; } + if (Invocation.getFrontendOptions().actionIsImmediate()) { + TypeCheckOptions |= TypeCheckingFlags::ForImmediateMode; + } // Parse the main file last. if (MainBufferID != NO_SUCH_BUFFER) { diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index d9883fe3edbb1..1e7479722e864 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -517,6 +517,9 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, auto &DefinedFunctions = TC.definedFunctions; if (Options.contains(TypeCheckingFlags::DebugTimeFunctionBodies)) TC.enableDebugTimeFunctionBodies(); + + if (Options.contains(TypeCheckingFlags::ForImmediateMode)) + TC.setInImmediateMode(true); // Lookup the swift module. This ensures that we record all known // protocols in the AST. @@ -525,7 +528,7 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC, if (!Ctx.LangOpts.DisableAvailabilityChecking) { // Build the type refinement hierarchy for the primary // file before type checking. - TypeChecker::buildTypeRefinementContextHierarchy(SF, StartElem); + TC.buildTypeRefinementContextHierarchy(SF, StartElem); } // Resolve extensions. This has to occur first during type checking, @@ -841,7 +844,7 @@ class TypeRefinementContextBuilder : private ASTWalker { }; std::vector ContextStack; - ASTContext &AC; + TypeChecker &TC; /// A mapping from abstract storage declarations with accessors to /// to the type refinement contexts for those declarations. We refer to @@ -862,8 +865,8 @@ class TypeRefinementContextBuilder : private ASTWalker { } public: - TypeRefinementContextBuilder(TypeRefinementContext *TRC, ASTContext &AC) - : AC(AC) { + TypeRefinementContextBuilder(TypeRefinementContext *TRC, TypeChecker &TC) + : TC(TC) { assert(TRC); pushContext(TRC, ParentTy()); } @@ -941,11 +944,11 @@ class TypeRefinementContextBuilder : private ASTWalker { // The potential versions in the declaration are constrained by both // the declared availability of the declaration and the potential versions // of its lexical context. - VersionRange DeclVersionRange = TypeChecker::availableRange(D, AC); + VersionRange DeclVersionRange = TypeChecker::availableRange(D, TC.Context); DeclVersionRange.meetWith(getCurrentTRC()->getPotentialVersions()); TypeRefinementContext *NewTRC = - TypeRefinementContext::createForDecl(AC, D, getCurrentTRC(), + TypeRefinementContext::createForDecl(TC.Context, D, getCurrentTRC(), DeclVersionRange, refinementSourceRangeForDecl(D)); @@ -969,7 +972,7 @@ class TypeRefinementContextBuilder : private ASTWalker { // No need to introduce a context if the declaration does not have an // availability attribute. - if (!hasActiveAvailableAttribute(D, AC)) { + if (!hasActiveAvailableAttribute(D, TC.Context)) { return false; } @@ -1059,9 +1062,10 @@ class TypeRefinementContextBuilder : private ASTWalker { // Create a new context for the Then branch and traverse it in that new // context. auto *ThenTRC = - TypeRefinementContext::createForIfStmtThen(AC, IS, getCurrentTRC(), - RefinedRange.getValue()); - TypeRefinementContextBuilder(ThenTRC, AC).build(IS->getThenStmt()); + TypeRefinementContext::createForIfStmtThen(TC.Context, IS, + getCurrentTRC(), + RefinedRange.getValue()); + TypeRefinementContextBuilder(ThenTRC, TC).build(IS->getThenStmt()); } else { build(IS->getThenStmt()); } @@ -1087,8 +1091,8 @@ class TypeRefinementContextBuilder : private ASTWalker { // Create a new context for the branch and traverse it in the new // context. auto *ThenTRC = TypeRefinementContext::createForWhileStmtBody( - AC, WS, getCurrentTRC(), RefinedRange.getValue()); - TypeRefinementContextBuilder(ThenTRC, AC).build(WS->getBody()); + TC.Context, WS, getCurrentTRC(), RefinedRange.getValue()); + TypeRefinementContextBuilder(ThenTRC, TC).build(WS->getBody()); } else { build(WS->getBody()); } @@ -1124,7 +1128,7 @@ class TypeRefinementContextBuilder : private ASTWalker { // Create a new context for the fallthrough. auto *FallthroughTRC = - TypeRefinementContext::createForGuardStmtFallthrough(AC, GS, + TypeRefinementContext::createForGuardStmtFallthrough(TC.Context, GS, ParentBrace, getCurrentTRC(), RefinedRange.getValue()); pushContext(FallthroughTRC, ParentBrace); @@ -1167,9 +1171,9 @@ class TypeRefinementContextBuilder : private ASTWalker { // We couldn't find an appropriate spec for the current platform, // so rather than refining, emit a diagnostic and just use the current // TRC. - AC.Diags.diagnose(Query->getLoc(), + TC.Diags.diagnose(Query->getLoc(), diag::availability_query_required_for_platform, - platformString(targetPlatform(AC.LangOpts))); + platformString(targetPlatform(TC.getLangOpts()))); continue; } @@ -1183,22 +1187,30 @@ class TypeRefinementContextBuilder : private ASTWalker { // spec is useless. If so, report this. if (Spec->getKind() == AvailabilitySpecKind::VersionConstraint && CurTRC->getPotentialVersions().isContainedIn(Range)) { - DiagnosticEngine &Diags = AC.Diags; + DiagnosticEngine &Diags = TC.Diags; if (CurTRC->getReason() == TypeRefinementContext::Reason::Root) { - Diags.diagnose(Query->getLoc(), - diag::availability_query_useless_min_deployment, - platformString(targetPlatform(AC.LangOpts))); + // Diagnose for checks that are useless because the minimum deployment + // target ensures they will never be false. We suppress this warning + // when compiling for playgrounds because the developer cannot + // cannot explicitly set the minimum deployment target to silence + // the alarm. We also suppress in script mode (where setting the + // minimum deployment target requires a target triple). + if (!TC.getLangOpts().Playground && !TC.getInImmediateMode()) { + Diags.diagnose(Query->getLoc(), + diag::availability_query_useless_min_deployment, + platformString(targetPlatform(TC.getLangOpts()))); + } } else { Diags.diagnose(Query->getLoc(), diag::availability_query_useless_enclosing_scope, - platformString(targetPlatform(AC.LangOpts))); + platformString(targetPlatform(TC.getLangOpts()))); Diags.diagnose(CurTRC->getIntroductionLoc(), diag::availability_query_useless_enclosing_scope_here); } } auto *TRC = TypeRefinementContext::createForConditionFollowingQuery( - AC, Query, LastElement, getCurrentTRC(), Range); + TC.Context, Query, LastElement, getCurrentTRC(), Range); pushContext(TRC, ParentTy()); NestedCount++; @@ -1232,7 +1244,7 @@ class TypeRefinementContextBuilder : private ASTWalker { // properly. For example, on the OSXApplicationExtension platform // we want to chose the OSX spec unless there is an explicit // OSXApplicationExtension spec. - if (isPlatformActive(VersionSpec->getPlatform(), AC.LangOpts)) { + if (isPlatformActive(VersionSpec->getPlatform(), TC.getLangOpts())) { return VersionSpec; } } @@ -1245,7 +1257,7 @@ class TypeRefinementContextBuilder : private ASTWalker { /// Return the version range for the given availability spec. VersionRange rangeForSpec(AvailabilitySpec *Spec) { if (isa(Spec)) { - return VersionRange::allGTE(AC.LangOpts.getMinPlatformVersion()); + return VersionRange::allGTE(TC.getLangOpts().getMinPlatformVersion()); } auto *VersionSpec = cast(Spec); @@ -1285,7 +1297,7 @@ void TypeChecker::buildTypeRefinementContextHierarchy(SourceFile &SF, // Build refinement contexts, if necessary, for all declarations starting // with StartElem. - TypeRefinementContextBuilder Builder(RootTRC, AC); + TypeRefinementContextBuilder Builder(RootTRC, *this); for (auto D : llvm::makeArrayRef(SF.Decls).slice(StartElem)) { Builder.build(D); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 5b860a6e1e158..2f390b7427163 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -467,6 +467,11 @@ class TypeChecker final : public LazyResolver { /// to llvm::errs(). bool DebugTimeFunctionBodies = false; + /// Indicate that the type checker is checking code that will be + /// immediately executed. This will suppress certain warnings + /// when executing scripts. + bool InImmediateMode = false; + /// A helper to construct and typecheck call to super.init(). /// /// \returns NULL if the constructed expression does not typecheck. @@ -483,6 +488,14 @@ class TypeChecker final : public LazyResolver { void enableDebugTimeFunctionBodies() { DebugTimeFunctionBodies = true; } + + bool getInImmediateMode() { + return InImmediateMode; + } + + void setInImmediateMode(bool InImmediateMode) { + this->InImmediateMode = InImmediateMode; + } template InFlightDiagnostic diagnose(ArgTypes &&...Args) { @@ -1511,13 +1524,13 @@ class TypeChecker final : public LazyResolver { /// /// \param StartElem Where to start for incremental building of refinement /// contexts - static void buildTypeRefinementContextHierarchy(SourceFile &SF, - unsigned StartElem); + void buildTypeRefinementContextHierarchy(SourceFile &SF, + unsigned StartElem); /// Build the hierarchy of TypeRefinementContexts for the entire /// source file, if it has not already been built. Returns the root /// TypeRefinementContext for the source file. - static TypeRefinementContext *getOrBuildTypeRefinementContext(SourceFile *SF); + TypeRefinementContext *getOrBuildTypeRefinementContext(SourceFile *SF); /// Returns a diagnostic indicating why the declaration cannot be annotated /// with an @available() attribute indicating it is potentially unavailable diff --git a/test/Sema/availability_versions_playgrounds.swift b/test/Sema/availability_versions_playgrounds.swift new file mode 100644 index 0000000000000..8ec74ca348284 --- /dev/null +++ b/test/Sema/availability_versions_playgrounds.swift @@ -0,0 +1,36 @@ +// Playgrounds +// RUN: %target-parse-verify-swift -playground + +// Immediate mode +// RUN: %target-parse-verify-swift -interpret + +// REQUIRES: OS=macosx +// REQUIRES: swift_interpreter + +func someFunction() { + // We would normally emit a warning indicating this check is useless (because + // the minimum deployment target is 10.9) -- but do not when compiling for + // playgrounds because the developer cannot set the minimum deployment target + // herself. We also suppress this warning when compiling in immediate mode. + if #available(OSX 10.8, *) { + } + + if #available(OSX 10.11, *) { // expected-note {{enclosing scope here}} + // Still warn if the check is useless because an enclosing #available rules + // it out. + if #available(OSX 10.11, *) { // expected-warning {{unnecessary check for 'OSX'; enclosing scope ensures guard will always be true}} + } + } +} + +@available(OSX 10.11, *) +func availableOn10_11() { // expected-note {{enclosing scope here}} + // Still warn if the check is useless because an enclosing @available rules + // it out. + if #available(OSX 10.11, *) { // expected-warning {{unnecessary check for 'OSX'; enclosing scope ensures guard will always be true}} + } +} + +// Make sure we don't warn at the top level +if #available(OSX 10.8, *) { +}