Skip to content

Commit 4278d31

Browse files
SR-11295: Emit diagnostics for same type coercion
1 parent 0086eb0 commit 4278d31

37 files changed

+293
-115
lines changed

include/swift/AST/DiagnosticsSema.def

+12-8
Original file line numberDiff line numberDiff line change
@@ -949,18 +949,22 @@ ERROR(super_initializer_not_in_initializer,none,
949949
WARNING(isa_is_always_true,none, "'%0' test is always true",
950950
(StringRef))
951951
WARNING(isa_is_foreign_check,none,
952-
"'is' test is always true because %0 is a Core Foundation type",
953-
(Type))
952+
"'is' test is always true because %0 is a Core Foundation type",
953+
(Type))
954954
WARNING(conditional_downcast_coercion,none,
955-
"conditional cast from %0 to %1 always succeeds",
956-
(Type, Type))
957-
955+
"conditional cast from %0 to %1 always succeeds",
956+
(Type, Type))
957+
WARNING(unnecessary_same_type_coercion,none,
958+
"redundant cast to %0 has no effect",
959+
(Type))
960+
WARNING(unnecessary_same_typealias_type_coercion,none,
961+
"redundant cast from %0 to %1 has no effect",
962+
(Type, Type))
958963
WARNING(forced_downcast_noop,none,
959964
"forced cast of %0 to same type has no effect", (Type))
960-
961965
WARNING(forced_downcast_coercion,none,
962-
"forced cast from %0 to %1 always succeeds; did you mean to use 'as'?",
963-
(Type, Type))
966+
"forced cast from %0 to %1 always succeeds; did you mean to use 'as'?",
967+
(Type, Type))
964968

965969
// Note: the Boolean at the end indicates whether bridging is required after
966970
// the cast.

lib/Sema/CSDiagnostics.cpp

+31-3
Original file line numberDiff line numberDiff line change
@@ -4400,9 +4400,7 @@ bool InaccessibleMemberFailure::diagnoseAsError() {
44004400
auto &cs = getConstraintSystem();
44014401
auto *locator =
44024402
cs.getConstraintLocator(baseExpr, ConstraintLocator::Member);
4403-
if (llvm::any_of(cs.getFixes(), [&](const ConstraintFix *fix) {
4404-
return fix->getLocator() == locator;
4405-
}))
4403+
if (cs.hasFixFor(locator))
44064404
return false;
44074405
}
44084406

@@ -5031,6 +5029,36 @@ bool ThrowingFunctionConversionFailure::diagnoseAsError() {
50315029
return true;
50325030
}
50335031

