Skip to content

Commit a242ad8

Browse files
authored
Merge pull request #31938 from theblixguy/fix/SR-12839-5.3
[5.3] [Typechecker] Emit a specialised diagnostic for redeclaration errors when the declaration is synthesised
2 parents b981b7e + 2c64c47 commit a242ad8

File tree

6 files changed

+113
-4
lines changed

6 files changed

+113
-4
lines changed

include/swift/AST/Decl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5217,6 +5217,12 @@ class VarDecl : public AbstractStorageDecl {
52175217
Optional<PropertyWrapperMutability>
52185218
getPropertyWrapperMutability() const;
52195219

5220+
/// Returns whether this property is the backing storage property or a storage
5221+
/// wrapper for wrapper instance's projectedValue. If this property is
5222+
/// neither, then it returns `None`.
5223+
Optional<PropertyWrapperSynthesizedPropertyKind>
5224+
getPropertyWrapperSynthesizedPropertyKind() const;
5225+
52205226
/// Retrieve the backing storage property for a property that has an
52215227
/// attached property wrapper.
52225228
///

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,12 +760,20 @@ ERROR(invalid_redecl,none,"invalid redeclaration of %0", (DeclName))
760760
ERROR(invalid_redecl_init,none,
761761
"invalid redeclaration of synthesized %select{|memberwise }1%0",
762762
(DeclName, bool))
763+
ERROR(invalid_redecl_implicit,none,
764+
"invalid redeclaration of synthesized "
765+
"%select{%0|implementation for protocol requirement}1 %2",
766+
(DescriptiveDeclKind, bool, DeclName))
763767
WARNING(invalid_redecl_swift5_warning,none,
764768
"redeclaration of %0 is deprecated and will be an error in Swift 5",
765769
(DeclName))
766770

767771
NOTE(invalid_redecl_prev,none,
768772
"%0 previously declared here", (DeclName))
773+
NOTE(invalid_redecl_implicit_wrapper,none,
774+
"%0 synthesized for property wrapper "
775+
"%select{projected value|backing storage}1",
776+
(DeclName, bool))
769777

770778
ERROR(ambiguous_type_base,none,
771779
"%0 is ambiguous for type lookup in this context", (DeclNameRef))

lib/AST/Decl.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5950,6 +5950,17 @@ VarDecl::getPropertyWrapperMutability() const {
59505950
None);
59515951
}
59525952

