Skip to content

Commit b3763ee

Browse files
committed
Encapsulate each kind of "unsafe use" in a value we can safe to replay
For now, just use this as an intermediate state to provide a single place to render diagnostics.
1 parent 6e1cd3a commit b3763ee

10 files changed

+519
-99
lines changed

include/swift/AST/UnsafeUse.h

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
//===--- UnsafeUse.h - A use of an unsafe construct -------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_AST_UNSAFEUSE_H
14+
#define SWIFT_AST_UNSAFEUSE_H
15+
16+
#include "swift/AST/Decl.h"
17+
#include "swift/AST/ProtocolConformance.h"
18+
#include "swift/AST/Type.h"
19+
#include "swift/Basic/SourceLoc.h"
20+
#include "swift/Basic/Unreachable.h"
21+
22+
namespace swift {
23+
24+
class NormalProtocolConformance;
25+
26+
/// Describes a use of an unsafe construct.
27+
///
28+
/// Every use of an unsafe construct that should be diagnosed will be captured
29+
/// in an instance of this type.
30+
class UnsafeUse {
31+
public:
32+
enum Kind {
33+
/// An unsafe declaration overriding a safe one.
34+
Override,
35+
/// An unsafe declaration witnessing a safe one.
36+
Witness,
37+
/// An unsafe type witnesses a safe associated type.
38+
TypeWitness,
39+
/// An @unsafe or @unchecked conformance (e.g., Sendable).
40+
UnsafeConformance,
41+
/// A reference to an unowned(unsafe) entity.
42+
UnownedUnsafe,
43+
/// A reference to a nonisolated(unsafe) entity.
44+
NonisolatedUnsafe,
45+
/// A reference to an unsafe declaration.
46+
ReferenceToUnsafe,
47+
/// A call to an unsafe declaration.
48+
CallToUnsafe,
49+
};
50+
51+
private:
52+
Kind kind;
53+
54+
union {
55+
struct {
56+
const Decl *decl;
57+
const Decl *original;
58+
NormalProtocolConformance *conformance;
59+
} polymorphic;
60+
61+
struct {
62+
const AssociatedTypeDecl *assocType;
63+
TypeBase *type;
64+
NormalProtocolConformance *conformance;
65+
const void *location;
66+
} typeWitness;
67+
68+
struct {
69+
NormalProtocolConformance *conformance;
70+
const void *location;
71+
} conformance;
72+
73+
struct {
74+
const Decl *decl;
75+
DeclContext *declContext;
76+
TypeBase *type;
77+
const void *location;
78+
} entity;
79+
} storage;
80+
81+
UnsafeUse(Kind kind) : kind(kind) { }
82+
83+
static UnsafeUse forPolymorphic(
84+
Kind kind,
85+
const Decl *decl,
86+
const Decl *original,
87+
NormalProtocolConformance *conformance
88+
) {
89+
assert(kind == Override || kind == Witness);
90+
91+
UnsafeUse result(kind);
92+
result.storage.polymorphic.decl = decl;
93+
result.storage.polymorphic.original = original;
94+
result.storage.polymorphic.conformance = conformance;
95+
return result;
96+
}
97+
98+
static UnsafeUse forReference(
99+
Kind kind,
100+
DeclContext *dc,
101+
const Decl *decl,
102+
Type type,
103+
SourceLoc location
104+
) {
105+
assert(kind == UnownedUnsafe ||
106+
kind == NonisolatedUnsafe ||
107+
kind == ReferenceToUnsafe ||
108+
kind == CallToUnsafe);
109+
110+
UnsafeUse result(kind);
111+
result.storage.entity.decl = decl;
112+
result.storage.entity.declContext = dc;
113+
result.storage.entity.type = type.getPointer();
114+
result.storage.entity.location = location.getOpaquePointerValue();
115+
return result;
116+
117+
}
118+
119+
public:
120+
static UnsafeUse forOverride(const Decl *decl, const Decl *overridden) {
121+
return forPolymorphic(Override, decl, overridden, nullptr);
122+
}
123+
124+
static UnsafeUse forWitness(const Decl *witness, const Decl *requirement,
125+
NormalProtocolConformance *conformance) {
126+
return forPolymorphic(Witness, witness, requirement, conformance);
127+
}
128+
129+
static UnsafeUse forTypeWitness(const AssociatedTypeDecl *assocType,
130+
Type typeWitness,
131+
NormalProtocolConformance *conformance,
132+
SourceLoc location) {
133+
UnsafeUse result(TypeWitness);
134+
result.storage.typeWitness.assocType = assocType;
135+
result.storage.typeWitness.type = typeWitness.getPointer();
136+
result.storage.typeWitness.conformance = conformance;
137+
result.storage.typeWitness.location = location.getOpaquePointerValue();
138+
return result;
139+
}
140+
141+
static UnsafeUse forConformance(NormalProtocolConformance *conformance,
142+
SourceLoc location) {
143+
UnsafeUse result(UnsafeConformance);
144+
result.storage.conformance.conformance = conformance;
145+
result.storage.conformance.location = location.getOpaquePointerValue();
146+
return result;
147+
}
148+
149+
static UnsafeUse forUnownedUnsafe(const Decl *decl,
150+
SourceLoc location,
151+
DeclContext *dc) {
152+
return forReference(UnownedUnsafe, dc, decl, Type(), location);
153+
}
154+
155+
static UnsafeUse forNonisolatedUnsafe(const Decl *decl,
156+
SourceLoc location,
157+
DeclContext *dc) {
158+
return forReference(NonisolatedUnsafe, dc, decl, Type(), location);
159+
}
160+
161+
static UnsafeUse forReferenceToUnsafe(const Decl *decl,
162+
bool isCall,
163+
DeclContext *dc,
164+
Type type,
165+
SourceLoc location) {
166+
return forReference(isCall ? CallToUnsafe: ReferenceToUnsafe, dc,
167+
decl, type, location);
168+
}
169+
170+
Kind getKind() const { return kind; }
171+
172+
/// The location at which this use will be diagnosed.
173+
SourceLoc getLocation() const {
174+
switch (getKind()) {
175+
case Override:
176+
case Witness:
177+
return getDecl()->getLoc();
178+
179+
case UnsafeConformance:
180+
return getConformance()->getLoc();
181+
182+
case TypeWitness:
183+
return SourceLoc(
184+
llvm::SMLoc::getFromPointer(
185+
(const char *)storage.typeWitness.location));
186+
187+
case UnownedUnsafe:
188+
case NonisolatedUnsafe:
189+
case ReferenceToUnsafe:
190+
case CallToUnsafe:
191+
return SourceLoc(
192+
llvm::SMLoc::getFromPointer((const char *)storage.entity.location));
193+
}
194+
}
195+
196+
/// Get the main declaration, when there is one.
197+
const Decl *getDecl() const {
198+
switch (getKind()) {
199+
case Override:
200+
case Witness:
201+
return storage.polymorphic.decl;
202+
203+
case TypeWitness:
204+
return storage.typeWitness.assocType;
205+
206+
case UnownedUnsafe:
207+
case NonisolatedUnsafe:
208+
case ReferenceToUnsafe:
209+
case CallToUnsafe:
210+
return storage.entity.decl;
211+
212+
case UnsafeConformance:
213+
return nullptr;
214+
}
215+
}
216+
217+
/// Get the associated type declaration for a type witness.
218+
const AssociatedTypeDecl *getAssociatedType() const {
219+
assert(getKind() == TypeWitness);
220+
return storage.typeWitness.assocType;
221+
}
222+
223+
/// Retrieve the declaration context in which the reference occurs.
224+
DeclContext *getDeclContext() const {
225+
switch (getKind()) {
226+
case UnownedUnsafe:
227+
case NonisolatedUnsafe:
228+
case ReferenceToUnsafe:
229+
case CallToUnsafe:
230+
return storage.entity.declContext;
231+
232+
case Override:
233+
case Witness:
234+
case TypeWitness:
235+
case UnsafeConformance:
236+
return nullptr;
237+
}
238+
}
239+
240+
/// Get the original declaration for an issue with a polymorphic
241+
/// implementation, e.g., an overridden declaration or a protocol
242+
/// requirement.
243+
const Decl *getOriginalDecl() const {
244+
switch (getKind()) {
245+
case Override:
246+
case Witness:
247+
return storage.polymorphic.original;
248+
249+
case TypeWitness:
250+
case UnownedUnsafe:
251+
case NonisolatedUnsafe:
252+
case ReferenceToUnsafe:
253+
case CallToUnsafe:
254+
case UnsafeConformance:
255+
return nullptr;
256+
}
257+
}
258+
259+
/// Get the type of the reference, if there is one.
260+
Type getType() const {
261+
switch (getKind()) {
262+
case Override:
263+
case Witness:
264+
case UnsafeConformance:
265+
return nullptr;
266+
267+
case TypeWitness:
268+
return storage.typeWitness.type;
269+
270+
case UnownedUnsafe:
271+
case NonisolatedUnsafe:
272+
case ReferenceToUnsafe:
273+
case CallToUnsafe:
274+
return storage.entity.type;
275+
}
276+
}
277+
278+
/// Get the protocol conformance, if there is one.
279+
NormalProtocolConformance *getConformance() const {
280+
switch (getKind()) {
281+
case UnsafeConformance:
282+
return storage.conformance.conformance;
283+
284+
case Witness:
285+
return storage.polymorphic.conformance;
286+
287+
case TypeWitness:
288+
return storage.typeWitness.conformance;
289+
290+
case Override:
291+
case UnownedUnsafe:
292+
case NonisolatedUnsafe:
293+
case ReferenceToUnsafe:
294+
case CallToUnsafe:
295+
return nullptr;
296+
}
297+
}
298+
};
299+
300+
} // end namespace swift
301+
302+
#endif // SWIFT_AST_UNSAFEUSE_H

