@@ -88,6 +88,10 @@ namespace swift {
8888 };
8989 }
9090
91+ template <class ... ArgTypes>
92+ using DiagArgTuple =
93+ std::tuple<typename detail::PassArgument<ArgTypes>::type...>;
94+
9195 // / A family of wrapper types for compiler data types that forces its
9296 // / underlying data to be formatted with full qualification.
9397 // /
@@ -476,20 +480,28 @@ namespace swift {
476480 friend DiagnosticEngine;
477481 friend class InFlightDiagnostic ;
478482
483+ Diagnostic (DiagID ID) : ID(ID) {}
484+
479485 public:
480486 // All constructors are intentionally implicit.
481487 template <typename ...ArgTypes>
482488 Diagnostic (Diag<ArgTypes...> ID,
483489 typename detail::PassArgument<ArgTypes>::type... VArgs)
484- : ID(ID.ID) {
485- DiagnosticArgument DiagArgs[] = {
486- DiagnosticArgument (0 ), std::move (VArgs)...
487- };
488- Args.append (DiagArgs + 1 , DiagArgs + 1 + sizeof ...(VArgs));
490+ : Diagnostic(ID.ID) {
491+ Args.reserve (sizeof ...(ArgTypes));
492+ gatherArgs (VArgs...);
489493 }
490494
491495 /* implicit*/ Diagnostic(DiagID ID, ArrayRef<DiagnosticArgument> Args)
492496 : ID(ID), Args(Args.begin(), Args.end()) {}
497+
498+ template <class ... ArgTypes>
499+ static Diagnostic fromTuple (Diag<ArgTypes...> id,
500+ const DiagArgTuple<ArgTypes...> &tuple) {
501+ Diagnostic result (id.ID );
502+ result.gatherArgsFromTuple <DiagArgTuple<ArgTypes...>, 0 , ArgTypes...>(tuple);
503+ return result;
504+ }
493505
494506 // Accessors.
495507 DiagID getID () const { return ID; }
@@ -528,6 +540,37 @@ namespace swift {
528540
529541 void addChildNote (Diagnostic &&D);
530542 void insertChildNote (unsigned beforeIndex, Diagnostic &&D);
543+
544+ private:
545+ // gatherArgs could just be `Args.emplace_back(args)...;` if C++
546+ // allowed pack expansions in statement context.
547+
548+ // Base case.
549+ void gatherArgs () {}
550+
551+ // Pull one off the pack.
552+ template <class ArgType , class ... RemainingArgTypes>
553+ void gatherArgs (ArgType arg, RemainingArgTypes... remainingArgs) {
554+ Args.emplace_back (arg);
555+ gatherArgs (remainingArgs...);
556+ }
557+
558+ // gatherArgsFromTuple could just be
559+ // `Args.emplace_back(std::get<packIndexOf<ArgTypes>>(tuple))...;`
560+ // in a better world.
561+
562+ // Base case.
563+ template <class Tuple , size_t Index>
564+ void gatherArgsFromTuple (const Tuple &tuple) {}
565+
566+ // Pull one off the pack.
567+ template <class Tuple , size_t Index,
568+ class ArgType , class ... RemainingArgTypes>
569+ void gatherArgsFromTuple (const Tuple &tuple) {
570+ Args.emplace_back (std::move (std::get<Index>(tuple)));
571+ gatherArgsFromTuple<Tuple, Index + 1 , RemainingArgTypes...>(
572+ std::move (tuple));
573+ }
531574 };
532575
533576 // / A diagnostic that has no input arguments, so it is trivially-destructable.
@@ -866,6 +909,73 @@ namespace swift {
866909 DiagnosticState &operator =(DiagnosticState &&) = default ;
867910 };
868911
912+ // / A lightweight reference to a diagnostic that's been fully applied to
913+ // / its arguments. This allows a general routine (in the parser, say) to
914+ // / be customized to emit an arbitrary diagnostic without needing to
915+ // / eagerly construct a full Diagnostic. Like ArrayRef and function_ref,
916+ // / this stores a reference to what's likely to be a temporary, so it
917+ // / should only be used as a function parameter. If you need to persist
918+ // / the diagnostic, you'll have to call createDiagnostic().
919+ // /
920+ // / You can initialize a DiagRef parameter in one of two ways:
921+ // / - passing a Diag<> as the argument, e.g.
922+ // / diag::circular_reference
923+ // / or
924+ // / - constructing it with a Diag and its arguments, e.g.
925+ // / {diag::circular_protocol_def, {proto->getName()}}
926+ // /
927+ // / It'd be nice to let people write `{diag::my_error, arg0, arg1}`
928+ // / instead of `{diag::my_error, {arg0, arg1}}`, but we can't: the
929+ // / temporary needs to be created in the calling context.
930+ class DiagRef {
931+ DiagID id;
932+
933+ // / If this is null, then id is a Diag<> and there are no arguments.
934+ Diagnostic (*createFn)(DiagID id, const void *opaqueStorage);
935+ const void *opaqueStorage;
936+
937+ public:
938+ // / Construct a diagnostic from a diagnostic ID that's known to not take
939+ // / arguments.
940+ DiagRef (Diag<> id)
941+ : id(id.ID), createFn(nullptr ), opaqueStorage(nullptr ) {}
942+
943+ // / Construct a diagnostic from a diagnostic ID and its arguments.
944+ template <class ... ArgTypes>
945+ DiagRef (Diag<ArgTypes...> id, const DiagArgTuple<ArgTypes...> &tuple)
946+ : id(id.ID),
947+ createFn (&createFromTuple<ArgTypes...>),
948+ opaqueStorage(&tuple) {}
949+
950+ // A specialization of the general constructor above for diagnostics
951+ // with no arguments; this is a useful optimization when a DiagRef
952+ // is constructed generically.
953+ DiagRef (Diag<> id, const DiagArgTuple<> &tuple)
954+ : DiagRef(id) {}
955+
956+ // / Return the diagnostic ID that this will emit.
957+ DiagID getID () const {
958+ return id;
959+ }
960+
961+ // / Create a full Diagnostic. It's safe to do this multiple times on
962+ // / a single DiagRef.
963+ Diagnostic createDiagnostic () {
964+ if (!createFn) {
965+ return Diagnostic (Diag<> {id});
966+ } else {
967+ return createFn (id, opaqueStorage);
968+ }
969+ }
970+
971+ private:
972+ template <class ... ArgTypes>
973+ static Diagnostic createFromTuple (DiagID id, const void *opaqueStorage) {
974+ auto tuple = static_cast <const DiagArgTuple<ArgTypes...> *>(opaqueStorage);
975+ return Diagnostic::fromTuple (Diag<ArgTypes...> {id}, *tuple);
976+ }
977+ };
978+
869979 // / Class responsible for formatting diagnostics and presenting them
870980 // / to the user.
871981 class DiagnosticEngine {
@@ -1113,6 +1223,12 @@ namespace swift {
11131223 return diagnose (Loc, Diagnostic (ID, std::move (Args)...));
11141224 }
11151225
1226+ // / Emit the given lazily-applied diagnostic at the specified
1227+ // / source location.
1228+ InFlightDiagnostic diagnose (SourceLoc loc, DiagRef diag) {
1229+ return diagnose (loc, diag.createDiagnostic ());
1230+ }
1231+
11161232 // / Delete an API that may lead clients to avoid specifying source location.
11171233 template <typename ...ArgTypes>
11181234 InFlightDiagnostic
0 commit comments