Skip to content

Commit 5a7fd68

Browse files
authored
Merge pull request #36732 from Strieker/composed_property_wrapper_improved_error_handling
Improved error handling for composed property wrapper mismatches
2 parents 40b5aa0 + 72fc506 commit 5a7fd68

File tree

12 files changed

+126
-16
lines changed

12 files changed

+126
-16
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5672,8 +5672,8 @@ ERROR(wrapped_value_mismatch, none,
56725672
"property type %0 does not match 'wrappedValue' type %1",
56735673
(Type, Type))
56745674
ERROR(composed_property_wrapper_mismatch, none,
5675-
"composed wrapper type %0 does not match former 'wrappedValue' type %1",
5676-
(Type, Type))
5675+
"composed wrapper type %0 does not match type of '%1.wrappedValue', which is %2",
5676+
(Type, StringRef, Type))
56775677

56785678
ERROR(property_wrapper_type_access,none,
56795679
"%select{%select{variable|constant}0|property}1 "

include/swift/Sema/CSFix.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,12 @@ enum class FixKind : uint8_t {
322322
/// even though result type of the reference doesn't conform
323323
/// to an expected protocol.
324324
AllowInvalidStaticMemberRefOnProtocolMetatype,
325+
326+
/// Allow the wrappedValue type of any property wrapper that is a
327+
/// part of a composed property wrapper to mismatch the type of
328+
/// another property wrapper that is a part of the same composed
329+
/// property wrapper.
330+
AllowWrappedValueMismatch,
325331
};
326332

327333
class ConstraintFix {
@@ -615,6 +621,20 @@ class TreatArrayLiteralAsDictionary final : public ContextualMismatch {
615621
ConstraintLocator *loc);
616622
};
617623

624+
class AllowWrappedValueMismatch : public ContextualMismatch {
625+
AllowWrappedValueMismatch(ConstraintSystem &cs, Type lhs, Type rhs,
626+
ConstraintLocator *locator)
627+
: ContextualMismatch(cs, FixKind::AllowWrappedValueMismatch, lhs, rhs, locator) {}
628+
629+
public:
630+
std::string getName() const override { return "fix wrapped value type mismatch"; }
631+
632+
bool diagnose(const Solution &solution, bool asNote = false) const override;
633+
634+
static AllowWrappedValueMismatch *create(ConstraintSystem &cs, Type lhs, Type rhs,
635+
ConstraintLocator *locator);
636+
};
637+
618638
/// Mark function type as explicitly '@escaping'.
619639
class MarkExplicitlyEscaping final : public ContextualMismatch {
620640
MarkExplicitlyEscaping(ConstraintSystem &cs, Type lhs, Type rhs,

include/swift/Sema/ConstraintLocator.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,12 @@ class ConstraintLocator : public llvm::FoldingSetNode {
212212
/// Determine whether this locator points to the generic parameter.
213213
bool isForGenericParameter() const;
214214

215+
/// Determine whether this locator points to any of the
216+
/// property wrappers' types for a given composed property
217+
/// wrapper, with the exception of the innermost property wrapper's
218+
/// type.
219+
bool isForWrappedValue() const;
220+
215221
/// Determine whether this locator points to the element type of a
216222
/// sequence in a for ... in ... loop.
217223
bool isForSequenceElementType() const;
@@ -356,6 +362,11 @@ class ConstraintLocator : public llvm::FoldingSetNode {
356362
/// If this locator points to generic parameter return its type.
357363
GenericTypeParamType *getGenericParameter() const;
358364

365+
/// If this locator points to any of the property wrappers' types
366+
/// for a given composed property wrapper, with the exception
367+
/// of the innermost property wrapper's type.
368+
Type getWrappedValue() const;
369+
359370
/// Produce a profile of this locator, for use in a folding set.
360371
static void Profile(llvm::FoldingSetNodeID &id, ASTNode anchor,
361372
ArrayRef<PathElement> path);
@@ -692,6 +703,20 @@ class LocatorPathElt::GenericParameter final : public StoredPointerElement<Gener
692703
}
693704
};
694705

706+
class LocatorPathElt::WrappedValue final : public StoredPointerElement<TypeBase> {
707+
public:
708+
WrappedValue(Type type)
709+
: StoredPointerElement(PathElementKind::WrappedValue, type.getPointer()) {
710+
assert(type.getPointer() != nullptr);
711+
}
712+
713+
Type getType() const { return getStoredPointer(); }
714+
715+
static bool classof(const LocatorPathElt *elt) {
716+
return elt->getKind() == PathElementKind::WrappedValue;
717+
}
718+
};
719+
695720
class LocatorPathElt::OpenedGeneric final : public StoredPointerElement<GenericSignatureImpl> {
696721
public:
697722
OpenedGeneric(GenericSignature sig)

include/swift/Sema/ConstraintLocatorPathElts.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ SIMPLE_LOCATOR_PATH_ELT(InstanceType)
9696
/// Also contains the generic parameter type itself.
9797
CUSTOM_LOCATOR_PATH_ELT(GenericParameter)
9898

99+
/// The wrappedValue type for a property wrapper.
100+
///
101+
/// Must equal the property declaration's property wrapper type.
102+
CUSTOM_LOCATOR_PATH_ELT(WrappedValue)
103+
99104
/// A component of a key path.
100105
CUSTOM_LOCATOR_PATH_ELT(KeyPathComponent)
101106

lib/Sema/CSDiagnostics.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,16 @@ bool RequirementFailure::isStaticOrInstanceMember(const ValueDecl *decl) {
348348
return decl->isStatic();
349349
}
350350

351+
bool WrappedValueMismatch::diagnoseAsError() {
352+
auto *locator = getLocator();
353+
auto elt = locator->castLastElementTo<LocatorPathElt::WrappedValue>();
354+
355+
emitDiagnostic(diag::composed_property_wrapper_mismatch, getFromType(),
356+
resolveType(elt.getType())->getString(), getToType());
357+
358+
return true;
359+
}
360+
351361
bool RequirementFailure::diagnoseAsError() {
352362
const auto *reqDC = getRequirementDC();
353363
auto *genericCtx = getGenericContext();
@@ -639,11 +649,10 @@ Optional<Diag<Type, Type>> GenericArgumentsMismatchFailure::getDiagnosticFor(
639649
return diag::cannot_convert_condition_value;
640650
case CTP_WrappedProperty:
641651
return diag::wrapped_value_mismatch;
642-
case CTP_ComposedPropertyWrapper:
643-
return diag::composed_property_wrapper_mismatch;
644652

645653
case CTP_ThrowStmt:
646654
case CTP_ForEachStmt:
655+
case CTP_ComposedPropertyWrapper:
647656
case CTP_Unused:
648657
case CTP_CannotFail:
649658
case CTP_YieldByReference:
@@ -3132,11 +3141,10 @@ ContextualFailure::getDiagnosticFor(ContextualTypePurpose context,
31323141

31333142
case CTP_WrappedProperty:
31343143
return diag::wrapped_value_mismatch;
3135-
case CTP_ComposedPropertyWrapper:
3136-
return diag::composed_property_wrapper_mismatch;
31373144

31383145
case CTP_ThrowStmt:
31393146
case CTP_ForEachStmt:
3147+
case CTP_ComposedPropertyWrapper:
31403148
case CTP_Unused:
31413149
case CTP_CannotFail:
31423150
case CTP_YieldByReference:

lib/Sema/CSDiagnostics.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,16 @@ class MissingOptionalUnwrapFailure final : public ContextualFailure {
769769
void offerForceUnwrapFixIt(const Expr *expr) const;
770770
};
771771

772+
class WrappedValueMismatch final : public ContextualFailure {
773+
public:
774+
WrappedValueMismatch(const Solution &solution, Type fromType,
775+
Type toType, ConstraintLocator *locator)
776+
: ContextualFailure(solution, fromType, toType, locator) {
777+
}
778+
779+
bool diagnoseAsError() override;
780+
};
781+
772782
/// Diagnostics for mismatched generic arguments e.g
773783
/// ```swift
774784
/// struct F<G> {}

lib/Sema/CSFix.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,18 @@ ContextualMismatch *ContextualMismatch::create(ConstraintSystem &cs, Type lhs,
372372
return new (cs.getAllocator()) ContextualMismatch(cs, lhs, rhs, locator);
373373
}
374374

375+
bool AllowWrappedValueMismatch::diagnose(const Solution &solution, bool asError) const {
376+
WrappedValueMismatch failure(solution, getFromType(), getToType(), getLocator());
377+
return failure.diagnoseAsError();
378+
}
379+
380+
AllowWrappedValueMismatch *AllowWrappedValueMismatch::create(ConstraintSystem &cs,
381+
Type lhs,
382+
Type rhs,
383+
ConstraintLocator *locator) {
384+
return new (cs.getAllocator()) AllowWrappedValueMismatch(cs, lhs, rhs, locator);
385+
}
386+
375387
/// Computes the contextual type information for a type mismatch of a
376388
/// component in a structural type (tuple or function type).
377389
///

lib/Sema/CSGen.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3590,6 +3590,7 @@ static bool generateWrappedPropertyTypeConstraints(
35903590
auto dc = wrappedVar->getInnermostDeclContext();
35913591

35923592
Type wrappedValueType;
3593+
Type wrapperType;
35933594
auto wrapperAttributes = wrappedVar->getAttachedPropertyWrappers();
35943595
for (unsigned i : indices(wrapperAttributes)) {
35953596
// FIXME: We should somehow pass an OpenUnboundGenericTypeFn to
@@ -3601,22 +3602,25 @@ static bool generateWrappedPropertyTypeConstraints(
36013602
return true;
36023603

36033604
auto *typeExpr = wrapperAttributes[i]->getTypeExpr();
3604-
auto *locator = cs.getConstraintLocator(typeExpr);
3605-
auto wrapperType = cs.replaceInferableTypesWithTypeVars(rawWrapperType, locator);
3606-
cs.setType(typeExpr, wrapperType);
36073605

36083606
if (!wrappedValueType) {
36093607
// Equate the outermost wrapper type to the initializer type.
3608+
auto *locator = cs.getConstraintLocator(typeExpr);
3609+
wrapperType =
3610+
cs.replaceInferableTypesWithTypeVars(rawWrapperType, locator);
36103611
if (initializerType)
36113612
cs.addConstraint(ConstraintKind::Equal, wrapperType, initializerType, locator);
36123613
} else {
36133614
// The former wrappedValue type must be equal to the current wrapper type
3614-
cs.addConstraint(ConstraintKind::Equal, wrapperType, wrappedValueType,
3615-
cs.getConstraintLocator(locator, LocatorPathElt::ContextualType()));
3616-
cs.setContextualType(typeExpr, TypeLoc::withoutLoc(wrappedValueType),
3617-
CTP_ComposedPropertyWrapper);
3615+
auto *locator = cs.getConstraintLocator(
3616+
typeExpr, LocatorPathElt::WrappedValue(wrapperType));
3617+
wrapperType =
3618+
cs.replaceInferableTypesWithTypeVars(rawWrapperType, locator);
3619+
cs.addConstraint(ConstraintKind::Equal, wrapperType, wrappedValueType, locator);
36183620
}
36193621

3622+
cs.setType(typeExpr, wrapperType);
3623+
36203624
wrappedValueType = wrapperType->getTypeOfMember(
36213625
dc->getParentModule(), wrapperInfo.valueVar);
36223626
}

lib/Sema/CSSimplify.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4312,6 +4312,12 @@ bool ConstraintSystem::repairFailures(
43124312
break;
43134313
}
43144314

4315+
case ConstraintLocator::WrappedValue: {
4316+
conversionsOrFixes.push_back(AllowWrappedValueMismatch::create(
4317+
*this, lhs, rhs, getConstraintLocator(locator)));
4318+
break;
4319+
}
4320+
43154321
case ConstraintLocator::FunctionArgument: {
43164322
auto *argLoc = getConstraintLocator(
43174323
locator.withPathElement(LocatorPathElt::SynthesizedArgument(0)));
@@ -11254,6 +11260,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1125411260
case FixKind::AllowUnsupportedRuntimeCheckedCast:
1125511261
case FixKind::AllowAlwaysSucceedCheckedCast:
1125611262
case FixKind::AllowInvalidStaticMemberRefOnProtocolMetatype:
11263+
case FixKind::AllowWrappedValueMismatch:
1125711264
case FixKind::RemoveExtraneousArguments: {
1125811265
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
1125911266
}

lib/Sema/ConstraintLocator.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const {
6060
case ConstraintLocator::DynamicType:
6161
case ConstraintLocator::SubscriptMember:
6262
case ConstraintLocator::OpenedGeneric:
63+
case ConstraintLocator::WrappedValue:
6364
case ConstraintLocator::GenericParameter:
6465
case ConstraintLocator::GenericArgument:
6566
case ConstraintLocator::NamedTupleElement:
@@ -188,6 +189,10 @@ bool ConstraintLocator::isForGenericParameter() const {
188189
return isLastElement<LocatorPathElt::GenericParameter>();
189190
}
190191

192+
bool ConstraintLocator::isForWrappedValue() const {
193+
return isLastElement<LocatorPathElt::WrappedValue>();
194+
}
195+
191196
bool ConstraintLocator::isForSequenceElementType() const {
192197
return isLastElement<LocatorPathElt::SequenceElementType>();
193198
}
@@ -218,6 +223,12 @@ GenericTypeParamType *ConstraintLocator::getGenericParameter() const {
218223
castLastElementTo<LocatorPathElt::GenericParameter>().getType() : nullptr;
219224
}
220225

226+
Type ConstraintLocator::getWrappedValue() const {
227+
return isForWrappedValue()
228+
? castLastElementTo<LocatorPathElt::WrappedValue>().getType()
229+
: Type();
230+
}
231+
221232
void ConstraintLocator::dump(SourceManager *sm) const {
222233
dump(sm, llvm::errs());
223234
llvm::errs() << "\n";
@@ -270,6 +281,12 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const {
270281
out << "generic parameter '" << gpElt.getType()->getString(PO) << "'";
271282
break;
272283
}
284+
case WrappedValue: {
285+
auto wrappedValueElt = elt.castTo<LocatorPathElt::WrappedValue>();
286+
out << "composed property wrapper type '"
287+
<< wrappedValueElt.getType()->getString(PO) << "'";
288+
break;
289+
}
273290
case ApplyArgument:
274291
out << "apply argument";
275292
break;

test/decl/var/property_wrappers.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,8 +1096,10 @@ struct TestComposition {
10961096
@WrapperD<WrapperE, Int, String> @WrapperE var p3: Int?
10971097
@WrapperD<WrapperC, Int, String> @WrapperC var p4: Int?
10981098
@WrapperD<WrapperC, Int, String> @WrapperE var p5: Int // expected-error{{generic parameter 'Value' could not be inferred}}
1099-
// expected-note@-1 {{explicitly specify the generic arguments to fix this issue}}
1100-
// expected-error@-2 {{composed wrapper type 'WrapperE<Int>' does not match former 'wrappedValue' type 'WrapperC<Value>'}}
1099+
// expected-note@-1 {{explicitly specify the generic arguments to fix this issue}}
1100+
// expected-error@-2 {{composed wrapper type 'WrapperE<Int>' does not match type of 'WrapperD<WrapperC<Value>, Int, String>.wrappedValue', which is 'WrapperC<Value>'}}
1101+
1102+
@Wrapper<String> @Wrapper var value: Int // expected-error{{composed wrapper type 'Wrapper<Int>' does not match type of 'Wrapper<String>.wrappedValue', which is 'String'}}
11011103

11021104
func triggerErrors(d: Double) { // expected-note 6 {{mark method 'mutating' to make 'self' mutable}} {{2-2=mutating }}
11031105
p1 = d // expected-error{{cannot assign value of type 'Double' to type 'Int?'}} {{8-8=Int(}} {{9-9=)}}

validation-test/compiler_crashers_2_fixed/sr11599.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ struct B {
1111
}
1212

1313
struct C {
14-
@A @B var foo: Int // expected-error{{composed wrapper type 'B' does not match former 'wrappedValue' type 'Int'}}
14+
@A @B var foo: Int // expected-error{{composed wrapper type 'B' does not match type of 'A.wrappedValue', which is 'Int'}}
1515
}

0 commit comments

Comments
 (0)