Merge pull request #23685 from DougGregor/custom-attributes-parse

diff --git a/include/swift/AST/ASTTypeIDZone.def b/include/swift/AST/ASTTypeIDZone.def
new file mode 100644
index 0000000..3a19b31
--- /dev/null
+++ b/include/swift/AST/ASTTypeIDZone.def
@@ -0,0 +1,19 @@
+//===--- ASTTypeIDZone.def - Define the AST TypeID Zone ---------*- C++ -*-===//
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+//
+//===----------------------------------------------------------------------===//
+//
+//  This definition file describes the types in the "AST" TypeID zone,
+//  for use with the TypeID template.
+//
+//===----------------------------------------------------------------------===//
+SWIFT_TYPEID_NAMED(NominalTypeDecl *, NominalTypeDecl)
+SWIFT_TYPEID_NAMED(VarDecl *, VarDecl)
+SWIFT_TYPEID(PropertyBehaviorTypeInfo)
diff --git a/include/swift/AST/ASTTypeIDs.h b/include/swift/AST/ASTTypeIDs.h
new file mode 100644
index 0000000..e13342c
--- /dev/null
+++ b/include/swift/AST/ASTTypeIDs.h
@@ -0,0 +1,36 @@
+//===--- ASTTypeIDs.h - AST Type Ids ----------------------------*- C++ -*-===//
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2019 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines TypeID support for AST types.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SWIFT_AST_ASTTYPEIDS_H
+#define SWIFT_AST_ASTTYPEIDS_H
+
+#include "swift/Basic/TypeID.h"
+namespace swift {
+
+class NominalTypeDecl;
+struct PropertyBehaviorTypeInfo;
+class VarDecl;
+
+#define SWIFT_AST_TYPEID_ZONE 1
+
+// Define the AST type zone (zone 1)
+#define SWIFT_TYPEID_ZONE SWIFT_AST_TYPEID_ZONE
+#define SWIFT_TYPEID_HEADER "swift/AST/ASTTypeIDZone.def"
+#include "swift/Basic/DefineTypeIDZone.h"
+
+} // end namespace swift
+
+#endif /* SWIFT_AST_ASTTYPEIDS_H */
diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def
index 28aef75..f9f0be8 100644
--- a/include/swift/AST/Attr.def
+++ b/include/swift/AST/Attr.def
@@ -393,6 +393,9 @@
 SIMPLE_DECL_ATTR(_implementationOnly, ImplementationOnly,
   OnImport | UserInaccessible,
   84)
+DECL_ATTR(_custom, Custom,
+  OnAnyDecl | UserInaccessible,
+  85)
 
 #undef TYPE_ATTR
 #undef DECL_ATTR_ALIAS
diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h
index d6b3598..c9e5324 100644
--- a/include/swift/AST/Attr.h
+++ b/include/swift/AST/Attr.h
@@ -32,6 +32,7 @@
 #include "swift/AST/Ownership.h"
 #include "swift/AST/PlatformKind.h"
 #include "swift/AST/Requirement.h"
+#include "swift/AST/TrailingCallArguments.h"
 #include "swift/AST/TypeLoc.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
@@ -49,6 +50,7 @@
 class ClassDecl;
 class GenericFunctionType;
 class LazyConformanceLoader;
+class PatternBindingInitializer;
 class TrailingWhereClause;
 
 /// TypeAttributes - These are attributes that may be applied to types.
@@ -1408,6 +1410,54 @@
   }
 };
 
+/// Defines a custom attribute.
+class CustomAttr final : public DeclAttribute,
+                         public TrailingCallArguments<CustomAttr> {
+  TypeLoc type;
+  Expr *arg;
+  PatternBindingInitializer *initContext;
+
+  unsigned hasArgLabelLocs : 1;
+  unsigned numArgLabels : 16;
+
+  CustomAttr(SourceLoc atLoc, SourceRange range, TypeLoc type,
+             PatternBindingInitializer *initContext, Expr *arg,
+             ArrayRef<Identifier> argLabels, ArrayRef<SourceLoc> argLabelLocs,
+             bool implicit);
+
+public:
+  static CustomAttr *create(ASTContext &ctx, SourceLoc atLoc, TypeLoc type,
+                            bool implicit = false) {
+    return create(ctx, atLoc, type, false, nullptr, SourceLoc(), { }, { }, { },
+                  SourceLoc(), implicit);
+  }
+
+  static CustomAttr *create(ASTContext &ctx, SourceLoc atLoc, TypeLoc type,
+                            bool hasInitializer,
+                            PatternBindingInitializer *initContext,
+                            SourceLoc lParenLoc,
+                            ArrayRef<Expr *> args,
+                            ArrayRef<Identifier> argLabels,
+                            ArrayRef<SourceLoc> argLabelLocs,
+                            SourceLoc rParenLoc,
+                            bool implicit = false);
+
+  unsigned getNumArguments() const { return numArgLabels; }
+  bool hasArgumentLabelLocs() const { return hasArgLabelLocs; }
+
+  TypeLoc &getTypeLoc() { return type; }
+  const TypeLoc &getTypeLoc() const { return type; }
+
+  Expr *getArg() const { return arg; }
+  void setArg(Expr *newArg) { arg = newArg; }
+
+  PatternBindingInitializer *getInitContext() const { return initContext; }
+
+  static bool classof(const DeclAttribute *DA) {
+    return DA->getKind() == DAK_Custom;
+  }
+};
+
 /// Attributes that may be applied to declarations.
 class DeclAttributes {
   /// Linked list of declaration attributes.
@@ -1584,6 +1634,8 @@
   SourceLoc getStartLoc(bool forModifiers = false) const;
 };
 
