[TableGen] Improve algorithms for processing template arguments

Rework template argument checking so that all arguments are type-checked
and cast if necessary.

Add a test.

Differential Revision: https://reviews.llvm.org/D96416
This commit is contained in:
Paul C. Anagnostopoulos 2021-01-20 13:14:43 -05:00
parent af06ff1cf8
commit d248cce44e
7 changed files with 346 additions and 192 deletions

View File

@ -299,7 +299,7 @@ wide range of records conveniently and compactly.
:token:`ClassID`
Specifying a class name in a type context indicates
that the type of the defined value must
be a subclass of the specified class. This is useful in conjunction with
be a subclass of the specified class. This is useful in conjunction with
the ``list`` type; for example, to constrain the elements of the list to a
common base class (e.g., a ``list<Register>`` can only contain definitions
derived from the ``Register`` class).
@ -554,19 +554,22 @@ classes and records can inherit.
TemplateArgDecl: `Type` `TokIdentifier` ["=" `Value`]
A class can be parameterized by a list of "template arguments," whose values
can be used in the class's record body. These template arguments are
can be used in the class's record body. These template arguments are
specified each time the class is inherited by another class or record.
If a template argument is not assigned a default value with ``=``, it is
uninitialized (has the "value" ``?``) and must be specified in the template
argument list when the class is inherited. If an argument is assigned a
default value, then it need not be specified in the argument list. The
template argument default values are evaluated from left to right.
argument list when the class is inherited (required argument). If an
argument is assigned a default value, then it need not be specified in the
argument list (optional argument). In the declaration, all required template
arguments must precede any optional arguments. The template argument default
values are evaluated from left to right.
The :token:`RecordBody` is defined below. It can include a list of
superclasses from which the current class inherits, along with field definitions
and other statements. When a class ``C`` inherits from another class ``D``,
the fields of ``D`` are effectively merged into the fields of ``C``.
superclasses from which the current class inherits, along with field
definitions and other statements. When a class ``C`` inherits from another
class ``D``, the fields of ``D`` are effectively merged into the fields of
``C``.
A given class can only be defined once. A ``class`` statement is
considered to define the class if *any* of the following are true (the
@ -605,7 +608,7 @@ of the fields of the class or record.
RecordBody: `ParentClassList` `Body`
ParentClassList: [":" `ParentClassListNE`]
ParentClassListNE: `ClassRef` ("," `ClassRef`)*
ClassRef: (`ClassID` | `MultiClassID`) ["<" `ValueList` ">"]
ClassRef: (`ClassID` | `MultiClassID`) ["<" [`ValueList`] ">"]
A :token:`ParentClassList` containing a :token:`MultiClassID` is valid only
in the class list of a ``defm`` statement. In that case, the ID must be the

View File

@ -2024,6 +2024,12 @@ public:
void set(Init *Key, Init *Value) { Map[Key] = {Value, false}; }
bool isComplete(Init *VarName) const {
auto It = Map.find(VarName);
assert(It != Map.end() && "key must be present in map");
return It->second.V->isComplete();
}
Init *resolve(Init *VarName) override;
};

View File

@ -2344,13 +2344,13 @@ void Record::resolveReferences(Resolver &R, const RecordVal *SkipVal) {
if (TypedInit *VRT = dyn_cast<TypedInit>(VR))
Type =
(Twine("of type '") + VRT->getType()->getAsString() + "' ").str();
PrintFatalError(getLoc(), Twine("Invalid value ") + Type +
"is found when setting '" +
Value.getNameInitAsString() +
"' of type '" +
Value.getType()->getAsString() +
"' after resolving references: " +
VR->getAsUnquotedString() + "\n");
PrintFatalError(
getLoc(),
Twine("Invalid value ") + Type + "found when setting field '" +
Value.getNameInitAsString() + "' of type '" +
Value.getType()->getAsString() +
"' after resolving references: " + VR->getAsUnquotedString() +
"\n");
}
}
}

View File

