Skip to content
Open
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
2 changes: 2 additions & 0 deletions include/swift/AST/DiagnosticEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ namespace swift {
class FuncDecl;
class SourceManager;
class SourceFile;
class ParamDecl;
class AnyPattern;

/// Enumeration describing all of possible diagnostics.
///
Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/DiagnosticGroups.def
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ GROUP(WeakMutability, "weak-mutability")
GROUP(PerformanceHints, "performance-hints")
GROUP(ReturnTypeImplicitCopy, "return-type-implicit-copy")
GROUP_LINK(PerformanceHints, ReturnTypeImplicitCopy)
GROUP(ExistentialType, "existential-type")
GROUP_LINK(PerformanceHints, ExistentialType)

#define UNDEFINE_DIAGNOSTIC_GROUPS_MACROS
#include "swift/AST/DefineDiagnosticGroupsMacros.h"
24 changes: 24 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -8946,6 +8946,30 @@ GROUPED_WARNING(perf_hint_closure_returns_array,ReturnTypeImplicitCopy,DefaultIg
"Performance: closure returns a%select{ dictionary|n array}0, leading to implicit copies. "
"Consider using an 'inout' parameter instead.", (bool))

GROUPED_WARNING(perf_hint_param_expects_existential,ExistentialType,DefaultIgnore,
"Performance: %0 expects an existential, leading to heap allocation, reference counting, "
"and dynamic dispatch. Consider using generic constraints or concrete types instead.",
(const ParamDecl*))
GROUPED_WARNING(perf_hint_func_returns_existential,ExistentialType,DefaultIgnore,
"Performance: %0 returns an existential, leading to heap allocation, reference counting, "
"and dynamic dispatch. Consider using generic constraints or concrete types instead.",
(const FuncDecl*))
GROUPED_WARNING(perf_hint_closure_returns_existential,ExistentialType,DefaultIgnore,
"Performance: closure returns an existential, leading to heap allocation, reference counting, "
"and dynamic dispatch. Consider using generic constraints or concrete types instead.", ())
GROUPED_WARNING(perf_hint_var_uses_existential,ExistentialType,DefaultIgnore,
"Performance: %0 uses an existential, leading to heap allocation, reference counting, "
"and dynamic dispatch. Consider using generic constraints or concrete types instead.",
(const VarDecl *))
GROUPED_WARNING(perf_hint_any_pattern_uses_existential,ExistentialType,DefaultIgnore,
"Performance: declaration uses an existential, leading to heap allocation, reference counting, "
"and dynamic dispatch. Consider using generic constraints or concrete types instead.",
())
GROUPED_WARNING(perf_hint_typealias_uses_existential,ExistentialType,DefaultIgnore,
"Performance: %0 aliases an existential type, leading to heap allocation, reference counting, "
"and dynamic dispatch. Consider using generic constraints or concrete types instead.",
(const TypeAliasDecl *))

ERROR(unsafe_self_dependent_result_attr_on_invalid_decl,none,
"invalid use of @_unsafeSelfDependentResult", ())

Expand Down
122 changes: 117 additions & 5 deletions lib/Sema/PerformanceHints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,27 @@
#include "swift/AST/Evaluator.h"
#include "swift/AST/Expr.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/AST/TypeVisitor.h"

using namespace swift;

bool swift::performanceHintDiagnosticsEnabled(ASTContext &ctx) {
return !ctx.Diags.isIgnoredDiagnostic(diag::perf_hint_closure_returns_array.ID) ||
!ctx.Diags.isIgnoredDiagnostic(diag::perf_hint_function_returns_array.ID);
return !ctx.Diags.isIgnoredDiagnostic(
diag::perf_hint_closure_returns_array.ID) ||
!ctx.Diags.isIgnoredDiagnostic(
diag::perf_hint_function_returns_array.ID) ||
!ctx.Diags.isIgnoredDiagnostic(
diag::perf_hint_param_expects_existential.ID) ||
!ctx.Diags.isIgnoredDiagnostic(
diag::perf_hint_func_returns_existential.ID) ||
!ctx.Diags.isIgnoredDiagnostic(
diag::perf_hint_closure_returns_existential.ID) ||
!ctx.Diags.isIgnoredDiagnostic(
diag::perf_hint_var_uses_existential.ID) ||
!ctx.Diags.isIgnoredDiagnostic(
diag::perf_hint_any_pattern_uses_existential.ID) ||
!ctx.Diags.isIgnoredDiagnostic(
diag::perf_hint_typealias_uses_existential.ID);
}

namespace {
Expand All @@ -52,6 +67,55 @@ void checkImplicitCopyReturnType(const ClosureExpr *Closure,
}
}

bool isExistentialType(Type T) {
return dyn_cast<ExistentialType>(T) != nullptr;
}

bool hasExistentialAnyInType(Type type) {
return type->getCanonicalType().findIf(isExistentialType);
}

void checkExistentialInFunctionReturnType(const FuncDecl *FD,
DiagnosticEngine &Diags) {
Type T = FD->getResultInterfaceType();

if (hasExistentialAnyInType(T))
Diags.diagnose(FD, diag::perf_hint_func_returns_existential, FD);
}

void checkExistentialInClosureReturnType(const ClosureExpr *CE,
DiagnosticEngine &Diags) {
Type T = CE->getResultType();

if (hasExistentialAnyInType(T))
Diags.diagnose(CE->getLoc(), diag::perf_hint_closure_returns_existential);
}

void checkExistentialInVariableType(const VarDecl *VD,
DiagnosticEngine &Diags) {
Type T = VD->getInterfaceType();

if (hasExistentialAnyInType(T))
Diags.diagnose(VD, diag::perf_hint_var_uses_existential, VD);
}

void checkExistentialInPatternType(const AnyPattern *AP,
DiagnosticEngine &Diags) {
Type T = AP->getType();

if (hasExistentialAnyInType(T))
Diags.diagnose(AP->getLoc(), diag::perf_hint_any_pattern_uses_existential);
}

void checkExistentialInTypeAlias(const TypeAliasDecl *TAD,
DiagnosticEngine &Diags) {
Type T = TAD->getUnderlyingType();

if (hasExistentialAnyInType(T))
Diags.diagnose(TAD->getLoc(), diag::perf_hint_typealias_uses_existential,
TAD);
}

/// Produce performance hint diagnostics for a SourceFile.
class PerformanceHintDiagnosticWalker final : public ASTWalker {
ASTContext &Ctx;
Expand All @@ -64,16 +128,64 @@ class PerformanceHintDiagnosticWalker final : public ASTWalker {
SF->walk(Walker);
}

PreWalkResult<Pattern *> walkToPatternPre(Pattern *P) override {
if (P->isImplicit())
return Action::SkipNode(P);

return Action::Continue(P);
}

PostWalkResult<Pattern *> walkToPatternPost(Pattern *P) override {
assert(!P->isImplicit() &&
"Traversing implicit patterns is disabled in the pre-walk visitor");

if (const AnyPattern *AP = dyn_cast<AnyPattern>(P)) {
checkExistentialInPatternType(AP, Ctx.Diags);
}

return Action::Continue(P);
}

PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
if (auto Closure = dyn_cast<ClosureExpr>(E))
checkImplicitCopyReturnType(Closure, Ctx.Diags);
if (E->isImplicit())
return Action::SkipNode(E);

return Action::Continue(E);
}

PostWalkResult<Expr *> walkToExprPost(Expr *E) override {
assert(
!E->isImplicit() &&
"Traversing implicit expressions is disabled in the pre-walk visitor");

if (const ClosureExpr *CE = dyn_cast<ClosureExpr>(E)) {
checkImplicitCopyReturnType(CE, Ctx.Diags);
checkExistentialInClosureReturnType(CE, Ctx.Diags);
}

return Action::Continue(E);
}

PreWalkAction walkToDeclPre(Decl *D) override {
if (auto *FD = dyn_cast<FuncDecl>(D))
if (D->isImplicit())
return Action::SkipNode();

return Action::Continue();
}

PostWalkAction walkToDeclPost(Decl *D) override {
assert(
!D->isImplicit() &&
"Traversing implicit declarations is disabled in the pre-walk visitor");

if (const FuncDecl *FD = dyn_cast<FuncDecl>(D)) {
checkImplicitCopyReturnType(FD, Ctx.Diags);
checkExistentialInFunctionReturnType(FD, Ctx.Diags);
} else if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
checkExistentialInVariableType(VD, Ctx.Diags);
} else if (const TypeAliasDecl *TAD = dyn_cast<TypeAliasDecl>(D)) {
checkExistentialInTypeAlias(TAD, Ctx.Diags);
}

return Action::Continue();
}
Expand Down
Loading