Merge pull request #17729 from DougGregor/minimize-override-checking

[Type checker] Minimize checking needed to compute the set of overridden declarations
diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp
index b7e16eb..58b2af2 100644
--- a/lib/Sema/CodeSynthesis.cpp
+++ b/lib/Sema/CodeSynthesis.cpp
@@ -2412,6 +2412,10 @@
         ctor, {classDecl, superclassCtor}, ctx);
   }
 
+  // Wire up the overrides.
+  ctor->getAttrs().add(new (ctx) OverrideAttr(/*IsImplicit=*/true));
+  ctor->setOverriddenDecl(superclassCtor);
+
   if (superclassCtor->isObjC()) {
     // Inherit the @objc name from the superclass initializer, if it
     // has one.
@@ -2430,10 +2434,6 @@
     ctor->getAttrs().add(new (ctx) RequiredAttr(/*IsImplicit=*/true));
   if (superclassCtor->isDynamic())
     ctor->getAttrs().add(new (ctx) DynamicAttr(/*IsImplicit*/true));
-
-  // Wire up the overrides.
-  ctor->getAttrs().add(new (ctx) OverrideAttr(/*IsImplicit=*/true));
-  ctor->setOverriddenDecl(superclassCtor);
 }
 
 ConstructorDecl *
diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp
index 57facce..df67867 100644
--- a/lib/Sema/TypeCheckDecl.cpp
+++ b/lib/Sema/TypeCheckDecl.cpp
@@ -1393,12 +1393,17 @@
     (D->getOverriddenDecl() &&
      D->getOverriddenDecl()->hasClangNode());
 
+  bool overridesDyanmic =
+    (D->getOverriddenDecl() &&
+     D->getOverriddenDecl()->isDynamic());
+
   bool isNSManaged = D->getAttrs().hasAttribute<NSManagedAttr>();
 
   bool isExtension = isa<ExtensionDecl>(D->getDeclContext());
 
   // We only infer 'dynamic' in these three cases.
-  if (!isExtension && !isNSManaged && !overridesImportedMethod)
+  if (!isExtension && !isNSManaged && !overridesImportedMethod &&
+      !overridesDyanmic)
     return;
 
   // The presence of 'final' blocks the inference of 'dynamic'.
@@ -2904,6 +2909,19 @@
       }
     }
 
+    if (!checkOverrides(TC, VD)) {
+      // If a property has an override attribute but does not override
+      // anything, complain.
+      auto overridden = VD->getOverriddenDecl();
+      if (auto *OA = VD->getAttrs().getAttribute<OverrideAttr>()) {
+        if (!overridden) {
+          TC.diagnose(VD, diag::property_does_not_override)
+            .highlight(OA->getLocation());
+          OA->setInvalid();
+        }
+      }
+    }
+
     TC.checkDeclAttributes(VD);
 
     triggerAccessorSynthesis(TC, VD);
@@ -3049,6 +3067,18 @@
     AccessControlChecker::checkAccessControl(TC, SD);
     UsableFromInlineChecker::checkUsableFromInline(TC, SD);
 
+    if (!checkOverrides(TC, SD)) {
+      // If a subscript has an override attribute but does not override
+      // anything, complain.
+      if (auto *OA = SD->getAttrs().getAttribute<OverrideAttr>()) {
+        if (!SD->getOverriddenDecl()) {
+          TC.diagnose(SD, diag::subscript_does_not_override)
+            .highlight(OA->getLocation());
+          OA->setInvalid();
+        }
+      }
+    }
+
     triggerAccessorSynthesis(TC, SD);
   }
 
@@ -3539,6 +3569,18 @@
     AccessControlChecker::checkAccessControl(TC, FD);
     UsableFromInlineChecker::checkUsableFromInline(TC, FD);
 