lib/Sema/AssociatedTypeInference.cpp

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "DerivedConformances.h"
3535
#include "TypeAccessScopeChecker.h"
3636
#include "TypeChecker.h"
37+
#include "TypeCheckAvailability.h"
3738
#include "TypeCheckType.h"
3839

3940
#include "swift/AST/ConformanceLookup.h"
@@ -47,6 +48,7 @@
4748
#include "swift/AST/TypeMatcher.h"
4849
#include "swift/AST/Types.h"
4950
#include "swift/AST/TypeCheckRequests.h"
51+
#include "swift/AST/UnsafeUse.h"
5052
#include "swift/Basic/Assertions.h"
5153
#include "swift/Basic/Defer.h"
5254
#include "swift/Basic/Statistic.h"
@@ -363,16 +365,8 @@ static void recordTypeWitness(NormalProtocolConformance *conformance,
363365
SourceLoc loc = typeDecl->getLoc();
364366
if (loc.isInvalid())
365367
loc = conformance->getLoc();
366-
diagnoseUnsafeType(ctx,
367-
loc,
368-
type,
369-
conformance->getDeclContext(),
370-
[&](Type specificType) {
371-
ctx.Diags.diagnose(
372-
loc, diag::type_witness_unsafe, specificType, assocType->getName());
373-
suggestUnsafeMarkerOnConformance(conformance);
374-
assocType->diagnose(diag::decl_declared_here, assocType);
375-
});
368+
diagnoseUnsafeUse(
369+
UnsafeUse::forTypeWitness(assocType, type, conformance, loc));
376370
}
377371