+void simple_display(llvm::raw_ostream &out, const DeclAttribute *attr);
+
 } // end namespace swift
 
 #endif
diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def
index 87ea871..69815cb 100644
--- a/include/swift/AST/DiagnosticsSema.def
+++ b/include/swift/AST/DiagnosticsSema.def
@@ -1104,6 +1104,9 @@
 ERROR(attribute_requires_single_argument,none,
       "'%0' requires a function with one argument", (StringRef))
 
+ERROR(nominal_type_not_attribute,none,
+      "%0 %1 cannot be used as an attribute", (DescriptiveDeclKind, DeclName))
+
 ERROR(mutating_invalid_global_scope,none, "%0 is only valid on methods",
       (SelfAccessKind))
 ERROR(mutating_invalid_classes,none, "%0 isn't valid on methods in "
diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h
index c9804f2..ec3935a 100644
--- a/include/swift/AST/Expr.h
+++ b/include/swift/AST/Expr.h
@@ -22,6 +22,7 @@
 #include "swift/AST/DeclNameLoc.h"
 #include "swift/AST/FunctionRefKind.h"
 #include "swift/AST/ProtocolConformanceRef.h"
+#include "swift/AST/TrailingCallArguments.h"
 #include "swift/AST/TypeAlignments.h"
 #include "swift/AST/TypeLoc.h"
 #include "swift/AST/TypeRepr.h"
@@ -570,105 +571,6 @@
   }
 };
 
-/// Helper class to capture trailing call argument labels and related
-/// information, for expression nodes that involve argument labels, trailing
-/// closures, etc.
-template<typename Derived>
-class TrailingCallArguments
-    : private llvm::TrailingObjects<Derived, Identifier, SourceLoc> {
-  // We need to friend TrailingObjects twice here to work around an MSVC bug.
-  // If we have two functions of the same name with the parameter
-  // typename TrailingObjectsIdentifier::template OverloadToken<T> where T is
-  // different for each function, then MSVC reports a "member function already
-  // defined or declared" error, which is incorrect.
-  using TrailingObjectsIdentifier = llvm::TrailingObjects<Derived, Identifier>;
-  friend TrailingObjectsIdentifier;
-
-  using TrailingObjects = llvm::TrailingObjects<Derived, Identifier, SourceLoc>;
-  friend TrailingObjects;
-
-  Derived &asDerived() {
-    return *static_cast<Derived *>(this);
-  }
-
-  const Derived &asDerived() const {
-    return *static_cast<const Derived *>(this);
-  }
-
-  size_t numTrailingObjects(
-      typename TrailingObjectsIdentifier::template OverloadToken<Identifier>)
-      const {
-    return asDerived().getNumArguments();
-  }
-
-  size_t numTrailingObjects(
-      typename TrailingObjectsIdentifier::template OverloadToken<SourceLoc>)
-      const {
-    return asDerived().hasArgumentLabelLocs() ? asDerived().getNumArguments()
-                                              : 0;
-  }
-
-  /// Retrieve the buffer containing the argument labels.
-  MutableArrayRef<Identifier> getArgumentLabelsBuffer() {
-    return { this->template getTrailingObjects<Identifier>(),
-             asDerived().getNumArguments() };
-  }
-
-  /// Retrieve the buffer containing the argument label locations.
-  MutableArrayRef<SourceLoc> getArgumentLabelLocsBuffer() {
-    if (!asDerived().hasArgumentLabelLocs())
-      return { };
-    
-    return { this->template getTrailingObjects<SourceLoc>(),
-             asDerived().getNumArguments() };
-  }
-
-protected:
-  /// Determine the total size to allocate.
-  static size_t totalSizeToAlloc(ArrayRef<Identifier> argLabels,
-                                 ArrayRef<SourceLoc> argLabelLocs,
-                                 bool hasTrailingClosure) {
-    return TrailingObjects::template totalSizeToAlloc<Identifier, SourceLoc>(
-        argLabels.size(), argLabelLocs.size());
-  }
-
-  /// Initialize the actual call arguments.
-  void initializeCallArguments(ArrayRef<Identifier> argLabels,
-                               ArrayRef<SourceLoc> argLabelLocs,
-                               bool hasTrailingClosure) {
-    if (!argLabels.empty()) {
-      std::uninitialized_copy(argLabels.begin(), argLabels.end(),
-                              this->template getTrailingObjects<Identifier>());
-    }
-    
-    if (!argLabelLocs.empty())
-      std::uninitialized_copy(argLabelLocs.begin(), argLabelLocs.end(),
-                              this->template getTrailingObjects<SourceLoc>());
-  }
-
-public:
-  /// Retrieve the argument labels provided at the call site.
-  ArrayRef<Identifier> getArgumentLabels() const {
-    return { this->template getTrailingObjects<Identifier>(),
-             asDerived().getNumArguments() };
-  }
-
-  /// Retrieve the buffer containing the argument label locations.
-  ArrayRef<SourceLoc> getArgumentLabelLocs() const {
-    if (!asDerived().hasArgumentLabelLocs())
-      return { };
-    
-    return { this->template getTrailingObjects<SourceLoc>(),
-             asDerived().getNumArguments() };
-  }
-
-  /// Retrieve the location of the ith argument label.
-  SourceLoc getArgumentLabelLoc(unsigned i) const {
-    auto locs = getArgumentLabelLocs();
-    return i < locs.size() ? locs[i] : SourceLoc();
-  }
-};
-
 /// ErrorExpr - Represents a semantically erroneous subexpression in the AST,
 /// typically this will have an ErrorType.
 class ErrorExpr : public Expr {
@@ -5448,7 +5350,27 @@
 }
 
 #undef SWIFT_FORWARD_SOURCE_LOCS_TO
