Merge pull request #18778 from jrose-apple/towards-stability

[ModuleInterface] More changes to printing and parsing .swiftinterface files
This commit is contained in:
Jordan Rose
2018-08-17 09:10:57 -07:00
committed by GitHub
7 changed files with 129 additions and 34 deletions

View File

@@ -112,8 +112,8 @@ public:
};
struct ShouldPrintChecker {
virtual bool shouldPrint(const Decl *D, PrintOptions &Options);
bool shouldPrint(const Pattern *P, PrintOptions &Options);
virtual bool shouldPrint(const Decl *D, const PrintOptions &Options);
bool shouldPrint(const Pattern *P, const PrintOptions &Options);
virtual ~ShouldPrintChecker() = default;
};

View File

@@ -72,8 +72,9 @@ PrintOptions PrintOptions::printTextualInterfaceFile() {
result.FullyQualifiedTypes = true;
result.SkipImports = true;
class UsableFromInlineOnly : public ShouldPrintChecker {
bool shouldPrint(const Decl *D, PrintOptions &options) override {
class ShouldPrintForTextualInterface : public ShouldPrintChecker {
bool shouldPrint(const Decl *D, const PrintOptions &options) override {
// Skip anything that isn't 'public' or '@usableFromInline'.
if (auto *VD = dyn_cast<ValueDecl>(D)) {
AccessScope accessScope =
VD->getFormalAccessScope(/*useDC*/nullptr,
@@ -81,10 +82,29 @@ PrintOptions PrintOptions::printTextualInterfaceFile() {
if (!accessScope.isPublic())
return false;
}
// Skip typealiases that just redeclare generic parameters.
if (auto *alias = dyn_cast<TypeAliasDecl>(D)) {
if (alias->isImplicit()) {
const Decl *parent =
D->getDeclContext()->getAsDeclOrDeclExtensionContext();
if (auto *genericCtx = parent->getAsGenericContext()) {
bool matchesGenericParam =
llvm::any_of(genericCtx->getInnermostGenericParamTypes(),
[alias](const GenericTypeParamType *param) {
return param->getName() == alias->getName();
});
if (matchesGenericParam)
return false;
}
}
}
return ShouldPrintChecker::shouldPrint(D, options);
}
};
result.CurrentPrintabilityChecker = std::make_shared<UsableFromInlineOnly>();
result.CurrentPrintabilityChecker =
std::make_shared<ShouldPrintForTextualInterface>();
// FIXME: We don't really need 'public' on everything; we could just change
// the default to 'public' and mark the 'internal' things.
@@ -1308,7 +1328,8 @@ void PrintAST::printPatternType(const Pattern *P) {
}
}
bool ShouldPrintChecker::shouldPrint(const Pattern *P, PrintOptions &Options) {
bool ShouldPrintChecker::shouldPrint(const Pattern *P,
const PrintOptions &Options) {
bool ShouldPrint = false;
P->forEachVariable([&](const VarDecl *VD) {
ShouldPrint |= shouldPrint(VD, Options);
@@ -1316,7 +1337,8 @@ bool ShouldPrintChecker::shouldPrint(const Pattern *P, PrintOptions &Options) {
return ShouldPrint;
}
bool ShouldPrintChecker::shouldPrint(const Decl *D, PrintOptions &Options) {
bool ShouldPrintChecker::shouldPrint(const Decl *D,
const PrintOptions &Options) {
if (auto *ED= dyn_cast<ExtensionDecl>(D)) {
if (Options.printExtensionContentAsMembers(ED))
return false;

View File

@@ -26,7 +26,7 @@
using namespace swift;
static bool shouldPrintAsFavorable(const Decl *D, PrintOptions &Options) {
static bool shouldPrintAsFavorable(const Decl *D, const PrintOptions &Options) {
if (!Options.TransformContext ||
!D->getDeclContext()->isExtensionContext() ||
!Options.TransformContext->isPrintingSynthesizedExtension())
@@ -42,7 +42,7 @@ static bool shouldPrintAsFavorable(const Decl *D, PrintOptions &Options) {
}
class ModulePrinterPrintableChecker: public ShouldPrintChecker {
bool shouldPrint(const Decl *D, PrintOptions &Options) override {
bool shouldPrint(const Decl *D, const PrintOptions &Options) override {
if (!shouldPrintAsFavorable(D, Options))
return false;
return ShouldPrintChecker::shouldPrint(D, Options);

View File

@@ -5481,6 +5481,8 @@ ParserResult<EnumDecl> Parser::parseDeclEnum(ParseDeclOptions Flags,
{ }, nullptr, CurDeclContext);
setLocalDiscriminator(ED);
ED->getAttrs() = Attributes;
if (SF.Kind == SourceFileKind::Interface)
ED->setAddedImplicitInitializers();
ContextChange CC(*this, ED);
@@ -5753,6 +5755,8 @@ ParserResult<StructDecl> Parser::parseDeclStruct(ParseDeclOptions Flags,
CurDeclContext);
setLocalDiscriminator(SD);
SD->getAttrs() = Attributes;
if (SF.Kind == SourceFileKind::Interface)
SD->setAddedImplicitInitializers();
ContextChange CC(*this, SD);
@@ -5840,9 +5844,9 @@ ParserResult<ClassDecl> Parser::parseDeclClass(ParseDeclOptions Flags,
ClassDecl *CD = new (Context) ClassDecl(ClassLoc, ClassName, ClassNameLoc,
{ }, nullptr, CurDeclContext);
setLocalDiscriminator(CD);
// Attach attributes.
CD->getAttrs() = Attributes;
if (SF.Kind == SourceFileKind::Interface)
CD->setAddedImplicitInitializers();
ContextChange CC(*this, CD);

View File

@@ -2520,45 +2520,61 @@ public:
if (!var->hasStorage())
return;
if (var->isInvalid() || PBD->isInvalid())
return;
auto *varDC = var->getDeclContext();
auto markVarAndPBDInvalid = [PBD, var] {
PBD->setInvalid();
var->setInvalid();
if (!var->hasType())
var->markInvalid();
};
// Non-member observing properties need an initializer.
if (var->getWriteImpl() == WriteImplKind::StoredWithObservers &&
!isTypeContext && !var->isInvalid() && !PBD->isInvalid()) {
!isTypeContext) {
TC.diagnose(var->getLoc(), diag::observingprop_requires_initializer);
PBD->setInvalid();
var->setInvalid();
if (!var->hasType()) {
var->markInvalid();
}
markVarAndPBDInvalid();
return;
}
// Static/class declarations require an initializer unless in a
// protocol.
if (var->isStatic() && !isa<ProtocolDecl>(varDC) &&
!var->isInvalid() && !PBD->isInvalid()) {
if (var->isStatic() && !isa<ProtocolDecl>(varDC)) {
// ...but don't enforce this for SIL or textual interface files.
switch (varDC->getParentSourceFile()->Kind) {
case SourceFileKind::Interface:
case SourceFileKind::SIL:
return;
case SourceFileKind::Main:
case SourceFileKind::REPL:
case SourceFileKind::Library:
break;
}
TC.diagnose(var->getLoc(), diag::static_requires_initializer,
var->getCorrectStaticSpelling());
PBD->setInvalid();
var->setInvalid();
if (!var->hasType()) {
var->markInvalid();
}
markVarAndPBDInvalid();
return;
}
// Global variables require an initializer (except in top level code).
if (varDC->isModuleScopeContext() &&
!varDC->getParentSourceFile()->isScriptMode() &&
!var->isInvalid() && !PBD->isInvalid()) {
TC.diagnose(var->getLoc(),
diag::global_requires_initializer, var->isLet());
PBD->setInvalid();
var->setInvalid();
if (!var->hasType()) {
var->markInvalid();
// Global variables require an initializer in normal source files.
if (varDC->isModuleScopeContext()) {
switch (varDC->getParentSourceFile()->Kind) {
case SourceFileKind::Main:
case SourceFileKind::REPL:
case SourceFileKind::Interface:
case SourceFileKind::SIL:
return;
case SourceFileKind::Library:
break;
}
TC.diagnose(var->getLoc(), diag::global_requires_initializer,
var->isLet());
markVarAndPBDInvalid();
return;
}
});
@@ -5122,6 +5138,19 @@ static void diagnoseClassWithoutInitializers(TypeChecker &tc,
}
void TypeChecker::maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl) {
if (auto *SF = classDecl->getParentSourceFile()) {
// Allow classes without initializers in SIL and textual interface files.
switch (SF->Kind) {
case SourceFileKind::SIL:
case SourceFileKind::Interface:
return;
case SourceFileKind::Library:
case SourceFileKind::Main:
case SourceFileKind::REPL:
break;
}
}
// Some heuristics to skip emitting a diagnostic if the class is already
// irreperably busted.
if (classDecl->isInvalid() ||

View File

@@ -22,10 +22,21 @@ public class TestClass {
// CHECK: public var prop: Int{{$}}
public var prop: Int { get set }
// CHECK: public static var propWithNoAccessors: Int{{$}}
public static var propWithNoAccessors: Int
// NEGATIVE-NOT: deinit
deinit
} // CHECK: {{^}$}}
// CHECK-LABEL: public class TestEmptyClass {
public class TestEmptyClass {
} // CHECK-NEXT: {{^}$}}
// CHECK-LABEL: public struct TestEmptyStruct {
public struct TestEmptyStruct {
} // CHECK-NEXT: {{^}$}}
// CHECK-LABEL: public enum TestEnum
public enum TestEnum {
// CHECK: case a
@@ -42,6 +53,9 @@ public enum TestEnum {
// CHECK: public var prop: Int{{$}}
public var prop: Int { get set }
// CHECK: public static var propWithNoAccessors: Int{{$}}
public static var propWithNoAccessors: Int
} // CHECK: {{^}$}}
// CHECK-LABEL: public struct TestStruct
@@ -57,8 +71,14 @@ public struct TestStruct {
// CHECK: public var prop: Int{{$}}
public var prop: Int { get set }
// CHECK: public static var propWithNoAccessors: Int{{$}}
public static var propWithNoAccessors: Int
} // CHECK: {{^}$}}
// CHECK: public let globalWithNoAccessors: Int{{$}}
public let globalWithNoAccessors: Int
// CHECK: public var readOnlyVar: Int { get }{{$}}
public var readOnlyVar: Int { get }

View File

@@ -0,0 +1,20 @@
// RUN: %target-swift-frontend -emit-interface-path %t.swiftinterface -emit-module -o /dev/null %s
// RUN: %FileCheck %s < %t.swiftinterface
// RUN: %FileCheck -check-prefix NEGATIVE %s < %t.swiftinterface
// CHECK-LABEL: public protocol SimpleProto {
public protocol SimpleProto {
// CHECK: associatedtype Element
associatedtype Element
// CHECK: associatedtype Inferred
associatedtype Inferred
func inference(_: Inferred)
} // CHECK: {{^}$}}
// CHECK-LABEL: public struct SimplImpl<Element> : SimpleProto {
public struct SimplImpl<Element>: SimpleProto {
// NEGATIVE-NOT: typealias Element =
// CHECK: public func inference(_: Int){{$}}
public func inference(_: Int) {}
// CHECK: public typealias Inferred = Swift.Int
} // CHECK: {{^}$}}