Skip to content

Commit d992ff3

Browse files
committed
Requestify Operator Validation
Restructure the way operators and precedencegroup declarations are validated. First, factor out the common operator validation code into the DeclChecker. Then, redo the lookup and validation code for precedence groups to work with the request model. Finally, delete the validateDecl overloads on the TypeChecker. Unfortunately, the evaluator is not capable of detecting all the cycles TypeCheckDecl can. In particular, certain cross-file precedence group cycles cannot be diagnosed via request alone. That infrastructure all stays in place.
1 parent 6226644 commit d992ff3

File tree

3 files changed

+106
-132
lines changed

3 files changed

+106
-132
lines changed

lib/Sema/TypeCheckDecl.cpp

Lines changed: 102 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "swift/Parse/Parser.h"
4747
#include "swift/Serialization/SerializedModuleLoader.h"
4848
#include "swift/Strings.h"
49+
#include "swift/AST/NameLookupRequests.h"
4950
#include "swift/AST/TypeCheckRequests.h"
5051
#include "swift/Basic/Defer.h"
5152
#include "llvm/ADT/APFloat.h"
@@ -1613,15 +1614,15 @@ swift::findNonImplicitRequiredInit(const ConstructorDecl *CD) {
16131614
return CD;
16141615
}
16151616

