Skip to content

Commit 063d420

Browse files
authored
Merge pull request #32672 from hborla/property-wrapper-diagnostics
[Property Wrappers] Improve diagnostics for property wrappers initialized out-of-line
2 parents d6982cf + 9b4f1f0 commit 063d420

File tree

14 files changed

+214
-130
lines changed

14 files changed

+214
-130
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5036,6 +5036,12 @@ NOTE(property_wrapper_direct_init,none,
50365036
ERROR(property_wrapper_incompatible_property, none,
50375037
"property type %0 does not match that of the 'wrappedValue' property of "
50385038
"its wrapper type %1", (Type, Type))
5039+
ERROR(wrapped_value_mismatch, none,
5040+
"property type %0 does not match 'wrappedValue' type %1",
5041+
(Type, Type))
5042+
ERROR(composed_property_wrapper_mismatch, none,
5043+
"composed wrapper type %0 does not match former 'wrappedValue' type %1",
5044+
(Type, Type))
50395045

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

lib/Sema/CSApply.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8039,6 +8039,8 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) {
80398039
case swift::CTP_AssignSource:
80408040
case swift::CTP_SubscriptAssignSource:
80418041
case swift::CTP_Condition:
8042+
case swift::CTP_WrappedProperty:
8043+
case swift::CTP_ComposedPropertyWrapper:
80428044
case swift::CTP_CannotFail:
80438045
result.setExpr(rewrittenExpr);
80448046
break;
@@ -8139,6 +8141,17 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) {
81398141
patternBinding->setInit(index, resultTarget->getAsExpr());
81408142
}
81418143