@ -229,38 +229,33 @@ bool TGParser::SetValue(Record *CurRec, SMLoc Loc, Init *ValName,
/// args as SubClass's template arguments.
bool TGParser::AddSubClass(Record *CurRec, SubClassReference &SubClass) {
Record *SC = SubClass.Rec;
// Add all of the values in the subclass into the current class.
for (const RecordVal &Val : SC->getValues())
if (AddValue(CurRec, SubClass.RefRange.Start, Val))
return true;
ArrayRef<Init *> TArgs = SC->getTemplateArgs();
// Ensure that an appropriate number of template arguments are specified.
if (TArgs.size() < SubClass.TemplateArgs.size())
return Error(SubClass.RefRange.Start,
"More template args specified than expected");
// Loop over all of the template arguments, setting them to the specified
// value or leaving them as the default if necessary.
MapResolver R(CurRec);
for (unsigned i = 0, e = TArgs.size(); i != e; ++i) {
if (i < SubClass.TemplateArgs.size()) {
// If a value is specified for this template arg, set it now.
if (SetValue(CurRec, SubClass.RefRange.Start, TArgs[i],
None, SubClass.TemplateArgs[i]))
// Loop over all the subclass record's fields. Add template arguments
// to the resolver map. Add regular fields to the new record.
for (const RecordVal &Field : SC->getValues()) {
if (Field.isTemplateArg()) {
R.set(Field.getNameInit(), Field.getValue());
} else {
if (AddValue(CurRec, SubClass.RefRange.Start, Field))
return true;
} else if (!CurRec->getValue(TArgs[i])->getValue()->isComplete()) {
return Error(SubClass.RefRange.Start,
"Value not specified for template argument #" +
Twine(i) + " (" + TArgs[i]->getAsUnquotedString() +
") of subclass '" + SC->getNameInitAsString() + "'!");
}
}
R.set(TArgs[i], CurRec->getValue(TArgs[i])->getValue());
ArrayRef<Init *> TArgs = SC->getTemplateArgs();
assert(SubClass.TemplateArgs.size() <= TArgs.size() &&
"Too many template arguments allowed");
CurRec->removeValue(TArgs[i]);
// Loop over the template argument names. If a value was specified,
// reset the map value. If not and there was no default, complain.
for (unsigned I = 0, E = TArgs.size(); I != E; ++I) {
if (I < SubClass.TemplateArgs.size())
R.set(TArgs[I], SubClass.TemplateArgs[I]);
else if (!R.isComplete(TArgs[I]))
return Error(SubClass.RefRange.Start,
"Value not specified for template argument '" +
TArgs[I]->getAsUnquotedString() + "' (#" + Twine(I) +
") of parent class '" + SC->getNameInitAsString() + "'");
}
Init *Name;
@ -584,8 +579,8 @@ MultiClass *TGParser::ParseMultiClassID() {
return Result;
}
/// ParseSubClassReference - Parse a reference to a subclass or to a templated
/// subclass. This returns a SubClassRefTy with a null Record* on error.
/// ParseSubClassReference - Parse a reference to a subclass or a
/// multiclass. This returns a SubClassRefTy with a null Record* on error.
///
/// SubClassRef ::= ClassID
/// SubClassRef ::= ClassID '<' ValueList '>'
@ -609,25 +604,18 @@ ParseSubClassReference(Record *CurRec, bool isDefm) {
return Result;
}
if (Lex.getCode() == tgtok::greater) {
TokError("subclass reference requires a non-empty list of template values");
Result.Rec = nullptr;
if (ParseTemplateArgValueList(Result.TemplateArgs, CurRec, Result.Rec)) {
Result.Rec = nullptr; // Error parsing value list.
return Result;
}
ParseValueList(Result.TemplateArgs, CurRec, Result.Rec);
if (Result.TemplateArgs.empty()) {
Result.Rec = nullptr; // Error parsing value list.
if (CheckTemplateArgValues(Result.TemplateArgs, Result.RefRange.Start,
Result.Rec)) {
Result.Rec = nullptr; // Error checking value list.
return Result;
}
if (!consume(tgtok::greater)) {
TokError("expected '>' in template value list");
Result.Rec = nullptr;
return Result;
}
Result.RefRange.End = Lex.getLoc();
return Result;
}
@ -652,23 +640,12 @@ ParseSubMultiClassReference(MultiClass *CurMC) {
return Result;
}
if (Lex.getCode() == tgtok::greater) {
TokError("subclass reference requires a non-empty list of template values");
Result.MC = nullptr;
if (ParseTemplateArgValueList(Result.TemplateArgs, &CurMC->Rec,
&Result.MC->Rec)) {
Result.MC = nullptr; // Error parsing value list.
return Result;
}
ParseValueList(Result.TemplateArgs, &CurMC->Rec, &Result.MC->Rec);
if (Result.TemplateArgs.empty()) {
Result.MC = nullptr; // Error parsing value list.
return Result;
}
if (!consume(tgtok::greater)) {
TokError("expected '>' in template value list");
Result.MC = nullptr;
return Result;
}
Result.RefRange.End = Lex.getLoc();
return Result;
@ -2032,15 +2009,9 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
if (Lex.Lex() != tgtok::less) // consume the Id.
return ParseIDValue(CurRec, Name, NameLoc, Mode); // Value ::= IDValue
// Value ::= ID '<' ValueListNE '>'
if (Lex.Lex() == tgtok::greater) {
TokError("expected non-empty value list");
return nullptr;
}
// This is a CLASS<initvalslist> expression. This is supposed to synthesize
// a new anonymous definition, deriving from CLASS<initvalslist> with no
// body.
// Value ::= CLASSID '<' ValueListNE '>' (CLASSID has been consumed)
// This is supposed to synthesize a new anonymous definition, deriving
// from the class with the template arguments, but no body.
Record *Class = Records.getClass(Name->getValue());
if (!Class) {
Error(NameLoc, "Expected a class name, got '" + Name->getValue() + "'");
@ -2048,44 +2019,26 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
}
SmallVector<Init *, 8> Args;
ParseValueList(Args, CurRec, Class);
if (Args.empty()) return nullptr;
Lex.Lex(); // consume the <
if (ParseTemplateArgValueList(Args, CurRec, Class))
return nullptr; // Error parsing value list.
if (!consume(tgtok::greater)) {
TokError("expected '>' at end of value list");
return nullptr;
}
if (CheckTemplateArgValues(Args, NameLoc, Class))
return nullptr; // Error checking template argument values.
// 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;
}
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;
// Loop through the arguments that were not specified and make sure
// they have a complete value.
// TODO: If we just keep a required argument count, we can do away
// with this checking.
ArrayRef<Init *> TArgs = Class->getTemplateArgs();
for (unsigned I = Args.size(), E = TArgs.size(); I < E; ++I) {
RecordVal *Arg = Class->getValue(TArgs[I]);
if (!Arg->getValue()->isComplete())
Error(NameLoc, "Value not specified for template argument '" +
TArgs[I]->getAsUnquotedString() + "' (#" + Twine(I) +
") of parent class '" +
Class->getNameInitAsString() + "'");
}
return VarDefInit::get(Class, Args)->Fold();
@ -2158,7 +2111,7 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
}
if (Lex.getCode() != tgtok::r_square) {
ParseValueList(Vals, CurRec, nullptr,
ParseValueList(Vals, CurRec,
GivenListTy ? GivenListTy->getElementType() : nullptr);
if (Vals.empty()) return nullptr;
}
@ -2522,32 +2475,15 @@ void TGParser::ParseDagArgList(
}
}
/// ParseValueList - Parse a comma separated list of values, returning them as a
/// vector. Note that this always expects to be able to parse at least one
/// value. It returns an empty list if this is not possible.
/// ParseValueList - Parse a comma separated list of values, returning them
/// in a vector. Note that this always expects to be able to parse at least one
/// value. It returns an empty list if this is not possible.
///
/// ValueList ::= Value (',' Value)
///
void TGParser::ParseValueList(SmallVectorImpl<Init*> &Result, Record *CurRec,
Record *ArgsRec, RecTy *EltTy) {
RecTy *ItemType = EltTy;
unsigned int ArgN = 0;
if (ArgsRec && !EltTy) {
ArrayRef<Init *> TArgs = ArgsRec->getTemplateArgs();
if (TArgs.empty()) {
TokError("template argument provided to non-template class");
Result.clear();
return;
}
const RecordVal *RV = ArgsRec->getValue(TArgs[ArgN]);
if (!RV) {
errs() << "Cannot find template arg " << ArgN << " (" << TArgs[ArgN]
<< ")\n";
}
assert(RV && "Template argument record not found??");
ItemType = RV->getType();
++ArgN;
}
void TGParser::ParseValueList(SmallVectorImpl<Init *> &Result, Record *CurRec,
RecTy *ItemType) {
Result.push_back(ParseValue(CurRec, ItemType));
if (!Result.back()) {
Result.clear();
@ -2558,19 +2494,6 @@ void TGParser::ParseValueList(SmallVectorImpl<Init*> &Result, Record *CurRec,
// ignore trailing comma for lists
if (Lex.getCode() == tgtok::r_square)
return;
if (ArgsRec && !EltTy) {
ArrayRef<Init *> TArgs = ArgsRec->getTemplateArgs();
if (ArgN >= TArgs.size()) {
TokError("too many template arguments");
Result.clear();
return;
}
const RecordVal *RV = ArgsRec->getValue(TArgs[ArgN]);
assert(RV && "Template argument record not found??");
ItemType = RV->getType();
++ArgN;
}
Result.push_back(ParseValue(CurRec, ItemType));
if (!Result.back()) {
Result.clear();
@ -2579,9 +2502,48 @@ void TGParser::ParseValueList(SmallVectorImpl<Init*> &Result, Record *CurRec,
}
}
// ParseTemplateArgValueList - Parse a template argument list with the syntax
// shown, filling in the Result vector. The open angle has been consumed.
// An empty argument list is allowed. Return false if okay, true if an
// error was detected.
//
// TemplateArgList ::= '<' [Value {',' Value}*] '>'
bool TGParser::ParseTemplateArgValueList(SmallVectorImpl<Init *> &Result,
Record *CurRec, Record *ArgsRec) {
assert(Result.empty() && "Result vector is not empty");
ArrayRef<Init *> TArgs = ArgsRec->getTemplateArgs();
unsigned ArgIndex = 0;
RecTy *ItemType;
if (consume(tgtok::greater)) // empty value list
return false;
while (true) {
if (ArgIndex >= TArgs.size()) {
TokError("Too many template arguments: " + utostr(ArgIndex + 1));
return true;
}
const RecordVal *Arg = ArgsRec->getValue(TArgs[ArgIndex]);
assert(Arg && "Template argument record not found");
ItemType = Arg->getType();
Init *Value = ParseValue(CurRec, ItemType);
if (!Value)
return true;
Result.push_back(Value);
if (consume(tgtok::greater)) // end of argument list?
return false;
if (!consume(tgtok::comma)) // must be comma
return true;
++ArgIndex;
}
}
/// ParseDeclaration - Read a declaration, returning the name of field ID, or an
/// empty string on error. This can happen in a number of different context's,
/// including within a def or in the template args for a def (which which case
/// empty string on error. This can happen in a number of different contexts,
/// including within a def or in the template args for a class (in which case
/// CurRec will be non-null) and within the template args for a multiclass (in
/// which case CurRec will be null, but CurMultiClass will be set). This can
/// also happen within a def that is within a multiclass, which will set both
@ -2612,23 +2574,28 @@ Init *TGParser::ParseDeclaration(Record *CurRec,
Init *DeclName = StringInit::get(Str);
Lex.Lex();
if (ParsingTemplateArgs) {
if (CurRec)
DeclName = QualifyName(*CurRec, CurMultiClass, DeclName, ":");
else
assert(CurMultiClass);
if (CurMultiClass)
DeclName = QualifyName(CurMultiClass->Rec, CurMultiClass, DeclName,
"::");
}
bool BadField;
if (!ParsingTemplateArgs) { // def, possibly in a multiclass
BadField = AddValue(CurRec, IdLoc,
RecordVal(DeclName, IdLoc, Type,
HasField ? RecordVal::FK_NonconcreteOK
: RecordVal::FK_Normal));
// Add the field to the record.
if (AddValue(CurRec, IdLoc, RecordVal(DeclName, IdLoc, Type,
HasField ? RecordVal::FK_NonconcreteOK
: RecordVal::FK_Normal)))
} else if (CurRec) { // class template argument
DeclName = QualifyName(*CurRec, CurMultiClass, DeclName, ":");
BadField = AddValue(CurRec, IdLoc, RecordVal(DeclName, IdLoc, Type,
RecordVal::FK_TemplateArg));
} else { // multiclass template argument
assert(CurMultiClass && "invalid context for template argument");
DeclName = QualifyName(CurMultiClass->Rec, CurMultiClass, DeclName, "::");
BadField = AddValue(CurRec, IdLoc, RecordVal(DeclName, IdLoc, Type,
RecordVal::FK_TemplateArg));
}
if (BadField)
return nullptr;
// If a value is present, parse it.
// If a value is present, parse it and set new field's value.
if (consume(tgtok::equal)) {
SMLoc ValLoc = Lex.getLoc();
Init *Val = ParseValue(CurRec, Type);
@ -2715,7 +2682,7 @@ VarInit *TGParser::ParseForeachDeclaration(Init *&ForeachListValue) {
if (!Ranges.empty()) {
assert(!IterType && "Type already initialized?");
IterType = IntRecTy::get();
std::vector<Init*> Values;
std::vector<Init *> Values;
for (unsigned R : Ranges)
Values.push_back(IntInit::get(R));
ForeachListValue = ListInit::get(Values, IterType);
@ -2729,7 +2696,7 @@ VarInit *TGParser::ParseForeachDeclaration(Init *&ForeachListValue) {
/// ParseTemplateArgList - Read a template argument list, which is a non-empty
/// sequence of template-declarations in <>'s. If CurRec is non-null, these are
/// template args for a def, which may or may not be in a multiclass. If null,
/// template args for a class, which may or may not be in a multiclass. If null,
/// these are the template args for a multiclass.
///
/// TemplateArgList ::= '<' Declaration (',' Declaration)* '>'
@ -3493,32 +3460,28 @@ bool TGParser::ParseDefm(MultiClass *CurMultiClass) {
while (true) {
if (!Ref.Rec) return true;
// To instantiate a multiclass, we need to first get the multiclass, then
// instantiate each def contained in the multiclass with the SubClassRef
// template parameters.
// To instantiate a multiclass, we get the multiclass and then loop
// through its template argument names. Substs contains a substitution
// value for each argument, either the value specified or the default.
// Then we can resolve the template arguments.
MultiClass *MC = MultiClasses[std::string(Ref.Rec->getName())].get();
assert(MC && "Didn't lookup multiclass correctly?");
ArrayRef<Init*> TemplateVals = Ref.TemplateArgs;
// Verify that the correct number of template arguments were specified.
ArrayRef<Init *> TemplateVals = Ref.TemplateArgs;
ArrayRef<Init *> TArgs = MC->Rec.getTemplateArgs();
if (TArgs.size() < TemplateVals.size())
return Error(SubClassLoc,
"more template args specified than multiclass expects");
SubstStack Substs;
for (unsigned i = 0, e = TArgs.size(); i != e; ++i) {
if (i < TemplateVals.size()) {
Substs.emplace_back(TArgs[i], TemplateVals[i]);
} else {
Init *Default = MC->Rec.getValue(TArgs[i])->getValue();
if (!Default->isComplete()) {
if (!Default->isComplete())
return Error(SubClassLoc,
"value not specified for template argument #" +
Twine(i) + " (" + TArgs[i]->getAsUnquotedString() +
") of multiclass '" + MC->Rec.getNameInitAsString() +
"'");
}
"value not specified for template argument '" +
TArgs[i]->getAsUnquotedString() + "' (#" +
Twine(i) + ") of multiclass '" +
MC->Rec.getNameInitAsString() + "'");
Substs.emplace_back(TArgs[i], Default);
}
}
@ -3537,7 +3500,7 @@ bool TGParser::ParseDefm(MultiClass *CurMultiClass) {
SubClassLoc = Lex.getLoc();
// A defm can inherit from regular classes (non-multiclass) as
// A defm can inherit from regular classes (non-multiclasses) as
// long as they come in the end of the inheritance list.
InheritFromClass = (Records.getClass(Lex.getCurStrVal()) != nullptr);
@ -3642,6 +3605,41 @@ bool TGParser::ParseFile() {
return TokError("Unexpected token at top level");
}
// Check the types of the template argument values for a class
// inheritance, multiclass invocation, or anonymous class invocation.
// If necessary, replace an argument with a cast to the required type.
// The argument count has already been checked.
bool TGParser::CheckTemplateArgValues(SmallVectorImpl<llvm::Init *> &Values,
SMLoc Loc, Record *ArgsRec) {
ArrayRef<Init *> TArgs = ArgsRec->getTemplateArgs();
for (unsigned I = 0, E = Values.size(); I < E; ++I) {
RecordVal *Arg = ArgsRec->getValue(TArgs[I]);
RecTy *ArgType = Arg->getType();
auto *Value = Values[I];
if (TypedInit *ArgValue = dyn_cast<TypedInit>(Value)) {
auto *CastValue = ArgValue->getCastTo(ArgType);
if (CastValue) {
assert((!isa<TypedInit>(CastValue) ||
cast<TypedInit>(CastValue)->getType()->typeIsA(ArgType)) &&
"result of template arg value cast has wrong type");
Values[I] = CastValue;
} else {
PrintFatalError(Loc,
"Value specified for template argument '" +
Arg->getNameInitAsString() + "' (#" + Twine(I) +
") is of type " + ArgValue->getType()->getAsString() +
"; expected type " + ArgType->getAsString() + ": " +
ArgValue->getAsString());
}
}
}
return false;
}
// Check an assertion: Obtain the condition value and be sure it is true.
// If not, print a nonfatal error along with the message.
void TGParser::CheckAssert(SMLoc Loc, Init *Condition, Init *Message) {

View File

@ -243,8 +243,10 @@ private: // Parser methods.
IDParseMode Mode = ParseValueMode);
Init *ParseValue(Record *CurRec, RecTy *ItemType = nullptr,
IDParseMode Mode = ParseValueMode);
void ParseValueList(SmallVectorImpl<llvm::Init*> &Result, Record *CurRec,
Record *ArgsRec = nullptr, RecTy *EltTy = nullptr);
void ParseValueList(SmallVectorImpl<llvm::Init*> &Result,
Record *CurRec, RecTy *ItemType = nullptr);
bool ParseTemplateArgValueList(SmallVectorImpl<llvm::Init *> &Result,
Record *CurRec, Record *ArgsRec);
void ParseDagArgList(
SmallVectorImpl<std::pair<llvm::Init*, StringInit*>> &Result,
Record *CurRec);
@ -264,6 +266,8 @@ private: // Parser methods.
MultiClass *ParseMultiClassID();
bool ApplyLetStack(Record *CurRec);
bool ApplyLetStack(RecordsEntry &Entry);
bool CheckTemplateArgValues(SmallVectorImpl<llvm::Init *> &Values,
SMLoc Loc, Record *ArgsRec);
void CheckAssert(SMLoc Loc, Init *Condition, Init *Message);
void CheckRecordAsserts(Record &Rec);
};

View File

@ -1,13 +1,14 @@
// RUN: not llvm-tblgen %s 2>&1 | FileCheck %s
// XFAIL: vg_leak
class A<A x> {
A a = x;
class Cl<Cl rec> {
Cl Arec = rec;
}
// At the time A0 is referenced, A has not yet been established as a superclass.
// This kind of self-reference is discourage, but if you *really* want it, you
// can force it with !cast.
//
// CHECK: Field 'A:x' of type 'A' is incompatible with value
def A0 : A<A0>;
// CHECK: alue specified for template argument 'Cl:rec'
def Rec0 : Cl<Rec0>;

View File

@ -0,0 +1,142 @@
// RUN: llvm-tblgen %s | FileCheck %s
// RUN: not llvm-tblgen -DERROR1 %s 2>&1 | FileCheck --check-prefix=ERROR1 %s
// RUN: not llvm-tblgen -DERROR2 %s 2>&1 | FileCheck --check-prefix=ERROR2 %s
// RUN: not llvm-tblgen -DERROR3 %s 2>&1 | FileCheck --check-prefix=ERROR3 %s
// RUN: not llvm-tblgen -DERROR4 %s 2>&1 | FileCheck --check-prefix=ERROR4 %s
// RUN: not llvm-tblgen -DERROR5 %s 2>&1 | FileCheck --check-prefix=ERROR5 %s
// RUN: not llvm-tblgen -DERROR6 %s 2>&1 | FileCheck --check-prefix=ERROR6 %s
// This file tests that template arguments are type-checked and cast
// if necessary.
// Class template arguments.
class Class1<string nm> {
string Name = nm;
}
// CHECK: def Rec1
// CHECK: string Name = "Alice"
// CHECK: string NameName = "AliceAlice"
def Rec1 : Class1<"Alice"> {
string NameName = Name # Name;
}
#ifdef ERROR1
// ERROR1: Value specified for template argument 'Class1:nm' (#0) is of type int
def Rec2 : Class1<42> {
}
#endif
class Class2<bits<8> cd> {
int Code = cd;
}
// CHECK: def Rec3
// CHECK: int Code = 42
// CHECK: list<int> CodeList = [42]
def Rec3 : Class2<0b00101010> {
list<int> CodeList = [Code];
}
// CHECK: def Rec4
// CHECK: int Code = 42
// CHECK: list<int> CodeList = [42]
def Rec4 : Class2<42> {
list<int> CodeList = [Code];
}
#ifdef ERROR2
// ERROR2: Value specified for template argument 'Class2:cd' (#0) is of type string
def Rec5 : Class2<"oops"> {
list<int> CodeList = [Code];
}
#endif
// Anonymous class instantiation template arguments.
// CHECK: def Rec6
// CHECK: string Name = "Ted"
def Rec6 {
string Name = Class1<"Ted">.Name;
}
#ifdef ERROR3
// ERROR3: Value specified for template argument 'Class1:nm' (#0) is of type int
def Rec7 {
string Name = Class1<42>.Name;
}
#endif
// CHECK: def Rec8
// CHECK: list<int> CodeList = [42]
def Rec8 {
list<int> CodeList = [Class2<42>.Code];
}
#ifdef ERROR4
// ERROR4: Value specified for template argument 'Class2:cd' (#0) is of type string
def Rec9 {
list<int> CodeList = [Class2<"huh?">.Code];
}
#endif
// Multiclass template arguments.
multiclass MC1<string nm> {
def _1 {
string Name = nm;
}
def _2 {
string NameNmae = nm # nm;
}
}
// CHECK: def RecMC1_1
// CHECK: string Name = "Carol"
// CHECK: def RecMC1_2
// CHECK: string NameNmae = "CarolCarol"
defm RecMC1 : MC1<"Carol">;
#ifdef ERROR5
// ERROR5: Value specified for template argument 'MC1::nm' (#0) is of type int
defm RecMC2 : MC1<42>;
#endif
multiclass MC2<bits<8> cd> {
def _1 {
bits<8> Code = cd;
}
def _2 {
int Code = cd;
}
def _3 {
list<int> CodeList = [cd];
}
}
// CHECK: def RecMC3_1
// CHECK: bits<8> Code = { 0, 0, 1, 0, 1, 0, 1, 0 }
// CHECK: def RecMC3_2
// CHECK: int Code = 42
// CHECK: def RecMC3_3
// CHECK: list<int> CodeList = [42]
defm RecMC3 : MC2<42>;
#ifdef ERROR6
// ERROR6: Value specified for template argument 'MC2::cd' (#0) is of type string
defm RecMC4 : MC2<"Bob">;
#endif