[llvm] r326788 - TableGen: Delay instantiating inline anonymous records

Nicolai Hähnle via llvm-commits llvm-commits at lists.llvm.org
Tue Mar 6 12:21:14 PST 2018


On 06.03.2018 19:05, Matt Morehouse wrote:
> One of the commits in this sequence seems to be breaking a couple tests: 
> http://lab.llvm.org:8011/builders/sanitizer-x86_64-linux-fast/builds/14892.

Yeah, they should all be fixed now.


> 
> Testing: 0 .. 10.. 20.. 30.. 40.. 50.. 60.. 70.. FAIL: Clang :: 
> TableGen/anonymous-groups.td <http://anonymous-groups.td> (9683 of 
> 12091) ******************** TEST 'Clang :: TableGen/anonymous-groups.td 
> <http://anonymous-groups.td>' FAILED ******************** Script: -- 
> clang-tblgen -gen-clang-diag-groups 
> -I/b/sanitizer-x86_64-linux-fast/build/llvm/tools/clang/test/TableGen 
> /b/sanitizer-x86_64-linux-fast/build/llvm/tools/clang/test/TableGen/anonymous-groups.td 
> <http://anonymous-groups.td> -o /dev/null 2>&1 | 
> /b/sanitizer-x86_64-linux-fast/build/llvm_build_asan/bin/FileCheck 
> --strict-whitespace 
> /b/sanitizer-x86_64-linux-fast/build/llvm/tools/clang/test/TableGen/anonymous-groups.td 
> <http://anonymous-groups.td> -- Exit Code: 1 Command Output (stderr): -- 
> /b/sanitizer-x86_64-linux-fast/build/llvm/tools/clang/test/TableGen/anonymous-groups.td:10:16: 
> error: expected string not found in input // CHECK: 
> anonymous-groups.td:[[@LINE-1]]:41: error: group 'name' is referred to 
> anonymously ^ <stdin>:1:1: note: scanning from here Included from 
> /b/sanitizer-x86_64-linux-fast/build/llvm/tools/clang/test/TableGen/anonymous-groups.td:2 
> <http://anonymous-groups.td:2>: ^ <stdin>:1:1: note: with expression 
> "@LINE-1" equal to "9" Included from 
> /b/sanitizer-x86_64-linux-fast/build/llvm/tools/clang/test/TableGen/anonymous-groups.td:2 
> <http://anonymous-groups.td:2>: ^ <stdin>:2:71: note: possible intended 
> match here 
> /b/sanitizer-x86_64-linux-fast/build/llvm/tools/clang/test/TableGen/DiagnosticBase.inc:15:7: 
> error: group 'name' is referred to anonymously ^ -- ******************** 
> Testing: 0 .. 10.. 20.. 30.. 40.. 50.. 60.. 70.. FAIL: Clang :: 
> TableGen/tg-fixits.td <http://tg-fixits.td> (9690 of 12091) 
> ******************** TEST 'Clang :: TableGen/tg-fixits.td 
> <http://tg-fixits.td>' FAILED ******************** Script: -- 
> clang-tblgen -gen-clang-diag-groups 
> -I/b/sanitizer-x86_64-linux-fast/build/llvm/tools/clang/test/TableGen 
> /b/sanitizer-x86_64-linux-fast/build/llvm/tools/clang/test/TableGen/tg-fixits.td 
> <http://tg-fixits.td> -o /dev/null 2>&1 | 
> /b/sanitizer-x86_64-linux-fast/build/llvm_build_asan/bin/FileCheck 
> --strict-whitespace 
> /b/sanitizer-x86_64-linux-fast/build/llvm/tools/clang/test/TableGen/tg-fixits.td 
> <http://tg-fixits.td> -- Exit Code: 1 Command Output (stderr): -- 
> /b/sanitizer-x86_64-linux-fast/build/llvm/tools/clang/test/TableGen/tg-fixits.td:7:16: 
> error: expected string not found in input // CHECK: 
> tg-fixits.td:[[@LINE-1]]:41: error: group 'name' is referred to 
> anonymously ^ <stdin>:1:1: note: scanning from here Included from 
> /b/sanitizer-x86_64-linux-fast/build/llvm/tools/clang/test/TableGen/tg-fixits.td:2 
> <http://tg-fixits.td:2>: ^ <stdin>:1:1: note: with expression "@LINE-1" 
> equal to "6" Included from 
> /b/sanitizer-x86_64-linux-fast/build/llvm/tools/clang/test/TableGen/tg-fixits.td:2 
> <http://tg-fixits.td:2>: ^ <stdin>:2:76: note: possible intended match 
> here 
> /b/sanitizer-x86_64-linux-fast/build/llvm/tools/clang/test/TableGen/DiagnosticBase.inc:15:7: 
> error: group 'name' is referred to anonymously ^ -- ******************** 
> Testing: 0 .. 10.. 20.. 30.. 40.. 50.. 60.. 70.. 80.. 90.. Testing Time: 
> 226.63s ******************** Failing Tests (2): Clang :: 
> TableGen/anonymous-groups.td <http://anonymous-groups.td> Clang :: 
> TableGen/tg-fixits.td <http://tg-fixits.td> Expected Passes : 12019 
> Expected Failures : 19 Unsupported Tests : 51 Unexpected Failures: 2 
> FAILED: tools/clang/test/CMakeFiles/check-clang
> 
> 
> Please take a look.
> 
> On Tue, Mar 6, 2018 at 5:49 AM, Nicolai Haehnle via llvm-commits 
> <llvm-commits at lists.llvm.org <mailto:llvm-commits at lists.llvm.org>> wrote:
> 
>     Author: nha
>     Date: Tue Mar  6 05:49:01 2018
>     New Revision: 326788
> 
>     URL: http://llvm.org/viewvc/llvm-project?rev=326788&view=rev
>     <http://llvm.org/viewvc/llvm-project?rev=326788&view=rev>
>     Log:
>     TableGen: Delay instantiating inline anonymous records
> 
>     Summary:
>     Only instantiate anonymous records once all variable references in
>     template
>     arguments have been resolved. This allows patterns like the new test
>     case,
>     which in practice can appear in expressions like:
> 
>        class IntrinsicTypeProfile<list<LLVMType> ty, int shift> {
>          list<LLVMType> types =
>            !listconcat(ty, [llvm_any_ty, LLVMMatchType<shift>]);
>        }
> 
>        class FooIntrinsic<IntrinsicTypeProfile P, ...>
>          : Intrinsic<..., P.types, ...>;
> 
>     Without this change, the anonymous LLVMMatchType instantiation would
>     never get resolved.
> 
>     Another consequence of this change is that anonymous inline
>     instantiations are uniqued via the folding set of the newly introduced
>     VarDefInit.
> 
>     Change-Id: I7a7041a20e297cf98c9109b28d85e64e176c932a
> 
>     Reviewers: arsenm, craig.topper, tra, MartinO
> 
>     Subscribers: wdng, llvm-commits
> 
>     Differential Revision: https://reviews.llvm.org/D43756
>     <https://reviews.llvm.org/D43756>
> 
>     Modified:
>          llvm/trunk/include/llvm/TableGen/Record.h
>          llvm/trunk/lib/TableGen/Record.cpp
>          llvm/trunk/lib/TableGen/TGParser.cpp
>          llvm/trunk/test/TableGen/AnonDefinitionOnDemand.td
> 
>     Modified: llvm/trunk/include/llvm/TableGen/Record.h
>     URL:
>     http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/TableGen/Record.h?rev=326788&r1=326787&r2=326788&view=diff
>     <http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/TableGen/Record.h?rev=326788&r1=326787&r2=326788&view=diff>
>     ==============================================================================
>     --- llvm/trunk/include/llvm/TableGen/Record.h (original)
>     +++ llvm/trunk/include/llvm/TableGen/Record.h Tue Mar  6 05:49:01 2018
>     @@ -320,6 +320,7 @@ protected:
>           IK_VarInit,
>           IK_VarListElementInit,
>           IK_VarBitInit,
>     +    IK_VarDefInit,
>           IK_LastTypedInit,
>           IK_UnsetInit
>         };
>     @@ -1052,6 +1053,58 @@ public:
>         }
>       };
> 
>     +/// classname<targs...> - Represent an uninstantiated anonymous class
>     +/// instantiation.
>     +class VarDefInit final : public TypedInit, public FoldingSetNode,
>     +                         public TrailingObjects<VarDefInit, Init *> {
>     +  Record *Class;
>     +  DefInit *Def = nullptr; // after instantiation
>     +  unsigned NumArgs;
>     +
>     +  explicit VarDefInit(Record *Class, unsigned N)
>     +    : TypedInit(IK_VarDefInit, RecordRecTy::get(Class)),
>     Class(Class), NumArgs(N) {}
>     +
>     +  DefInit *instantiate();
>     +
>     +public:
>     +  VarDefInit(const VarDefInit &) = delete;
>     +  VarDefInit &operator=(const VarDefInit &) = delete;
>     +
>     +  // Do not use sized deallocation due to trailing objects.
>     +  void operator delete(void *p) { ::operator delete(p); }
>     +
>     +  static bool classof(const Init *I) {
>     +    return I->getKind() == IK_VarDefInit;
>     +  }
>     +  static VarDefInit *get(Record *Class, ArrayRef<Init *> Args);
>     +
>     +  void Profile(FoldingSetNodeID &ID) const;
>     +
>     +  Init *resolveReferences(Resolver &R) const override;
>     +  Init *Fold() const;
>     +
>     +  std::string getAsString() const override;
>     +
>     +  Init *getArg(unsigned i) const {
>     +    assert(i < NumArgs && "Argument index out of range!");
>     +    return getTrailingObjects<Init *>()[i];
>     +  }
>     +
>     +  using const_iterator = Init *const *;
>     +
>     +  const_iterator args_begin() const { return
>     getTrailingObjects<Init *>(); }
>     +  const_iterator args_end  () const { return args_begin() + NumArgs; }
>     +
>     +  size_t         args_size () const { return NumArgs; }
>     +  bool           args_empty() const { return NumArgs == 0; }
>     +
>     +  ArrayRef<Init *> args() const { return makeArrayRef(args_begin(),
>     NumArgs); }
>     +
>     +  Init *getBit(unsigned Bit) const override {
>     +    llvm_unreachable("Illegal bit reference off anonymous def");
>     +  }
>     +};
>     +
>       /// X.Y - Represent a reference to a subfield of a variable
>       class FieldInit : public TypedInit {
>         Init *Rec;                // Record we are referring to
>     @@ -1754,6 +1807,21 @@ public:
>         }
>       };
> 
>     +/// (Optionally) delegate resolving to a sub-resolver, and keep
>     track whether
>     +/// there were unresolved references.
>     +class TrackUnresolvedResolver final : public Resolver {
>     +  Resolver *R;
>     +  bool FoundUnresolved = false;
>     +
>     +public:
>     +  explicit TrackUnresolvedResolver(Resolver *R = nullptr)
>     +      : Resolver(R ? R->getCurrentRecord() : nullptr), R(R) {}
>     +
>     +  bool foundUnresolved() const { return FoundUnresolved; }
>     +
>     +  Init *resolve(Init *VarName) override;
>     +};
>     +
>       } // end namespace llvm
> 
>       #endif // LLVM_TABLEGEN_RECORD_H
> 
>     Modified: llvm/trunk/lib/TableGen/Record.cpp
>     URL:
>     http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/TableGen/Record.cpp?rev=326788&r1=326787&r2=326788&view=diff
>     <http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/TableGen/Record.cpp?rev=326788&r1=326787&r2=326788&view=diff>
>     ==============================================================================
>     --- llvm/trunk/lib/TableGen/Record.cpp (original)
>     +++ llvm/trunk/lib/TableGen/Record.cpp Tue Mar  6 05:49:01 2018
>     @@ -1355,6 +1355,132 @@ std::string DefInit::getAsString() const
>         return Def->getName();
>       }
> 
>     +static void ProfileVarDefInit(FoldingSetNodeID &ID,
>     +                              Record *Class,
>     +                              ArrayRef<Init *> Args) {
>     +  ID.AddInteger(Args.size());
>     +  ID.AddPointer(Class);
>     +
>     +  for (Init *I : Args)
>     +    ID.AddPointer(I);
>     +}
>     +
>     +VarDefInit *VarDefInit::get(Record *Class, ArrayRef<Init *> Args) {
>     +  static FoldingSet<VarDefInit> ThePool;
>     +
>     +  FoldingSetNodeID ID;
>     +  ProfileVarDefInit(ID, Class, Args);
>     +
>     +  void *IP = nullptr;
>     +  if (VarDefInit *I = ThePool.FindNodeOrInsertPos(ID, IP))
>     +    return I;
>     +
>     +  void *Mem = Allocator.Allocate(totalSizeToAlloc<Init *>(Args.size()),
>     +                                 alignof(VarDefInit));
>     +  VarDefInit *I = new(Mem) VarDefInit(Class, Args.size());
>     +  std::uninitialized_copy(Args.begin(), Args.end(),
>     +                          I->getTrailingObjects<Init *>());
>     +  ThePool.InsertNode(I, IP);
>     +  return I;
>     +}
>     +
>     +void VarDefInit::Profile(FoldingSetNodeID &ID) const {
>     +  ProfileVarDefInit(ID, Class, args());
>     +}
>     +
>     +DefInit *VarDefInit::instantiate() {
>     +  if (!Def) {
>     +    RecordKeeper &Records = Class->getRecords();
>     +    auto NewRecOwner =
>     make_unique<Record>(Records.getNewAnonymousName(),
>     +                                           Class->getLoc(), Records,
>     +                                           /*IsAnonymous=*/true);
>     +    Record *NewRec = NewRecOwner.get();
>     +
>     +    // Copy values from class to instance
>     +    for (const RecordVal &Val : Class->getValues()) {
>     +      if (Val.getName() != "NAME")
>     +        NewRec->addValue(Val);
>     +    }
>     +
>     +    // Substitute and resolve template arguments
>     +    ArrayRef<Init *> TArgs = Class->getTemplateArgs();
>     +    MapResolver R(NewRec);
>     +
>     +    for (unsigned i = 0, e = TArgs.size(); i != e; ++i) {
>     +      if (i < args_size())
>     +        R.set(TArgs[i], getArg(i));
>     +      else
>     +        R.set(TArgs[i], NewRec->getValue(TArgs[i])->getValue());
>     +
>     +      NewRec->removeValue(TArgs[i]);
>     +    }
>     +
>     +    NewRec->resolveReferences(R);
>     +
>     +    // Add superclasses.
>     +    ArrayRef<std::pair<Record *, SMRange>> SCs =
>     Class->getSuperClasses();
>     +    for (const auto &SCPair : SCs)
>     +      NewRec->addSuperClass(SCPair.first, SCPair.second);
>     +
>     +    NewRec->addSuperClass(Class,
>     +                          SMRange(Class->getLoc().back(),
>     +                                  Class->getLoc().back()));
>     +
>     +    // Resolve internal references and store in record keeper
>     +    NewRec->resolveReferences();
>     +    Records.addDef(std::move(NewRecOwner));
>     +
>     +    Def = DefInit::get(NewRec);
>     +  }
>     +
>     +  return Def;
>     +}
>     +
>     +Init *VarDefInit::resolveReferences(Resolver &R) const {
>     +  TrackUnresolvedResolver UR(&R);
>     +  bool Changed = false;
>     +  SmallVector<Init *, 8> NewArgs;
>     +  NewArgs.reserve(args_size());
>     +
>     +  for (Init *Arg : args()) {
>     +    Init *NewArg = Arg->resolveReferences(UR);
>     +    NewArgs.push_back(NewArg);
>     +    Changed |= NewArg != Arg;
>     +  }
>     +
>     +  if (Changed) {
>     +    auto New = VarDefInit::get(Class, NewArgs);
>     +    if (!UR.foundUnresolved())
>     +      return New->instantiate();
>     +    return New;
>     +  }
>     +  return const_cast<VarDefInit *>(this);
>     +}
>     +
>     +Init *VarDefInit::Fold() const {
>     +  if (Def)
>     +    return Def;
>     +
>     +  TrackUnresolvedResolver R;
>     +  for (Init *Arg : args())
>     +    Arg->resolveReferences(R);
>     +
>     +  if (!R.foundUnresolved())
>     +    return const_cast<VarDefInit *>(this)->instantiate();
>     +  return const_cast<VarDefInit *>(this);
>     +}
>     +
>     +std::string VarDefInit::getAsString() const {
>     +  std::string Result = Class->getNameInitAsString() + "<";
>     +  const char *sep = "";
>     +  for (Init *Arg : args()) {
>     +    Result += sep;
>     +    sep = ", ";
>     +    Result += Arg->getAsString();
>     +  }
>     +  return Result + ">";
>     +}
>     +
>       FieldInit *FieldInit::get(Init *R, StringInit *FN) {
>         using Key = std::pair<Init *, StringInit *>;
>         static DenseMap<Key, FieldInit*> ThePool;
>     @@ -1917,3 +2043,23 @@ Init *RecordResolver::resolve(Init *VarN
>         Cache[VarName] = Val;
>         return Val;
>       }
>     +
>     +Init *TrackUnresolvedResolver::resolve(Init *VarName) {
>     +  Init *I = nullptr;
>     +
>     +  if (R) {
>     +    I = R->resolve(VarName);
>     +    if (I && !FoundUnresolved) {
>     +      // Do not recurse into the resolved initializer, as that
>     would change
>     +      // the behavior of the resolver we're delegating, but do
>     check to see
>     +      // if there are unresolved variables remaining.
>     +      TrackUnresolvedResolver Sub;
>     +      I->resolveReferences(Sub);
>     +      FoundUnresolved |= Sub.FoundUnresolved;
>     +    }
>     +  }
>     +
>     +  if (!I)
>     +    FoundUnresolved = true;
>     +  return I;
>     +}
> 
>     Modified: llvm/trunk/lib/TableGen/TGParser.cpp
>     URL:
>     http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/TableGen/TGParser.cpp?rev=326788&r1=326787&r2=326788&view=diff
>     <http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/TableGen/TGParser.cpp?rev=326788&r1=326787&r2=326788&view=diff>
>     ==============================================================================
>     --- llvm/trunk/lib/TableGen/TGParser.cpp (original)
>     +++ llvm/trunk/lib/TableGen/TGParser.cpp Tue Mar  6 05:49:01 2018
>     @@ -1346,61 +1346,49 @@ Init *TGParser::ParseSimpleValue(Record
>             return nullptr;
>           }
> 
>     -    SubClassReference SCRef;
>     -    ParseValueList(SCRef.TemplateArgs, CurRec, Class);
>     -    if (SCRef.TemplateArgs.empty()) return nullptr;
>     +    SmallVector<Init *, 8> Args;
>     +    ParseValueList(Args, CurRec, Class);
>     +    if (Args.empty()) return nullptr;
> 
>           if (Lex.getCode() != tgtok::greater) {
>             TokError("expected '>' at end of value list");
>             return nullptr;
>           }
>           Lex.Lex();  // eat the '>'
>     -    SMLoc EndLoc = Lex.getLoc();
> 
>     -    // Create the new record, set it as CurRec temporarily.
>     -    auto NewRecOwner =
>     -        make_unique<Record>(Records.getNewAnonymousName(), NameLoc,
>     Records,
>     -                            /*IsAnonymous=*/true);
>     -    Record *NewRec = NewRecOwner.get(); // Keep a copy since we may
>     release.
>     -    SCRef.RefRange = SMRange(NameLoc, EndLoc);
>     -    SCRef.Rec = Class;
>     -    // Add info about the subclass to NewRec.
>     -    if (AddSubClass(NewRec, SCRef))
>     +    // Typecheck the template arguments list
>     +    ArrayRef<Init *> ExpectedArgs = Class->getTemplateArgs();
>     +    if (ExpectedArgs.size() < Args.size()) {
>     +      Error(NameLoc,
>     +            "More template args specified than expected");
>             return nullptr;
>     +    }
> 
>     -    if (!CurMultiClass) {
>     -      NewRec->resolveReferences();
>     -      Records.addDef(std::move(NewRecOwner));
>     -    } else {
>     -      // This needs to get resolved once the multiclass template
>     arguments are
>     -      // known before any use.
>     -      NewRec->setResolveFirst(true);
>     -      // Otherwise, we're inside a multiclass, add it to the
>     multiclass.
>     -      CurMultiClass->DefPrototypes.push_back(std::move(NewRecOwner));
>     -
>     -      // Copy the template arguments for the multiclass into the def.
>     -      for (Init *TArg : CurMultiClass->Rec.getTemplateArgs()) {
>     -        const RecordVal *RV = CurMultiClass->Rec.getValue(TArg);
>     -        assert(RV && "Template arg doesn't exist?");
>     -        NewRec->addValue(*RV);
>     -      }
>     -
>     -      // We can't return the prototype def here, instead return:
>     -      // !cast<ItemType>(!strconcat(NAME, AnonName)).
>     -      const RecordVal *MCNameRV = CurMultiClass->Rec.getValue("NAME");
>     -      assert(MCNameRV && "multiclass record must have a NAME");
>     -
>     -      return UnOpInit::get(UnOpInit::CAST,
>     -                           BinOpInit::get(BinOpInit::STRCONCAT,
>     -                                         
>     VarInit::get(MCNameRV->getName(),
>     -                                                     
>       MCNameRV->getType()),
>     -                                          NewRec->getNameInit(),
>     -                                          StringRecTy::get()),
>     -                           NewRec->getDefInit()->getType());
>     +    for (unsigned i = 0, e = ExpectedArgs.size(); i != e; ++i) {
>     +      RecordVal *ExpectedArg = Class->getValue(ExpectedArgs[i]);
>     +      if (i < Args.size()) {
>     +        if (TypedInit *TI = dyn_cast<TypedInit>(Args[i])) {
>     +          RecTy *ExpectedType = ExpectedArg->getType();
>     +          if (!TI->getType()->typeIsConvertibleTo(ExpectedType)) {
>     +            Error(NameLoc,
>     +                  "Value specified for template argument #" +
>     Twine(i) + " (" +
>     +                  ExpectedArg->getNameInitAsString() + ") is of
>     type '" +
>     +                  TI->getType()->getAsString() + "', expected '" +
>     +                  ExpectedType->getAsString() + "': " +
>     TI->getAsString());
>     +            return nullptr;
>     +          }
>     +          continue;
>     +        }
>     +      } else if (ExpectedArg->getValue()->isComplete())
>     +        continue;
>     +
>     +      Error(NameLoc,
>     +            "Value not specified for template argument #" +
>     Twine(i) + " (" +
>     +            ExpectedArgs[i]->getAsUnquotedString() + ")");
>     +      return nullptr;
>           }
> 
>     -    // The result of the expression is a reference to the new record.
>     -    return DefInit::get(NewRec);
>     +    return VarDefInit::get(Class, Args)->Fold();
>         }
>         case tgtok::l_brace: {           // Value ::= '{' ValueList '}'
>           SMLoc BraceLoc = Lex.getLoc();
> 
>     Modified: llvm/trunk/test/TableGen/AnonDefinitionOnDemand.td
>     URL:
>     http://llvm.org/viewvc/llvm-project/llvm/trunk/test/TableGen/AnonDefinitionOnDemand.td?rev=326788&r1=326787&r2=326788&view=diff
>     <http://llvm.org/viewvc/llvm-project/llvm/trunk/test/TableGen/AnonDefinitionOnDemand.td?rev=326788&r1=326787&r2=326788&view=diff>
>     ==============================================================================
>     --- llvm/trunk/test/TableGen/AnonDefinitionOnDemand.td (original)
>     +++ llvm/trunk/test/TableGen/AnonDefinitionOnDemand.td Tue Mar  6
>     05:49:01 2018
>     @@ -1,6 +1,24 @@
>     -// RUN: llvm-tblgen < %s
>     +// RUN: llvm-tblgen %s | FileCheck %s
>       // XFAIL: vg_leak
> 
>     +// CHECK: --- Defs ---
>     +
>     +// CHECK: def X {
>     +// CHECK:   foo Y = anonymous_0;
>     +// CHECK: }
>     +
>     +// CHECK: def ZD {
>     +// CHECK:   foo Z = anonymous_1;
>     +// CHECK: }
>     +
>     +// CHECK: def anonymous_0 {
>     +// CHECK:   int THEVAL = 1;
>     +// CHECK: }
>     +
>     +// CHECK: def anonymous_1 {
>     +// CHECK:   int THEVAL = 42;
>     +// CHECK: }
>     +
>       class foo<int X> { int THEVAL = X; }
>       def foo_imp : foo<1>;
> 
>     @@ -11,3 +29,9 @@ def x {
>       def X {
>         foo Y = foo<1>;     // This should work too, synthesizing a new
>     foo<1>.
>       }
>     +
>     +class Z<int X> {
>     +  foo Z = foo<X>;
>     +}
>     +
>     +def ZD : Z<42>;
> 
> 
>     _______________________________________________
>     llvm-commits mailing list
>     llvm-commits at lists.llvm.org <mailto:llvm-commits at lists.llvm.org>
>     http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>     <http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits>
> 
> 


-- 
Lerne, wie die Welt wirklich ist,
Aber vergiss niemals, wie sie sein sollte.


More information about the llvm-commits mailing list