Skip to content

Commit d53cd53

Browse files
authored
[Typechecker] Emit a specialised diagnostic for redeclaration errors when the declaration is synthesised (swiftlang#31915)
* [Typechecker] Emit a specialized diagnostic for redeclaration errors when the declaration is synthesized * [Test] Update existing tests * [Typechecker] Diagnose the original wrapped property instead of the nearest non-implicit decl context * [Test] Update existing tests * [Typechecker] Do not diagnose redeclarations when both declarations are implicit * [Test] Update a AutoDiff test
1 parent fb66658 commit d53cd53

File tree

7 files changed

+118
-7
lines changed

7 files changed

+118
-7
lines changed

include/swift/AST/Decl.h

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

5235+
/// Returns whether this property is the backing storage property or a storage
5236+
/// wrapper for wrapper instance's projectedValue. If this property is
5237+
/// neither, then it returns `None`.
5238+
Optional<PropertyWrapperSynthesizedPropertyKind>
5239+
getPropertyWrapperSynthesizedPropertyKind() const;
5240+
52355241
/// Retrieve the backing storage property for a property that has an
52365242
/// attached property wrapper.
52375243
///

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
@@ -5970,6 +5970,17 @@ VarDecl::getPropertyWrapperMutability() const {
59705970
None);
59715971
}
59725972

5973+
Optional<PropertyWrapperSynthesizedPropertyKind>
5974+
VarDecl::getPropertyWrapperSynthesizedPropertyKind() const {
5975+
if (getOriginalWrappedProperty(
5976+
PropertyWrapperSynthesizedPropertyKind::Backing))
5977+
return PropertyWrapperSynthesizedPropertyKind::Backing;
5978+
if (getOriginalWrappedProperty(
5979+
PropertyWrapperSynthesizedPropertyKind::StorageWrapper))
5980+
return PropertyWrapperSynthesizedPropertyKind::StorageWrapper;
5981+
return None;
5982+
}
5983+
59735984
VarDecl *VarDecl::getPropertyWrapperBackingProperty() const {
59745985
return getPropertyWrapperBackingPropertyInfo().backingVar;
59755986
}

lib/Sema/TypeCheckDeclPrimary.cpp

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

test/AutoDiff/compiler_crashers_fixed/sr12642-differentiable-derivation-redeclared-property.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ struct Generic<T> {}
1616
extension Generic: Differentiable where T: Differentiable {}
1717

1818
struct WrappedProperties: Differentiable {
19-
// expected-note @+2 {{'int' previously declared here}}
20-
// expected-warning @+1 {{stored property 'int' has no derivative because 'Generic<Int>' does not conform to 'Differentiable'; add an explicit '@noDerivative' attribute}}
2119
@Wrapper var int: Generic<Int>
20+
// expected-note@-1 {{'int' previously declared here}}
21+
// expected-note@-2 {{'_int' synthesized for property wrapper backing storage}}
22+
// expected-warning@-3 {{stored property 'int' has no derivative because 'Generic<Int>' does not conform to 'Differentiable'; add an explicit '@noDerivative' attribute}}
2223

23-
// expected-error @+1 {{invalid redeclaration of 'int'}}
2424
@Wrapper var int: Generic<Int>
25+
// expected-error@-1 {{invalid redeclaration of 'int'}}
26+
// expected-error@-2 {{invalid redeclaration of synthesized property '_int'}}
2527
}

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)