378372
// Record the type witness.

lib/Sema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ add_swift_host_library(swiftSema STATIC
7878
TypeCheckStorage.cpp
7979
TypeCheckSwitchStmt.cpp
8080
TypeCheckType.cpp
81+
TypeCheckUnsafe.cpp
8182
TypeChecker.cpp
8283
IDETypeCheckingRequests.cpp)
8384
if(SWIFT_FORCE_OPTIMIZED_TYPECHECKER)

lib/Sema/TypeCheckAttr.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "swift/AST/SwiftNameTranslation.h"
4343
#include "swift/AST/TypeCheckRequests.h"
4444
#include "swift/AST/Types.h"
45+
#include "swift/AST/UnsafeUse.h"
4546
#include "swift/Basic/Assertions.h"
4647
#include "swift/Parse/Lexer.h"
4748
#include "swift/Parse/ParseDeclName.h"
@@ -4981,9 +4982,9 @@ Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type,
49814982
if (ownershipKind == ReferenceOwnership::Unmanaged &&
49824983
ctx.LangOpts.hasFeature(Feature::WarnUnsafe) &&
49834984
!var->allowsUnsafe()) {
4984-
Diags.diagnose(attr->getLocation(), diag::unowned_unsafe_is_unsafe);
4985-
var->diagnose(diag::make_enclosing_context_unsafe, var)
4986-
.fixItInsert(var->getAttributeInsertionLoc(false), "@unsafe ");
4985+
diagnoseUnsafeUse(
4986+
UnsafeUse::forUnownedUnsafe(var, attr->getLocation(),
4987+
var->getDeclContext()));
49874988
}
49884989

49894990
if (attr->isInvalid())
@@ -7205,11 +7206,8 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
72057206
Ctx.LangOpts.hasFeature(Feature::WarnUnsafe) &&
72067207
Ctx.LangOpts.StrictConcurrencyLevel == StrictConcurrency::Complete &&
72077208
!D->allowsUnsafe()) {
7208-
Ctx.Diags.diagnose(attr->getLocation(), diag::nonisolated_unsafe_is_unsafe);
7209-
if (auto var = dyn_cast<VarDecl>(D)) {
7210-
var->diagnose(diag::make_enclosing_context_unsafe, var)
7211-
.fixItInsert(var->getAttributeInsertionLoc(false), "@unsafe ");
7212-
}
7209+
diagnoseUnsafeUse(
7210+
UnsafeUse::forNonisolatedUnsafe(D, attr->getLocation(), dc));
72137211
}
72147212

72157213
if (auto var = dyn_cast<VarDecl>(D)) {

0 commit comments

Comments
 (0)