-  
+
+/// Pack the argument information into a single argument, to match the
+/// representation expected by the AST.
+///
+/// \param argLabels The argument labels, which might be updated by this
+/// function.
+///
+/// \param argLabelLocs The argument label locations, which might be updated by
+/// this function.
+Expr *packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc,
+                         ArrayRef<Expr *> args,
+                         ArrayRef<Identifier> &argLabels,
+                         ArrayRef<SourceLoc> &argLabelLocs,
+                         SourceLoc rParenLoc,
+                         Expr *trailingClosure, bool implicit,
+                         SmallVectorImpl<Identifier> &argLabelsScratch,
+                         SmallVectorImpl<SourceLoc> &argLabelLocsScratch,
+                         llvm::function_ref<Type(const Expr *)> getType =
+                              [](const Expr *E) -> Type {
+                                return E->getType();
+                              });
 } // end namespace swift
 
 #endif
diff --git a/include/swift/AST/NameLookupRequests.h b/include/swift/AST/NameLookupRequests.h
index 5f7177a..1c68cbd 100644
--- a/include/swift/AST/NameLookupRequests.h
+++ b/include/swift/AST/NameLookupRequests.h
@@ -17,6 +17,7 @@
 #define SWIFT_NAME_LOOKUP_REQUESTS_H
 
 #include "swift/AST/SimpleRequest.h"
+#include "swift/AST/ASTTypeIDs.h"
 #include "swift/Basic/Statistic.h"
 #include "llvm/ADT/TinyPtrVector.h"
 
@@ -238,6 +239,33 @@
   void noteCycleStep(DiagnosticEngine &diags) const;
 };
 
+/// Request the nominal type declaration to which the given custom attribute
+/// refers.
+class CustomAttrNominalRequest :
+    public SimpleRequest<CustomAttrNominalRequest,
+                         CacheKind::Cached,
+                         NominalTypeDecl *,
+                         CustomAttr *,
+                         DeclContext *> {
+public:
+  using SimpleRequest::SimpleRequest;
+
+private:
+  friend SimpleRequest;
+
+  // Evaluation.
+  llvm::Expected<NominalTypeDecl *>
+  evaluate(Evaluator &evaluator, CustomAttr *attr, DeclContext *dc) const;
+
+public:
+  // Caching
+  bool isCached() const { return true; }
+
+  // Cycle handling
+  void diagnoseCycle(DiagnosticEngine &diags) const;
+  void noteCycleStep(DiagnosticEngine &diags) const;
+};
+
 /// The zone number for name-lookup requests.
 #define SWIFT_NAME_LOOKUP_REQUESTS_TYPEID_ZONE 9
 
