Merge pull request #13351 from slavapestov/invalid-use-of-polymorphic-init

Diagnose an invalid use of non-polymorphic constructors
diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp
index 0625e61..ac9a650 100644
--- a/lib/Sema/CSApply.cpp
+++ b/lib/Sema/CSApply.cpp
@@ -321,6 +321,85 @@
   return true;
 }
 
+/// Determine whether the given type refers to a non-final class (or
+/// dynamic self of one).
+static bool isNonFinalClass(Type type) {
+  if (auto dynamicSelf = type->getAs<DynamicSelfType>())
+    type = dynamicSelf->getSelfType();
+
+  if (auto classDecl = type->getClassOrBoundGenericClass())
+    return !classDecl->isFinal();
+
+  if (auto archetype = type->getAs<ArchetypeType>())
+    if (auto super = archetype->getSuperclass())
+      return isNonFinalClass(super);
+
+  return false;
+}
+
+// Non-required constructors may not be not inherited. Therefore when
+// constructing a class object, either the metatype must be statically
+// derived (rather than an arbitrary value of metatype type) or the referenced
+// constructor must be required.
+static bool
+diagnoseInvalidDynamicConstructorReferences(ConstraintSystem &cs,
+                                            Expr *base,
+                                            DeclNameLoc memberRefLoc,
+                                            ConstructorDecl *ctorDecl,
+                                            bool SuppressDiagnostics) {
+  auto &tc = cs.getTypeChecker();
+  auto baseTy = cs.getType(base)->getRValueType();
+  auto instanceTy = baseTy->getRValueInstanceType();
+
+  bool isStaticallyDerived =
+    base->isStaticallyDerivedMetatype(
+      [&](const Expr *expr) -> Type {
+        return cs.getType(expr);
+      });
+
+  // 'super.' is always OK
+  if (isa<SuperRefExpr>(base))
+    return true;
+
+  // 'self.' reference with concrete type is OK
+  if (isa<DeclRefExpr>(base) &&
+      cast<DeclRefExpr>(base)->getDecl()->getBaseName() == tc.Context.Id_self &&
+      !baseTy->is<ArchetypeType>())
+    return true;
+
+  // FIXME: The "hasClangNode" check here is a complete hack.
+  if (isNonFinalClass(instanceTy) &&
+      !isStaticallyDerived &&
+      !ctorDecl->hasClangNode() &&
+      !(ctorDecl->isRequired() ||
+        ctorDecl->getDeclContext()->getAsProtocolOrProtocolExtensionContext())) {
+    if (SuppressDiagnostics)
+      return false;
+
+    tc.diagnose(memberRefLoc, diag::dynamic_construct_class, instanceTy)
+      .highlight(base->getSourceRange());
+    auto ctor = cast<ConstructorDecl>(ctorDecl);
+    tc.diagnose(ctorDecl, diag::note_nonrequired_initializer,
+                ctor->isImplicit(), ctor->getFullName());
+  // Constructors cannot be called on a protocol metatype, because there is no
+  // metatype to witness it.
+  } else if (isa<ConstructorDecl>(ctorDecl) &&
+             baseTy->is<MetatypeType>() &&
+             instanceTy->isExistentialType()) {
+    if (SuppressDiagnostics)
+      return false;
+
+    if (isStaticallyDerived) {
+      tc.diagnose(memberRefLoc, diag::construct_protocol_by_name, instanceTy)
+        .highlight(base->getSourceRange());
+    } else {
+      tc.diagnose(memberRefLoc, diag::construct_protocol_value, baseTy)
+        .highlight(base->getSourceRange());
+    }
+  }
+  return true;
+}
+
 namespace {
 
   /// \brief Rewrites an expression by applying the solution of a constraint
@@ -825,12 +904,12 @@
       if (auto baseMeta = baseTy->getAs<AnyMetatypeType>()) {
         baseIsInstance = false;
         baseTy = baseMeta->getInstanceType();
+
         // If the member is a constructor, verify that it can be legally
         // referenced from this base.
         if (auto ctor = dyn_cast<ConstructorDecl>(member)) {
-          cs.setExprTypes(base);
-          if (!tc.diagnoseInvalidDynamicConstructorReferences(base, memberLoc,
-                                           baseMeta, ctor, SuppressDiagnostics))
+          if (!diagnoseInvalidDynamicConstructorReferences(cs, base, memberLoc,
+                                           ctor, SuppressDiagnostics))
             return nullptr;
         }
       }
@@ -2466,6 +2545,13 @@
                            ConstructorDecl *ctor,
                            FunctionRefKind functionRefKind,
                            Type openedType) {
+
+      // If the member is a constructor, verify that it can be legally
+      // referenced from this base.
+      if (!diagnoseInvalidDynamicConstructorReferences(cs, base, nameLoc,
+                                                       ctor, SuppressDiagnostics))
+        return nullptr;
+
       // If the subexpression is a metatype, build a direct reference to the
       // constructor.
       if (cs.getType(base)->is<AnyMetatypeType>()) {
@@ -6805,67 +6891,6 @@
   return literal;
 }
 
-/// Determine whether the given type refers to a non-final class (or
-/// dynamic self of one).
-static bool isNonFinalClass(Type type) {
-  if (auto dynamicSelf = type->getAs<DynamicSelfType>())
-    type = dynamicSelf->getSelfType();
-
-  if (auto classDecl = type->getClassOrBoundGenericClass())
-    return !classDecl->isFinal();
-
-  if (auto archetype = type->getAs<ArchetypeType>())
-    if (auto super = archetype->getSuperclass())
-      return isNonFinalClass(super);
-
-  return false;
-}
-
-// Non-required constructors may not be not inherited. Therefore when
-// constructing a class object, either the metatype must be statically
-// derived (rather than an arbitrary value of metatype type) or the referenced
-// constructor must be required.
-bool
-TypeChecker::diagnoseInvalidDynamicConstructorReferences(Expr *base,
-                                                     DeclNameLoc memberRefLoc,
-                                                     AnyMetatypeType *metaTy,
-                                                     ConstructorDecl *ctorDecl,
-                                                     bool SuppressDiagnostics) {
-  auto ty = metaTy->getInstanceType();
-  
-  // FIXME: The "hasClangNode" check here is a complete hack.
-  if (isNonFinalClass(ty) &&
-      !base->isStaticallyDerivedMetatype() &&
-      !ctorDecl->hasClangNode() &&
-      !(ctorDecl->isRequired() ||
-        ctorDecl->getDeclContext()->getAsProtocolOrProtocolExtensionContext())) {
-    if (SuppressDiagnostics)
-      return false;
-
-    diagnose(memberRefLoc, diag::dynamic_construct_class, ty)
-      .highlight(base->getSourceRange());
-    auto ctor = cast<ConstructorDecl>(ctorDecl);
-    diagnose(ctorDecl, diag::note_nonrequired_initializer,
-             ctor->isImplicit(), ctor->getFullName());
-  // Constructors cannot be called on a protocol metatype, because there is no
-  // metatype to witness it.
-  } else if (isa<ConstructorDecl>(ctorDecl) &&
-             isa<MetatypeType>(metaTy) &&
-             ty->isExistentialType()) {
-    if (SuppressDiagnostics)
-      return false;
-
-    if (base->isStaticallyDerivedMetatype()) {
-      diagnose(memberRefLoc, diag::construct_protocol_by_name, ty)
-        .highlight(base->getSourceRange());
-    } else {
-      diagnose(memberRefLoc, diag::construct_protocol_value, metaTy)
-        .highlight(base->getSourceRange());
-    }
-  }
-  return true;
-}
-
 Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
                                 ConstraintLocatorBuilder locator) {
   TypeChecker &tc = cs.getTypeChecker();
diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h
index 83f5354..9bab480 100644
--- a/lib/Sema/TypeChecker.h
+++ b/lib/Sema/TypeChecker.h
@@ -2468,15 +2468,6 @@
   /// Diagnose assigning variable to itself.
   bool diagnoseSelfAssignment(const Expr *E);
 
-  /// When referencing a class initializer, check that the base expression is
-  /// either a static metatype or that the initializer is 'required'.
-  bool
-  diagnoseInvalidDynamicConstructorReferences(Expr *base,
-                                              DeclNameLoc memberRefLoc,
-                                              AnyMetatypeType *metaTy,
-                                              ConstructorDecl *ctorDecl,
-                                              bool SuppressDiagnostics);
-
   /// Builds a string representing a "default" generic argument list for
   /// \p typeDecl. In general, this means taking the bound of each generic
   /// parameter. The \p getPreferredType callback can be used to provide a
diff --git a/test/SILGen/protocol_extensions.swift b/test/SILGen/protocol_extensions.swift
index 37d1fbc..6920b92 100644
--- a/test/SILGen/protocol_extensions.swift
+++ b/test/SILGen/protocol_extensions.swift
@@ -813,7 +813,7 @@
 // rdar://problem/21370992 - delegation from an initializer in a
 // protocol extension to an @objc initializer in a class.
 class ObjCInitClass {
-  @objc init() { }
+  @objc required init() { }
 }
 
 protocol ProtoDelegatesToObjC { }
diff --git a/test/decl/ext/protocol.swift b/test/decl/ext/protocol.swift
index 36af2df..1425c51 100644
--- a/test/decl/ext/protocol.swift
+++ b/test/decl/ext/protocol.swift
@@ -267,6 +267,22 @@
   func f4(x: NestedNominal) {}
 }
 
+// rdar://problem/21991470 & https://bugs.swift.org/browse/SR-5022
+class NonPolymorphicInit {
+  init() { } // expected-note {{selected non-required initializer 'init()'}}
+}
+
+protocol EmptyProtocol { }
+
+// The diagnostic is not very accurate, but at least we reject this.
+
+extension EmptyProtocol where Self : NonPolymorphicInit {
+  init(string: String) {
+    self.init()
+    // expected-error@-1 {{constructing an object of class type 'Self' with a metatype value must use a 'required' initializer}}
+  }
+}
+
 // ----------------------------------------------------------------------------
 // Using protocol extensions to satisfy requirements
 // ----------------------------------------------------------------------------