From f8fbe9eb0497bbbedc886c4f766775f603341acf Mon Sep 17 00:00:00 2001 From: "Paul C. Anagnostopoulos" Date: Thu, 25 Feb 2021 16:33:08 -0500 Subject: [PATCH] [TableGen] Improve handling of template arguments This requires changes to TableGen files and some C++ files due to incompatible multiclass template arguments that slipped through before the improved handling. --- docs/TableGen/ProgRef.rst | 21 +- include/llvm/TableGen/Record.h | 6 + lib/TableGen/Record.cpp | 14 +- lib/TableGen/TGParser.cpp | 338 +++++++++++----------- lib/TableGen/TGParser.h | 8 +- test/TableGen/self-reference-typeerror.td | 9 +- test/TableGen/template-args.td | 142 +++++++++ 7 files changed, 346 insertions(+), 192 deletions(-) create mode 100644 test/TableGen/template-args.td diff --git a/docs/TableGen/ProgRef.rst b/docs/TableGen/ProgRef.rst index c60bffef3ed..9799e29a63e 100644 --- a/docs/TableGen/ProgRef.rst +++ b/docs/TableGen/ProgRef.rst @@ -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`` 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 diff --git a/include/llvm/TableGen/Record.h b/include/llvm/TableGen/Record.h index e75b7f01c86..ea47d671302 100644 --- a/include/llvm/TableGen/Record.h +++ b/include/llvm/TableGen/Record.h @@ -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; }; diff --git a/lib/TableGen/Record.cpp b/lib/TableGen/Record.cpp index 13212098514..3172d711e7f 100644 --- a/lib/TableGen/Record.cpp +++ b/lib/TableGen/Record.cpp @@ -2344,13 +2344,13 @@ void Record::resolveReferences(Resolver &R, const RecordVal *SkipVal) { if (TypedInit *VRT = dyn_cast(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"); } } } diff --git a/lib/TableGen/TGParser.cpp b/lib/TableGen/TGParser.cpp index 87faf77671c..974df42de4c 100644 --- a/lib/TableGen/TGParser.cpp +++ b/lib/TableGen/TGParser.cpp @@ -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 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 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 expression. This is supposed to synthesize - // a new anonymous definition, deriving from CLASS 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 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 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(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 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 &Result, Record *CurRec, - Record *ArgsRec, RecTy *EltTy) { - RecTy *ItemType = EltTy; - unsigned int ArgN = 0; - if (ArgsRec && !EltTy) { - ArrayRef 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 &Result, Record *CurRec, + RecTy *ItemType) { + Result.push_back(ParseValue(CurRec, ItemType)); if (!Result.back()) { Result.clear(); @@ -2558,19 +2494,6 @@ void TGParser::ParseValueList(SmallVectorImpl &Result, Record *CurRec, // ignore trailing comma for lists if (Lex.getCode() == tgtok::r_square) return; - - if (ArgsRec && !EltTy) { - ArrayRef 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 &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 &Result, + Record *CurRec, Record *ArgsRec) { + + assert(Result.empty() && "Result vector is not empty"); + ArrayRef 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 Values; + std::vector 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 TemplateVals = Ref.TemplateArgs; - // Verify that the correct number of template arguments were specified. + ArrayRef TemplateVals = Ref.TemplateArgs; ArrayRef 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 &Values, + SMLoc Loc, Record *ArgsRec) { + + ArrayRef 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(Value)) { + auto *CastValue = ArgValue->getCastTo(ArgType); + if (CastValue) { + assert((!isa(CastValue) || + cast(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) { diff --git a/lib/TableGen/TGParser.h b/lib/TableGen/TGParser.h index 578a56c9d01..5b847ab7344 100644 --- a/lib/TableGen/TGParser.h +++ b/lib/TableGen/TGParser.h @@ -243,8 +243,10 @@ private: // Parser methods. IDParseMode Mode = ParseValueMode); Init *ParseValue(Record *CurRec, RecTy *ItemType = nullptr, IDParseMode Mode = ParseValueMode); - void ParseValueList(SmallVectorImpl &Result, Record *CurRec, - Record *ArgsRec = nullptr, RecTy *EltTy = nullptr); + void ParseValueList(SmallVectorImpl &Result, + Record *CurRec, RecTy *ItemType = nullptr); + bool ParseTemplateArgValueList(SmallVectorImpl &Result, + Record *CurRec, Record *ArgsRec); void ParseDagArgList( SmallVectorImpl> &Result, Record *CurRec); @@ -264,6 +266,8 @@ private: // Parser methods. MultiClass *ParseMultiClassID(); bool ApplyLetStack(Record *CurRec); bool ApplyLetStack(RecordsEntry &Entry); + bool CheckTemplateArgValues(SmallVectorImpl &Values, + SMLoc Loc, Record *ArgsRec); void CheckAssert(SMLoc Loc, Init *Condition, Init *Message); void CheckRecordAsserts(Record &Rec); }; diff --git a/test/TableGen/self-reference-typeerror.td b/test/TableGen/self-reference-typeerror.td index 35c6131fa2c..6f8da4dae13 100644 --- a/test/TableGen/self-reference-typeerror.td +++ b/test/TableGen/self-reference-typeerror.td @@ -1,13 +1,14 @@ // RUN: not llvm-tblgen %s 2>&1 | FileCheck %s // XFAIL: vg_leak -class A { - A a = x; +class Cl { + 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; +// CHECK: alue specified for template argument 'Cl:rec' + +def Rec0 : Cl; diff --git a/test/TableGen/template-args.td b/test/TableGen/template-args.td new file mode 100644 index 00000000000..2a931adffe9 --- /dev/null +++ b/test/TableGen/template-args.td @@ -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 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 cd> { + int Code = cd; +} + +// CHECK: def Rec3 +// CHECK: int Code = 42 +// CHECK: list CodeList = [42] + +def Rec3 : Class2<0b00101010> { + list CodeList = [Code]; +} + +// CHECK: def Rec4 +// CHECK: int Code = 42 +// CHECK: list CodeList = [42] + +def Rec4 : Class2<42> { + list CodeList = [Code]; +} + +#ifdef ERROR2 +// ERROR2: Value specified for template argument 'Class2:cd' (#0) is of type string + +def Rec5 : Class2<"oops"> { + list 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 CodeList = [42] + +def Rec8 { + list CodeList = [Class2<42>.Code]; +} + +#ifdef ERROR4 +// ERROR4: Value specified for template argument 'Class2:cd' (#0) is of type string + +def Rec9 { + list CodeList = [Class2<"huh?">.Code]; +} +#endif + +// Multiclass template arguments. + +multiclass MC1 { + 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 cd> { + def _1 { + bits<8> Code = cd; + } + def _2 { + int Code = cd; + } + def _3 { + list 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 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