15
15
// ===----------------------------------------------------------------------===//
16
16
17
17
#include " MiscDiagnostics.h"
18
- #include " TypeChecker .h"
18
+ #include " ConstraintSystem .h"
19
19
#include " TypeCheckAvailability.h"
20
+ #include " TypeChecker.h"
20
21
#include " swift/AST/ASTWalker.h"
21
22
#include " swift/AST/NameLookup.h"
22
23
#include " swift/AST/NameLookupRequests.h"
34
35
35
36
#define DEBUG_TYPE " Sema"
36
37
using namespace swift ;
38
+ using namespace constraints ;
37
39
38
40
// / Return true if this expression is an implicit promotion from T to T?.
39
41
static Expr *isImplicitPromotionToOptional (Expr *E) {
@@ -4438,6 +4440,131 @@ static void diagnoseExplicitUseOfLazyVariableStorage(const Expr *E,
4438
4440
const_cast <Expr *>(E)->walk (Walker);
4439
4441
}
4440
4442
4443
+ static void diagnoseComparisonWithNaN (const Expr *E, const DeclContext *DC) {
4444
+ class ComparisonWithNaNFinder : public ASTWalker {
4445
+ const ASTContext &C;
4446
+ const DeclContext *DC;
4447
+
4448
+ public:
4449
+ ComparisonWithNaNFinder (const DeclContext *dc)
4450
+ : C(dc->getASTContext ()), DC(dc) {}
4451
+
4452
+ void tryDiagnoseComparisonWithNaN (BinaryExpr *BE) {
4453
+ ValueDecl *comparisonDecl = nullptr ;
4454
+
4455
+ // Comparison functions like == or <= take two arguments.
4456
+ if (BE->getArg ()->getNumElements () != 2 ) {
4457
+ return ;
4458
+ }
4459
+
4460
+ // Dig out the function declaration.
4461
+ if (auto Fn = BE->getFn ()) {
4462
+ if (auto DSCE = dyn_cast<DotSyntaxCallExpr>(Fn)) {
4463
+ comparisonDecl = DSCE->getCalledValue ();
4464
+ } else {
4465
+ comparisonDecl = BE->getCalledValue ();
4466
+ }
4467
+ }
4468
+
4469
+ // Bail out if it isn't a function.
4470
+ if (!comparisonDecl || !isa<FuncDecl>(comparisonDecl)) {
4471
+ return ;
4472
+ }
4473
+
4474
+ // We're only interested in comparison functions like == or <=.
4475
+ auto comparisonDeclName = comparisonDecl->getBaseIdentifier ();
4476
+ if (!comparisonDeclName.isStandardComparisonOperator ()) {
4477
+ return ;
4478
+ }
4479
+
4480
+ auto firstArg = BE->getArg ()->getElement (0 );
4481
+ auto secondArg = BE->getArg ()->getElement (1 );
4482
+
4483
+ // Both arguments must conform to FloatingPoint protocol.
4484
+ if (!conformsToKnownProtocol (const_cast <DeclContext *>(DC),
4485
+ firstArg->getType (),
4486
+ KnownProtocolKind::FloatingPoint) ||
4487
+ !conformsToKnownProtocol (const_cast <DeclContext *>(DC),
4488
+ secondArg->getType (),
4489
+ KnownProtocolKind::FloatingPoint)) {
4490
+ return ;
4491
+ }
4492
+
4493
+ // Convenience utility to extract argument decl.
4494
+ auto extractArgumentDecl = [&](Expr *arg) -> ValueDecl * {
4495
+ if (auto DRE = dyn_cast<DeclRefExpr>(arg)) {
4496
+ return DRE->getDecl ();
4497
+ } else if (auto MRE = dyn_cast<MemberRefExpr>(arg)) {
4498
+ return MRE->getMember ().getDecl ();
4499
+ }
4500
+ return nullptr ;
4501
+ };
4502
+
4503
+ // Dig out the declarations for the arguments.
4504
+ auto *firstVal = extractArgumentDecl (firstArg);
4505
+ auto *secondVal = extractArgumentDecl (secondArg);
4506
+
4507
+ // If we can't find declarations for both arguments, bail out,
4508
+ // because one of them has to be '.nan'.
4509
+ if (!firstArg && !secondArg) {
4510
+ return ;
4511
+ }
4512
+
4513
+ // Convenience utility to check if this is a 'nan' variable.
4514
+ auto isNanDecl = [&](ValueDecl *VD) {
4515
+ return VD && isa<VarDecl>(VD) && VD->getBaseIdentifier ().is (" nan" );
4516
+ };
4517
+
4518
+ // Diagnose comparison with '.nan'.
4519
+ //
4520
+ // If the comparison is done using '<=', '<', '==', '>', '>=', then
4521
+ // the result is always false. If the comparison is done using '!=',
4522
+ // then the result is always true.
4523
+ //
4524
+ // Emit a different diagnostic which doesn't mention using '.isNaN' if
4525
+ // the comparison isn't done using '==' or '!=' or if both sides are
4526
+ // '.nan'.
4527
+ if (isNanDecl (firstVal) && isNanDecl (secondVal)) {
4528
+ C.Diags .diagnose (BE->getLoc (), diag::nan_comparison_both_nan,
4529
+ comparisonDeclName.str (), comparisonDeclName.is (" !=" ));
4530
+ } else if (isNanDecl (firstVal) || isNanDecl (secondVal)) {
4531
+ if (comparisonDeclName.is (" ==" ) || comparisonDeclName.is (" !=" )) {
4532
+ auto exprStr =
4533
+ C.SourceMgr
4534
+ .extractText (Lexer::getCharSourceRangeFromSourceRange (
4535
+ C.SourceMgr , firstArg->getSourceRange ()))
4536
+ .str ();
4537
+ auto prefix = exprStr;
4538
+ if (comparisonDeclName.is (" !=" )) {
4539
+ prefix = " !" + prefix;
4540
+ }
4541
+ C.Diags .diagnose (BE->getLoc (), diag::nan_comparison,
4542
+ comparisonDeclName, comparisonDeclName.is (" !=" ),
4543
+ prefix, exprStr);
4544
+ } else {
4545
+ C.Diags .diagnose (BE->getLoc (), diag::nan_comparison_without_isnan,
4546
+ comparisonDeclName, comparisonDeclName.is (" !=" ));
4547
+ }
4548
+ }
4549
+ }
4550
+
4551
+ std::pair<bool , Expr *> walkToExprPre (Expr *E) override {
4552
+ if (!E || isa<ErrorExpr>(E) || !E->getType ())
4553
+ return {false , E};
4554
+
4555
+ if (auto *BE = dyn_cast<BinaryExpr>(E)) {
4556
+ tryDiagnoseComparisonWithNaN (BE);
4557
+ return {false , E};
4558
+ }
4559
+
4560
+ return {true , E};
4561
+ }
4562
+ };
4563
+
4564
+ ComparisonWithNaNFinder Walker (DC);
4565
+ const_cast <Expr *>(E)->walk (Walker);
4566
+ }
4567
+
4441
4568
// ===----------------------------------------------------------------------===//
4442
4569
// High-level entry points.
4443
4570
// ===----------------------------------------------------------------------===//
@@ -4454,6 +4581,7 @@ void swift::performSyntacticExprDiagnostics(const Expr *E,
4454
4581
diagnoseUnintendedOptionalBehavior (E, DC);
4455
4582
maybeDiagnoseCallToKeyValueObserveMethod (E, DC);
4456
4583
diagnoseExplicitUseOfLazyVariableStorage (E, DC);
4584
+ diagnoseComparisonWithNaN (E, DC);
4457
4585
if (!ctx.isSwiftVersionAtLeast (5 ))
4458
4586
diagnoseDeprecatedWritableKeyPath (E, DC);
4459
4587
if (!ctx.LangOpts .DisableAvailabilityChecking )
0 commit comments