Merge pull request #21505 from DougGregor/dynamic-replacement-ambiguity-5.0
[5.0] [Type checker] Basic ambiguity resolution + diagnostics for dynamic replacement
diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def
index 77f7917..7b61a50 100644
--- a/include/swift/AST/DiagnosticsSema.def
+++ b/include/swift/AST/DiagnosticsSema.def
@@ -3942,6 +3942,10 @@
"replaced function %0 could not be found", (DeclName))
ERROR(dynamic_replacement_accessor_not_found, none,
"replaced accessor for %0 could not be found", (DeclName))
+ERROR(dynamic_replacement_accessor_ambiguous, none,
+ "replaced accessor for %0 occurs in multiple places", (DeclName))
+NOTE(dynamic_replacement_accessor_ambiguous_candidate, none,
+ "candidate accessor found in module %0", (DeclName))
ERROR(dynamic_replacement_function_of_type_not_found, none,
"replaced function %0 of type %1 could not be found", (DeclName, Type))
NOTE(dynamic_replacement_found_function_of_type, none,
diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp
index 5577763..a1d2ad6 100644
--- a/lib/Sema/TypeCheckAttr.cpp
+++ b/lib/Sema/TypeCheckAttr.cpp
@@ -2036,8 +2036,9 @@
replacement->getModuleScopeContext(), nullptr,
attr->getLocation());
if (lookup.isSuccess()) {
- for (auto entry : lookup.Results)
+ for (auto entry : lookup.Results) {
results.push_back(entry.getValueDecl());
+ }
}
return;
}
@@ -2051,6 +2052,28 @@
{typeCtx}, replacedDeclName, NL_QualifiedDefault, results);
}
+/// Remove any argument labels from the interface type of the given value that
+/// are extraneous from the type system's point of view, producing the
+/// type to compare against for the purposes of dynamic replacement.
+static Type getDynamicComparisonType(ValueDecl *value) {
+ unsigned numArgumentLabels = 0;
+
+ if (isa<AbstractFunctionDecl>(value)) {
+ ++numArgumentLabels;
+
+ if (value->getDeclContext()->isTypeContext())
+ ++numArgumentLabels;
+ } else if (isa<SubscriptDecl>(value)) {
+ ++numArgumentLabels;
+ }
+
+ auto interfaceType = value->getInterfaceType();
+ if (interfaceType->hasError())
+ return interfaceType;
+
+ return interfaceType->removeArgumentLabels(numArgumentLabels);
+}
+
static FuncDecl *findReplacedAccessor(DeclName replacedVarName,
AccessorDecl *replacement,
DynamicReplacementAttr *attr,
@@ -2060,13 +2083,47 @@
SmallVector<ValueDecl *, 4> results;
lookupReplacedDecl(replacedVarName, attr, replacement, results);
+ // Filter out any accessors that won't work.
+ if (!results.empty()) {
+ auto replacementStorage = replacement->getStorage();
+ TC.validateDecl(replacementStorage);
+ Type replacementStorageType = getDynamicComparisonType(replacementStorage);
+ results.erase(std::remove_if(results.begin(), results.end(),
+ [&](ValueDecl *result) {
+ // Check for static/instance mismatch.
+ if (result->isStatic() != replacementStorage->isStatic())
+ return true;
+
+ // Check for type mismatch.
+ TC.validateDecl(result);
+ auto resultType = getDynamicComparisonType(result);
+ if (!resultType->isEqual(replacementStorageType)) {
+ return true;
+ }
+
+ return false;
+ }),
+ results.end());
+ }
+
if (results.empty()) {
TC.diagnose(attr->getLocation(),
diag::dynamic_replacement_accessor_not_found, replacedVarName);
attr->setInvalid();
return nullptr;
}
- assert(results.size() == 1 && "Should only have on var or fun");
+
+ if (results.size() > 1) {
+ TC.diagnose(attr->getLocation(),
+ diag::dynamic_replacement_accessor_ambiguous, replacedVarName);
+ for (auto result : results) {
+ TC.diagnose(result,
+ diag::dynamic_replacement_accessor_ambiguous_candidate,
+ result->getModuleContext()->getFullName());
+ }
+ attr->setInvalid();
+ return nullptr;
+ }
assert(!isa<FuncDecl>(results[0]));
TC.validateDecl(results[0]);
@@ -2117,6 +2174,10 @@
lookupReplacedDecl(replacedFunctionName, attr, replacement, results);
for (auto *result : results) {
+ // Check for static/instance mismatch.
+ if (result->isStatic() != replacement->isStatic())
+ continue;
+
TC.validateDecl(result);
if (result->getInterfaceType()->getCanonicalType()->matches(
replacement->getInterfaceType()->getCanonicalType(),
diff --git a/test/attr/Inputs/dynamicReplacementA.swift b/test/attr/Inputs/dynamicReplacementA.swift
new file mode 100644
index 0000000..3572996
--- /dev/null
+++ b/test/attr/Inputs/dynamicReplacementA.swift
@@ -0,0 +1,2 @@
+public struct TheReplaceables {
+}
diff --git a/test/attr/Inputs/dynamicReplacementB.swift b/test/attr/Inputs/dynamicReplacementB.swift
new file mode 100644
index 0000000..7b2918b
--- /dev/null
+++ b/test/attr/Inputs/dynamicReplacementB.swift
@@ -0,0 +1,9 @@
+import A
+
+public extension TheReplaceables {
+ dynamic var property1: Int { return 0 }
+ dynamic var property2: Int { return 0 }
+
+ dynamic subscript (i: Int) -> Int { return 0 }
+ dynamic subscript (i: Int) -> String { return "" }
+}
diff --git a/test/attr/Inputs/dynamicReplacementC.swift b/test/attr/Inputs/dynamicReplacementC.swift
new file mode 100644
index 0000000..25b4a8f
--- /dev/null
+++ b/test/attr/Inputs/dynamicReplacementC.swift
@@ -0,0 +1,9 @@
+import A
+
+public extension TheReplaceables {
+ dynamic var property1: Int { return 0 }
+ dynamic var property2: String { return "" }
+
+ dynamic subscript (i: Int) -> Int { return 0 }
+ dynamic subscript (s: String) -> String { return "" }
+}
diff --git a/test/attr/dynamicReplacement.swift b/test/attr/dynamicReplacement.swift
new file mode 100644
index 0000000..48aaf28
--- /dev/null
+++ b/test/attr/dynamicReplacement.swift
@@ -0,0 +1,40 @@
+// RUN: %empty-directory(%t)
+// RUN: %target-swift-frontend -emit-module -swift-version 5 -enable-implicit-dynamic %S/Inputs/dynamicReplacementA.swift -o %t -module-name A
+// RUN: %target-swift-frontend -emit-module -swift-version 5 -enable-implicit-dynamic -c %S/Inputs/dynamicReplacementB.swift -o %t -I %t -module-name B
+// RUN: %target-swift-frontend -emit-module -swift-version 5 -enable-implicit-dynamic -c %S/Inputs/dynamicReplacementC.swift -o %t -I %t -module-name C
+// RUN: %target-typecheck-verify-swift -swift-version 5 -enable-implicit-dynamic -I %t
+import A
+import B
+import C
+
+// rdar://problem/46737657: static properties
+struct StaticProperties {
+ dynamic var property: Int { return 1 }
+ dynamic static var property: Int { return 11 }
+}
+
+extension StaticProperties {
+ @_dynamicReplacement(for: property)
+ var replacement_property: Int { return 2 }
+}
+
+// Replacements involving different types.
+extension TheReplaceables {
+ @_dynamicReplacement(for: property1) // expected-error{{replaced accessor for 'property1' occurs in multiple places}}
+ var replace_property1: Int { return 0 }
+
+ @_dynamicReplacement(for: property2)
+ var replace_property2_int: Int { return 1 }
+
+ @_dynamicReplacement(for: property2)
+ var replace_property2_string: String { return "replaced" }
+
+ @_dynamicReplacement(for: subscript(_:)) // expected-error{{replaced accessor for 'subscript(_:)' occurs in multiple places}}
+ subscript (int_int i: Int) -> Int { return 0 }
+
+ @_dynamicReplacement(for: subscript(_:))
+ subscript (int_string i: Int) -> String { return "" }
+
+ @_dynamicReplacement(for: subscript(_:))
+ subscript (string_string i: String) -> String { return "" }
+}