5032+
bool UnnecessaryCoercionFailure::diagnoseAsError() {
5033+
auto expr = cast<CoerceExpr>(getAnchor());
5034+
auto sourceRange =
5035+
SourceRange(expr->getLoc(), expr->getCastTypeLoc().getSourceRange().End);
5036+
5037+
if (isa<TypeAliasType>(getFromType().getPointer()) &&
5038+
isa<TypeAliasType>(getToType().getPointer())) {
5039+
auto fromTypeAlias = cast<TypeAliasType>(getFromType().getPointer());
5040+
auto toTypeAlias = cast<TypeAliasType>(getToType().getPointer());
5041+
// If the typealias are different, we need a warning
5042+
// mentioning both types.
5043+
if (fromTypeAlias->getDecl() != toTypeAlias->getDecl()) {
5044+
emitDiagnostic(expr->getLoc(),
5045+
diag::unnecessary_same_typealias_type_coercion,
5046+
getFromType(), getToType())
5047+
5048+
.fixItRemove(sourceRange);
5049+
} else {
5050+
emitDiagnostic(expr->getLoc(), diag::unnecessary_same_type_coercion,
5051+
getToType())
5052+
.fixItRemove(sourceRange);
5053+
}
5054+
} else {
5055+
emitDiagnostic(expr->getLoc(), diag::unnecessary_same_type_coercion,
5056+
getToType())
5057+
.fixItRemove(sourceRange);
5058+
}
5059+
return true;
5060+
}
5061+
50345062
bool InOutConversionFailure::diagnoseAsError() {
50355063
auto *anchor = getAnchor();
50365064
auto *locator = getLocator();

lib/Sema/CSDiagnostics.h

+35-8
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,13 @@ class FailureDiagnostic {
156156
}
157157

158158
/// \returns true is locator hasn't been simplified down to expression.
159-
bool hasComplexLocator() const { return HasComplexLocator; }
159+
bool hasComplexLocator() const {
160+
bool isExplicitCoercion =
161+
getLocator()
162+
->isLastElement<
163+
ConstraintLocator::PathElement::ExplicitTypeCoercion>();
164+
return HasComplexLocator && !isExplicitCoercion;
165+
}
160166

161167
/// \returns A parent expression if sub-expression is contained anywhere
162168
/// in the root expression or `nullptr` otherwise.
@@ -707,8 +713,8 @@ class ContextualFailure : public FailureDiagnostic {
707713
/// If we're trying to convert something to `nil`.
708714
bool diagnoseConversionToNil() const;
709715

710-
// If we're trying to convert something of type "() -> T" to T,
711-
// then we probably meant to call the value.
716+
/// If we're trying to convert something of type "() -> T" to T,
717+
/// then we probably meant to call the value.
712718
bool diagnoseMissingFunctionCall() const;
713719

714720
/// Produce a specialized diagnostic if this is an invalid conversion to Bool.
@@ -1460,11 +1466,11 @@ class MutatingMemberRefOnImmutableBase final : public FailureDiagnostic {
14601466
bool diagnoseAsError() override;
14611467
};
14621468

1463-
// Diagnose an attempt to use AnyObject as the root type of a KeyPath
1464-
//
1465-
// ```swift
1466-
// let keyPath = \AnyObject.bar
1467-
// ```
1469+
/// Diagnose an attempt to use AnyObject as the root type of a KeyPath
1470+
///
1471+
/// ```swift
1472+
/// let keyPath = \AnyObject.bar
1473+
/// ```
14681474
class AnyObjectKeyPathRootFailure final : public FailureDiagnostic {
14691475

14701476
public:
@@ -1774,6 +1780,27 @@ class ExpandArrayIntoVarargsFailure final : public ContextualFailure {
17741780
void tryDropArrayBracketsFixIt(Expr *anchor) const;
17751781
};
17761782

1783+
/// Diagnose a situation where there is an explicit type coercion
1784+
/// to the same type e.g.:
1785+
///
1786+
/// ```swift
1787+
/// Double(1) as Double // redundant cast to 'Double' has no effect
1788+
/// 1 as Double as Double // redundant cast to 'Double' has no effect
1789+
/// let string = "String"
1790+
/// let s = string as String // redundant cast to 'String' has no effect
1791+
/// ```
1792+
class UnnecessaryCoercionFailure final
1793+
: public ContextualFailure {
1794+
1795+
public:
1796+
UnnecessaryCoercionFailure(Expr *root, ConstraintSystem &cs,
1797+
Type fromType, Type toType,
1798+
ConstraintLocator *locator)
1799+
: ContextualFailure(root, cs, fromType, toType, locator) {}
1800+
1801+
bool diagnoseAsError() override;
1802+
};
1803+
17771804
/// Diagnose a situation there is a mismatch between argument and parameter
17781805
/// types e.g.:
17791806
///

lib/Sema/CSFix.cpp

+30
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,36 @@ IgnoreContextualType *IgnoreContextualType::create(ConstraintSystem &cs,
857857
IgnoreContextualType(cs, resultTy, specifiedTy, locator);
858858
}
859859

860+
bool RemoveUnnecessaryCoercion::diagnose(Expr *root, bool asNote) const {
861+
auto &cs = getConstraintSystem();
862+
UnnecessaryCoercionFailure failure(root, cs, getFromType(), getToType(),
863+
getLocator());
864+
return failure.diagnose(asNote);
865+
}
866+
867+
bool RemoveUnnecessaryCoercion::attempt(ConstraintSystem &cs, Type fromType,
868+
Type toType,
869+
ConstraintLocatorBuilder locator) {
870+
if (auto last = locator.last()) {
871+
if (last->is<LocatorPathElt::ExplicitTypeCoercion>()) {
872+
auto expr = cast<CoerceExpr>(locator.getAnchor());
873+
auto toTypeRepr = expr->getCastTypeLoc().getTypeRepr();
874+
875+
// only diagnosing for coercion where the left-side is a DeclRefExpr
876+
// or a explicit/implicit coercion e.g. Double(1) as Double
877+
if (!isa<ImplicitlyUnwrappedOptionalTypeRepr>(toTypeRepr) &&
878+
(isa<DeclRefExpr>(expr->getSubExpr()) ||
879+
isa<CoerceExpr>(expr->getSubExpr()))) {
880+
auto *fix = new (cs.getAllocator()) RemoveUnnecessaryCoercion(
881+
cs, fromType, toType, cs.getConstraintLocator(locator));
882+
883+
return cs.recordFix(fix);
884+
}
885+
}
886+
}
887+
return false;
888+
}
889+
860890
bool IgnoreAssignmentDestinationType::diagnose(Expr *root, bool asNote) const {
861891
auto &cs = getConstraintSystem();
862892
auto *AE = cast<AssignExpr>(getAnchor());

lib/Sema/CSFix.h

+21
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ enum class FixKind : uint8_t {
196196
/// Allow a single tuple parameter to be matched with N arguments
197197
/// by forming all of the given arguments into a single tuple.
198198
AllowTupleSplatForSingleParameter,
199+
200+
/// Remove an unnecessary coercion ('as') if the types are already equal. e.g. Double(1) as Double
201+
RemoveUnnecessaryCoercion,
199202

200203
/// Allow a single argument type mismatch. This is the most generic
201204
/// failure related to argument-to-parameter conversions.
@@ -1340,6 +1343,24 @@ class IgnoreContextualType : public ContextualMismatch {
13401343
ConstraintLocator *locator);
13411344
};
13421345

1346+
class RemoveUnnecessaryCoercion : public ContextualMismatch {
1347+
protected:
1348+
RemoveUnnecessaryCoercion(ConstraintSystem &cs,
1349+
Type fromType, Type toType,
1350+
ConstraintLocator *locator)
1351+
: ContextualMismatch(cs, FixKind::RemoveUnnecessaryCoercion, fromType, toType,
1352+
locator, /*isWarning*/ true) {}
1353+
1354+
public:
1355+
std::string getName() const override { return "remove unnecessary explicit type coercion"; }
1356+
1357+
bool diagnose(Expr *root, bool asNote = false) const override;
1358+
1359+
static bool attempt(ConstraintSystem &cs,
1360+
Type fromType, Type toType,
1361+
ConstraintLocatorBuilder locator);
1362+
};
1363+
13431364
class IgnoreAssignmentDestinationType final : public ContextualMismatch {
13441365
IgnoreAssignmentDestinationType(ConstraintSystem &cs, Type sourceTy,
13451366
Type destTy, ConstraintLocator *locator)

lib/Sema/CSSimplify.cpp

+23-5
Original file line numberDiff line numberDiff line change
@@ -2587,7 +2587,8 @@ bool ConstraintSystem::repairFailures(
25872587
});
25882588
};
25892589

2590-
if (path.empty()) {
2590+
if (path.empty() ||
2591+
path.back().getKind() == ConstraintLocator::ExplicitTypeCoercion) {
25912592
if (!anchor)
25922593
return false;
25932594

@@ -3212,9 +3213,16 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
32123213
// let's defer it until later proper check.
32133214
if (!(desugar1->is<DependentMemberType>() &&
32143215
desugar2->is<DependentMemberType>())) {
3215-
// If the types are obviously equivalent, we're done.
3216-
if (desugar1->isEqual(desugar2) && !isa<InOutType>(desugar2)) {
3217-
return getTypeMatchSuccess();
3216+
if (desugar1->isEqual(desugar2)) {
3217+
if (kind >= ConstraintKind::Conversion &&
3218+
!flags.contains(TMF_ApplyingFix)) {
3219+
if (RemoveUnnecessaryCoercion::attempt(*this, type1, type2,
3220+
getConstraintLocator(locator))) {
3221+
return getTypeMatchFailure(locator);
3222+
}
3223+
}
3224+
if (!isa<InOutType>(desugar2))
3225+
return getTypeMatchSuccess();
32183226
}
32193227
}
32203228

@@ -7999,6 +8007,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
79998007
case FixKind::GenericArgumentsMismatch:
80008008
case FixKind::AllowMutatingMemberOnRValueBase:
80018009
case FixKind::AllowTupleSplatForSingleParameter:
8010+
case FixKind::RemoveUnnecessaryCoercion:
80028011
llvm_unreachable("handled elsewhere");
80038012
}
80048013

@@ -8305,11 +8314,20 @@ void ConstraintSystem::addExplicitConversionConstraint(
83058314
SmallVector<Constraint *, 3> constraints;
83068315

83078316
auto locatorPtr = getConstraintLocator(locator);
8317+
ConstraintLocator *coerceLocator = locatorPtr;
8318+
8319+
if (allowFixes && shouldAttemptFixes()) {
8320+
auto *anchor = locator.getAnchor();
8321+
if (isa<CoerceExpr>(anchor) && !anchor->isImplicit()) {
8322+
coerceLocator =
8323+
getConstraintLocator(anchor, LocatorPathElt::ExplicitTypeCoercion());
8324+
}
8325+
}
83088326

83098327
// Coercion (the common case).
83108328
Constraint *coerceConstraint =
83118329
Constraint::create(*this, ConstraintKind::Conversion,
8312-
fromType, toType, locatorPtr);
8330+
fromType, toType, coerceLocator);
83138331
coerceConstraint->setFavored();
83148332
constraints.push_back(coerceConstraint);
83158333

lib/Sema/ConstraintLocator.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
8686
case KeyPathRoot:
8787
case KeyPathValue:
8888
case KeyPathComponentResult:
89+
case ExplicitTypeCoercion:
8990
auto numValues = numNumericValuesInPathElement(elt.getKind());
9091
for (unsigned i = 0; i < numValues; ++i)
9192
id.AddInteger(elt.getValue(i));
@@ -132,6 +133,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const {
132133
case ConstraintLocator::KeyPathRoot:
133134
case ConstraintLocator::KeyPathValue:
134135
case ConstraintLocator::KeyPathComponentResult:
136+
case ConstraintLocator::ExplicitTypeCoercion:
135137
return 0;
136138

137139
case ConstraintLocator::FunctionArgument:
@@ -456,6 +458,10 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) {
456458
case KeyPathComponentResult:
457459
out << "key path component result";
458460
break;
461+
462+
case ExplicitTypeCoercion:
463+
out << "type coercion";
464+
break;
459465
}
460466
}
461467
out << ']';

lib/Sema/ConstraintLocator.h

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
8989
case KeyPathRoot:
9090
case KeyPathValue:
9191
case KeyPathComponentResult:
92+
case ExplicitTypeCoercion:
9293
return 0;
9394

9495
case ContextualType:

lib/Sema/ConstraintLocatorPathElts.def

+3
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ SIMPLE_LOCATOR_PATH_ELT(UnresolvedMember)
164164
/// The candidate witness during protocol conformance checking.
165165
CUSTOM_LOCATOR_PATH_ELT(Witness)
166166

167+
/// An explicit type coercion.
168+
SIMPLE_LOCATOR_PATH_ELT(ExplicitTypeCoercion)
169+
167170
#undef LOCATOR_PATH_ELT
168171
#undef CUSTOM_LOCATOR_PATH_ELT
169172
#undef SIMPLE_LOCATOR_PATH_ELT

test/ClangImporter/Darwin_test.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ _ = nil as Fract? // expected-error{{use of undeclared type 'Fract'}}
99
_ = nil as Darwin.Fract? // okay
1010

1111
_ = 0 as OSErr
12-
_ = noErr as OSStatus // noErr is from the overlay
12+
// noErr is from the overlay
13+
_ = noErr as OSStatus // expected-warning {{redundant cast to 'OSStatus' (aka 'Int32') has no effect}} {{11-23=}}
1314
_ = 0 as UniChar
1415

1516
_ = ProcessSerialNumber()

test/ClangImporter/MixedSource/mixed-target-using-header.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,13 @@ func testSuppressed() {
5353
#endif
5454

5555
func testMacro() {
56-
_ = CONSTANT as CInt
56+
_ = CONSTANT as CInt // expected-warning {{redundant cast to 'CInt' (aka 'Int32') has no effect}} {{16-24=}}
5757
}
5858

5959
func testFoundationOverlay() {
6060
_ = NSUTF8StringEncoding // no ambiguity
61-
_ = NSUTF8StringEncoding as UInt // and we should get the overlay version
61+
// and we should get the overlay version
62+
_ = NSUTF8StringEncoding as UInt // expected-warning {{redundant cast to 'UInt' has no effect}} {{28-36=}}
6263
}
6364

6465
#if !SILGEN

test/ClangImporter/Security_test.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
import Security
66

7-
_ = kSecClass as CFString
8-
_ = kSecClassGenericPassword as CFString
7+
_ = kSecClass as CFString // expected-warning {{redundant cast to 'CFString' has no effect}} {{15-27=}}
8+
_ = kSecClassGenericPassword as CFString // expected-warning {{redundant cast to 'CFString' has no effect}} {{30-42=}}
99
_ = kSecClassGenericPassword as CFDictionary // expected-error {{'CFString?' is not convertible to 'CFDictionary'}} {{30-32=as!}}
1010

1111
func testIntegration() {

test/ClangImporter/attr-swift_private.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public func testTopLevel() {
102102

103103
_ = __PrivAnonymousA
104104
_ = __E0PrivA
105-
_ = __PrivE1A as __PrivE1
105+
_ = __PrivE1A as __PrivE1 // expected-warning {{redundant cast to '__PrivE1' has no effect}} {{15-27=}}
106106
_ = NSEnum.__privA
107107
_ = NSEnum.B
108108
_ = NSOptions.__privA

test/ClangImporter/cf.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,9 @@ func testTollFree1(_ ccmduct: CCMutableDuct) {
9090
}
9191

9292
func testChainedAliases(_ fridge: CCRefrigerator) {
93-
_ = fridge as CCRefrigerator
93+
_ = fridge as CCRefrigerator // expected-warning {{redundant cast to 'CCRefrigerator' has no effect}} {{14-32=}}
9494

95-
_ = fridge as CCFridge
95+
_ = fridge as CCFridge // expected-warning {{redundant cast to 'CCFridge' (aka 'CCRefrigerator') has no effect}} {{14-26=}}
9696
_ = fridge as CCFridgeRef // expected-error{{'CCFridgeRef' has been renamed to 'CCFridge'}} {{17-28=CCFridge}}
9797
}
9898

test/ClangImporter/cfuncs_parse.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func test_cfunc2(_ i: Int) {
1515
#else
1616
let f = cfunc2(i, 17)
1717
#endif
18-
_ = f as Float
18+
_ = f as Float // expected-warning {{redundant cast to 'Float' has no effect}} {{9-18=}}
1919
cfunc2(b:17, a:i) // expected-error{{extraneous argument labels 'b:a:' in call}}
2020
// expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'Int32'}}
2121
cfunc2(17, i) // expected-error{{cannot convert value of type 'Int' to expected argument type 'Int32'}}

0 commit comments

Comments
 (0)