+    if (!checkOverrides(TC, FD)) {
+      // If a method has an 'override' keyword but does not
+      // override anything, complain.
+      if (auto *OA = FD->getAttrs().getAttribute<OverrideAttr>()) {
+        if (!FD->getOverriddenDecl()) {
+          TC.diagnose(FD, diag::method_does_not_override)
+            .highlight(OA->getLocation());
+          OA->setInvalid();
+        }
+      }
+    }
+
     if (FD->hasBody()) {
       // Record the body.
       TC.definedFunctions.push_back(FD);
@@ -3668,6 +3710,61 @@
   void visitConstructorDecl(ConstructorDecl *CD) {
     TC.validateDecl(CD);
 
+    // Check whether this initializer overrides an initializer in its
+    // superclass.
+    if (!checkOverrides(TC, CD)) {
+      // If an initializer has an override attribute but does not override
+      // anything or overrides something that doesn't need an 'override'
+      // keyword (e.g., a convenience initializer), complain.
+      // anything, or overrides something that complain.
+      if (auto *attr = CD->getAttrs().getAttribute<OverrideAttr>()) {
+        if (!CD->getOverriddenDecl()) {
+          TC.diagnose(CD, diag::initializer_does_not_override)
+            .highlight(attr->getLocation());
+          attr->setInvalid();
+        } else if (attr->isImplicit()) {
+          // Don't diagnose implicit attributes.
+        } else if (!overrideRequiresKeyword(CD->getOverriddenDecl())) {
+          // Special case: we are overriding a 'required' initializer, so we
+          // need (only) the 'required' keyword.
+          if (cast<ConstructorDecl>(CD->getOverriddenDecl())->isRequired()) {
+            if (CD->getAttrs().hasAttribute<RequiredAttr>()) {
+              TC.diagnose(CD, diag::required_initializer_override_keyword)
+                .fixItRemove(attr->getLocation());
+            } else {
+              TC.diagnose(CD, diag::required_initializer_override_wrong_keyword)
+                .fixItReplace(attr->getLocation(), "required");
+              CD->getAttrs().add(
+                new (TC.Context) RequiredAttr(/*IsImplicit=*/true));
+            }
+
+            TC.diagnose(findNonImplicitRequiredInit(CD->getOverriddenDecl()),
+                        diag::overridden_required_initializer_here);
+          } else {
+            // We tried to override a convenience initializer.
+            TC.diagnose(CD, diag::initializer_does_not_override)
+              .highlight(attr->getLocation());
+            TC.diagnose(CD->getOverriddenDecl(),
+                        diag::convenience_init_override_here);
+          }
+        }
+      }
+
+      // A failable initializer cannot override a non-failable one.
+      // This would normally be diagnosed by the covariance rules;
+      // however, those are disabled so that we can provide a more
+      // specific diagnostic here.
+      if (CD->getFailability() != OTK_None &&
+          CD->getOverriddenDecl() &&
+          CD->getOverriddenDecl()->getFailability() == OTK_None) {
+        TC.diagnose(CD, diag::failable_initializer_override,
+                    CD->getFullName());
+        TC.diagnose(CD->getOverriddenDecl(),
+                    diag::nonfailable_initializer_override_here,
+                    CD->getOverriddenDecl()->getFullName());
+      }
+    }
+
     // If this initializer overrides a 'required' initializer, it must itself
     // be marked 'required'.
     if (!CD->getAttrs().hasAttribute<RequiredAttr>()) {
@@ -4440,19 +4537,6 @@
     checkDeclAttributesEarly(VD);
     validateAttributes(*this, VD);
 
-    if (!checkOverrides(*this, VD)) {
-      // If a property has an override attribute but does not override
-      // anything, complain.
-      auto overridden = VD->getOverriddenDecl();
-      if (auto *OA = VD->getAttrs().getAttribute<OverrideAttr>()) {
-        if (!overridden) {
-          diagnose(VD, diag::property_does_not_override)
-            .highlight(OA->getLocation());
-          OA->setInvalid();
-        }
-      }
-    }
-
     // Properties need some special validation logic.
     if (auto *nominalDecl = VD->getDeclContext()
             ->getAsNominalTypeOrNominalTypeExtensionContext()) {
@@ -4731,18 +4815,6 @@
 
     // Member functions need some special validation logic.
     if (FD->getDeclContext()->isTypeContext()) {
-      if (!checkOverrides(*this, FD)) {
-        // If a method has an 'override' keyword but does not
-        // override anything, complain.
-        if (auto *OA = FD->getAttrs().getAttribute<OverrideAttr>()) {
-          if (!FD->getOverriddenDecl()) {
-            diagnose(FD, diag::method_does_not_override)
-              .highlight(OA->getLocation());
-            OA->setInvalid();
-          }
-        }
-      }
-
       if (FD->isOperator())
         checkMemberOperator(*this, FD);
 
@@ -4929,59 +5001,6 @@
 
     validateAttributes(*this, CD);
 
-    // Check whether this initializer overrides an initializer in its
-    // superclass.
-    if (!checkOverrides(*this, CD)) {
-      // If an initializer has an override attribute but does not override
-      // anything or overrides something that doesn't need an 'override'
-      // keyword (e.g., a convenience initializer), complain.
-      // anything, or overrides something that complain.
-      if (auto *attr = CD->getAttrs().getAttribute<OverrideAttr>()) {
-        if (!CD->getOverriddenDecl()) {
-          diagnose(CD, diag::initializer_does_not_override)
-            .highlight(attr->getLocation());
-          attr->setInvalid();
-        } else if (!overrideRequiresKeyword(CD->getOverriddenDecl())) {
-          // Special case: we are overriding a 'required' initializer, so we
-          // need (only) the 'required' keyword.
-          if (cast<ConstructorDecl>(CD->getOverriddenDecl())->isRequired()) {
-            if (CD->getAttrs().hasAttribute<RequiredAttr>()) {
-              diagnose(CD, diag::required_initializer_override_keyword)
-                .fixItRemove(attr->getLocation());
-            } else {
-              diagnose(CD, diag::required_initializer_override_wrong_keyword)
-                .fixItReplace(attr->getLocation(), "required");
-              CD->getAttrs().add(
-                new (Context) RequiredAttr(/*IsImplicit=*/true));
-            }
-
-            diagnose(findNonImplicitRequiredInit(CD->getOverriddenDecl()),
-                     diag::overridden_required_initializer_here);
-          } else {
-            // We tried to override a convenience initializer.
-            diagnose(CD, diag::initializer_does_not_override)
-              .highlight(attr->getLocation());
-            diagnose(CD->getOverriddenDecl(),
-                     diag::convenience_init_override_here);
-          }
-        }
-      }
-
-      // A failable initializer cannot override a non-failable one.
-      // This would normally be diagnosed by the covariance rules;
-      // however, those are disabled so that we can provide a more
-      // specific diagnostic here.
-      if (CD->getFailability() != OTK_None &&
-          CD->getOverriddenDecl() &&
-          CD->getOverriddenDecl()->getFailability() == OTK_None) {
-        diagnose(CD, diag::failable_initializer_override,
-                 CD->getFullName());
-        diagnose(CD->getOverriddenDecl(),
-                 diag::nonfailable_initializer_override_here,
-                 CD->getOverriddenDecl()->getFullName());
-      }
-    }
-
     // An initializer is ObjC-compatible if it's explicitly @objc or a member
     // of an ObjC-compatible class.
     if (CD->getDeclContext()->isTypeContext()) {
@@ -5123,18 +5142,6 @@
           new (C) ImplicitlyUnwrappedOptionalAttr(/* implicit= */ true));
     }
 
-    if (!checkOverrides(*this, SD)) {
-      // If a subscript has an override attribute but does not override
-      // anything, complain.
-      if (auto *OA = SD->getAttrs().getAttribute<OverrideAttr>()) {
-        if (!SD->getOverriddenDecl()) {
-          diagnose(SD, diag::subscript_does_not_override)
-              .highlight(OA->getLocation());
-          OA->setInvalid();
-        }
-      }
-    }
-
     // Member subscripts need some special validation logic.
     if (dc->isTypeContext()) {
       // If this is a class member, mark it final if the class is final.
diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp
index d571e8c..7cf2352 100644
--- a/lib/Sema/TypeCheckDeclOverride.cpp
+++ b/lib/Sema/TypeCheckDeclOverride.cpp
@@ -72,12 +72,24 @@
   assert((method || abstractStorage) && "Not a method or abstractStorage?");
   SubscriptDecl *subscript = dyn_cast_or_null<SubscriptDecl>(abstractStorage);
 
+  if (!member->hasInterfaceType()) {
+    auto lazyResolver = ctx.getLazyResolver();
+    assert(lazyResolver && "Need to resolve interface type");
+    lazyResolver->resolveDeclSignature(member);
+  }
+
   auto memberType = member->getInterfaceType();
   if (derivedDecl) {
     auto *dc = derivedDecl->getDeclContext();
     auto owningType = dc->getDeclaredInterfaceType();
     assert(owningType);
 
+    if (!derivedDecl->hasInterfaceType()) {
+      auto lazyResolver = ctx.getLazyResolver();
+      assert(lazyResolver && "Need to resolve interface type");
+      lazyResolver->resolveDeclSignature(derivedDecl);
+    }
+
     memberType = owningType->adjustSuperclassMemberDeclType(member, derivedDecl,
                                                             memberType);
     if (memberType->hasError())
@@ -956,8 +968,21 @@
 ///
 /// \returns true if an error occurred.
 bool swift::checkOverrides(TypeChecker &TC, ValueDecl *decl) {
-  if (decl->getOverriddenDecl())
-    return false;
+  // If we already computed overridden declarations and either succeeded
+  // or invalidated the attribute, there's nothing more to do.
+  if (decl->overriddenDeclsComputed()) {
+    // If we computed an overridden declaration successfully, we're done.
+    if (decl->getOverriddenDecl())
+      return false;
+
+    // If we set the override attribute to "invalid", we already diagnosed
+    // something here.
+    if (decl->getAttrs().hasAttribute<OverrideAttr>(/*AllowInvalid=*/true) &&
+        !decl->getAttrs().hasAttribute<OverrideAttr>())
+      return true;
+
+    // Otherwise, we have more checking to do.
+  }
 
   // Set up matching, but bail out if there's nothing to match.
   OverrideMatcher matcher(TC, decl);
@@ -1168,6 +1193,10 @@
       if (Override->isFinal())
         return;
 
+      // Must be @objc to be 'dynamic'.
+      if (!Override->isObjC())
+        return;
+
       makeDynamic(Override->getASTContext(), Override);
     }
 
@@ -1462,13 +1491,6 @@
     diagnoseOverrideForAvailability(override, base);
   }
 
-  /// Check attributes associated with the base; some may need to merged with
-  /// or checked against attributes in the overriding declaration.
-  AttributeOverrideChecker attrChecker(base, override);
-  for (auto attr : base->getAttrs()) {
-    attrChecker.visit(attr);
-  }
-
   if (auto overridingFunc = dyn_cast<FuncDecl>(override)) {
     overridingFunc->setOverriddenDecl(cast<FuncDecl>(base));
   } else if (auto overridingCtor = dyn_cast<ConstructorDecl>(override)) {
@@ -1476,8 +1498,20 @@
   } else if (auto overridingASD = dyn_cast<AbstractStorageDecl>(override)) {
     auto *baseASD = cast<AbstractStorageDecl>(base);
     overridingASD->setOverriddenDecl(baseASD);
+  } else {
+    llvm_unreachable("Unexpected decl");
+  }
 
+  /// Check attributes associated with the base; some may need to merged with
+  /// or checked against attributes in the overriding declaration.
+  AttributeOverrideChecker attrChecker(base, override);
+  for (auto attr : base->getAttrs()) {
+    attrChecker.visit(attr);
+  }
+
+  if (auto overridingASD = dyn_cast<AbstractStorageDecl>(override)) {
     // Make sure we get consistent overrides for the accessors as well.
+    auto *baseASD = cast<AbstractStorageDecl>(base);
     assert(baseASD->getGetter());
 
     auto recordAccessorOverride = [&](AccessorKind kind) {
@@ -1515,8 +1549,6 @@
     recordAccessorOverride(AccessorKind::Get);
     recordAccessorOverride(AccessorKind::Set);
     recordAccessorOverride(AccessorKind::MaterializeForSet);
-  } else {
-    llvm_unreachable("Unexpected decl");
   }
 
   return false;
@@ -1647,6 +1679,41 @@
   if (!isa<ConstructorDecl>(VD) && !VD->getAttrs().hasAttribute<OverrideAttr>())
     return;
 
-  // FIXME: We should perform more minimal validation.
-  validateDeclForNameLookup(VD);
+  // Invalidate an existing "override" attribute or add a new invalid "override"
+  // attribute, which will suppress additional checking.
+  auto invalidateOverrideAttribute = [VD]() {
+    auto overrideAttr = VD->getAttrs().getAttribute<OverrideAttr>(true);
+    if (!overrideAttr) {
+      overrideAttr = new (VD->getASTContext()) OverrideAttr(true);
+      VD->getAttrs().add(overrideAttr);
+    }
+
+    overrideAttr->setInvalid();
+  };
+
+  // Try to match potential overridden declarations.
+  OverrideMatcher matcher(*this, VD);
+  if (!matcher) {
+    return;
+  }
+
+  auto matches = matcher.match(OverrideCheckingAttempt::PerfectMatch);
+  if (matches.empty()) {
+    return;
+  }
+
+  // If we have more than one potential match, diagnose the ambiguity and
+  // fail.
+  if (matches.size() > 1) {
+    diagnoseGeneralOverrideFailure(VD, matches,
+                                   OverrideCheckingAttempt::PerfectMatch);
+    invalidateOverrideAttribute();
+    return;
+  }
+
+  // Check the correctness of the override.
+  // FIXME: This also records the override.
+  if (matcher.checkOverride(matches.front().Decl,
+                            OverrideCheckingAttempt::PerfectMatch))
+    invalidateOverrideAttribute();
 }
diff --git a/test/ClangImporter/objc_override.swift b/test/ClangImporter/objc_override.swift
index bf8bdd7..f27995f 100644
--- a/test/ClangImporter/objc_override.swift
+++ b/test/ClangImporter/objc_override.swift
@@ -19,7 +19,8 @@
   @objc func initWithString(_ string: String) { }
 
   var isEnabled: Bool { // expected-error{{overriding declaration requires an 'override' keyword}}
-    get { return super.isEnabled }
+    // FIXE: The error that follows is a bit strange.
+    get { return super.isEnabled } // expected-error{{'@objc' getter for non-'@objc' property}}
   }
 
   @objc(enabled)
diff --git a/test/SourceKit/CursorInfo/cursor_overrides.swift b/test/SourceKit/CursorInfo/cursor_overrides.swift
index 1e6415a..666dd7d 100644
--- a/test/SourceKit/CursorInfo/cursor_overrides.swift
+++ b/test/SourceKit/CursorInfo/cursor_overrides.swift
@@ -5,11 +5,11 @@
 }
 
 class Cls : S1, Prot {
-  func meth() {}
+  override func meth() {}
 }
 
 class SubCls : Cls {
-  func meth() {}
+  override func meth() {}
 }
 
 func goo(x: SubCls) {
@@ -27,7 +27,7 @@
 
 // REQUIRES: objc_interop
 // RUN: %sourcekitd-test -req=cursor -pos=16:7 %s -- -embed-bitcode -I %S/Inputs/cursor-overrides %mcp_opt %s | %FileCheck -check-prefix=CHECK1 %s
-// CHECK1: source.lang.swift.ref.function.method.instance (12:8-12:14)
+// CHECK1: source.lang.swift.ref.function.method.instance (12:17-12:23)
 // CHECK1: c:@M@cursor_overrides@objc(cs)SubCls(im)meth
 // CHECK1: (SubCls) -> () -> ()
 // CHECK1:      OVERRIDES BEGIN
diff --git a/test/multifile/Inputs/overrideB.swift b/test/multifile/Inputs/overrideB.swift
new file mode 100644
index 0000000..88e6191
--- /dev/null
+++ b/test/multifile/Inputs/overrideB.swift
@@ -0,0 +1,5 @@
+class Super {
+  func bar<T>(_: Into<T>) { }
+
+  func wibble() { }
+}
diff --git a/test/multifile/Inputs/overrideC.swift b/test/multifile/Inputs/overrideC.swift
new file mode 100644
index 0000000..59d7e0c
--- /dev/null
+++ b/test/multifile/Inputs/overrideC.swift
@@ -0,0 +1,5 @@
+class Sub: Super {
+  override func bar<T>(_: Into<T>) { }
+
+  func wibble() { }
+}
diff --git a/test/multifile/override.swift b/test/multifile/override.swift
new file mode 100644
index 0000000..831170a
--- /dev/null
+++ b/test/multifile/override.swift
@@ -0,0 +1,22 @@
+// Test that overrides in other source files get checked lazily.
+
+// Make sure the overrides are resolved... but we don't diagnose a missing
+// 'override' keyword from another source file.
+// RUN: %target-swift-frontend -typecheck -primary-file %s %S/Inputs/overrideB.swift %S/Inputs/overrideC.swift -verify
+
+// Make sure we still diagnose the missing 'override' when looking at the
+// source file where it occurs.
+// RUN: not %target-swift-frontend -typecheck %s %S/Inputs/overrideB.swift -primary-file %S/Inputs/overrideC.swift 2> %t.err
+// RUN: %FileCheck %s < %t.err
+
+// expected-no-diagnostics
+
+class Foo {
+  func foo(sub: Sub) {
+    sub.bar(Into<Int>())
+  }
+}
+
+struct Into<T> { }
+
+// CHECK: overriding declaration requires an 'override' keyword
diff --git a/validation-test/compiler_crashers/28780-hasinterfacetype-no-interface-type-was-set.swift b/validation-test/compiler_crashers_fixed/28780-hasinterfacetype-no-interface-type-was-set.swift
similarity index 83%
rename from validation-test/compiler_crashers/28780-hasinterfacetype-no-interface-type-was-set.swift
rename to validation-test/compiler_crashers_fixed/28780-hasinterfacetype-no-interface-type-was-set.swift
index 3cd2147..075332f 100644
--- a/validation-test/compiler_crashers/28780-hasinterfacetype-no-interface-type-was-set.swift
+++ b/validation-test/compiler_crashers_fixed/28780-hasinterfacetype-no-interface-type-was-set.swift
@@ -5,6 +5,6 @@
 // See https://swift.org/LICENSE.txt for license information
 // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
 
-// REQUIRES: asserts
-// RUN: not --crash %target-swift-frontend %s -emit-ir
+
+// RUN: not %target-swift-frontend %s -emit-ir
 class a{func a:A.a:class A:a{{}func a
diff --git a/validation-test/compiler_crashers/28841-swift-valuedecl-getformalaccessscope-swift-declcontext-const-bool-const.swift b/validation-test/compiler_crashers_fixed/28841-swift-valuedecl-getformalaccessscope-swift-declcontext-const-bool-const.swift
similarity index 87%
rename from validation-test/compiler_crashers/28841-swift-valuedecl-getformalaccessscope-swift-declcontext-const-bool-const.swift
rename to validation-test/compiler_crashers_fixed/28841-swift-valuedecl-getformalaccessscope-swift-declcontext-const-bool-const.swift
index 5aa4c9f..838601b 100644
--- a/validation-test/compiler_crashers/28841-swift-valuedecl-getformalaccessscope-swift-declcontext-const-bool-const.swift
+++ b/validation-test/compiler_crashers_fixed/28841-swift-valuedecl-getformalaccessscope-swift-declcontext-const-bool-const.swift
@@ -5,5 +5,5 @@
 // See https://swift.org/LICENSE.txt for license information
 // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
 
-// RUN: not --crash %target-swift-frontend %s -emit-ir
+// RUN: not %target-swift-frontend %s -emit-ir
 extension a{var f=a}class a:a