8144+
return target;
8145+
} else if (auto *wrappedVar = target.getAsUninitializedWrappedVar()) {
8146+
// Get the outermost wrapper type from the solution
8147+
auto outermostWrapper = wrappedVar->getAttachedPropertyWrappers().front();
8148+
auto backingType = solution.simplifyType(
8149+
solution.getType(outermostWrapper->getTypeRepr()));
8150+
8151+
auto &ctx = solution.getConstraintSystem().getASTContext();
8152+
ctx.setSideCachedPropertyWrapperBackingPropertyType(
8153+
wrappedVar, backingType->mapTypeOutOfContext());
8154+
81428155
return target;
81438156
} else {
81448157
auto fn = *target.getAsFunction();
@@ -8419,6 +8432,9 @@ SolutionApplicationTarget SolutionApplicationTarget::walk(ASTWalker &walker) {
84198432

84208433
case Kind::patternBinding:
84218434
return *this;
8435+
8436+
case Kind::uninitializedWrappedVar:
8437+
return *this;
84228438
}
84238439

84248440
llvm_unreachable("invalid target kind");

lib/Sema/CSDiagnostics.cpp

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ void RequirementFailure::emitRequirementNote(const Decl *anchor, Type lhs,
414414
}
415415

416416
bool MissingConformanceFailure::diagnoseAsError() {
417-
auto *anchor = castToExpr(getAnchor());
417+
auto anchor = getAnchor();
418418
auto nonConformingType = getLHS();
419419
auto protocolType = getRHS();
420420

@@ -423,8 +423,9 @@ bool MissingConformanceFailure::diagnoseAsError() {
423423
// with it and if so skip conformance error, otherwise we'd
424424
// produce an unrelated `<type> doesn't conform to Equatable protocol`
425425
// diagnostic.
426-
if (isPatternMatchingOperator(const_cast<Expr *>(anchor))) {
427-
if (auto *binaryOp = dyn_cast_or_null<BinaryExpr>(findParentExpr(anchor))) {
426+
if (isPatternMatchingOperator(anchor)) {
427+
auto *expr = castToExpr(anchor);
428+
if (auto *binaryOp = dyn_cast_or_null<BinaryExpr>(findParentExpr(expr))) {
428429
auto *caseExpr = binaryOp->getArg()->getElement(0);
429430

430431
llvm::SmallPtrSet<Expr *, 4> anchors;
@@ -452,6 +453,7 @@ bool MissingConformanceFailure::diagnoseAsError() {
452453
// says that conformances for enums with associated values can't be
453454
// synthesized.
454455
if (isStandardComparisonOperator(anchor)) {
456+
auto *expr = castToExpr(anchor);
455457
auto isEnumWithAssociatedValues = [](Type type) -> bool {
456458
if (auto *enumType = type->getAs<EnumType>())
457459
return !enumType->getDecl()->hasOnlyCasesWithoutAssociatedValues();
@@ -464,7 +466,7 @@ bool MissingConformanceFailure::diagnoseAsError() {
464466
(protocol->isSpecificProtocol(KnownProtocolKind::Equatable) ||
465467
protocol->isSpecificProtocol(KnownProtocolKind::Comparable))) {
466468
if (RequirementFailure::diagnoseAsError()) {
467-
auto opName = getOperatorName(anchor);
469+
auto opName = getOperatorName(expr);
468470
emitDiagnostic(diag::no_binary_op_overload_for_enum_with_payload,
469471
opName->str());
470472
return true;
@@ -613,6 +615,10 @@ Optional<Diag<Type, Type>> GenericArgumentsMismatchFailure::getDiagnosticFor(
613615
return diag::cannot_convert_subscript_assign;
614616
case CTP_Condition:
615617
return diag::cannot_convert_condition_value;
618+
case CTP_WrappedProperty:
619+
return diag::wrapped_value_mismatch;
620+
case CTP_ComposedPropertyWrapper:
621+
return diag::composed_property_wrapper_mismatch;
616622

617623
case CTP_ThrowStmt:
618624
case CTP_ForEachStmt:
@@ -2207,6 +2213,8 @@ getContextualNilDiagnostic(ContextualTypePurpose CTP) {
22072213
case CTP_ThrowStmt:
22082214
case CTP_ForEachStmt:
22092215
case CTP_YieldByReference:
2216+
case CTP_WrappedProperty:
2217+
case CTP_ComposedPropertyWrapper:
22102218
return None;
22112219

22122220
case CTP_EnumCaseRawValue:
@@ -2905,6 +2913,11 @@ ContextualFailure::getDiagnosticFor(ContextualTypePurpose context,
29052913
case CTP_Condition:
29062914
return diag::cannot_convert_condition_value;
29072915

2916+
case CTP_WrappedProperty:
2917+
return diag::wrapped_value_mismatch;
2918+
case CTP_ComposedPropertyWrapper:
2919+
return diag::composed_property_wrapper_mismatch;
2920+
29082921
case CTP_ThrowStmt:
29092922
case CTP_ForEachStmt:
29102923
case CTP_Unused:
@@ -5001,8 +5014,15 @@ bool MissingGenericArgumentsFailure::diagnoseAsError() {
50015014
scopedParameters[base].push_back(GP);
50025015
});
50035016

5004-
if (!isScoped)
5005-
return diagnoseForAnchor(castToExpr(getAnchor()), Parameters);
5017+
// FIXME: this code should be generalized now that we can anchor the
5018+
// fixes on the TypeRepr with the missing generic arg.
5019+
if (!isScoped) {
5020+
assert(getAnchor().is<Expr *>() || getAnchor().is<TypeRepr *>());
5021+
if (auto *expr = getAsExpr(getAnchor()))
5022+
return diagnoseForAnchor(expr, Parameters);
5023+
5024+
return diagnoseForAnchor(getAnchor().get<TypeRepr *>(), Parameters);
5025+
}
50065026

50075027
bool diagnosed = false;
50085028
for (const auto &scope : scopedParameters)

lib/Sema/CSDiagnostics.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,9 @@ class RequirementFailure : public FailureDiagnostic {
260260
assert(getGenericContext() &&
261261
"Affected decl not within a generic context?");
262262

263-
if (auto *parentExpr = findParentExpr(getRawAnchor().get<Expr *>()))
264-
Apply = dyn_cast<ApplyExpr>(parentExpr);
263+
if (auto *expr = getAsExpr(getRawAnchor()))
264+
if (auto *parentExpr = findParentExpr(expr))
265+
Apply = dyn_cast<ApplyExpr>(parentExpr);
265266
}
266267

267268
unsigned getRequirementIndex() const {

lib/Sema/CSGen.cpp

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3879,35 +3879,38 @@ static Expr *generateConstraintsFor(ConstraintSystem &cs, Expr *expr,
38793879
/// \param wrappedVar The property that has a property wrapper.
38803880
/// \returns the type of the property.
38813881
static Type generateWrappedPropertyTypeConstraints(
3882-
ConstraintSystem &cs, Type initializerType,
3883-
VarDecl *wrappedVar, ConstraintLocator *locator) {
3882+
ConstraintSystem &cs, Type initializerType, VarDecl *wrappedVar) {
38843883
auto dc = wrappedVar->getInnermostDeclContext();
38853884

38863885
Type wrapperType = LValueType::get(initializerType);
38873886
Type wrappedValueType;
38883887

3889-
for (unsigned i : indices(wrappedVar->getAttachedPropertyWrappers())) {
3888+
auto wrapperAttributes = wrappedVar->getAttachedPropertyWrappers();
3889+
for (unsigned i : indices(wrapperAttributes)) {
38903890
Type rawWrapperType = wrappedVar->getAttachedPropertyWrapperType(i);
3891-
if (!rawWrapperType || rawWrapperType->hasError())
3891+
auto wrapperInfo = wrappedVar->getAttachedPropertyWrapperTypeInfo(i);
3892+
if (rawWrapperType->hasError() || !wrapperInfo)
38923893
return Type();
38933894

38943895
// The former wrappedValue type must be equal to the current wrapper type
38953896
if (wrappedValueType) {
3897+
auto *typeRepr = wrapperAttributes[i]->getTypeRepr();
3898+
auto *locator =
3899+
cs.getConstraintLocator(typeRepr, LocatorPathElt::ContextualType());
38963900
wrapperType = cs.openUnboundGenericTypes(rawWrapperType, locator);
3897-
cs.addConstraint(ConstraintKind::Equal, wrappedValueType, wrapperType,
3901+
cs.addConstraint(ConstraintKind::Equal, wrapperType, wrappedValueType,
38983902
locator);
3903+
cs.setContextualType(typeRepr, TypeLoc::withoutLoc(wrappedValueType),
3904+
CTP_ComposedPropertyWrapper);
38993905
}
39003906

3901-
auto wrapperInfo = wrappedVar->getAttachedPropertyWrapperTypeInfo(i);
3902-
if (!wrapperInfo)
3903-
return Type();
3904-
39053907
wrappedValueType = wrapperType->getTypeOfMember(
39063908
dc->getParentModule(), wrapperInfo.valueVar);
39073909
}
39083910

39093911
// Set up an equality constraint to drop the lvalue-ness of the value
39103912
// type we produced.
3913+
auto locator = cs.getConstraintLocator(wrappedVar);
39113914
Type propertyType = cs.createTypeVariable(locator, 0);
39123915
cs.addConstraint(ConstraintKind::Equal, propertyType, wrappedValueType, locator);
39133916
return propertyType;
@@ -3929,7 +3932,7 @@ static bool generateInitPatternConstraints(
39293932

39303933
if (auto wrappedVar = target.getInitializationWrappedVar()) {
39313934
Type propertyType = generateWrappedPropertyTypeConstraints(
3932-
cs, cs.getType(target.getAsExpr()), wrappedVar, locator);
3935+
cs, cs.getType(target.getAsExpr()), wrappedVar);
39333936
if (!propertyType)
39343937
return true;
39353938

@@ -4172,6 +4175,27 @@ bool ConstraintSystem::generateConstraints(
41724175

41734176
return hadError;
41744177
}
4178+
4179+
case SolutionApplicationTarget::Kind::uninitializedWrappedVar: {
4180+
auto *wrappedVar = target.getAsUninitializedWrappedVar();
4181+
auto *outermostWrapper = wrappedVar->getAttachedPropertyWrappers().front();
4182+
auto *typeRepr = outermostWrapper->getTypeRepr();
4183+
auto backingType = openUnboundGenericTypes(outermostWrapper->getType(),
4184+
getConstraintLocator(typeRepr));
4185+
setType(typeRepr, backingType);
4186+
4187+
auto wrappedValueType =
4188+
generateWrappedPropertyTypeConstraints(*this, backingType, wrappedVar);
4189+
Type propertyType = wrappedVar->getType();
4190+
if (!wrappedValueType || propertyType->hasError())
4191+
return true;
4192+
4193+
addConstraint(ConstraintKind::Equal, propertyType, wrappedValueType,
4194+
getConstraintLocator(wrappedVar, LocatorPathElt::ContextualType()));
4195+
setContextualType(wrappedVar, TypeLoc::withoutLoc(wrappedValueType),
4196+
CTP_WrappedProperty);
4197+
return false;
4198+
}
41754199
}
41764200
}
41774201

lib/Sema/CSSimplify.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10156,6 +10156,8 @@ void ConstraintSystem::addContextualConversionConstraint(
1015610156
case CTP_CoerceOperand:
1015710157
case CTP_SubscriptAssignSource:
1015810158
case CTP_ForEachStmt:
10159+
case CTP_WrappedProperty:
10160+
case CTP_ComposedPropertyWrapper:
1015910161
break;
1016010162
}
1016110163

lib/Sema/ConstraintSystem.cpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4237,11 +4237,17 @@ Optional<Identifier> constraints::getOperatorName(Expr *expr) {
42374237
return None;
42384238
}
42394239

4240-
bool constraints::isPatternMatchingOperator(Expr *expr) {
4240+
bool constraints::isPatternMatchingOperator(ASTNode node) {
4241+
auto *expr = getAsExpr(node);
4242+
if (!expr) return false;
4243+
42414244
return isOperator(expr, "~=");
42424245
}
42434246

4244-
bool constraints::isStandardComparisonOperator(Expr *expr) {
4247+
bool constraints::isStandardComparisonOperator(ASTNode node) {
4248+
auto *expr = getAsExpr(node);
4249+
if (!expr) return false;
4250+
42454251
if (auto opName = getOperatorName(expr)) {
42464252
return opName->is("==") || opName->is("!=") || opName->is("===") ||
42474253
opName->is("!==") || opName->is("<") || opName->is(">") ||
@@ -4662,6 +4668,11 @@ SolutionApplicationTarget SolutionApplicationTarget::forForEachStmt(
46624668
return target;
46634669
}
46644670

4671+
SolutionApplicationTarget
4672+
SolutionApplicationTarget::forUninitializedWrappedVar(VarDecl *wrappedVar) {
4673+
return SolutionApplicationTarget(wrappedVar);
4674+
}
4675+
46654676
ContextualPattern
46664677
SolutionApplicationTarget::getContextualPattern() const {
46674678
assert(kind == Kind::expression);
@@ -4729,6 +4740,8 @@ bool SolutionApplicationTarget::contextualTypeIsOnlyAHint() const {
47294740
case CTP_AssignSource:
47304741
case CTP_SubscriptAssignSource:
47314742
case CTP_Condition:
4743+
case CTP_WrappedProperty:
4744+
case CTP_ComposedPropertyWrapper:
47324745
case CTP_CannotFail:
47334746
return false;
47344747
}
@@ -4769,6 +4782,18 @@ void ConstraintSystem::diagnoseFailureFor(SolutionApplicationTarget target) {
47694782
// diagnostic various cases that come up.
47704783
DE.diagnose(expr->getLoc(), diag::type_of_expression_is_ambiguous)
47714784
.highlight(expr->getSourceRange());
4785+
} else if (auto *wrappedVar = target.getAsUninitializedWrappedVar()) {
4786+
auto *wrapper = wrappedVar->getAttachedPropertyWrappers().back();
4787+
Type propertyType = wrappedVar->getInterfaceType();
4788+
Type wrapperType = wrapper->getType();
4789+
4790+
// Emit the property wrapper fallback diagnostic
4791+
wrappedVar->diagnose(diag::property_wrapper_incompatible_property,
4792+
propertyType, wrapperType);
4793+
if (auto nominal = wrapperType->getAnyNominal()) {
4794+
nominal->diagnose(diag::property_wrapper_declared_here,
4795+
nominal->getName());
4796+
}
47724797
} else {
47734798
// Emit a poor fallback message.
47744799
DE.diagnose(target.getAsFunction()->getLoc(),

0 commit comments

Comments
 (0)