Skip to content

Commit 746cad5

Browse files
authored
Merge pull request #26459 from xedin/diag-conversion-to-specified-type
[ConstraintSystem] Add a fix to ignore contextual type mismatch
2 parents c3b98ef + 129092e commit 746cad5

29 files changed

+845
-701
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 49 additions & 562 deletions
Large diffs are not rendered by default.

lib/Sema/CSDiagnostics.cpp

Lines changed: 451 additions & 65 deletions
Large diffs are not rendered by default.

lib/Sema/CSDiagnostics.h

Lines changed: 101 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -661,31 +661,89 @@ class AssignmentFailure final : public FailureDiagnostic {
661661
/// Intended to diagnose any possible contextual failure
662662
/// e.g. argument/parameter, closure result, conversions etc.
663663
class ContextualFailure : public FailureDiagnostic {
664+
ContextualTypePurpose CTP;
664665
Type FromType, ToType;
665666

666667
public:
667668
ContextualFailure(Expr *root, ConstraintSystem &cs, Type lhs, Type rhs,
668669
ConstraintLocator *locator)
669-
: FailureDiagnostic(root, cs, locator), FromType(resolve(lhs)),
670-
ToType(resolve(rhs)) {}
670+
: ContextualFailure(root, cs, cs.getContextualTypePurpose(), lhs, rhs,
671+
locator) {}
671672

672-
Type getFromType() const { return resolveType(FromType); }
673+
ContextualFailure(Expr *root, ConstraintSystem &cs,
674+
ContextualTypePurpose purpose, Type lhs, Type rhs,
675+
ConstraintLocator *locator)
676+
: FailureDiagnostic(root, cs, locator), CTP(purpose),
677+
FromType(resolve(lhs)), ToType(resolve(rhs)) {}
678+
679+
Type getFromType() const { return FromType; }
673680

674-
Type getToType() const { return resolveType(ToType); }
681+
Type getToType() const { return ToType; }
675682

676683
bool diagnoseAsError() override;
677684

678685
// If we're trying to convert something of type "() -> T" to T,
679686
// then we probably meant to call the value.
680687
bool diagnoseMissingFunctionCall() const;
681688

689+
/// Produce a specialized diagnostic if this is an invalid conversion to Bool.
690+
bool diagnoseConversionToBool() const;
691+
692+
/// Produce a specialized diagnostic if this is an attempt to initialize
693+
/// or convert an array literal to a dictionary e.g. `let _: [String: Int] = ["A", 0]`
694+
bool diagnoseConversionToDictionary() const;
695+
696+
/// Attempt to attach any relevant fix-its to already produced diagnostic.
697+
void tryFixIts(InFlightDiagnostic &diagnostic) const;
698+
699+
/// Attempts to add fix-its for these two mistakes:
700+
///
701+
/// - Passing an integer where a type conforming to RawRepresentable is
702+
/// expected, by wrapping the expression in a call to the contextual
703+
/// type's initializer
704+
///
705+
/// - Passing a type conforming to RawRepresentable where an integer is
706+
/// expected, by wrapping the expression in a call to the rawValue
707+
/// accessor
708+
///
709+
/// - Return true on the fixit is added, false otherwise.
710+
///
711+
/// This helps migration with SDK changes.
712+
bool
713+
tryRawRepresentableFixIts(InFlightDiagnostic &diagnostic,
714+
KnownProtocolKind rawRepresentablePrococol) const;
715+
716+
/// Attempts to add fix-its for these two mistakes:
717+
///
718+
/// - Passing an integer with the right type but which is getting wrapped with
719+
/// a different integer type unnecessarily. The fixit removes the cast.
720+
///
721+
/// - Passing an integer but expecting different integer type. The fixit adds
722+
/// a wrapping cast.
723+
///
724+
/// - Return true on the fixit is added, false otherwise.
725+
///
726+
/// This helps migration with SDK changes.
727+
bool tryIntegerCastFixIts(InFlightDiagnostic &diagnostic) const;
728+
729+
protected:
682730
/// Try to add a fix-it when converting between a collection and its slice
683731
/// type, such as String <-> Substring or (eventually) Array <-> ArraySlice
684-
static bool trySequenceSubsequenceFixIts(InFlightDiagnostic &diag,
685-
ConstraintSystem &CS, Type fromType,
686-
Type toType, Expr *expr);
732+
bool trySequenceSubsequenceFixIts(InFlightDiagnostic &diagnostic) const;
733+
734+
/// Try to add a fix-it that suggests to explicitly use `as` or `as!`
735+
/// to coerce one type to another if type-checker can prove that such
736+
/// conversion is possible.
737+
bool tryTypeCoercionFixIt(InFlightDiagnostic &diagnostic) const;
738+
739+
/// Check whether this contextual failure represents an invalid
740+
/// conversion from array literal to dictionary.
741+
static bool isInvalidDictionaryConversion(ConstraintSystem &cs, Expr *anchor,
742+
Type contextualType);
687743

688744
private:
745+
ContextualTypePurpose getContextualTypePurpose() const { return CTP; }
746+
689747
Type resolve(Type rawType) {
690748
auto type = resolveType(rawType)->getWithoutSpecifierType();
691749
if (auto *BGT = type->getAs<BoundGenericType>()) {
@@ -698,6 +756,42 @@ class ContextualFailure : public FailureDiagnostic {
698756
/// Try to add a fix-it to convert a stored property into a computed
699757
/// property
700758
void tryComputedPropertyFixIts(Expr *expr) const;
759+
760+
bool isIntegerType(Type type) const {
761+
return conformsToKnownProtocol(
762+
getConstraintSystem(), type,
763+
KnownProtocolKind::ExpressibleByIntegerLiteral);
764+
}
765+
766+
/// Return true if the conversion from fromType to toType is
767+
/// an invalid string index operation.
768+
bool isIntegerToStringIndexConversion() const;
769+
770+
protected:
771+
static Optional<Diag<Type, Type>>
772+
getDiagnosticFor(ContextualTypePurpose context, bool forProtocol);
773+
};
774+
775+
/// Diagnose failures related to conversion between throwing function type
776+
/// and non-throwing one e.g.
777+
///
778+
/// ```swift
779+
/// func foo<T>(_ t: T) throws -> Void {}
780+
/// let _: (Int) -> Void = foo // `foo` can't be implictly converted to
781+
/// // non-throwing type `(Int) -> Void`
782+
/// ```
783+
class ThrowingFunctionConversionFailure final : public ContextualFailure {
784+
public:
785+
ThrowingFunctionConversionFailure(Expr *root, ConstraintSystem &cs,
786+
Type fromType, Type toType,
787+
ConstraintLocator *locator)
788+
: ContextualFailure(root, cs, fromType, toType, locator) {
789+
auto fnType1 = fromType->castTo<FunctionType>();
790+
auto fnType2 = toType->castTo<FunctionType>();
791+
assert(fnType1->throws() != fnType2->throws());
792+
}
793+
794+
bool diagnoseAsError() override;
701795
};
702796

703797
/// Diagnose failures related attempt to implicitly convert types which
@@ -1422,10 +1516,6 @@ class MissingContextualConformanceFailure final : public ContextualFailure {
14221516
}
14231517

14241518
bool diagnoseAsError() override;
1425-
1426-
private:
1427-
static Optional<Diag<Type, Type>>
1428-
getDiagnosticFor(ContextualTypePurpose purpose);
14291519
};
14301520

14311521
/// Diagnose generic argument omission e.g.

lib/Sema/CSFix.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,3 +743,32 @@ bool AllowTupleSplatForSingleParameter::attempt(
743743

744744
return cs.recordFix(fix);
745745
}
746+
747+
bool DropThrowsAttribute::diagnose(Expr *root, bool asNote) const {
748+
auto &cs = getConstraintSystem();
749+
ThrowingFunctionConversionFailure failure(root, cs, getFromType(),
750+
getToType(), getLocator());
751+
return failure.diagnose(asNote);
752+
}
753+
754+
DropThrowsAttribute *DropThrowsAttribute::create(ConstraintSystem &cs,
755+
FunctionType *fromType,
756+
FunctionType *toType,
757+
ConstraintLocator *locator) {
758+
return new (cs.getAllocator())
759+
DropThrowsAttribute(cs, fromType, toType, locator);
760+
}
761+
762+
bool IgnoreContextualType::diagnose(Expr *root, bool asNote) const {
763+
auto &cs = getConstraintSystem();
764+
ContextualFailure failure(root, cs, getFromType(), getToType(), getLocator());
765+
return failure.diagnose(asNote);
766+
}
767+
768+
IgnoreContextualType *IgnoreContextualType::create(ConstraintSystem &cs,
769+
Type resultTy,
770+
Type specifiedTy,
771+
ConstraintLocator *locator) {
772+
return new (cs.getAllocator())
773+
IgnoreContextualType(cs, resultTy, specifiedTy, locator);
774+
}

lib/Sema/CSFix.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,26 @@ class ContextualMismatch : public ConstraintFix {
494494
ConstraintLocator *locator);
495495
};
496496

497+
/// This is a contextual mismatch between throwing and non-throwing
498+
/// function types, repair it by dropping `throws` attribute.
499+
class DropThrowsAttribute final : public ContextualMismatch {
500+
DropThrowsAttribute(ConstraintSystem &cs, FunctionType *fromType,
501+
FunctionType *toType, ConstraintLocator *locator)
502+
: ContextualMismatch(cs, fromType, toType, locator) {
503+
assert(fromType->throws() != toType->throws());
504+
}
505+
506+
public:
507+
std::string getName() const override { return "drop 'throws' attribute"; }
508+
509+
bool diagnose(Expr *root, bool asNote = false) const override;
510+
511+
static DropThrowsAttribute *create(ConstraintSystem &cs,
512+
FunctionType *fromType,
513+
FunctionType *toType,
514+
ConstraintLocator *locator);
515+
};
516+
497517
/// Append 'as! T' to force a downcast to the specified type.
498518
class ForceDowncast final : public ContextualMismatch {
499519
ForceDowncast(ConstraintSystem &cs, Type fromType, Type toType,
@@ -1282,6 +1302,23 @@ class AllowTupleSplatForSingleParameter final : public ConstraintFix {
12821302
ConstraintLocatorBuilder locator);
12831303
};
12841304

1305+
class IgnoreContextualType : public ContextualMismatch {
1306+
IgnoreContextualType(ConstraintSystem &cs, Type resultTy, Type specifiedTy,
1307+
ConstraintLocator *locator)
1308+
: ContextualMismatch(cs, resultTy, specifiedTy, locator) {}
1309+
1310+
public:
1311+
std::string getName() const override {
1312+
return "ignore specified contextual type";
1313+
}
1314+
1315+
bool diagnose(Expr *root, bool asNote = false) const override;
1316+
1317+
static IgnoreContextualType *create(ConstraintSystem &cs, Type resultTy,
1318+
Type specifiedTy,
1319+
ConstraintLocator *locator);
1320+
};
1321+
12851322
} // end namespace constraints
12861323
} // end namespace swift
12871324

lib/Sema/CSSimplify.cpp

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,8 +1325,15 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
13251325
// A non-throwing function can be a subtype of a throwing function.
13261326
if (func1->throws() != func2->throws()) {
13271327
// Cannot drop 'throws'.
1328-
if (func1->throws() || kind < ConstraintKind::Subtype)
1329-
return getTypeMatchFailure(locator);
1328+
if (func1->throws() || kind < ConstraintKind::Subtype) {
1329+
if (!shouldAttemptFixes())
1330+
return getTypeMatchFailure(locator);
1331+
1332+
auto *fix = DropThrowsAttribute::create(*this, func1, func2,
1333+
getConstraintLocator(locator));
1334+
if (recordFix(fix))
1335+
return getTypeMatchFailure(locator);
1336+
}
13301337
}
13311338

13321339
// A non-@noescape function type can be a subtype of a @noescape function
@@ -1768,7 +1775,7 @@ ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2,
17681775

17691776
if (!recordFix(fix)) {
17701777
// Increase the solution's score for each mismtach this fixes.
1771-
increaseScore(SK_Fix, mismatches.size());
1778+
increaseScore(SK_Fix, mismatches.size() - 1);
17721779
return getTypeMatchSuccess();
17731780
}
17741781
return result;
@@ -2533,7 +2540,41 @@ bool ConstraintSystem::repairFailures(
25332540
auto *fix = AllowTupleTypeMismatch::create(*this, lhs, rhs,
25342541
getConstraintLocator(locator));
25352542
conversionsOrFixes.push_back(fix);
2543+
break;
25362544
}
2545+
2546+
// If either side is not yet resolved, it's too early for this fix.
2547+
if (lhs->isTypeVariableOrMember() || rhs->isTypeVariableOrMember())
2548+
break;
2549+
2550+
// If contextual type is an existential value, it's handled
2551+
// after conversion restriction is attempted.
2552+
if (rhs->isExistentialType())
2553+
break;
2554+
2555+
// TODO(diagnostics): This is a problem related to `inout` mismatch,
2556+
// in argument position, and we got here from CSDiag. Once
2557+
// argument-to-pararameter conversion failures are implemented,
2558+
// this check could be removed.
2559+
if (lhs->is<InOutType>() || rhs->is<InOutType>())
2560+
break;
2561+
2562+
// If there is a deep equality, superclass restriction
2563+
// already recorded, let's not add bother ignoring
2564+
// contextual type, because actual fix is going to
2565+
// be perform once restriction is applied.
2566+
if (llvm::any_of(conversionsOrFixes,
2567+
[](const RestrictionOrFix &entry) -> bool {
2568+
return entry.IsRestriction &&
2569+
(entry.getRestriction() ==
2570+
ConversionRestrictionKind::Superclass ||
2571+
entry.getRestriction() ==
2572+
ConversionRestrictionKind::DeepEquality);
2573+
}))
2574+
break;
2575+
2576+
conversionsOrFixes.push_back(IgnoreContextualType::create(
2577+
*this, lhs, rhs, getConstraintLocator(locator)));
25372578
break;
25382579
}
25392580