1616-
16171617
/// For building the higher-than component of the diagnostic path,
16181618
/// we use the visited set, which we've embellished with information
16191619
/// about how we reached a particular node. This is reasonable because
16201620
/// we need to maintain the set anyway.
1621-
static void buildHigherThanPath(PrecedenceGroupDecl *last,
1622-
const llvm::DenseMap<PrecedenceGroupDecl *,
1623-
PrecedenceGroupDecl *> &visitedFrom,
1624-
raw_ostream &out) {
1621+
static void buildHigherThanPath(
1622+
PrecedenceGroupDecl *last,
1623+
const llvm::DenseMap<PrecedenceGroupDecl *, PrecedenceGroupDecl *>
1624+
&visitedFrom,
1625+
raw_ostream &out) {
16251626
auto it = visitedFrom.find(last);
16261627
assert(it != visitedFrom.end());
16271628
auto from = it->second;
@@ -1634,8 +1635,7 @@ static void buildHigherThanPath(PrecedenceGroupDecl *last,
16341635
/// For building the lower-than component of the diagnostic path,
16351636
/// we just do a depth-first search to find a path.
16361637
static bool buildLowerThanPath(PrecedenceGroupDecl *start,
1637-
PrecedenceGroupDecl *target,
1638-
raw_ostream &out) {
1638+
PrecedenceGroupDecl *target, raw_ostream &out) {
16391639
if (start == target) {
16401640
out << start->getName();
16411641
return true;
@@ -1654,20 +1654,21 @@ static bool buildLowerThanPath(PrecedenceGroupDecl *start,
16541654
return false;
16551655
}
16561656

1657-
static void checkPrecedenceCircularity(TypeChecker &TC,
1657+
static void checkPrecedenceCircularity(DiagnosticEngine &D,
16581658
PrecedenceGroupDecl *PGD) {
16591659
// Don't diagnose if this group is already marked invalid.
1660-
if (PGD->isInvalid()) return;
1660+
if (PGD->isInvalid())
1661+
return;
16611662

16621663
// The cycle doesn't necessarily go through this specific group,
16631664
// so we need a proper visited set to avoid infinite loops. We
16641665
// also record a back-reference so that we can easily reconstruct
16651666
// the cycle.
1666-
llvm::DenseMap<PrecedenceGroupDecl*, PrecedenceGroupDecl*> visitedFrom;
1667-
SmallVector<PrecedenceGroupDecl*, 4> stack;
1667+
llvm::DenseMap<PrecedenceGroupDecl *, PrecedenceGroupDecl *> visitedFrom;
1668+
SmallVector<PrecedenceGroupDecl *, 4> stack;
16681669

16691670
// Fill out the targets set.
1670-
llvm::SmallPtrSet<PrecedenceGroupDecl*, 4> targets;
1671+
llvm::SmallPtrSet<PrecedenceGroupDecl *, 4> targets;
16711672
stack.push_back(PGD);
16721673
do {
16731674
auto cur = stack.pop_back_val();
@@ -1681,7 +1682,8 @@ static void checkPrecedenceCircularity(TypeChecker &TC,
16811682
targets.insert(cur);
16821683

16831684
for (auto &rel : cur->getLowerThan()) {
1684-
if (!rel.Group) continue;
1685+
if (!rel.Group)
1686+
continue;
16851687

16861688
// We can't have cycles in the lower-than relationship
16871689
// because it has to point outside of the module.
@@ -1704,7 +1706,8 @@ static void checkPrecedenceCircularity(TypeChecker &TC,
17041706
}
17051707

17061708
for (auto &rel : cur->getHigherThan()) {
1707-
if (!rel.Group) continue;
1709+
if (!rel.Group)
1710+
continue;
17081711

17091712
// Check whether we've reached a target declaration.
17101713
if (!targets.count(rel.Group)) {
@@ -1730,40 +1733,20 @@ static void checkPrecedenceCircularity(TypeChecker &TC,
17301733
// Build the lowerThan portion of the path (rel.Group -> PGD).
17311734
buildLowerThanPath(PGD, rel.Group, str);
17321735
}
1733-
1734-
TC.diagnose(PGD->getHigherThanLoc(), diag::precedence_group_cycle, path);
1736+
1737+
D.diagnose(PGD->getHigherThanLoc(), diag::precedence_group_cycle, path);
17351738
PGD->setInvalid();
17361739
return;
17371740
}
17381741
} while (!stack.empty());
17391742
}
17401743

1741-
/// Do a primitive lookup for the given precedence group. This does
1742-
/// not validate the precedence group or diagnose if the lookup fails
1743-
/// (other than via ambiguity); for that, use
1744-
/// TypeChecker::lookupPrecedenceGroup.
1745-
///
1746-
/// Pass an invalid source location to suppress diagnostics.
1747-
static PrecedenceGroupDecl *
1748-
lookupPrecedenceGroupPrimitive(DeclContext *dc, Identifier name,
1749-
SourceLoc nameLoc) {
1750-
if (auto sf = dc->getParentSourceFile()) {
1751-
bool cascading = dc->isCascadingContextForLookup(false);
1752-
return sf->lookupPrecedenceGroup(name, cascading, nameLoc);
1753-
} else {
1754-
return dc->getParentModule()->lookupPrecedenceGroup(name, nameLoc);
1755-
}
1756-
}
1757-
1758-
void TypeChecker::validateDecl(PrecedenceGroupDecl *PGD) {
1759-
checkDeclAttributes(PGD);
1760-
1744+
static void validatePrecedenceGroup(PrecedenceGroupDecl *PGD) {
1745+
assert(PGD && "Cannot validate a null precedence group!");
17611746
if (PGD->isInvalid() || PGD->hasValidationStarted())
17621747
return;
17631748
DeclValidationRAII IBV(PGD);
17641749

1765-
bool isInvalid = false;
1766-
17671750
auto &Diags = PGD->getASTContext().Diags;
17681751

17691752
// Validate the higherThan relationships.
@@ -1775,7 +1758,6 @@ void TypeChecker::validateDecl(PrecedenceGroupDecl *PGD) {
17751758
rel.Name, rel.NameLoc);
17761759
if (group) {
17771760
rel.Group = group;
1778-
validateDecl(group);
17791761
addedHigherThan = true;
17801762
} else if (!PGD->isInvalid()) {
17811763
Diags.diagnose(rel.NameLoc, diag::unknown_precedence_group, rel.Name);
@@ -1790,42 +1772,37 @@ void TypeChecker::validateDecl(PrecedenceGroupDecl *PGD) {
17901772
auto dc = PGD->getDeclContext();
17911773
auto group = TypeChecker::lookupPrecedenceGroup(dc, rel.Name, rel.NameLoc);
17921774
if (group) {
1793-
if (group->getDeclContext()->getParentModule()
1794-
== dc->getParentModule()) {
1775+
if (group->getDeclContext()->getParentModule() == dc->getParentModule()) {
17951776
if (!PGD->isInvalid()) {
1796-
diagnose(rel.NameLoc, diag::precedence_group_lower_within_module);
1797-
diagnose(group->getNameLoc(), diag::kind_declared_here,
1798-
DescriptiveDeclKind::PrecedenceGroup);
1799-
isInvalid = true;
1777+
Diags.diagnose(rel.NameLoc,
1778+
diag::precedence_group_lower_within_module);
1779+
Diags.diagnose(group->getNameLoc(), diag::kind_declared_here,
1780+
DescriptiveDeclKind::PrecedenceGroup);
1781+
PGD->setInvalid();
18001782
}
18011783
} else {
18021784
rel.Group = group;
1803-
validateDecl(group);
18041785
}
18051786
} else if (!PGD->isInvalid()) {
1806-
diagnose(rel.NameLoc, diag::unknown_precedence_group, rel.Name);
1807-
isInvalid = true;
1787+
Diags.diagnose(rel.NameLoc, diag::unknown_precedence_group, rel.Name);
1788+
PGD->setInvalid();
18081789
}
18091790
}
1810-
1791+
18111792
// Check for circularity.
18121793
if (addedHigherThan) {
1813-
checkPrecedenceCircularity(*this, PGD);
1794+
checkPrecedenceCircularity(Diags, PGD);
18141795
}
1815-
1816-
if (isInvalid) PGD->setInvalid();
18171796
}
18181797

18191798
PrecedenceGroupDecl *TypeChecker::lookupPrecedenceGroup(DeclContext *dc,
18201799
Identifier name,
18211800
SourceLoc nameLoc) {
1822-
auto group = lookupPrecedenceGroupPrimitive(dc, name, nameLoc);
1823-
if (group) {
1824-
validateDecl(group);
1825-
} else if (nameLoc.isValid()) {
1826-
// FIXME: avoid diagnosing this multiple times per source file.
1827-
diagnose(nameLoc, diag::unknown_precedence_group, name);
1828-
}
1801+
auto *group = evaluateOrDefault(
1802+
dc->getASTContext().evaluator,
1803+
LookupPrecedenceGroupRequest({dc, name, nameLoc}), nullptr);
1804+
if (group)
1805+
validatePrecedenceGroup(group);
18291806
return group;
18301807
}
18311808

@@ -1872,85 +1849,70 @@ static bool checkDesignatedTypes(OperatorDecl *OD,
18721849
/// This establishes key invariants, such as an InfixOperatorDecl's
18731850
/// reference to its precedence group and the transitive validity of that
18741851
/// group.
1875-
void TypeChecker::validateDecl(OperatorDecl *OD) {
1876-
checkDeclAttributes(OD);
1877-
1878-
auto IOD = dyn_cast<InfixOperatorDecl>(OD);
1879-
1852+
llvm::Expected<PrecedenceGroupDecl *>
1853+
OperatorPrecedenceGroupRequest::evaluate(Evaluator &evaluator,
1854+
InfixOperatorDecl *IOD) const {
18801855
auto enableOperatorDesignatedTypes =
1881-
getLangOpts().EnableOperatorDesignatedTypes;
1882-
1883-
// Pre- or post-fix operator?
1884-
if (!IOD) {
1885-
auto nominalTypes = OD->getDesignatedNominalTypes();
1886-
if (nominalTypes.empty() && enableOperatorDesignatedTypes) {
1887-
auto identifiers = OD->getIdentifiers();
1888-
auto identifierLocs = OD->getIdentifierLocs();
1889-
if (checkDesignatedTypes(OD, identifiers, identifierLocs, *this))
1890-
OD->setInvalid();
1891-
}
1892-
return;
1893-
}
1856+
IOD->getASTContext().LangOpts.EnableOperatorDesignatedTypes;
1857+
1858+
auto &Diags = IOD->getASTContext().Diags;
1859+
PrecedenceGroupDecl *group = nullptr;
18941860

1895-
if (!IOD->getPrecedenceGroup()) {
1896-
PrecedenceGroupDecl *group = nullptr;
1861+
auto identifiers = IOD->getIdentifiers();
1862+
auto identifierLocs = IOD->getIdentifierLocs();
18971863

1898-
auto identifiers = IOD->getIdentifiers();
1899-
auto identifierLocs = IOD->getIdentifierLocs();
1864+
if (!identifiers.empty()) {
1865+
group = TypeChecker::lookupPrecedenceGroup(
1866+
IOD->getDeclContext(), identifiers[0], identifierLocs[0]);
19001867

1901-
if (!identifiers.empty()) {
1902-
group = lookupPrecedenceGroupPrimitive(IOD->getDeclContext(),
1903-
identifiers[0], identifierLocs[0]);
1904-
if (group) {
1868+
if (group) {
1869+
identifiers = identifiers.slice(1);
1870+
identifierLocs = identifierLocs.slice(1);
1871+
} else {
1872+
// If we're either not allowing types, or we are allowing them
1873+
// and this identifier is not a type, emit an error as if it's
1874+
// a precedence group.
1875+
auto *DC = IOD->getDeclContext();
1876+
if (!(enableOperatorDesignatedTypes &&
1877+
resolveSingleNominalTypeDecl(DC, identifierLocs[0], identifiers[0],
1878+
IOD->getASTContext(),
1879+
TypeResolutionFlags::SilenceErrors))) {
1880+
Diags.diagnose(identifierLocs[0], diag::unknown_precedence_group,
1881+
identifiers[0]);
19051882
identifiers = identifiers.slice(1);
19061883
identifierLocs = identifierLocs.slice(1);
1907-
} else {
1908-
// If we're either not allowing types, or we are allowing them
1909-
// and this identifier is not a type, emit an error as if it's
1910-
// a precedence group.
1911-
auto *DC = OD->getDeclContext();
1912-
if (!(enableOperatorDesignatedTypes &&
1913-
resolveSingleNominalTypeDecl(
1914-
DC, identifierLocs[0], identifiers[0], *this,
1915-
TypeResolutionFlags::SilenceErrors))) {
1916-
diagnose(identifierLocs[0], diag::unknown_precedence_group,
1917-
identifiers[0]);
1918-
identifiers = identifiers.slice(1);
1919-
identifierLocs = identifierLocs.slice(1);
1920-
}
19211884
}
19221885
}
1886+
}
19231887

1924-
if (!identifiers.empty() && !enableOperatorDesignatedTypes) {
1925-
assert(!group);
1926-
diagnose(identifierLocs[0], diag::unknown_precedence_group,
1927-
identifiers[0]);
1928-
identifiers = identifiers.slice(1);
1929-
identifierLocs = identifierLocs.slice(1);
1930-
assert(identifiers.empty() && identifierLocs.empty());
1931-
}
1888+
if (!identifiers.empty() && !enableOperatorDesignatedTypes) {
1889+
assert(!group);
1890+
Diags.diagnose(identifierLocs[0], diag::unknown_precedence_group,
1891+
identifiers[0]);
1892+
identifiers = identifiers.slice(1);
1893+
identifierLocs = identifierLocs.slice(1);
1894+
assert(identifiers.empty() && identifierLocs.empty());
1895+
}
19321896

1933-
if (!group) {
1934-
group = lookupPrecedenceGroupPrimitive(
1935-
IOD->getDeclContext(), Context.Id_DefaultPrecedence, SourceLoc());
1936-
}
1897+
if (!group) {
1898+
group = TypeChecker::lookupPrecedenceGroup(
1899+
IOD->getDeclContext(), IOD->getASTContext().Id_DefaultPrecedence,
1900+
SourceLoc());
1901+
}
19371902

1938-
if (group) {
1939-
validateDecl(group);
1940-
IOD->setPrecedenceGroup(group);
1941-
} else {
1942-
diagnose(IOD->getLoc(), diag::missing_builtin_precedence_group,
1943-
Context.Id_DefaultPrecedence);
1944-
}
1903+
if (!group) {
1904+
Diags.diagnose(IOD->getLoc(), diag::missing_builtin_precedence_group,
1905+
IOD->getASTContext().Id_DefaultPrecedence);
1906+
}
19451907

1946-
auto nominalTypes = IOD->getDesignatedNominalTypes();
1947-
if (nominalTypes.empty() && enableOperatorDesignatedTypes) {
1948-
if (checkDesignatedTypes(IOD, identifiers, identifierLocs, *this)) {
1949-
IOD->setInvalid();
1950-
return;
1951-
}
1908+
auto nominalTypes = IOD->getDesignatedNominalTypes();
1909+
if (nominalTypes.empty() && enableOperatorDesignatedTypes) {
1910+
if (checkDesignatedTypes(IOD, identifiers, identifierLocs,
1911+
IOD->getASTContext())) {
1912+
IOD->setInvalid();
19521913
}
19531914
}
1915+
return group;
19541916
}
19551917

19561918
llvm::Expected<SelfAccessKind>
@@ -2115,12 +2077,26 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
21152077
}
21162078

21172079
void visitOperatorDecl(OperatorDecl *OD) {
2118-
TC.validateDecl(OD);
2080+
TC.checkDeclAttributes(OD);
2081+
auto &Ctx = OD->getASTContext();
2082+
if (auto *IOD = dyn_cast<InfixOperatorDecl>(OD)) {
2083+
(void)IOD->getPrecedenceGroup();
2084+
} else {
2085+
auto nominalTypes = OD->getDesignatedNominalTypes();
2086+
if (nominalTypes.empty() && Ctx.LangOpts.EnableOperatorDesignatedTypes) {
2087+
auto identifiers = OD->getIdentifiers();
2088+
auto identifierLocs = OD->getIdentifierLocs();
2089+
if (checkDesignatedTypes(OD, identifiers, identifierLocs, Ctx))
2090+
OD->setInvalid();
2091+
}
2092+
return;
2093+
}
21192094
checkAccessControl(TC, OD);
21202095
}
21212096

21222097
void visitPrecedenceGroupDecl(PrecedenceGroupDecl *PGD) {
2123-
TC.validateDecl(PGD);
2098+
TC.checkDeclAttributes(PGD);
2099+
validatePrecedenceGroup(PGD);
21242100
checkAccessControl(TC, PGD);
21252101
}
21262102

lib/Sema/TypeChecker.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -782,8 +782,6 @@ class TypeChecker final : public LazyResolver {
782782
DeclContext *DC);
783783

784784
void validateDecl(ValueDecl *D);
785-
void validateDecl(OperatorDecl *decl);
786-
void validateDecl(PrecedenceGroupDecl *decl);
787785

788786
/// Validate the given extension declaration, ensuring that it
789787
/// properly extends the nominal type it names.

test/decl/precedencegroup/circularity.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,20 @@ precedencegroup B { // expected-note {{precedence group declared here}}
1414
}
1515

1616
precedencegroup C0 {
17-
higherThan: C1
17+
higherThan: C1 // expected-error {{cycle in higherThan relation: C0 -> C1 -> C0}}
1818
}
1919
precedencegroup C1 {
20-
higherThan: C0 // expected-error {{cycle in higherThan relation: C1 -> C0 -> C1}}
20+
higherThan: C0
2121
}
2222

2323
precedencegroup D0 {
24-
higherThan: D1
24+
higherThan: D1 // expected-error {{cycle in higherThan relation: D0 -> D1 -> D2 -> D0}}
2525
}
2626
precedencegroup D1 {
2727
higherThan: D2
2828
}
2929
precedencegroup D2 {
30-
higherThan: D0 // expected-error {{cycle in higherThan relation: D2 -> D0 -> D1 -> D2}}
30+
higherThan: D0
3131
}
3232

3333
precedencegroup E0 {

0 commit comments

Comments
 (0)