diff --git a/include/swift/AST/NameLookupTypeIDZone.def b/include/swift/AST/NameLookupTypeIDZone.def
index d00cfbc..1d380d2 100644
--- a/include/swift/AST/NameLookupTypeIDZone.def
+++ b/include/swift/AST/NameLookupTypeIDZone.def
@@ -20,3 +20,4 @@
 SWIFT_TYPEID(ExtendedNominalRequest)
 SWIFT_TYPEID(SelfBoundsFromWhereClauseRequest)
 SWIFT_TYPEID(TypeDeclsFromWhereClauseRequest)
+SWIFT_TYPEID(CustomAttrNominalRequest)
diff --git a/include/swift/AST/TrailingCallArguments.h b/include/swift/AST/TrailingCallArguments.h
new file mode 100644
index 0000000..5279421
--- /dev/null
+++ b/include/swift/AST/TrailingCallArguments.h
@@ -0,0 +1,130 @@
+//===--- TrailingCallArguments.h - Trailing Call Arguments ------*- C++ -*-===//
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the TrailingCallArguments template, which is used
+//  to tail-allocate the names and source locations of argument labels in a
+//  call.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SWIFT_AST_TRAILINGCALLARGUMENTS_H
+#define SWIFT_AST_TRAILINGCALLARGUMENTS_H
+
+#include "swift/AST/Identifier.h"
+#include "swift/Basic/SourceLoc.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/TrailingObjects.h"
+
+namespace swift {
+
+/// Helper class to capture trailing call argument labels and related
+/// information, for expression nodes that involve argument labels, trailing
+/// closures, etc.
+template<typename Derived>
+class TrailingCallArguments
+    : private llvm::TrailingObjects<Derived, Identifier, SourceLoc> {
+  // We need to friend TrailingObjects twice here to work around an MSVC bug.
+  // If we have two functions of the same name with the parameter
+  // typename TrailingObjectsIdentifier::template OverloadToken<T> where T is
+  // different for each function, then MSVC reports a "member function already
+  // defined or declared" error, which is incorrect.
+  using TrailingObjectsIdentifier = llvm::TrailingObjects<Derived, Identifier>;
+  friend TrailingObjectsIdentifier;
+
+  using TrailingObjects = llvm::TrailingObjects<Derived, Identifier, SourceLoc>;
+  friend TrailingObjects;
+
+  Derived &asDerived() {
+    return *static_cast<Derived *>(this);
+  }
+
+  const Derived &asDerived() const {
+    return *static_cast<const Derived *>(this);
+  }
+
+  size_t numTrailingObjects(
+      typename TrailingObjectsIdentifier::template OverloadToken<Identifier>)
+      const {
+    return asDerived().getNumArguments();
+  }
+
+  size_t numTrailingObjects(
+      typename TrailingObjectsIdentifier::template OverloadToken<SourceLoc>)
+      const {
+    return asDerived().hasArgumentLabelLocs() ? asDerived().getNumArguments()
+                                              : 0;
+  }
+
+  /// Retrieve the buffer containing the argument labels.
+  MutableArrayRef<Identifier> getArgumentLabelsBuffer() {
+    return { this->template getTrailingObjects<Identifier>(),
+             asDerived().getNumArguments() };
+  }
+
+  /// Retrieve the buffer containing the argument label locations.
+  MutableArrayRef<SourceLoc> getArgumentLabelLocsBuffer() {
+    if (!asDerived().hasArgumentLabelLocs())
+      return { };
+
+    return { this->template getTrailingObjects<SourceLoc>(),
+             asDerived().getNumArguments() };
+  }
+
+protected:
+  /// Determine the total size to allocate.
+  static size_t totalSizeToAlloc(ArrayRef<Identifier> argLabels,
+                                 ArrayRef<SourceLoc> argLabelLocs,
+                                 bool hasTrailingClosure) {
+    return TrailingObjects::template totalSizeToAlloc<Identifier, SourceLoc>(
+        argLabels.size(), argLabelLocs.size());
+  }
+
+  /// Initialize the actual call arguments.
+  void initializeCallArguments(ArrayRef<Identifier> argLabels,
+                               ArrayRef<SourceLoc> argLabelLocs,
+                               bool hasTrailingClosure) {
+    if (!argLabels.empty()) {
+      std::uninitialized_copy(argLabels.begin(), argLabels.end(),
+                              this->template getTrailingObjects<Identifier>());
+    }
+
+    if (!argLabelLocs.empty())
+      std::uninitialized_copy(argLabelLocs.begin(), argLabelLocs.end(),
+                              this->template getTrailingObjects<SourceLoc>());
+  }
+
+public:
+  /// Retrieve the argument labels provided at the call site.
+  ArrayRef<Identifier> getArgumentLabels() const {
+    return { this->template getTrailingObjects<Identifier>(),
+             asDerived().getNumArguments() };
+  }
+
+  /// Retrieve the buffer containing the argument label locations.
+  ArrayRef<SourceLoc> getArgumentLabelLocs() const {
+    if (!asDerived().hasArgumentLabelLocs())
+      return { };
+
+    return { this->template getTrailingObjects<SourceLoc>(),
+             asDerived().getNumArguments() };
+  }
+
+  /// Retrieve the location of the ith argument label.
+  SourceLoc getArgumentLabelLoc(unsigned i) const {
+    auto locs = getArgumentLabelLocs();
+    return i < locs.size() ? locs[i] : SourceLoc();
+  }
+};
+
+} // end namespace swift
+
+#endif /* SWIFT_AST_TRAILINGCALLARGUMENTS_H */
diff --git a/include/swift/Serialization/ModuleFormat.h b/include/swift/Serialization/ModuleFormat.h
index d8d04cc..9eee6c0 100644
--- a/include/swift/Serialization/ModuleFormat.h
+++ b/include/swift/Serialization/ModuleFormat.h
@@ -52,7 +52,7 @@
 /// describe what change you made. The content of this comment isn't important;
 /// it just ensures a conflict if two people change the module format.
 /// Don't worry about adhering to the 80-column limit for this line.
