Skip to content

Commit

Permalink
[Sema] Suppress warning about useless availability checks in playgrou…
Browse files Browse the repository at this point in the history
…nds 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
  • Loading branch information
devincoughlin committed Jun 24, 2015
1 parent 786141f commit 3e96e1f
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 28 deletions.
6 changes: 5 additions & 1 deletion include/swift/Subsystems.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions lib/Frontend/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
60 changes: 36 additions & 24 deletions lib/Sema/TypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
Expand Down Expand Up @@ -841,7 +844,7 @@ class TypeRefinementContextBuilder : private ASTWalker {
};

std::vector<ContextInfo> 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
Expand All @@ -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());
}
Expand Down Expand Up @@ -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));

Expand All @@ -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;
}

Expand Down Expand Up @@ -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());
}
Expand All @@ -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());
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand All @@ -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++;
Expand Down Expand Up @@ -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;
}
}
Expand All @@ -1245,7 +1257,7 @@ class TypeRefinementContextBuilder : private ASTWalker {
/// Return the version range for the given availability spec.
VersionRange rangeForSpec(AvailabilitySpec *Spec) {
if (isa<OtherPlatformAvailabilitySpec>(Spec)) {
return VersionRange::allGTE(AC.LangOpts.getMinPlatformVersion());
return VersionRange::allGTE(TC.getLangOpts().getMinPlatformVersion());
}

auto *VersionSpec = cast<VersionConstraintAvailabilitySpec>(Spec);
Expand Down Expand Up @@ -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);
}
Expand Down
19 changes: 16 additions & 3 deletions lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<typename ...ArgTypes>
InFlightDiagnostic diagnose(ArgTypes &&...Args) {
Expand Down Expand Up @@ -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
Expand Down
36 changes: 36 additions & 0 deletions test/Sema/availability_versions_playgrounds.swift
Original file line number Diff line number Diff line change
@@ -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, *) {
}

0 comments on commit 3e96e1f

Please sign in to comment.