def NamedGroup : DiagGroup<"a">;
def InNamedGroup1 : Warning<"">, InGroup<DiagGroup<"a">>;
def InNamedGroup2 : Warning<"">, InGroup < DiagGroup<"a"> >;
-// CHECK: redefined-group.td:[[@LINE-3]]:1: error: group 'a' is defined more than once
-// CHECK: redefined-group.td:[[@LINE-3]]:1: note: also implicitly defined here
-// CHECK: redefined-group.td:[[@LINE-3]]:1: note: also implicitly defined here
+// CHECK: redefined-group.td:[[@LINE-3]]:5: error: group 'a' is defined more than once
+// CHECK: redefined-group.td:[[@LINE-3]]:5: note: also implicitly defined here
+// CHECK: redefined-group.td:[[@LINE-3]]:5: note: also implicitly defined here
def : DiagGroup<"b">;
def InUnnamedGroup : Warning<"">, InGroup<DiagGroup<"b">>;
// CHECK: redefined-group.td:[[@LINE-2]]:1: error: group 'b' is defined more than once
-// CHECK: redefined-group.td:[[@LINE-2]]:1: note: also implicitly defined here
+// CHECK: redefined-group.td:[[@LINE-2]]:5: note: also implicitly defined here
def ImplicitGroup1 : Warning<"">, InGroup<DiagGroup<"c">>;
def ImplicitGroup2 : Warning<"">, InGroup<DiagGroup<"c">>;
def ImplicitGroup3 : Warning<"">,
InGroup<DiagGroup<"c">>;
-// CHECK: redefined-group.td:[[@LINE-4]]:1: error: group 'c' is implicitly defined more than once
-// CHECK: redefined-group.td:[[@LINE-4]]:1: note: also implicitly defined here
-// CHECK: redefined-group.td:[[@LINE-4]]:1: note: also implicitly defined here
+// CHECK: redefined-group.td:[[@LINE-4]]:5: error: group 'c' is implicitly defined more than once
+// CHECK: redefined-group.td:[[@LINE-4]]:5: note: also implicitly defined here
+// CHECK: redefined-group.td:[[@LINE-4]]:5: note: also implicitly defined here
def NamedAndUnnamed : DiagGroup<"d">;
def : DiagGroup<"d">;
-// CHECK: redefined-group.td:[[@LINE-2]]:1: error: group 'd' is defined more than once
+// CHECK: redefined-group.td:[[@LINE-2]]:5: error: group 'd' is defined more than once
// CHECK: redefined-group.td:[[@LINE-2]]:1: note: also defined here
def : DiagGroup<"e">;
def NamedAndUnnamed2 : DiagGroup<"e">;
-// CHECK: redefined-group.td:[[@LINE-1]]:1: error: group 'e' is defined more than once
+// CHECK: redefined-group.td:[[@LINE-1]]:5: error: group 'e' is defined more than once
// CHECK: redefined-group.td:[[@LINE-3]]:1: note: also defined here
def InGroupF1 : Warning<"">, InGroup<DiagGroup<"f">>;
def InGroupF3 : Warning<"">, InGroup<GroupF>;
def InGroupF4 : Warning<"">, InGroup<DiagGroup<"f">>;
// CHECK: redefined-group.td:[[@LINE-5]]:1: error: group 'f' is defined more than once
-// CHECK: redefined-group.td:[[@LINE-7]]:1: note: also implicitly defined here
-// CHECK: redefined-group.td:[[@LINE-6]]:1: note: also implicitly defined here
-// CHECK: redefined-group.td:[[@LINE-4]]:1: note: also implicitly defined here
+// CHECK: redefined-group.td:[[@LINE-7]]:5: note: also implicitly defined here
+// CHECK: redefined-group.td:[[@LINE-6]]:5: note: also implicitly defined here
+// CHECK: redefined-group.td:[[@LINE-4]]:5: note: also implicitly defined here
Init *Value;
bool IsUsed = false;
+ /// Reference locations to this record value.
+ SmallVector<SMRange> ReferenceLocs;
+
public:
RecordVal(Init *N, RecTy *T, FieldKind K);
RecordVal(Init *N, SMLoc Loc, RecTy *T, FieldKind K);
/// Set the value and source location of the field.
bool setValue(Init *V, SMLoc NewLoc);
+ /// Add a reference to this record value.
+ void addReferenceLoc(SMRange Loc) { ReferenceLocs.push_back(Loc); }
+
+ /// Return the references of this record value.
+ ArrayRef<SMRange> getReferenceLocs() const { return ReferenceLocs; }
+
/// Whether this value is used. Useful for reporting warnings, for example
/// when a template argument is unused.
void setUsed(bool Used) { IsUsed = Used; }
private:
Init *Name;
// Location where record was instantiated, followed by the location of
- // multiclass prototypes used.
+ // multiclass prototypes used, and finally by the locations of references to
+ // this record.
SmallVector<SMLoc, 4> Locs;
SmallVector<SMLoc, 0> ForwardDeclarationLocs;
+ SmallVector<SMRange, 0> ReferenceLocs;
SmallVector<Init *, 0> TemplateArgs;
SmallVector<RecordVal, 0> Values;
SmallVector<AssertionInfo, 0> Assertions;
return ForwardDeclarationLocs;
}
+ /// Add a reference to this record value.
+ void appendReferenceLoc(SMRange Loc) { ReferenceLocs.push_back(Loc); }
+
+ /// Return the references of this record value.
+ ArrayRef<SMRange> getReferenceLocs() const { return ReferenceLocs; }
+
// Update a class location when encountering a (re-)definition.
void updateClassLoc(SMLoc Loc);
return SMLoc::getFromPointer(TokStart);
}
+SMRange TGLexer::getLocRange() const {
+ return {getLoc(), SMLoc::getFromPointer(CurPtr)};
+}
+
/// ReturnError - Set the error to the specified string at the specified
/// location. This is defined to always return tgtok::Error.
tgtok::TokKind TGLexer::ReturnError(SMLoc Loc, const Twine &Msg) {
}
SMLoc getLoc() const;
+ SMRange getLocRange() const;
private:
/// LexToken - Read the next token and return its code.
/// Return true on error, false on success.
bool TGParser::SetValue(Record *CurRec, SMLoc Loc, Init *ValName,
ArrayRef<unsigned> BitList, Init *V,
- bool AllowSelfAssignment) {
+ bool AllowSelfAssignment, bool OverrideDefLoc) {
if (!V) return false;
if (!CurRec) CurRec = &CurMultiClass->Rec;
V = BitsInit::get(Records, NewBits);
}
- if (RV->setValue(V, Loc)) {
+ if (OverrideDefLoc ? RV->setValue(V, Loc) : RV->setValue(V)) {
std::string InitType;
if (BitsInit *BI = dyn_cast<BitsInit>(V))
InitType = (Twine("' of type bit initializer with length ") +
Lex.getCurStrVal() + "'");
else
TokError(Msg);
+ } else {
+ Result->appendReferenceLoc(Lex.getLocRange());
}
Lex.Lex();
}
/// ParseIDValue
-Init *TGParser::ParseIDValue(Record *CurRec, StringInit *Name, SMLoc NameLoc,
+Init *TGParser::ParseIDValue(Record *CurRec, StringInit *Name, SMRange NameLoc,
IDParseMode Mode) {
if (CurRec) {
- if (const RecordVal *RV = CurRec->getValue(Name))
+ if (RecordVal *RV = CurRec->getValue(Name)) {
+ RV->addReferenceLoc(NameLoc);
return VarInit::get(Name, RV->getType());
+ }
}
if ((CurRec && CurRec->isClass()) || CurMultiClass) {
RecordVal *RV = TemplateRec->getValue(TemplateArgName);
assert(RV && "Template arg doesn't exist??");
RV->setUsed(true);
+ RV->addReferenceLoc(NameLoc);
return VarInit::get(TemplateArgName, RV->getType());
} else if (Name->getValue() == "NAME") {
return VarInit::get(TemplateArgName, StringRecTy::get(Records));
if (Mode == ParseNameMode)
return Name;
- if (Init *I = Records.getGlobal(Name->getValue()))
+ if (Init *I = Records.getGlobal(Name->getValue())) {
+ // Add a reference to the global if it's a record.
+ if (auto *Def = dyn_cast<DefInit>(I))
+ Def->getDef()->appendReferenceLoc(NameLoc);
return I;
+ }
// Allow self-references of concrete defs, but delay the lookup so that we
// get the correct type.
CurRec->getNameInit() == Name)
return UnOpInit::get(UnOpInit::CAST, Name, CurRec->getType());
- Error(NameLoc, "Variable not defined: '" + Name->getValue() + "'");
+ Error(NameLoc.Start, "Variable not defined: '" + Name->getValue() + "'");
return nullptr;
}
Lex.Lex();
break;
case tgtok::Id: {
- SMLoc NameLoc = Lex.getLoc();
+ SMRange NameLoc = Lex.getLocRange();
StringInit *Name = StringInit::get(Records, Lex.getCurStrVal());
if (Lex.Lex() != tgtok::less) // consume the Id.
return ParseIDValue(CurRec, Name, NameLoc, Mode); // Value ::= IDValue
// 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() + "'");
+ Error(NameLoc.Start,
+ "Expected a class name, got '" + Name->getValue() + "'");
return nullptr;
}
if (ParseTemplateArgValueList(Args, CurRec, Class))
return nullptr; // Error parsing value list.
- if (CheckTemplateArgValues(Args, NameLoc, Class))
+ if (CheckTemplateArgValues(Args, NameLoc.Start, Class))
return nullptr; // Error checking template argument values.
// Loop through the arguments that were not specified and make sure
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() + "'");
-
+ if (!Arg->getValue()->isComplete()) {
+ Error(NameLoc.Start, "Value not specified for template argument '" +
+ TArgs[I]->getAsUnquotedString() + "' (#" +
+ Twine(I) + ") of parent class '" +
+ Class->getNameInitAsString() + "'");
+ }
}
+ Class->appendReferenceLoc(NameLoc);
return VarDefInit::get(Class, Args)->Fold();
}
case tgtok::l_brace: { // Value ::= '{' ValueList '}'
TokError("expected field identifier after '.'");
return nullptr;
}
+ SMRange FieldNameLoc = Lex.getLocRange();
StringInit *FieldName = StringInit::get(Records, Lex.getCurStrVal());
if (!Result->getFieldType(FieldName)) {
TokError("Cannot access field '" + Lex.getCurStrVal() + "' of value '" +
Result->getAsString() + "'");
return nullptr;
}
+
+ // Add a reference to this field if we know the record class.
+ if (auto *DI = dyn_cast<DefInit>(Result)) {
+ DI->getDef()->getValue(FieldName)->addReferenceLoc(FieldNameLoc);
+ } else if (auto *TI = dyn_cast<TypedInit>(Result)) {
+ if (auto *RecTy = dyn_cast<RecordRecTy>(TI->getType())) {
+ for (Record *R : RecTy->getClasses())
+ if (auto *RV = R->getValue(FieldName))
+ RV->addReferenceLoc(FieldNameLoc);
+ }
+ }
+
Result = FieldInit::get(Result, FieldName)->Fold(CurRec);
Lex.Lex(); // eat field name
break;
SMLoc ValLoc = Lex.getLoc();
Init *Val = ParseValue(CurRec, Type);
if (!Val ||
- SetValue(CurRec, ValLoc, DeclName, None, Val))
+ SetValue(CurRec, ValLoc, DeclName, None, Val,
+ /*AllowSelfAssignment=*/false, /*OverrideDefLoc=*/false)) {
// Return the name, even if an error is thrown. This is so that we can
// continue to make some progress, even without the value having been
// initialized.
return DeclName;
+ }
}
return DeclName;
assert(Lex.getCode() == tgtok::Def && "Unknown tok");
Lex.Lex(); // Eat the 'def' token.
+ // If the name of the def is an Id token, use that for the location.
+ // Otherwise, the name is more complex and we use the location of the 'def'
+ // token.
+ SMLoc NameLoc = Lex.getCode() == tgtok::Id ? Lex.getLoc() : DefLoc;
+
// Parse ObjectName and make a record for it.
std::unique_ptr<Record> CurRec;
Init *Name = ParseObjectName(CurMultiClass);
if (!Name)
return true;
- if (isa<UnsetInit>(Name))
- CurRec = std::make_unique<Record>(Records.getNewAnonymousName(), DefLoc, Records,
+ if (isa<UnsetInit>(Name)) {
+ CurRec =
+ std::make_unique<Record>(Records.getNewAnonymousName(), DefLoc, Records,
/*Anonymous=*/true);
- else
- CurRec = std::make_unique<Record>(Name, DefLoc, Records);
+ } else {
+ CurRec = std::make_unique<Record>(Name, NameLoc, Records);
+ }
if (ParseObjectBody(CurRec.get()))
return true;
private: // Semantic analysis methods.
bool AddValue(Record *TheRec, SMLoc Loc, const RecordVal &RV);
+ /// Set the value of a RecordVal within the given record. If `OverrideDefLoc`
+ /// is set, the provided location overrides any existing location of the
+ /// RecordVal.
bool SetValue(Record *TheRec, SMLoc Loc, Init *ValName,
ArrayRef<unsigned> BitList, Init *V,
- bool AllowSelfAssignment = false);
+ bool AllowSelfAssignment = false, bool OverrideDefLoc = true);
bool AddSubClass(Record *Rec, SubClassReference &SubClass);
bool AddSubClass(RecordsEntry &Entry, SubClassReference &SubClass);
bool AddSubMultiClass(MultiClass *CurMC,
SubClassReference ParseSubClassReference(Record *CurRec, bool isDefm);
SubMultiClassReference ParseSubMultiClassReference(MultiClass *CurMC);
- Init *ParseIDValue(Record *CurRec, StringInit *Name, SMLoc NameLoc,
+ Init *ParseIDValue(Record *CurRec, StringInit *Name, SMRange NameLoc,
IDParseMode Mode = ParseValueMode);
Init *ParseSimpleValue(Record *CurRec, RecTy *ItemType = nullptr,
IDParseMode Mode = ParseValueMode);
include "ConstraintChecking.inc"
-// CHECK: [[FILE]]:[[@LINE+1]]:1: error: Unrecognized constraint '$dest1 ~ $src2' in 'Foo'
+// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Unrecognized constraint '$dest1 ~ $src2' in 'Foo'
def Foo : TestInstructionWithConstraints<"$dest1 ~ $src2">;
include "ConstraintChecking.inc"
-// CHECK: [[FILE]]:[[@LINE+1]]:1: error: Illegal format for @earlyclobber constraint in 'Foo'
+// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Illegal format for @earlyclobber constraint in 'Foo'
def Foo : TestInstructionWithConstraints<"@earlyclobber ">;
// (This is illegal because the '=' has to be surrounded by whitespace)
-// CHECK: [[FILE]]:[[@LINE+1]]:1: error: Illegal format for tied-to constraint in 'Foo'
+// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Illegal format for tied-to constraint in 'Foo'
def Foo : TestInstructionWithConstraints<"$dest1=$dest2">;
include "ConstraintChecking.inc"
-// CHECK: [[FILE]]:[[@LINE+1]]:1: error: Output operands '$dest1' and '$dest2' of 'Foo' cannot be tied!
+// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Output operands '$dest1' and '$dest2' of 'Foo' cannot be tied!
def Foo : TestInstructionWithConstraints<"$dest1 = $dest2">;
include "ConstraintChecking.inc"
-// CHECK: [[FILE]]:[[@LINE+1]]:1: error: Input operands '$src1' and '$src2' of 'Foo' cannot be tied!
+// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Input operands '$src1' and '$src2' of 'Foo' cannot be tied!
def Foo : TestInstructionWithConstraints<"$src1 = $src2">;
include "ConstraintChecking.inc"
-// CHECK: [[FILE]]:[[@LINE+1]]:1: error: Operand '$dest1' of 'Foo' cannot have multiple operands tied to it!
+// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Operand '$dest1' of 'Foo' cannot have multiple operands tied to it!
def Foo : TestInstructionWithConstraints<"$dest1 = $src1, $dest1 = $src2">;
include "ConstraintChecking.inc"
-// CHECK: [[FILE]]:[[@LINE+1]]:1: error: Operand '$src1' of 'Foo' cannot have multiple constraints!
+// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Operand '$src1' of 'Foo' cannot have multiple constraints!
def Foo : TestInstructionWithConstraints<"$dest1 = $src1, $dest2 = $src1">;
[(set GPR32:$dst, (i32 (setcc i32:$src0, i32:$src1, SETEQ)))]>;
// Check there is an error if not a CondCode operand.
-// ERR: [[FILE]]:[[@LINE+1]]:1: warning: Skipped pattern: Unable to handle CondCode
+// ERR: [[FILE]]:[[@LINE+1]]:5: warning: Skipped pattern: Unable to handle CondCode
def FCMP_NOTCC : I<(outs GPR32:$dst), (ins FPR32Op:$src0, FPR32:$src1),
[(set GPR32:$dst, (i32 (setcc f32:$src0, f32:$src1, i32)))]>;
def MyTarget : Target;
def R0 : Register<"r0">;
-def ClassA : RegisterClass<"MyTarget", [], 32, (add R0)>; // CHECK: [[@LINE]]:1: error: RegTypes list must not be empty!
+def ClassA : RegisterClass<"MyTarget", [], 32, (add R0)>; // CHECK: [[@LINE]]:5: error: RegTypes list must not be empty!
def TestTarget : Target;
-// CHECK: [[FILE]]:[[@LINE+1]]:1: error: No schedule information for instruction 'TestInst' in SchedMachineModel 'TestSchedModel'
+// CHECK: [[FILE]]:[[@LINE+1]]:5: error: No schedule information for instruction 'TestInst' in SchedMachineModel 'TestSchedModel'
def TestInst : Instruction {
let OutOperandList = (outs);
let InOperandList = (ins);
}
def DFoo : DEntry<"foo", 1>;
-// ERROR1: [[@LINE+1]]:1: error: Record 'DBar' for table 'DTable' is missing field 'Val1'
+// ERROR1: [[@LINE+1]]:5: error: Record 'DBar' for table 'DTable' is missing field 'Val1'
def DBar : DEntry<"bar", ?>;
def DTable : GenericTable {
// Add references to the definition.
for (SMLoc loc : def.getLoc().drop_front())
insertRef(sym, lsp::convertTokenLocToRange(loc));
-
- // Add references to any super classes.
- for (auto &it : def.getSuperClasses())
- insertRef(getOrInsertDef(it.first),
- lsp::convertTokenLocToRange(it.second.Start));
+ for (SMRange loc : def.getReferenceLocs())
+ insertRef(sym, loc);
// Add definitions for any values.
for (const llvm::RecordVal &value : def.getValues()) {
auto *sym = getOrInsertDef(&value);
insertRef(sym, sym->defLoc, /*isDef=*/true);
+ for (SMRange refLoc : value.getReferenceLocs())
+ insertRef(sym, refLoc);
}
}
}