-const uint16_t SWIFTMODULE_VERSION_MINOR = 480; // Last change: import control
+const uint16_t SWIFTMODULE_VERSION_MINOR = 481; // Last change: custom attrs
 
 using DeclIDField = BCFixed<31>;
 
@@ -1610,6 +1610,12 @@
     BCArray<IdentifierIDField>
   >;
 
+  using CustomDeclAttrLayout = BCRecordLayout<
+    Custom_DECL_ATTR,
+    BCFixed<1>,  // implicit flag
+    TypeIDField // type referenced by this custom attribute
+  >;
+
 }
 
 /// Returns the encoding kind for the given decl.
diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp
index a8751e9..4541b48 100644
--- a/lib/AST/Attr.cpp
+++ b/lib/AST/Attr.cpp
@@ -18,6 +18,7 @@
 #include "swift/AST/ASTContext.h"
 #include "swift/AST/ASTPrinter.h"
 #include "swift/AST/Decl.h"
+#include "swift/AST/Expr.h"
 #include "swift/AST/GenericEnvironment.h"
 #include "swift/AST/Module.h"
 #include "swift/AST/Types.h"
@@ -618,6 +619,16 @@
     break;
   }
 
+  case DAK_Custom: {
+    Printer.printAttrName("@");
+    const TypeLoc &typeLoc = cast<CustomAttr>(this)->getTypeLoc();
+    if (auto type = typeLoc.getType())
+      type->print(Printer, Options);
+    else
+      typeLoc.getTypeRepr()->print(Printer, Options);
+    break;
+  }
+
   case DAK_Count:
     llvm_unreachable("exceed declaration attribute kinds");
 
@@ -744,6 +755,8 @@
     return "_implements";
   case DAK_ClangImporterSynthesizedType:
     return "_clangImporterSynthesizedType";
+  case DAK_Custom:
+    return "<<custom>>";
   }
   llvm_unreachable("bad DeclAttrKind");
 }
@@ -1134,3 +1147,51 @@
 TypeLoc &ImplementsAttr::getProtocolType() {
   return ProtocolType;
 }
