Skip to content

Commit ad90309

Browse files
authored
Merge pull request #41386 from beccadax/your-library-is-overdue
Weaken some type checks for @preconcurrency decls
2 parents 23ba70e + c2ccb87 commit ad90309

File tree

12 files changed

+227
-85
lines changed

12 files changed

+227
-85
lines changed

include/swift/AST/ActorIsolation.h

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ bool isLetAccessibleAnywhere(const ModuleDecl *fromModule, VarDecl *let);
4747
/// the actors with which it can interact.
4848
class ActorIsolation {
4949
public:
50-
enum Kind {
50+
enum Kind : uint8_t {
5151
/// The actor isolation has not been specified. It is assumed to be
5252
/// unsafe to interact with this declaration from any actor.
5353
Unspecified = 0,
@@ -69,18 +69,19 @@ class ActorIsolation {
6969
};
7070

7171
private:
72-
Kind kind;
7372
union {
7473
NominalTypeDecl *actor;
7574
Type globalActor;
7675
void *pointer;
7776
};
77+
uint8_t kind : 3;
78+
uint8_t isolatedByPreconcurrency : 1;
7879

7980
ActorIsolation(Kind kind, NominalTypeDecl *actor)
80-
: kind(kind), actor(actor) { }
81+
: actor(actor), kind(kind), isolatedByPreconcurrency(false) { }
8182

8283
ActorIsolation(Kind kind, Type globalActor)
83-
: kind(kind), globalActor(globalActor) { }
84+
: globalActor(globalActor), kind(kind), isolatedByPreconcurrency(false) { }
8485

8586
public:
8687
static ActorIsolation forUnspecified() {
@@ -100,7 +101,7 @@ class ActorIsolation {
100101
unsafe ? GlobalActorUnsafe : GlobalActor, globalActor);
101102
}
102103

103-
Kind getKind() const { return kind; }
104+
Kind getKind() const { return (Kind)kind; }
104105

105106
operator Kind() const { return getKind(); }
106107

@@ -122,6 +123,16 @@ class ActorIsolation {
122123
return globalActor;
123124
}
124125

126+
bool preconcurrency() const {
127+
return isolatedByPreconcurrency;
128+
}
129+
130+
ActorIsolation withPreconcurrency(bool value) const {
131+
auto copy = *this;
132+
copy.isolatedByPreconcurrency = value;
133+
return copy;
134+
}
135+
125136
/// Determine whether this isolation will require substitution to be
126137
/// evaluated.
127138
bool requiresSubstitution() const;
@@ -134,10 +145,10 @@ class ActorIsolation {
134145
if (lhs.isGlobalActor() && rhs.isGlobalActor())
135146
return areTypesEqual(lhs.globalActor, rhs.globalActor);
136147

137-
if (lhs.kind != rhs.kind)
148+
if (lhs.getKind() != rhs.getKind())
138149
return false;
139150

140-
switch (lhs.kind) {
151+
switch (lhs.getKind()) {
141152
case Independent:
142153
case Unspecified:
143154
return true;

include/swift/AST/Expr.h

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ class alignas(8) Expr : public ASTAllocated<Expr> {
255255
Kind : 2
256256
);
257257

258-
SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1,
258+
SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1,
259259
/// True if closure parameters were synthesized from anonymous closure
260260
/// variables.
261261
HasAnonymousClosureVars : 1,
@@ -266,7 +266,11 @@ class alignas(8) Expr : public ASTAllocated<Expr> {
266266

267267
/// True if this @Sendable async closure parameter should implicitly
268268
/// inherit the actor context from where it was formed.
269-
InheritActorContext : 1
269+
InheritActorContext : 1,
270+
271+
/// True if this closure's actor isolation behavior was determined by an
272+
/// \c \@preconcurrency declaration.
273+
IsolatedByPreconcurrency : 1
270274
);
271275

272276
SWIFT_INLINE_BITFIELD_FULL(BindOptionalExpr, Expr, 16,
@@ -3537,40 +3541,46 @@ class ClosureActorIsolation {
35373541
};
35383542

35393543
private:
3540-
/// The actor to which this closure is isolated.
3544+
/// The actor to which this closure is isolated, plus a bit indicating
3545+
/// whether the isolation was imposed by a preconcurrency declaration.
35413546
///
3542-
/// There are three possible states:
3547+
/// There are three possible states for the pointer:
35433548
/// - NULL: The closure is independent of any actor.
35443549
/// - VarDecl*: The 'self' variable for the actor instance to which
35453550
/// this closure is isolated. It will always have a type that conforms
35463551
/// to the \c Actor protocol.
35473552
/// - Type: The type of the global actor on which
3548-
llvm::PointerUnion<VarDecl *, Type> storage;
3553+
llvm::PointerIntPair<llvm::PointerUnion<VarDecl *, Type>, 1, bool> storage;
35493554

3550-
ClosureActorIsolation(VarDecl *selfDecl) : storage(selfDecl) { }
3551-
ClosureActorIsolation(Type globalActorType) : storage(globalActorType) { }
3555+
ClosureActorIsolation(VarDecl *selfDecl, bool preconcurrency)
3556+
: storage(selfDecl, preconcurrency) { }
3557+
ClosureActorIsolation(Type globalActorType, bool preconcurrency)
3558+
: storage(globalActorType, preconcurrency) { }
35523559

35533560
public:
3554-
ClosureActorIsolation() : storage() { }
3561+
ClosureActorIsolation(bool preconcurrency = false)
3562+
: storage(nullptr, preconcurrency) { }
35553563

3556-
static ClosureActorIsolation forIndependent() {
3557-
return ClosureActorIsolation();
3564+
static ClosureActorIsolation forIndependent(bool preconcurrency) {
3565+
return ClosureActorIsolation(preconcurrency);
35583566
}
35593567

3560-
static ClosureActorIsolation forActorInstance(VarDecl *selfDecl) {
3561-
return ClosureActorIsolation(selfDecl);
3568+
static ClosureActorIsolation forActorInstance(VarDecl *selfDecl,
3569+
bool preconcurrency) {
3570+
return ClosureActorIsolation(selfDecl, preconcurrency);
35623571
}
35633572

3564-
static ClosureActorIsolation forGlobalActor(Type globalActorType) {
3565-
return ClosureActorIsolation(globalActorType);
3573+
static ClosureActorIsolation forGlobalActor(Type globalActorType,
3574+
bool preconcurrency) {
3575+
return ClosureActorIsolation(globalActorType, preconcurrency);
35663576
}
35673577

35683578
/// Determine the kind of isolation.
35693579
Kind getKind() const {
3570-
if (storage.isNull())
3580+
if (storage.getPointer().isNull())
35713581
return Kind::Independent;
35723582

3573-
if (storage.is<VarDecl *>())
3583+
if (storage.getPointer().is<VarDecl *>())
35743584
return Kind::ActorInstance;
35753585

35763586
return Kind::GlobalActor;
@@ -3587,11 +3597,15 @@ class ClosureActorIsolation {
35873597
}
35883598

35893599
VarDecl *getActorInstance() const {
3590-
return storage.dyn_cast<VarDecl *>();
3600+
return storage.getPointer().dyn_cast<VarDecl *>();
35913601
}
35923602

35933603
Type getGlobalActor() const {
3594-
return storage.dyn_cast<Type>();
3604+
return storage.getPointer().dyn_cast<Type>();
3605+
}
3606+
3607+
bool preconcurrency() const {
3608+
return storage.getInt();
35953609
}
35963610
};
35973611

@@ -3864,6 +3878,16 @@ class ClosureExpr : public AbstractClosureExpr {
38643878
Bits.ClosureExpr.InheritActorContext = value;
38653879
}
38663880

3881+
/// Whether the closure's concurrency behavior was determined by an
3882+
/// \c \@preconcurrency declaration.
3883+
bool isIsolatedByPreconcurrency() const {
3884+
return Bits.ClosureExpr.IsolatedByPreconcurrency;
3885+
}
3886+
3887+
void setIsolatedByPreconcurrency(bool value = true) {
3888+
Bits.ClosureExpr.IsolatedByPreconcurrency = value;
3889+
}
3890+
38673891
/// Determine whether this closure expression has an
38683892
/// explicitly-specified result type.
38693893
bool hasExplicitResultType() const { return ArrowLoc.isValid(); }

lib/AST/Decl.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9007,11 +9007,13 @@ ActorIsolation swift::getActorIsolationOfContext(DeclContext *dc) {
90079007
if (auto *closure = dyn_cast<AbstractClosureExpr>(dc)) {
90089008
switch (auto isolation = closure->getActorIsolation()) {
90099009
case ClosureActorIsolation::Independent:
9010-
return ActorIsolation::forIndependent();
9010+
return ActorIsolation::forIndependent()
9011+
.withPreconcurrency(isolation.preconcurrency());
90119012

90129013
case ClosureActorIsolation::GlobalActor: {
90139014
return ActorIsolation::forGlobalActor(
9014-
isolation.getGlobalActor(), /*unsafe=*/false);
9015+
isolation.getGlobalActor(), /*unsafe=*/false)
9016+
.withPreconcurrency(isolation.preconcurrency());
90159017
}
90169018

90179019
case ClosureActorIsolation::ActorInstance: {
@@ -9020,7 +9022,8 @@ ActorIsolation swift::getActorIsolationOfContext(DeclContext *dc) {
90209022
->getClassOrBoundGenericClass();
90219023
// FIXME: Doesn't work properly with generics
90229024
assert(actorClass && "Bad closure actor isolation?");
9023-
return ActorIsolation::forActorInstance(actorClass);
9025+
return ActorIsolation::forActorInstance(actorClass)
9026+
.withPreconcurrency(isolation.preconcurrency());
90249027
}
90259028
}
90269029
}

lib/AST/TypeCheckRequests.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1551,7 +1551,8 @@ ActorIsolation ActorIsolation::subst(SubstitutionMap subs) const {
15511551
case GlobalActor:
15521552
case GlobalActorUnsafe:
15531553
return forGlobalActor(
1554-
getGlobalActor().subst(subs), kind == GlobalActorUnsafe);
1554+
getGlobalActor().subst(subs), kind == GlobalActorUnsafe)
1555+
.withPreconcurrency(preconcurrency());
15551556
}
15561557
llvm_unreachable("unhandled actor isolation kind!");
15571558
}

lib/Frontend/DiagnosticVerifier.cpp

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ struct ExpectedDiagnosticInfo {
7272
// This specifies the full range of the "expected-foo {{}}" specifier.
7373
const char *ExpectedStart, *ExpectedEnd = nullptr;
7474

75+
// This specifies the full range of the classification string.
76+
const char *ClassificationStart, *ClassificationEnd = nullptr;
77+
7578
DiagnosticKind Classification;
7679

7780
// This is true if a '*' constraint is present to say that the diagnostic
@@ -108,8 +111,11 @@ struct ExpectedDiagnosticInfo {
108111
Optional<ExpectedEducationalNotes> EducationalNotes;
109112

110113
ExpectedDiagnosticInfo(const char *ExpectedStart,
114+
const char *ClassificationStart,
115+
const char *ClassificationEnd,
111116
DiagnosticKind Classification)
112-
: ExpectedStart(ExpectedStart), Classification(Classification) {}
117+
: ExpectedStart(ExpectedStart), ClassificationStart(ClassificationStart),
118+
ClassificationEnd(ClassificationEnd), Classification(Classification) {}
113119
};
114120

115121
static std::string getDiagKindString(DiagnosticKind Kind) {
@@ -139,11 +145,15 @@ renderEducationalNotes(llvm::SmallVectorImpl<std::string> &EducationalNotes) {
139145
return OS.str();
140146
}
141147

142-
/// If we find the specified diagnostic in the list, return it.
143-
/// Otherwise return CapturedDiagnostics.end().
144-
static std::vector<CapturedDiagnosticInfo>::iterator
148+
/// If we find the specified diagnostic in the list, return it with \c true .
149+
/// If we find a near-match that varies only in classification, return it with
150+
/// \c false.
151+
/// Otherwise return \c CapturedDiagnostics.end() with \c false.
152+
static std::tuple<std::vector<CapturedDiagnosticInfo>::iterator, bool>
145153
findDiagnostic(std::vector<CapturedDiagnosticInfo> &CapturedDiagnostics,
146154
const ExpectedDiagnosticInfo &Expected, StringRef BufferName) {
155+
auto fallbackI = CapturedDiagnostics.end();
156+
147157
for (auto I = CapturedDiagnostics.begin(), E = CapturedDiagnostics.end();
148158
I != E; ++I) {
149159
// Verify the file and line of the diagnostic.
@@ -155,15 +165,22 @@ findDiagnostic(std::vector<CapturedDiagnosticInfo> &CapturedDiagnostics,
155165
continue;
156166

157167
// Verify the classification and string.
158-
if (I->Classification != Expected.Classification ||
159-
I->Message.find(Expected.MessageStr) == StringRef::npos)
168+
if (I->Message.find(Expected.MessageStr) == StringRef::npos)
160169
continue;
161170

171+
// Verify the classification and, if incorrect, remember as a second choice.
172+
if (I->Classification != Expected.Classification) {
173+
if (fallbackI == E && !Expected.MessageStr.empty())
174+
fallbackI = I;
175+
continue;
176+
}
177+
162178
// Okay, we found a match, hurray!
163-
return I;
179+
return { I, true };
164180
}
165181

166-
return CapturedDiagnostics.end();
182+
// No perfect match; we'll return the fallback or `end()` instead.
183+
return { fallbackI, false };
167184
}
168185

169186
/// If there are any -verify errors (e.g. differences between expectations
@@ -481,20 +498,22 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
481498
// the next match.
482499
StringRef MatchStart = InputFile.substr(Match);
483500
const char *DiagnosticLoc = MatchStart.data();
501+
MatchStart = MatchStart.substr(strlen("expected-"));
502+
const char *ClassificationStartLoc = MatchStart.data();
484503

485504
DiagnosticKind ExpectedClassification;
486-
if (MatchStart.startswith("expected-note")) {
505+
if (MatchStart.startswith("note")) {
487506
ExpectedClassification = DiagnosticKind::Note;
488-
MatchStart = MatchStart.substr(strlen("expected-note"));
489-
} else if (MatchStart.startswith("expected-warning")) {
507+
MatchStart = MatchStart.substr(strlen("note"));
508+
} else if (MatchStart.startswith("warning")) {
490509
ExpectedClassification = DiagnosticKind::Warning;
491-
MatchStart = MatchStart.substr(strlen("expected-warning"));
492-
} else if (MatchStart.startswith("expected-error")) {
510+
MatchStart = MatchStart.substr(strlen("warning"));
511+
} else if (MatchStart.startswith("error")) {
493512
ExpectedClassification = DiagnosticKind::Error;
494-
MatchStart = MatchStart.substr(strlen("expected-error"));
495-
} else if (MatchStart.startswith("expected-remark")) {
513+
MatchStart = MatchStart.substr(strlen("error"));
514+
} else if (MatchStart.startswith("remark")) {
496515
ExpectedClassification = DiagnosticKind::Remark;
497-
MatchStart = MatchStart.substr(strlen("expected-remark"));
516+
MatchStart = MatchStart.substr(strlen("remark"));
498517
} else
499518
continue;
500519

@@ -508,7 +527,9 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
508527
continue;
509528
}
510529

511-
ExpectedDiagnosticInfo Expected(DiagnosticLoc, ExpectedClassification);
530+
ExpectedDiagnosticInfo Expected(DiagnosticLoc, ClassificationStartLoc,
531+
/*ClassificationEndLoc=*/MatchStart.data(),
532+
ExpectedClassification);
512533
int LineOffset = 0;
513534

514535
if (TextStartIdx > 0 && MatchStart[0] == '@') {
@@ -750,18 +771,40 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
750771
auto &expected = ExpectedDiagnostics[i];
751772

752773
// Check to see if we had this expected diagnostic.
753-
auto FoundDiagnosticIter =
774+
auto FoundDiagnosticInfo =
754775
findDiagnostic(CapturedDiagnostics, expected, BufferName);
776+
auto FoundDiagnosticIter = std::get<0>(FoundDiagnosticInfo);
755777
if (FoundDiagnosticIter == CapturedDiagnostics.end()) {
756778
// Diagnostic didn't exist. If this is a 'mayAppear' diagnostic, then
757779
// we're ok. Otherwise, leave it in the list.
758780
if (expected.mayAppear)
759781
ExpectedDiagnostics.erase(ExpectedDiagnostics.begin()+i);
760782
continue;
761783
}
762-
784+
785+
auto emitFixItsError = [&](const char *location, const Twine &message,
786+
const char *replStartLoc, const char *replEndLoc,
787+
const std::string &replStr) {
788+
llvm::SMFixIt fix(llvm::SMRange(SMLoc::getFromPointer(replStartLoc),
789+
SMLoc::getFromPointer(replEndLoc)),
790+
replStr);
791+
addError(location, message, fix);
792+
};
793+
763794
auto &FoundDiagnostic = *FoundDiagnosticIter;
764795

796+
if (!std::get<1>(FoundDiagnosticInfo)) {
797+
// Found a diagnostic with the right location and text but the wrong
798+
// classification. We'll emit an error about the mismatch and
799+
// thereafter pretend that the diagnostic fully matched.
800+
auto expectedKind = getDiagKindString(expected.Classification);
801+
auto actualKind = getDiagKindString(FoundDiagnostic.Classification);
802+
emitFixItsError(expected.ClassificationStart,
803+
llvm::Twine("expected ") + expectedKind + ", not " + actualKind,
804+
expected.ClassificationStart, expected.ClassificationEnd,
805+
actualKind);
806+
}
807+
765808
const char *missedFixitLoc = nullptr;
766809
// Verify that any expected fix-its are present in the diagnostic.
767810
for (auto fixit : expected.Fixits) {
@@ -791,15 +834,6 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
791834
actualFixitsStr};
792835
};
793836

794-
auto emitFixItsError = [&](const char *location, const Twine &message,
795-
const char *replStartLoc, const char *replEndLoc,
796-
const std::string &replStr) {
797-
llvm::SMFixIt fix(llvm::SMRange(SMLoc::getFromPointer(replStartLoc),
798-
SMLoc::getFromPointer(replEndLoc)),
799-
replStr);
800-
addError(location, message, fix);
801-
};
802-
803837
// If we have any expected fixits that didn't get matched, then they are
804838
// wrong. Replace the failed fixit with what actually happened.
805839

0 commit comments

Comments
 (0)