@@ -257,6 +257,12 @@ static DeclTy *findNamedWitnessImpl(TypeChecker &tc, DeclContext *dc, Type type,
257
257
conformance->getWitness (requirement, &tc).getDecl ());
258
258
}
259
259
260
+ static VarDecl *findNamedPropertyWitness (TypeChecker &tc, DeclContext *dc,
261
+ Type type, ProtocolDecl *proto,
262
+ Identifier name, Diag<> diag) {
263
+ return findNamedWitnessImpl<VarDecl>(tc, dc, type, proto, name, diag);
264
+ }
265
+
260
266
static bool shouldAccessStorageDirectly (Expr *base, VarDecl *member,
261
267
DeclContext *DC) {
262
268
// This only matters for stored properties.
@@ -6271,10 +6277,20 @@ bool ConstraintSystem::applySolutionFix(Expr *expr,
6271
6277
6272
6278
// If we didn't manage to resolve directly to an expression, we don't
6273
6279
// have a great diagnostic to give, so bail.
6274
- if (!resolved || !resolved->getAnchor () ||
6275
- !resolved->getPath ().empty ())
6280
+ if (!resolved || !resolved->getAnchor ())
6276
6281
return false ;
6277
6282
6283
+ if (!resolved->getPath ().empty ()) {
6284
+ // We allow OptionalToBoolean fixes with an opened type to refer to the
6285
+ // Boolean conformance.
6286
+ if (fix.first .getKind () == FixKind::OptionalToBoolean &&
6287
+ resolved->getPath ().size () == 1 &&
6288
+ resolved->getPath ()[0 ].getKind () == ConstraintLocator::OpenedGeneric)
6289
+ ; /* ok */
6290
+ else
6291
+ return false ;
6292
+ }
6293
+
6278
6294
Expr *affected = resolved->getAnchor ();
6279
6295
6280
6296
switch (fix.first .getKind ()) {
@@ -6365,6 +6381,74 @@ bool ConstraintSystem::applySolutionFix(Expr *expr,
6365
6381
return true ;
6366
6382
}
6367
6383
6384
+ case FixKind::OptionalToBoolean: {
6385
+ // If we're implicitly trying to treat an optional type as a boolean,
6386
+ // let the user know that they should be testing for a value manually
6387
+ // instead.
6388
+ Expr *errorExpr = expr;
6389
+ StringRef prefix = " ((" ;
6390
+ StringRef suffix = " ) != nil)" ;
6391
+
6392
+ // In the common case of a !x, post the error against the inner
6393
+ // expression as an == comparison.
6394
+ if (auto PUE =
6395
+ dyn_cast<PrefixUnaryExpr>(errorExpr->getSemanticsProvidingExpr ())){
6396
+ bool isNot = false ;
6397
+ if (auto *D = PUE->getCalledValue ())
6398
+ isNot = D->getNameStr () == " !" ;
6399
+ else if (auto *ODR = dyn_cast<OverloadedDeclRefExpr>(PUE->getFn ()))
6400
+ isNot = ODR->getDecls ()[0 ]->getNameStr () == " !" ;
6401
+
6402
+ if (isNot) {
6403
+ suffix = " ) == nil)" ;
6404
+ errorExpr = PUE->getArg ();
6405
+
6406
+ // Check if we need the inner parentheses.
6407
+ // Technically we only need them if there's something in 'expr' with
6408
+ // lower precedence than '!=', but the code actually comes out nicer
6409
+ // in most cases with parens on anything non-trivial.
6410
+ if (errorExpr->canAppendCallParentheses ()) {
6411
+ prefix = prefix.drop_back ();
6412
+ suffix = suffix.drop_front ();
6413
+ }
6414
+ // FIXME: The outer parentheses may be superfluous too.
6415
+
6416
+
6417
+ TC.diagnose (errorExpr->getLoc (),diag::optional_used_as_true_boolean,
6418
+ simplifyType (errorExpr->getType ())->getRValueType ())
6419
+ .fixItRemove (PUE->getLoc ())
6420
+ .fixItInsert (errorExpr->getStartLoc (), prefix)
6421
+ .fixItInsertAfter (errorExpr->getEndLoc (), suffix);
6422
+ return true ;
6423
+ }
6424
+ }
6425
+
6426
+ // If we can, post the fix-it to the sub-expression if it's a better
6427
+ // fit.
6428
+ if (auto ifExpr = dyn_cast<IfExpr>(errorExpr))
6429
+ errorExpr = ifExpr->getCondExpr ();
6430
+ if (auto prefixUnaryExpr = dyn_cast<PrefixUnaryExpr>(errorExpr))
6431
+ errorExpr = prefixUnaryExpr->getArg ();
6432
+
6433
+
6434
+ // Check if we need the inner parentheses.
6435
+ // Technically we only need them if there's something in 'expr' with
6436
+ // lower precedence than '!=', but the code actually comes out nicer
6437
+ // in most cases with parens on anything non-trivial.
6438
+ if (errorExpr->canAppendCallParentheses ()) {
6439
+ prefix = prefix.drop_back ();
6440
+ suffix = suffix.drop_front ();
6441
+ }
6442
+ // FIXME: The outer parentheses may be superfluous too.
6443
+
6444
+ TC.diagnose (errorExpr->getLoc (), diag::optional_used_as_boolean,
6445
+ simplifyType (errorExpr->getType ())->getRValueType ())
6446
+ .fixItInsert (errorExpr->getStartLoc (), prefix)
6447
+ .fixItInsertAfter (errorExpr->getEndLoc (), suffix);
6448
+
6449
+ return true ;
6450
+ }
6451
+
6368
6452
case FixKind::CoerceToCheckedCast: {
6369
6453
if (auto *coerceExpr = dyn_cast<CoerceExpr>(locator->getAnchor ())) {
6370
6454
Expr *subExpr = coerceExpr->getSubExpr ();
@@ -6661,16 +6745,25 @@ Expr *TypeChecker::callWitness(Expr *base, DeclContext *dc,
6661
6745
// / conforms to the give \c protocol.
6662
6746
// / \param expr The expression to convert.
6663
6747
// / \param locator The locator describing where the conversion occurs.
6748
+ // / \param protocol The protocol to use for conversion.
6749
+ // / \param generalName The name of the protocol method to use for the
6750
+ // / conversion.
6664
6751
// / \param builtinName The name of the builtin method to use for the
6665
6752
// / last step of the conversion.
6753
+ // / \param brokenProtocolDiag Diagnostic to emit if the protocol
6754
+ // / definition is missing.
6666
6755
// / \param brokenBuiltinDiag Diagnostic to emit if the builtin definition
6667
6756
// / is broken.
6668
6757
// /
6669
6758
// / \returns the converted expression.
6670
- static Expr *convertViaMember (const Solution &solution, Expr *expr,
6671
- ConstraintLocator *locator,
6672
- Identifier builtinName,
6673
- Diag<> brokenBuiltinDiag) {
6759
+ static Expr *convertViaBuiltinProtocol (const Solution &solution,
6760
+ Expr *expr,
6761
+ ConstraintLocator *locator,
6762
+ ProtocolDecl *protocol,
6763
+ Identifier generalName,
6764
+ Identifier builtinName,
6765
+ Diag<> brokenProtocolDiag,
6766
+ Diag<> brokenBuiltinDiag) {
6674
6767
auto &cs = solution.getConstraintSystem ();
6675
6768
6676
6769
// FIXME: Cache name.
@@ -6683,17 +6776,66 @@ static Expr *convertViaMember(const Solution &solution, Expr *expr,
6683
6776
NameLookupOptions lookupOptions = defaultMemberLookupOptions;
6684
6777
if (isa<AbstractFunctionDecl>(cs.DC ))
6685
6778
lookupOptions |= NameLookupFlags::KnownPrivate;
6686
- auto members = tc.lookupMember (cs.DC , type->getRValueType (), builtinName,
6779
+ auto witnesses = tc.lookupMember (cs.DC , type->getRValueType (), builtinName,
6687
6780
lookupOptions);
6688
-
6781
+ if (!witnesses) {
6782
+ auto protocolType = protocol->getType ()->
6783
+ getAs<MetatypeType>()->getInstanceType ();
6784
+
6785
+ // Find the witness we need to use.
6786
+ ValueDecl *witness = nullptr ;
6787
+
6788
+ if (!protocolType->isEqual (type)) {
6789
+ witness = findNamedPropertyWitness (tc, cs.DC , type -> getRValueType (),
6790
+ protocol, generalName,
6791
+ brokenProtocolDiag);
6792
+ } else {
6793
+ // If the expression is already typed to the protocol, lookup the protocol
6794
+ // method directly.
6795
+ witnesses = tc.lookupMember (cs.DC , type->getRValueType (), generalName,
6796
+ lookupOptions);
6797
+ if (!witnesses) {
6798
+ tc.diagnose (protocol->getLoc (), brokenProtocolDiag);
6799
+ return nullptr ;
6800
+ }
6801
+ witness = witnesses[0 ];
6802
+ }
6803
+
6804
+ // Form a reference to this member.
6805
+ Expr *memberRef = new (ctx) MemberRefExpr (expr, expr->getStartLoc (),
6806
+ witness,
6807
+ DeclNameLoc (expr->getEndLoc ()),
6808
+ /* Implicit=*/ true );
6809
+ bool failed = tc.typeCheckExpressionShallow (memberRef, cs.DC );
6810
+ if (failed) {
6811
+ // If the member reference expression failed to type check, the Expr's
6812
+ // type does not conform to the given protocol.
6813
+ tc.diagnose (expr->getLoc (),
6814
+ diag::type_does_not_conform,
6815
+ type,
6816
+ protocol->getType ());
6817
+ return nullptr ;
6818
+ }
6819
+ expr = memberRef;
6820
+
6821
+ // At this point, we must have a type with the builtin member.
6822
+ type = expr->getType ();
6823
+ witnesses = tc.lookupMember (cs.DC , type->getRValueType (), builtinName,
6824
+ lookupOptions);
6825
+ if (!witnesses) {
6826
+ tc.diagnose (protocol->getLoc (), brokenProtocolDiag);
6827
+ return nullptr ;
6828
+ }
6829
+ }
6830
+
6689
6831
// Find the builtin method.
6690
- if (members .size () != 1 ) {
6691
- tc.diagnose (expr ->getLoc (), brokenBuiltinDiag);
6832
+ if (witnesses .size () != 1 ) {
6833
+ tc.diagnose (protocol ->getLoc (), brokenBuiltinDiag);
6692
6834
return nullptr ;
6693
6835
}
6694
- FuncDecl *builtinMethod = dyn_cast<FuncDecl>(members [0 ].Decl );
6836
+ FuncDecl *builtinMethod = dyn_cast<FuncDecl>(witnesses [0 ].Decl );
6695
6837
if (!builtinMethod) {
6696
- tc.diagnose (expr ->getLoc (), brokenBuiltinDiag);
6838
+ tc.diagnose (protocol ->getLoc (), brokenBuiltinDiag);
6697
6839
return nullptr ;
6698
6840
6699
6841
}
@@ -6722,9 +6864,15 @@ Expr *
6722
6864
Solution::convertBooleanTypeToBuiltinI1 (Expr *expr, ConstraintLocator *locator) const {
6723
6865
auto &tc = getConstraintSystem ().getTypeChecker ();
6724
6866
6725
- auto result = convertViaMember (*this , expr, locator,
6726
- tc.Context .Id_getBuiltinLogicValue ,
6727
- diag::broken_bool);
6867
+ // FIXME: Cache names.
6868
+ auto result = convertViaBuiltinProtocol (
6869
+ *this , expr, locator,
6870
+ tc.getProtocol (expr->getLoc (),
6871
+ KnownProtocolKind::Boolean ),
6872
+ tc.Context .Id_boolValue ,
6873
+ tc.Context .Id_getBuiltinLogicValue ,
6874
+ diag::condition_broken_proto,
6875
+ diag::broken_bool);
6728
6876
if (result && !result->getType ()->isBuiltinIntegerType (1 )) {
6729
6877
tc.diagnose (expr->getLoc (), diag::broken_bool);
6730
6878
return nullptr ;
0 commit comments