@@ -7130,7 +7171,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
71307171
case FixKind::RemoveAddressOf:
71317172
case FixKind::SkipSameTypeRequirement:
71327173
case FixKind::SkipSuperclassRequirement:
7133-
case FixKind::ContextualMismatch:
71347174
case FixKind::AddMissingArguments:
71357175
case FixKind::DefaultArgumentTypeMismatch:
71367176
case FixKind::SkipUnhandledConstructInFunctionBuilder:
@@ -7139,6 +7179,24 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
71397179
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
71407180
}
71417181

7182+
case FixKind::ContextualMismatch: {
7183+
if (recordFix(fix))
7184+
return SolutionKind::Error;
7185+
7186+
// If type produced by expression is a function type
7187+
// with result type matching contextual, it should have
7188+
// been diagnosed as "missing explicit call", let's
7189+
// increase the score to make sure that we don't impede that.
7190+
if (auto *fnType = type1->getAs<FunctionType>()) {
7191+
auto result = matchTypes(fnType->getResult(), type2, matchKind,
7192+
TMF_ApplyingFix, locator);
7193+
if (result == SolutionKind::Solved)
7194+
increaseScore(SK_Fix);
7195+
}
7196+
7197+
return SolutionKind::Solved;
7198+
}
7199+
71427200
case FixKind::UseSubscriptOperator:
71437201
case FixKind::ExplicitlyEscaping:
71447202
case FixKind::CoerceToCheckedCast:

lib/Sema/ConstraintSystem.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2739,6 +2739,43 @@ bool constraints::isAutoClosureArgument(Expr *argExpr) {
27392739
return false;
27402740
}
27412741

2742+
bool constraints::conformsToKnownProtocol(ConstraintSystem &cs, Type type,
2743+
KnownProtocolKind protocol) {
2744+
if (auto *proto = cs.TC.getProtocol(SourceLoc(), protocol))
2745+
return bool(TypeChecker::conformsToProtocol(
2746+
type, proto, cs.DC, ConformanceCheckFlags::InExpression));
2747+
return false;
2748+
}
2749+
2750+
/// Check whether given type conforms to `RawPepresentable` protocol
2751+
/// and return the witness type.
2752+
Type constraints::isRawRepresentable(ConstraintSystem &cs, Type type) {
2753+
auto &TC = cs.TC;
2754+
auto *DC = cs.DC;
2755+
2756+
auto rawReprType =
2757+
TC.getProtocol(SourceLoc(), KnownProtocolKind::RawRepresentable);
2758+
if (!rawReprType)
2759+
return Type();
2760+
2761+
auto conformance = TypeChecker::conformsToProtocol(
2762+
type, rawReprType, DC, ConformanceCheckFlags::InExpression);
2763+
if (!conformance)
2764+
return Type();
2765+
2766+
return conformance->getTypeWitnessByName(type, TC.Context.Id_RawValue);
2767+
}
2768+
2769+
Type constraints::isRawRepresentable(
2770+
ConstraintSystem &cs, Type type,
2771+
KnownProtocolKind rawRepresentableProtocol) {
2772+
Type rawTy = isRawRepresentable(cs, type);
2773+
if (!rawTy || !conformsToKnownProtocol(cs, rawTy, rawRepresentableProtocol))
2774+
return Type();
2775+
2776+
return rawTy;
2777+
}
2778+
27422779
void ConstraintSystem::generateConstraints(
27432780
SmallVectorImpl<Constraint *> &constraints, Type type,
27442781
ArrayRef<OverloadChoice> choices, DeclContext *useDC,

lib/Sema/ConstraintSystem.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3928,6 +3928,18 @@ Expr *getArgumentExpr(Expr *expr, unsigned index);
39283928
// }
39293929
bool isAutoClosureArgument(Expr *argExpr);
39303930

3931+
/// Check whether type conforms to a given known protocol.
3932+
bool conformsToKnownProtocol(ConstraintSystem &cs, Type type,
3933+
KnownProtocolKind protocol);
3934+
3935+
/// Check whether given type conforms to `RawPepresentable` protocol
3936+
/// and return witness type.
3937+
Type isRawRepresentable(ConstraintSystem &cs, Type type);
3938+
/// Check whether given type conforms to a specific known kind
3939+
/// `RawPepresentable` protocol and return witness type.
3940+
Type isRawRepresentable(ConstraintSystem &cs, Type type,
3941+
KnownProtocolKind rawRepresentableProtocol);
3942+
39313943
class DisjunctionChoice {
39323944
unsigned Index;
39333945
Constraint *Choice;

0 commit comments

Comments
 (0)