+
+CustomAttr::CustomAttr(SourceLoc atLoc, SourceRange range, TypeLoc type,
+                       PatternBindingInitializer *initContext, Expr *arg,
+                       ArrayRef<Identifier> argLabels,
+                       ArrayRef<SourceLoc> argLabelLocs, bool implicit)
+    : DeclAttribute(DAK_Custom, atLoc, range, implicit),
+      type(type),
+      arg(arg),
+      initContext(initContext) {
+  hasArgLabelLocs = !argLabelLocs.empty();
+  numArgLabels = argLabels.size();
+  initializeCallArguments(argLabels, argLabelLocs,
+                          /*hasTrailingClosure=*/false);
+}
+
+CustomAttr *CustomAttr::create(ASTContext &ctx, SourceLoc atLoc, TypeLoc type,
+                               bool hasInitializer,
+                               PatternBindingInitializer *initContext,
+                               SourceLoc lParenLoc,
+                               ArrayRef<Expr *> args,
+                               ArrayRef<Identifier> argLabels,
+                               ArrayRef<SourceLoc> argLabelLocs,
+                               SourceLoc rParenLoc,
+                               bool implicit) {
+  SmallVector<Identifier, 2> argLabelsScratch;
+  SmallVector<SourceLoc, 2> argLabelLocsScratch;
+  Expr *arg = nullptr;
+  if (hasInitializer) {
+    arg = packSingleArgument(ctx, lParenLoc, args, argLabels, argLabelLocs,
+                             rParenLoc, nullptr, implicit, argLabelsScratch,
+                             argLabelLocsScratch);
+  }
+
+  SourceRange range(atLoc, type.getSourceRange().End);
+  if (arg)
+    range.End = arg->getEndLoc();
+
+  size_t size = totalSizeToAlloc(argLabels, argLabelLocs,
+                                 /*hasTrailingClosure=*/false);
+  void *mem = ctx.Allocate(size, alignof(CustomAttr));
+  return new (mem) CustomAttr(atLoc, range, type, initContext, arg, argLabels,
+                              argLabelLocs, implicit);
+}
+
+void swift::simple_display(llvm::raw_ostream &out, const DeclAttribute *attr) {
+  if (attr)
+    attr->print(out);
+}
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index 98db5ba..3f9b3b0 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -1089,23 +1089,16 @@
   arg->setType(TupleType::get(typeElements, ctx));
 }
 
-/// Pack the argument information into a single argument, to match the
-/// representation expected by the AST.
-///
-/// \param argLabels The argument labels, which might be updated by this
-/// function.
-///
-/// \param argLabelLocs The argument label locations, which might be updated by
-/// this function.
-static Expr *
-packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc, ArrayRef<Expr *> args,
-                   ArrayRef<Identifier> &argLabels,
-                   ArrayRef<SourceLoc> &argLabelLocs, SourceLoc rParenLoc,
-                   Expr *trailingClosure, bool implicit,
-                   SmallVectorImpl<Identifier> &argLabelsScratch,
-                   SmallVectorImpl<SourceLoc> &argLabelLocsScratch,
-                   llvm::function_ref<Type(const Expr *)> getType =
-                       [](const Expr *E) -> Type { return E->getType(); }) {
+Expr *
+swift::packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc,
+                          ArrayRef<Expr *> args,
+                          ArrayRef<Identifier> &argLabels,
+                          ArrayRef<SourceLoc> &argLabelLocs,
+                          SourceLoc rParenLoc,
+                          Expr *trailingClosure, bool implicit,
+                          SmallVectorImpl<Identifier> &argLabelsScratch,
+                          SmallVectorImpl<SourceLoc> &argLabelLocsScratch,
+                          llvm::function_ref<Type(const Expr *)> getType) {
   // Clear out our scratch space.
   argLabelsScratch.clear();
   argLabelLocsScratch.clear();
diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp
index 9180840..a11c888 100644
--- a/lib/AST/NameLookup.cpp
+++ b/lib/AST/NameLookup.cpp
@@ -2093,6 +2093,31 @@
   return nominalTypes.empty() ? nullptr : nominalTypes.front();
 }
 
+llvm::Expected<NominalTypeDecl *>
+CustomAttrNominalRequest::evaluate(Evaluator &evaluator,
+                                   CustomAttr *attr, DeclContext *dc) const {
+  // Find the types referenced by the custom attribute.
+  auto &ctx = dc->getASTContext();
+  TypeLoc &typeLoc = attr->getTypeLoc();
+  DirectlyReferencedTypeDecls decls;
+  if (auto typeRepr = typeLoc.getTypeRepr()) {
+    decls = directReferencesForTypeRepr(
+        evaluator, ctx, typeRepr, dc);
+  } else if (Type type = typeLoc.getType()) {
+    decls = directReferencesForType(type);
+  }
+
+  // Dig out the nominal type declarations.
+  SmallVector<ModuleDecl *, 2> modulesFound;
+  bool anyObject = false;
+  auto nominals = resolveTypeDeclsToNominal(evaluator, ctx, decls,
+                                            modulesFound, anyObject);
+  if (nominals.size() == 1 && !isa<ProtocolDecl>(nominals.front()))
+    return nominals.front();
+
+  return nullptr;
+}
+
 void swift::getDirectlyInheritedNominalTypeDecls(
     llvm::PointerUnion<TypeDecl *, ExtensionDecl *> decl,
     unsigned i,
diff --git a/lib/AST/NameLookupRequests.cpp b/lib/AST/NameLookupRequests.cpp
index 0a82db6..3016b75 100644
--- a/lib/AST/NameLookupRequests.cpp
+++ b/lib/AST/NameLookupRequests.cpp
@@ -12,6 +12,7 @@
 
 #include "swift/AST/NameLookupRequests.h"
 #include "swift/Subsystems.h"
+#include "swift/AST/ASTContext.h"
 #include "swift/AST/Evaluator.h"
 #include "swift/AST/Decl.h"
 #include "swift/AST/Module.h"
@@ -176,6 +177,20 @@
   diags.diagnose(ext, diag::circular_reference_through);
 }
 