5953+
Optional<PropertyWrapperSynthesizedPropertyKind>
5954+
VarDecl::getPropertyWrapperSynthesizedPropertyKind() const {
5955+
if (getOriginalWrappedProperty(
5956+
PropertyWrapperSynthesizedPropertyKind::Backing))
5957+
return PropertyWrapperSynthesizedPropertyKind::Backing;
5958+
if (getOriginalWrappedProperty(
5959+
PropertyWrapperSynthesizedPropertyKind::StorageWrapper))
5960+
return PropertyWrapperSynthesizedPropertyKind::StorageWrapper;
5961+
return None;
5962+
}
5963+
59535964
VarDecl *VarDecl::getPropertyWrapperBackingProperty() const {
59545965
return getPropertyWrapperBackingPropertyInfo().backingVar;
59555966
}

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,75 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const {
711711
current->diagnose(diag::invalid_redecl_init,
712712
current->getName(),
713713
otherInit->isMemberwiseInitializer());
714-
} else if (!current->isImplicit() && !other->isImplicit()) {
714+
} else if (current->isImplicit() || other->isImplicit()) {
715+
// If both declarations are implicit, we do not diagnose anything
716+
// as it would lead to misleading diagnostics and it's likely that
717+
// there's nothing actionable about it due to its implicit nature.
718+
// One special case for this is property wrappers.
719+
//
720+
// Otherwise, if 'current' is implicit, then we diagnose 'other'
721+
// since 'other' is a redeclaration of 'current'. Similarly, if
722+
// 'other' is implicit, we diagnose 'current'.
723+
const Decl *declToDiagnose = nullptr;
724+
if (current->isImplicit() && other->isImplicit()) {
725+
// If 'current' is a property wrapper backing storage property
726+
// or projected value property, then diagnose the wrapped
727+
// property.
728+
if (auto VD = dyn_cast<VarDecl>(current)) {
729+
if (auto originalWrappedProperty =
730+
VD->getOriginalWrappedProperty()) {
731+
declToDiagnose = originalWrappedProperty;
732+
}
733+
}
734+
} else {
735+
declToDiagnose = current->isImplicit() ? other : current;
736+
}
737+
738+
if (declToDiagnose) {
739+
// Figure out if the the declaration we've redeclared is a
740+
// synthesized witness for a protocol requirement.
741+
bool isProtocolRequirement = false;
742+
if (auto VD = dyn_cast<ValueDecl>(current->isImplicit() ? current
743+
: other)) {
744+
isProtocolRequirement = llvm::any_of(
745+
VD->getSatisfiedProtocolRequirements(), [&](ValueDecl *req) {
746+
return req->getName() == VD->getName();
747+
});
748+
}
749+
declToDiagnose->diagnose(diag::invalid_redecl_implicit,
750+
current->getDescriptiveKind(),
751+
isProtocolRequirement, other->getName());
752+
}
753+
754+
// Emit a specialized note if the one of the declarations is
755+
// the backing storage property ('_foo') or projected value
756+
// property ('$foo') for a wrapped property. The backing or
757+
// projected var has the same source location as the wrapped
758+
// property we diagnosed above, so we don't need to extract
759+
// the original property.
760+
const VarDecl *varToDiagnose = nullptr;
761+
auto kind = PropertyWrapperSynthesizedPropertyKind::Backing;
762+
if (auto currentVD = dyn_cast<VarDecl>(current)) {
763+
if (auto currentKind =
764+
currentVD->getPropertyWrapperSynthesizedPropertyKind()) {
765+
varToDiagnose = currentVD;
766+
kind = *currentKind;
767+
}
768+
}
769+
if (auto otherVD = dyn_cast<VarDecl>(other)) {
770+
if (auto otherKind =
771+
otherVD->getPropertyWrapperSynthesizedPropertyKind()) {
772+
varToDiagnose = otherVD;
773+
kind = *otherKind;
774+
}
775+
}
776+
777+
if (varToDiagnose) {
778+
varToDiagnose->diagnose(
779+
diag::invalid_redecl_implicit_wrapper, varToDiagnose->getName(),
780+
kind == PropertyWrapperSynthesizedPropertyKind::Backing);
781+
}
782+
} else {
715783
ctx.Diags.diagnoseWithNotes(
716784
current->diagnose(diag::invalid_redecl,
717785
current->getName()), [&]() {

test/Sema/enum_conformance_synthesis.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func customHashable() {
6161
enum InvalidCustomHashable {
6262
case A, B
6363

64-
var hashValue: String { return "" }
64+
var hashValue: String { return "" } // expected-error {{invalid redeclaration of synthesized implementation for protocol requirement 'hashValue'}}
6565
}
6666
func ==(x: InvalidCustomHashable, y: InvalidCustomHashable) -> String {
6767
return ""
@@ -369,7 +369,6 @@ func canEatHotChip(_ birthyear:Birthyear) -> Bool {
369369
return birthyear > .nineties(3)
370370
}
371371
// FIXME: Remove -verify-ignore-unknown.
372-
// <unknown>:0: error: unexpected error produced: invalid redeclaration of 'hashValue'
373372
// <unknown>:0: error: unexpected note produced: candidate has non-matching type '(Foo, Foo) -> Bool'
374373
// <unknown>:0: error: unexpected note produced: candidate has non-matching type '<T> (Generic<T>, Generic<T>) -> Bool'
375374
// <unknown>:0: error: unexpected note produced: candidate has non-matching type '(InvalidCustomHashable, InvalidCustomHashable) -> Bool'

test/decl/var/property_wrappers.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -849,11 +849,28 @@ struct WrapperWithProjectedValue<T> {
849849
var projectedValue: T { return wrappedValue }
850850
}
851851

852-
class TestInvalidRedeclaration {
852+
class TestInvalidRedeclaration1 {
853+
853854
@WrapperWithProjectedValue var i = 17
854855
// expected-note@-1 {{'i' previously declared here}}
856+
// expected-note@-2 {{'$i' synthesized for property wrapper projected value}}
857+
// expected-note@-3 {{'_i' synthesized for property wrapper backing storage}}
858+
855859
@WrapperWithProjectedValue var i = 39
856860
// expected-error@-1 {{invalid redeclaration of 'i'}}
861+
// expected-error@-2 {{invalid redeclaration of synthesized property '$i'}}
862+
// expected-error@-3 {{invalid redeclaration of synthesized property '_i'}}
863+
}
864+
865+
// SR-12839
866+
struct TestInvalidRedeclaration2 {
867+
var _foo1 = 123 // expected-error {{invalid redeclaration of synthesized property '_foo1'}}
868+
@WrapperWithInitialValue var foo1 = 123 // expected-note {{'_foo1' synthesized for property wrapper backing storage}}
869+
}
870+
871+
struct TestInvalidRedeclaration3 {
872+
@WrapperWithInitialValue var foo1 = 123 // expected-note {{'_foo1' synthesized for property wrapper backing storage}}
873+
var _foo1 = 123 // expected-error {{invalid redeclaration of synthesized property '_foo1'}}
857874
}
858875

859876
// ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)