Skip to content

Commit 2c86e32

Browse files
author
Harlan
authored
[InterfaceGen] Print property initializers in resilient, fixed-layout types (#19619)
Augment the ASTPrinter to print the name and text of initializer expressions if a property has an initializer and the type is @_fixed_layout and resides in a resilient module, and serialize the text for partial modules. With this change, all .swiftinterface files in the project (except for SwiftLang) compile to swiftmodules on macOS. rdar://43774580 rdar://43812188
1 parent 690c6c3 commit 2c86e32

File tree

10 files changed

+262
-59
lines changed

10 files changed

+262
-59
lines changed

include/swift/AST/Decl.h

Lines changed: 86 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1864,76 +1864,114 @@ class ExtensionRange {
18641864
/// Pattern and Initialization expression. The pattern is always present, but
18651865
/// the initializer can be null if there is none.
18661866
class PatternBindingEntry {
1867-
Pattern *ThePattern;
1868-
1869-
/// The location of the equal '=' token.
1870-
SourceLoc EqualLoc;
1871-
18721867
enum class Flags {
18731868
Checked = 1 << 0,
18741869
Removed = 1 << 1,
1875-
Lazy = 1 << 2,
1870+
Lazy = 1 << 2
1871+
};
1872+
llvm::PointerIntPair<Pattern *, 3, OptionSet<Flags>> PatternAndFlags;
1873+
1874+
struct ExprAndEqualLoc {
1875+
// When the initializer is removed we don't actually clear the pointer
1876+
// because we might need to get initializer's source range. Since the
1877+
// initializer is ASTContext-allocated it is safe.
1878+
Expr *Node;
1879+
/// The location of the equal '=' token.
1880+
SourceLoc EqualLoc;
18761881
};
18771882

1878-
// When the initializer is removed we don't actually clear the pointer
1879-
// because we might need to get initializer's source range. Since the
1880-
// initializer is ASTContext-allocated it is safe.
1881-
llvm::PointerIntPair<Expr *, 3, OptionSet<Flags>> InitAndFlags;
1883+
union {
1884+
/// The initializer expression and its '=' token loc.
1885+
ExprAndEqualLoc InitExpr;
1886+
1887+
/// The text of the initializer expression if deserialized from a module.
1888+
StringRef InitStringRepresentation;
1889+
};
18821890

18831891
/// The initializer context used for this pattern binding entry.
1884-
DeclContext *InitContext = nullptr;
1892+
llvm::PointerIntPair<DeclContext *, 1, bool> InitContextAndIsText;
18851893

18861894
friend class PatternBindingInitializer;
18871895

18881896
public:
18891897
PatternBindingEntry(Pattern *P, SourceLoc EqualLoc, Expr *E,
18901898
DeclContext *InitContext)
1891-
: ThePattern(P), EqualLoc(EqualLoc), InitAndFlags(E, {}),
1892-
InitContext(InitContext) {}
1899+
: PatternAndFlags(P, {}), InitExpr({E, EqualLoc}),
1900+
InitContextAndIsText({InitContext, false}) {
1901+
}
18931902

1894-
Pattern *getPattern() const { return ThePattern; }
1895-
void setPattern(Pattern *P) { ThePattern = P; }
1903+
Pattern *getPattern() const { return PatternAndFlags.getPointer(); }
1904+
void setPattern(Pattern *P) { PatternAndFlags.setPointer(P); }
18961905
Expr *getInit() const {
1897-
return (InitAndFlags.getInt().contains(Flags::Removed))
1898-
? nullptr : InitAndFlags.getPointer();
1906+
if (PatternAndFlags.getInt().contains(Flags::Removed) ||
1907+
InitContextAndIsText.getInt())
1908+
return nullptr;
1909+
return InitExpr.Node;
18991910
}
19001911
Expr *getNonLazyInit() const {
19011912
return isInitializerLazy() ? nullptr : getInit();
19021913
}
19031914
SourceRange getOrigInitRange() const;
19041915
void setInit(Expr *E);
19051916

1917+
/// Gets the text of the initializer expression, stripping out inactive
1918+
/// branches of any #ifs inside the expression.
1919+
StringRef getInitStringRepresentation(SmallVectorImpl<char> &scratch) const;
1920+
1921+
/// Sets the initializer string representation to the string that was
1922+
/// deserialized from a partial module.
1923+
void setInitStringRepresentation(StringRef str) {
1924+
InitStringRepresentation = str;
1925+
InitContextAndIsText.setInt(true);
1926+
}
1927+
1928+
/// Whether this pattern entry can generate a string representation of its
1929+
/// initializer expression.
1930+
bool hasInitStringRepresentation() const;
1931+
19061932
/// Retrieve the location of the equal '=' token.
1907-
SourceLoc getEqualLoc() const { return EqualLoc; }
1933+
SourceLoc getEqualLoc() const {
1934+
return InitContextAndIsText.getInt() ? SourceLoc() : InitExpr.EqualLoc;
1935+
}
19081936

19091937
/// Set the location of the equal '=' token.
1910-
void setEqualLoc(SourceLoc equalLoc) { EqualLoc = equalLoc; }
1938+
void setEqualLoc(SourceLoc equalLoc) {
1939+
assert(!InitContextAndIsText.getInt() &&
1940+
"cannot set equal loc for textual initializer");
1941+
InitExpr.EqualLoc = equalLoc;
1942+
}
19111943

19121944
/// Retrieve the initializer as it was written in the source.
1913-
Expr *getInitAsWritten() const { return InitAndFlags.getPointer(); }
1945+
Expr *getInitAsWritten() const {
1946+
return InitContextAndIsText.getInt() ? nullptr : InitExpr.Node;
1947+
}
19141948

19151949
bool isInitializerChecked() const {
1916-
return InitAndFlags.getInt().contains(Flags::Checked);
1950+
return PatternAndFlags.getInt().contains(Flags::Checked);
19171951
}
19181952
void setInitializerChecked() {
1919-
InitAndFlags.setInt(InitAndFlags.getInt() | Flags::Checked);
1953+
PatternAndFlags.setInt(PatternAndFlags.getInt() | Flags::Checked);
19201954
}
19211955

19221956
bool isInitializerLazy() const {
1923-
return InitAndFlags.getInt().contains(Flags::Lazy);
1957+
return PatternAndFlags.getInt().contains(Flags::Lazy);
19241958
}
19251959
void setInitializerLazy() {
1926-
InitAndFlags.setInt(InitAndFlags.getInt() | Flags::Lazy);
1960+
PatternAndFlags.setInt(PatternAndFlags.getInt() | Flags::Lazy);
19271961
}
19281962

19291963
// Return the first variable initialized by this pattern.
19301964
VarDecl *getAnchoringVarDecl() const;
19311965

19321966
// Retrieve the declaration context for the initializer.
1933-
DeclContext *getInitContext() const { return InitContext; }
1967+
DeclContext *getInitContext() const {
1968+
return InitContextAndIsText.getPointer();
1969+
}
19341970

19351971
/// Override the initializer context.
1936-
void setInitContext(DeclContext *dc) { InitContext = dc; }
1972+
void setInitContext(DeclContext *dc) {
1973+
InitContextAndIsText.setPointer(dc);
1974+
}
19371975

19381976
/// Retrieve the source range covered by this pattern binding.
19391977
///
@@ -2007,6 +2045,10 @@ class PatternBindingDecl final : public Decl,
20072045
return const_cast<PatternBindingDecl*>(this)->getMutablePatternList();
20082046
}
20092047

2048+
void setInitStringRepresentation(unsigned i, StringRef str) {
2049+
getMutablePatternList()[i].setInitStringRepresentation(str);
2050+
}
2051+
20102052
Expr *getInit(unsigned i) const {
20112053
return getPatternList()[i].getInit();
20122054
}
@@ -2039,7 +2081,8 @@ class PatternBindingDecl final : public Decl,
20392081

20402082
/// Return the PatternEntry (a pattern + initializer pair) for the specified
20412083
/// VarDecl.
2042-
PatternBindingEntry getPatternEntryForVarDecl(const VarDecl *VD) const {
2084+
const PatternBindingEntry &getPatternEntryForVarDecl(
2085+
const VarDecl *VD) const {
20432086
return getPatternList()[getPatternEntryIndexForVarDecl(VD)];
20442087
}
20452088

@@ -2097,7 +2140,7 @@ class PatternBindingDecl final : public Decl,
20972140
static bool classof(const Decl *D) {
20982141
return D->getKind() == DeclKind::PatternBinding;
20992142
}
2100-
2143+
21012144
private:
21022145
MutableArrayRef<PatternBindingEntry> getMutablePatternList() {
21032146
// Pattern entries are tail allocated.
@@ -4606,6 +4649,14 @@ class VarDecl : public AbstractStorageDecl {
46064649
return nullptr;
46074650
}
46084651

4652+
// Return whether this VarDecl has an initial value, either by checking
4653+
// if it has an initializer in its parent pattern binding or if it has
4654+
// the @_hasInitialValue attribute.
4655+
bool hasInitialValue() const {
4656+
return getAttrs().hasAttribute<HasInitialValueAttr>() ||
4657+
getParentInitializer();
4658+
}
4659+
46094660
VarDecl *getOverriddenDecl() const {
46104661
return cast_or_null<VarDecl>(AbstractStorageDecl::getOverriddenDecl());
46114662
}
@@ -4687,6 +4738,13 @@ class VarDecl : public AbstractStorageDecl {
46874738
void setHasNonPatternBindingInit(bool V = true) {
46884739
Bits.VarDecl.HasNonPatternBindingInit = V;
46894740
}
4741+
4742+
/// Determines if this var has an initializer expression that should be
4743+
/// exposed to clients.
4744+
/// There's a very narrow case when we would: if the decl is an instance
4745+
/// member with an initializer expression and the parent type is
4746+
/// @_fixed_layout and resides in a resilient module.
4747+
bool isInitExposedToClients() const;
46904748

46914749
/// Is this a special debugger variable?
46924750
bool isDebuggerVar() const { return Bits.VarDecl.IsDebuggerVar; }

include/swift/Serialization/ModuleFile.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,9 @@ class ModuleFile
887887

888888
/// Reads inlinable body text from \c DeclTypeCursor, if present.
889889
Optional<StringRef> maybeReadInlinableBodyText();
890+
891+
/// Reads pattern initializer text from \c DeclTypeCursor, if present.
892+
Optional<StringRef> maybeReadPatternInitializerText();
890893
};
891894

892895
template <typename T, typename RawData>

include/swift/Serialization/ModuleFormat.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const uint16_t VERSION_MAJOR = 0;
5555
/// describe what change you made. The content of this comment isn't important;
5656
/// it just ensures a conflict if two people change the module format.
5757
/// Don't worry about adhering to the 80-column limit for this line.
58-
const uint16_t VERSION_MINOR = 450; // Last change: don't serialize requirement environment
58+
const uint16_t VERSION_MINOR = 451; // Last change: pattern initializer text
5959

6060
using DeclIDField = BCFixed<31>;
6161

@@ -1478,7 +1478,8 @@ namespace decls_block {
14781478
using PatternBindingInitializerLayout = BCRecordLayout<
14791479
PATTERN_BINDING_INITIALIZER_CONTEXT,
14801480
DeclIDField, // parent pattern binding decl
1481-
BCVBR<3> // binding index in the pattern binding decl
1481+
BCVBR<3>, // binding index in the pattern binding decl
1482+
BCBlob // initializer text, if present
14821483
>;
14831484

14841485
using DefaultArgumentInitializerLayout = BCRecordLayout<

lib/AST/ASTPrinter.cpp

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -842,26 +842,35 @@ void PrintAST::printAttributes(const Decl *D) {
842842
if (Options.SkipAttributes)
843843
return;
844844

845-
// Don't print a redundant 'final' if we are printing a 'static' decl.
845+
// Save the current number of exclude attrs to restore once we're done.
846846
unsigned originalExcludeAttrCount = Options.ExcludeAttrList.size();
847-
if (Options.PrintImplicitAttrs &&
848-
D->getDeclContext()->getSelfClassDecl() &&
849-
getCorrectStaticSpelling(D) == StaticSpellingKind::KeywordStatic) {
850-
Options.ExcludeAttrList.push_back(DAK_Final);
851-
}
852847

853-
// Don't print any contextual decl modifiers.
854-
// We will handle 'mutating' and 'nonmutating' separately.
855-
if (Options.PrintImplicitAttrs && isa<AccessorDecl>(D)) {
848+
if (Options.PrintImplicitAttrs) {
849+
850+
// Don't print a redundant 'final' if we are printing a 'static' decl.
851+
if (D->getDeclContext()->getSelfClassDecl() &&
852+
getCorrectStaticSpelling(D) == StaticSpellingKind::KeywordStatic) {
853+
Options.ExcludeAttrList.push_back(DAK_Final);
854+
}
855+
856+
// Don't print @_hasInitialValue if we're printing an initializer
857+
// expression.
858+
if (auto vd = dyn_cast<VarDecl>(D)) {
859+
if (vd->isInitExposedToClients())
860+
Options.ExcludeAttrList.push_back(DAK_HasInitialValue);
861+
}
862+
863+
// Don't print any contextual decl modifiers.
864+
// We will handle 'mutating' and 'nonmutating' separately.
865+
if (isa<AccessorDecl>(D)) {
856866
#define EXCLUDE_ATTR(Class) Options.ExcludeAttrList.push_back(DAK_##Class);
857867
#define CONTEXTUAL_DECL_ATTR(X, Class, Y, Z) EXCLUDE_ATTR(Class)
858868
#define CONTEXTUAL_SIMPLE_DECL_ATTR(X, Class, Y, Z) EXCLUDE_ATTR(Class)
859869
#define CONTEXTUAL_DECL_ATTR_ALIAS(X, Class) EXCLUDE_ATTR(Class)
860870
#include "swift/AST/Attr.def"
861-
}
871+
}
862872

863-
// If the declaration is implicitly @objc, print the attribute now.
864-
if (Options.PrintImplicitAttrs) {
873+
// If the declaration is implicitly @objc, print the attribute now.
865874
if (auto VD = dyn_cast<ValueDecl>(D)) {
866875
if (VD->isObjC() && !VD->getAttrs().hasAttribute<ObjCAttr>()) {
867876
Printer.printAttrName("@objc");
@@ -899,7 +908,10 @@ void PrintAST::printPattern(const Pattern *pattern) {
899908
recordDeclLoc(decl, [&]{
900909
if (Options.OmitNameOfInaccessibleProperties &&
901910
contributesToParentTypeStorage(decl) &&
902-
!isPublicOrUsableFromInline(decl))
911+
!isPublicOrUsableFromInline(decl) &&
912+
// FIXME: We need to figure out a way to generate an entry point
913+
// for the initializer expression without revealing the name.
914+
!decl->hasInitialValue())
903915
Printer << "_";
904916
else
905917
Printer.printName(named->getBoundName());
@@ -2045,7 +2057,7 @@ void PrintAST::visitPatternBindingDecl(PatternBindingDecl *decl) {
20452057
}
20462058

20472059
bool isFirst = true;
2048-
for (auto entry : decl->getPatternList()) {
2060+
for (auto &entry : decl->getPatternList()) {
20492061
if (!shouldPrintPattern(entry.getPattern()))
20502062
continue;
20512063
if (isFirst)
@@ -2065,6 +2077,13 @@ void PrintAST::visitPatternBindingDecl(PatternBindingDecl *decl) {
20652077
if (Options.VarInitializers) {
20662078
// FIXME: Implement once we can pretty-print expressions.
20672079
}
2080+
2081+
auto vd = entry.getAnchoringVarDecl();
2082+
if (entry.hasInitStringRepresentation() &&
2083+
vd->isInitExposedToClients()) {
2084+
SmallString<128> scratch;
2085+
Printer << " = " << entry.getInitStringRepresentation(scratch);
2086+
}
20682087
}
20692088
}
20702089

lib/AST/Decl.cpp

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,18 +1181,19 @@ unsigned PatternBindingDecl::getPatternEntryIndexForVarDecl(const VarDecl *VD) c
11811181
}
11821182

11831183
SourceRange PatternBindingEntry::getOrigInitRange() const {
1184-
auto Init = InitAndFlags.getPointer();
1184+
auto Init = getInitAsWritten();
11851185
return Init ? Init->getSourceRange() : SourceRange();
11861186
}
11871187

11881188
void PatternBindingEntry::setInit(Expr *E) {
1189-
auto F = InitAndFlags.getInt();
1189+
auto F = PatternAndFlags.getInt();
11901190
if (E) {
1191-
InitAndFlags.setInt(F - Flags::Removed);
1192-
InitAndFlags.setPointer(E);
1191+
PatternAndFlags.setInt(F - Flags::Removed);
11931192
} else {
1194-
InitAndFlags.setInt(F | Flags::Removed);
1193+
PatternAndFlags.setInt(F | Flags::Removed);
11951194
}
1195+
InitExpr.Node = E;
1196+
InitContextAndIsText.setInt(false);
11961197
}
11971198

11981199
VarDecl *PatternBindingEntry::getAnchoringVarDecl() const {
@@ -1225,6 +1226,25 @@ SourceRange PatternBindingEntry::getSourceRange(bool omitAccessors) const {
12251226
return SourceRange(startLoc, endLoc);
12261227
}
12271228

1229+
bool PatternBindingEntry::hasInitStringRepresentation() const {
1230+
if (InitContextAndIsText.getInt())
1231+
return !InitStringRepresentation.empty();
1232+
return getInit() && getInit()->getSourceRange().isValid();
1233+
}
1234+
1235+
StringRef PatternBindingEntry::getInitStringRepresentation(
1236+
SmallVectorImpl<char> &scratch) const {
1237+
1238+
assert(hasInitStringRepresentation() &&
1239+
"must check if pattern has string representation");
1240+
1241+
if (InitContextAndIsText.getInt() && !InitStringRepresentation.empty())
1242+
return InitStringRepresentation;
1243+
auto &sourceMgr = getAnchoringVarDecl()->getASTContext().SourceMgr;
1244+
auto init = getInit();
1245+
return extractInlinableText(sourceMgr, init, scratch);
1246+
}
1247+
12281248
SourceRange PatternBindingDecl::getSourceRange() const {
12291249
SourceLoc startLoc = getStartLoc();
12301250
SourceLoc endLoc = getPatternList().back().getSourceRange().End;
@@ -1279,6 +1299,19 @@ VarDecl *PatternBindingDecl::getSingleVar() const {
12791299
return nullptr;
12801300
}
12811301

1302+
bool VarDecl::isInitExposedToClients() const {
1303+
auto parent = dyn_cast<NominalTypeDecl>(getDeclContext());
1304+
if (!parent) return false;
1305+
if (!hasInitialValue())
1306+
return false;
1307+
if (isStatic())
1308+
return false;
1309+
if (!parent->getAttrs().hasAttribute<FixedLayoutAttr>())
1310+
return false;
1311+
auto *module = parent->getModuleContext();
1312+
return module->getResilienceStrategy() == ResilienceStrategy::Resilient;
1313+
}
1314+
12821315
/// Check whether the given type representation will be
12831316
/// default-initializable.
12841317
static bool isDefaultInitializable(const TypeRepr *typeRepr) {

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2483,7 +2483,7 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD,
24832483
// Add the attribute that preserves the "has an initializer" value across
24842484
// module generation, as required for TBDGen.
24852485
PBD->getPattern(patternNumber)->forEachVariable([&](VarDecl *VD) {
2486-
if (VD->hasStorage())
2486+
if (VD->hasStorage() && !VD->getAttrs().hasAttribute<HasInitialValueAttr>())
24872487
VD->getAttrs().add(new (ctx) HasInitialValueAttr(/*IsImplicit=*/true));
24882488
});
24892489

0 commit comments

Comments
 (0)