+void CustomAttrNominalRequest::diagnoseCycle(
+    DiagnosticEngine &diags) const {
+  auto attr = std::get<0>(getStorage());
+  ASTContext &ctx = std::get<1>(getStorage())->getASTContext();
+  ctx.Diags.diagnose(attr->getLocation(), diag::circular_reference);
+}
+
+void CustomAttrNominalRequest::noteCycleStep(
+    DiagnosticEngine &diags) const {
+  auto attr = std::get<0>(getStorage());
+  ASTContext &ctx = std::get<1>(getStorage())->getASTContext();
+  ctx.Diags.diagnose(attr->getLocation(), diag::circular_reference_through);
+}
+
 // Define request evaluation functions for each of the name lookup requests.
 static AbstractRequestFunction *nameLookupRequestFunctions[] = {
 #define SWIFT_TYPEID(Name)                                    \
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 7ac1dc4..6a3eb23 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -929,6 +929,7 @@
   case DAK_RestatedObjCConformance:
   case DAK_SynthesizedProtocol:
   case DAK_ClangImporterSynthesizedType:
+  case DAK_Custom:
     llvm_unreachable("virtual attributes should not be parsed "
                      "by attribute parsing code");
   case DAK_SetterAccess:
@@ -1852,8 +1853,66 @@
 
   if (TypeAttributes::getAttrKindFromString(Tok.getText()) != TAK_Count)
     diagnose(Tok, diag::type_attribute_applied_to_decl);
-  else
-    diagnose(Tok, diag::unknown_attribute, Tok.getText());
+  else if (Tok.isContextualKeyword("unknown")) {
+    diagnose(Tok, diag::unknown_attribute, "unknown");
+  } else {
+    // Parse a custom attribute.
+    auto type = parseType(diag::expected_type);
+    if (type.hasCodeCompletion() || type.isNull()) {
+      if (Tok.is(tok::l_paren))
+        skipSingle();
+
+      return true;
+    }
+
+    // Parse the optional arguments.
+    SourceLoc lParenLoc, rParenLoc;
+    SmallVector<Expr *, 2> args;
+    SmallVector<Identifier, 2> argLabels;
+    SmallVector<SourceLoc, 2> argLabelLocs;
+    Expr *trailingClosure = nullptr;
+    bool hasInitializer = false;
+
+    // If we're not in a local context, we'll need a context to parse
+    // initializers into (should we have one).  This happens for properties
+    // and global variables in libraries.
+    PatternBindingInitializer *initContext = nullptr;
+
+    if (Tok.isFollowingLParen()) {
+      SyntaxParsingContext InitCtx(SyntaxContext,
+                                   SyntaxKind::InitializerClause);
+
+      // If we have no local context to parse the initial value into, create one
+      // for the PBD we'll eventually create.  This allows us to have reasonable
+      // DeclContexts for any closures that may live inside of initializers.
+      Optional<ParseFunctionBody> initParser;
+      if (!CurDeclContext->isLocalContext()) {
+        initContext = new (Context) PatternBindingInitializer(CurDeclContext);
+        initParser.emplace(*this, initContext);
+      }
+
+      ParserStatus status = parseExprList(tok::l_paren, tok::r_paren,
+                                          /*isPostfix=*/false,
+                                          /*isExprBasic=*/true,
+                                          lParenLoc, args, argLabels,
+                                          argLabelLocs,
+                                          rParenLoc,
+                                          trailingClosure,
+                                          SyntaxKind::FunctionCallArgumentList);
+      if (status.hasCodeCompletion())
+        return true;
+
+      assert(!trailingClosure && "Cannot parse a trailing closure here");
+      hasInitializer = true;
+    }
+
+    // Form the attribute.
+    auto attr = CustomAttr::create(Context, AtLoc, type.get(), hasInitializer,
+                                   initContext, lParenLoc, args, argLabels,
+                                   argLabelLocs, rParenLoc);
+    Attributes.add(attr);
+    return false;
+  }
 
   // Recover by eating @foo(...) when foo is not known.
   consumeToken();
diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp
index be95d33..cd3a18c 100644
--- a/lib/Sema/TypeCheckAttr.cpp
+++ b/lib/Sema/TypeCheckAttr.cpp
@@ -20,8 +20,10 @@
 #include "swift/AST/GenericSignatureBuilder.h"
 #include "swift/AST/ASTVisitor.h"
 #include "swift/AST/ClangModuleLoader.h"
+#include "swift/AST/DiagnosticsParse.h"
 #include "swift/AST/GenericEnvironment.h"
 #include "swift/AST/NameLookup.h"
+#include "swift/AST/NameLookupRequests.h"
 #include "swift/AST/ParameterList.h"
 #include "swift/AST/TypeCheckRequests.h"
 #include "swift/AST/Types.h"
@@ -123,6 +125,7 @@
   IGNORED_ATTR(WeakLinked)
   IGNORED_ATTR(DynamicReplacement)
   IGNORED_ATTR(PrivateImport)
+  IGNORED_ATTR(Custom)
 #undef IGNORED_ATTR
 
   void visitAlignmentAttr(AlignmentAttr *attr) {
@@ -835,6 +838,7 @@
   void visitFrozenAttr(FrozenAttr *attr);
 
   void visitNonOverrideAttr(NonOverrideAttr *attr);
+  void visitCustomAttr(CustomAttr *attr);
 };
 } // end anonymous namespace
 
@@ -2399,6 +2403,36 @@
   }
 }
 
+void AttributeChecker::visitCustomAttr(CustomAttr *attr) {
+  auto dc = D->getInnermostDeclContext();
+
+  // Figure out which nominal declaration this custom attribute refers to.
+  auto nominal = evaluateOrDefault(
+    TC.Context.evaluator, CustomAttrNominalRequest{attr, dc}, nullptr);
+
+  // If there is no nominal type with this name, complain about this being
+  // an unknown attribute.
+  if (!nominal) {
+    std::string typeName;
+    if (auto typeRepr = attr->getTypeLoc().getTypeRepr()) {
+      llvm::raw_string_ostream out(typeName);
+      typeRepr->print(out);
+    } else {
+      typeName = attr->getTypeLoc().getType().getString();
+    }
+
+    TC.diagnose(attr->getLocation(), diag::unknown_attribute,
+                typeName);
+    attr->setInvalid();
+    return;
+  }
+
+  TC.diagnose(attr->getLocation(), diag::nominal_type_not_attribute,
+              nominal->getDescriptiveKind(), nominal->getFullName());
+  nominal->diagnose(diag::decl_declared_here, nominal->getFullName());
+  attr->setInvalid();
+}
+
 void TypeChecker::checkDeclAttributes(Decl *D) {
   AttributeChecker Checker(*this, D);
 
diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp
index 1542858..7b4f386 100644
--- a/lib/Sema/TypeCheckDeclOverride.cpp
+++ b/lib/Sema/TypeCheckDeclOverride.cpp
@@ -1321,6 +1321,7 @@
     UNINTERESTING_ATTR(Frozen)
     UNINTERESTING_ATTR(HasInitialValue)
     UNINTERESTING_ATTR(ImplementationOnly)
+    UNINTERESTING_ATTR(Custom)
 #undef UNINTERESTING_ATTR
 
     void visitAvailableAttr(AvailableAttr *attr) {
diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp
index 0fc9e64..92f360a 100644
--- a/lib/Serialization/Deserialization.cpp
+++ b/lib/Serialization/Deserialization.cpp
@@ -4011,6 +4011,24 @@
         break;
       }
 
+      case decls_block::Custom_DECL_ATTR: {
+        bool isImplicit;
+        TypeID typeID;
+        serialization::decls_block::CustomDeclAttrLayout::readRecord(
+          scratch, isImplicit, typeID);
+
+        Expected<Type> deserialized = MF.getTypeChecked(typeID);
+        if (!deserialized) {
+          MF.fatal(deserialized.takeError());
+          break;
+        }
+
+        Attr = CustomAttr::create(ctx, SourceLoc(),
+                                  TypeLoc::withoutLoc(deserialized.get()),
+                                  isImplicit);
+        break;
+      }
+
 #define SIMPLE_DECL_ATTR(NAME, CLASS, ...) \
       case decls_block::CLASS##_DECL_ATTR: { \
         bool isImplicit; \
diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp
index bbca78a..b987866 100644
--- a/lib/Serialization/Serialization.cpp
+++ b/lib/Serialization/Serialization.cpp
@@ -2407,6 +2407,15 @@
         addDeclRef(theAttr->getReplacedFunction()), pieces.size(), pieces);
     return;
   }
+
+    case DAK_Custom: {
+      auto abbrCode = DeclTypeAbbrCodes[CustomDeclAttrLayout::Code];
+      auto theAttr = cast<CustomAttr>(DA);
+      CustomDeclAttrLayout::emitRecord(
+        Out, ScratchRecord, abbrCode, theAttr->isImplicit(),
+        addTypeRef(theAttr->getTypeLoc().getType()));
+      return;
+    }
   }
 }