Skip to content

Commit

Permalink
Merge pull request #23358 from brentdax/a-type-is-a-set-of-its-instances
Browse files Browse the repository at this point in the history
Add static and class subscripts
  • Loading branch information
beccadax committed Apr 11, 2019
2 parents cfc03f8 + 7ae331d commit def1af6
Show file tree
Hide file tree
Showing 28 changed files with 708 additions and 35 deletions.
2 changes: 2 additions & 0 deletions include/swift/AST/Decl.h
Expand Up @@ -139,6 +139,8 @@ enum class DescriptiveDeclKind : uint8_t {
GenericClass,
GenericType,
Subscript,
StaticSubscript,
ClassSubscript,
Constructor,
Destructor,
LocalFunction,
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsCommon.def
Expand Up @@ -84,6 +84,9 @@ ERROR(class_func_not_in_class,none,
ERROR(class_var_not_in_class,none,
"class properties are only allowed within classes; "
"use 'static' to declare a %select{static|requirement fulfilled by either a static or class}0 property", (bool))
ERROR(class_subscript_not_in_class,none,
"class subscripts are only allowed within classes; "
"use 'static' to declare a %select{static|requirement fulfilled by either a static or class}0 subscript", (bool))

// FIXME: Used by both the parser and the type-checker.
ERROR(func_decl_without_brace,PointsToFirstBadToken,
Expand Down
2 changes: 0 additions & 2 deletions include/swift/AST/DiagnosticsParse.def
Expand Up @@ -392,8 +392,6 @@ ERROR(expected_lbrace_subscript_protocol,PointsToFirstBadToken,
"{ get set } specifier", ())
ERROR(subscript_without_get,none,
"subscript declarations must have a getter", ())
ERROR(subscript_static,none,
"subscript cannot be marked %0", (StaticSpellingKind))

// initializer
ERROR(invalid_nested_init,none,
Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Expand Up @@ -3646,6 +3646,8 @@ ERROR(objc_invalid_on_var,none,
ERROR(objc_invalid_on_subscript,none,
"subscript cannot be %" OBJC_ATTR_SELECT "0 because its type "
"cannot be represented in Objective-C", (unsigned))
ERROR(objc_invalid_on_static_subscript,none,
"%0 cannot be %" OBJC_ATTR_SELECT "1", (DescriptiveDeclKind, unsigned))
ERROR(objc_invalid_with_generic_params,none,
"method cannot be %" OBJC_ATTR_SELECT "0 because it has generic "
"parameters", (unsigned))
Expand Down
3 changes: 2 additions & 1 deletion include/swift/Parse/Parser.h
Expand Up @@ -1006,7 +1006,8 @@ class Parser {
DeclAttributes &Attributes);

ParserResult<SubscriptDecl>
parseDeclSubscript(ParseDeclOptions Flags, DeclAttributes &Attributes,
parseDeclSubscript(SourceLoc StaticLoc, StaticSpellingKind StaticSpelling,
ParseDeclOptions Flags, DeclAttributes &Attributes,
SmallVectorImpl<Decl *> &Decls);

ParserResult<ConstructorDecl>
Expand Down
3 changes: 3 additions & 0 deletions lib/AST/ASTPrinter.cpp
Expand Up @@ -2874,6 +2874,9 @@ void PrintAST::visitSubscriptDecl(SubscriptDecl *decl) {
printDocumentationComment(decl);
printAttributes(decl);
printAccess(decl);
if (!Options.SkipIntroducerKeywords && decl->isStatic() &&
Options.PrintStaticKeyword)
printStaticKeyword(decl->getCorrectStaticSpelling());
printContextIfNeeded(decl);
recordDeclLoc(decl, [&]{
Printer << "subscript";
Expand Down
15 changes: 14 additions & 1 deletion lib/AST/Decl.cpp
Expand Up @@ -150,7 +150,6 @@ DescriptiveDeclKind Decl::getDescriptiveKind() const {
TRIVIAL_KIND(GenericTypeParam);
TRIVIAL_KIND(AssociatedType);
TRIVIAL_KIND(Protocol);
TRIVIAL_KIND(Subscript);
TRIVIAL_KIND(Constructor);
TRIVIAL_KIND(Destructor);
TRIVIAL_KIND(EnumElement);
Expand Down Expand Up @@ -188,6 +187,18 @@ DescriptiveDeclKind Decl::getDescriptiveKind() const {
}
}

case DeclKind::Subscript: {
auto subscript = cast<SubscriptDecl>(this);
switch (subscript->getCorrectStaticSpelling()) {
case StaticSpellingKind::None:
return DescriptiveDeclKind::Subscript;
case StaticSpellingKind::KeywordStatic:
return DescriptiveDeclKind::StaticSubscript;
case StaticSpellingKind::KeywordClass:
return DescriptiveDeclKind::ClassSubscript;
}
}

case DeclKind::Accessor: {
auto accessor = cast<AccessorDecl>(this);

Expand Down Expand Up @@ -279,6 +290,8 @@ StringRef Decl::getDescriptiveKindName(DescriptiveDeclKind K) {
ENTRY(GenericClass, "generic class");
ENTRY(GenericType, "generic type");
ENTRY(Subscript, "subscript");
ENTRY(StaticSubscript, "static subscript");
ENTRY(ClassSubscript, "class subscript");
ENTRY(Constructor, "initializer");
ENTRY(Destructor, "deinitializer");
ENTRY(LocalFunction, "local function");
Expand Down
34 changes: 24 additions & 10 deletions lib/Parse/ParseDecl.cpp
Expand Up @@ -2870,13 +2870,10 @@ Parser::parseDecl(ParseDeclOptions Flags,
break;
case tok::kw_subscript: {
DeclParsingContext.setCreateSyntax(SyntaxKind::SubscriptDecl);
if (StaticLoc.isValid()) {
diagnose(Tok, diag::subscript_static, StaticSpelling)
.fixItRemove(SourceRange(StaticLoc));
StaticLoc = SourceLoc();
}
llvm::SmallVector<Decl *, 4> Entries;
DeclResult = parseDeclSubscript(Flags, Attributes, Entries);
DeclResult = parseDeclSubscript(StaticLoc, StaticSpelling, Flags,
Attributes, Entries);
StaticLoc = SourceLoc(); // we handled static if present.
if (DeclResult.hasCodeCompletion() && isCodeCompletionFirstPass())
break;
std::for_each(Entries.begin(), Entries.end(), Handler);
Expand Down Expand Up @@ -6277,9 +6274,26 @@ parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes) {
/// attribute-list? 'subscript' parameter-clause '->' type
/// \endverbatim
ParserResult<SubscriptDecl>
Parser::parseDeclSubscript(ParseDeclOptions Flags,
Parser::parseDeclSubscript(SourceLoc StaticLoc,
StaticSpellingKind StaticSpelling,
ParseDeclOptions Flags,
DeclAttributes &Attributes,
SmallVectorImpl<Decl *> &Decls) {
assert(StaticLoc.isInvalid() || StaticSpelling != StaticSpellingKind::None);

if (StaticLoc.isValid()) {
if (Flags.contains(PD_InStruct) || Flags.contains(PD_InEnum) ||
Flags.contains(PD_InProtocol)) {
if (StaticSpelling == StaticSpellingKind::KeywordClass) {
diagnose(Tok, diag::class_subscript_not_in_class,
Flags.contains(PD_InProtocol))
.fixItReplace(StaticLoc, "static");

StaticSpelling = StaticSpellingKind::KeywordStatic;
}
}
}

ParserStatus Status;
SourceLoc SubscriptLoc = consumeToken(tok::kw_subscript);

Expand Down Expand Up @@ -6354,7 +6368,7 @@ Parser::parseDeclSubscript(ParseDeclOptions Flags,
DeclName name = DeclName(Context, DeclBaseName::createSubscript(),
argumentNames);
auto *Subscript = new (Context) SubscriptDecl(name,
SourceLoc(), StaticSpellingKind::None,
StaticLoc, StaticSpelling,
SubscriptLoc, Indices.get(),
ArrowLoc, ElementTy.get(),
CurDeclContext,
Expand Down Expand Up @@ -6402,7 +6416,7 @@ Parser::parseDeclSubscript(ParseDeclOptions Flags,
} else {
Status |= parseGetSet(Flags, GenericParams,
Indices.get(), ElementTy.get(),
accessors, Subscript, /*StaticLoc=*/SourceLoc());
accessors, Subscript, StaticLoc);
}

bool Invalid = false;
Expand All @@ -6413,7 +6427,7 @@ Parser::parseDeclSubscript(ParseDeclOptions Flags,
}

accessors.record(*this, Subscript, (Invalid || !Status.isSuccess()),
Flags, /*static*/ SourceLoc(), Attributes,
Flags, StaticLoc, Attributes,
ElementTy.get(), Indices.get(), Decls);

// No need to setLocalDiscriminator because subscripts cannot
Expand Down
25 changes: 22 additions & 3 deletions lib/Sema/CSApply.cpp
Expand Up @@ -1376,6 +1376,12 @@ namespace {

auto &tc = cs.getTypeChecker();
auto baseTy = cs.getType(base)->getRValueType();

bool baseIsInstance = true;
if (auto baseMeta = baseTy->getAs<AnyMetatypeType>()) {
baseIsInstance = false;
baseTy = baseMeta->getInstanceType();
}

// Check whether the base is 'super'.
bool isSuper = base->isSuperExpr();
Expand Down Expand Up @@ -1461,9 +1467,22 @@ namespace {
auto openedBaseType =
getBaseType(openedFullFnType, /*wantsRValue*/ false);
auto containerTy = solution.simplifyType(openedBaseType);
base = coerceObjectArgumentToType(
base, containerTy, subscript, AccessSemantics::Ordinary,
locator.withPathElement(ConstraintLocator::MemberRefBase));

if (baseIsInstance) {
base = coerceObjectArgumentToType(
base, containerTy, subscript, AccessSemantics::Ordinary,
locator.withPathElement(ConstraintLocator::MemberRefBase));
} else {
base = coerceToType(base,
MetatypeType::get(containerTy),
locator.withPathElement(
ConstraintLocator::MemberRefBase));

if (!base)
return nullptr;

base = cs.coerceToRValue(base);
}
if (!base)
return nullptr;

Expand Down
4 changes: 3 additions & 1 deletion lib/Sema/MiscDiagnostics.cpp
Expand Up @@ -422,6 +422,7 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E,
// Allow references to types as a part of:
// - member references T.foo, T.Type, T.self, etc.
// - constructor calls T()
// - Subscripts T[]
if (auto *ParentExpr = Parent.getAsExpr()) {
// This is an exhaustive list of the accepted syntactic forms.
if (isa<ErrorExpr>(ParentExpr) ||
Expand All @@ -433,7 +434,8 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E,
isa<UnresolvedDotExpr>(ParentExpr) ||
isa<DotSyntaxBaseIgnoredExpr>(ParentExpr) ||
isa<UnresolvedSpecializeExpr>(ParentExpr) ||
isa<OpenExistentialExpr>(ParentExpr)) {
isa<OpenExistentialExpr>(ParentExpr) ||
isa<SubscriptExpr>(ParentExpr)) {
return;
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeCheckDecl.cpp
Expand Up @@ -4081,7 +4081,7 @@ void TypeChecker::validateDecl(ValueDecl *D) {
// Member subscripts need some special validation logic.
if (SD->getDeclContext()->isTypeContext()) {
// If this is a class member, mark it final if the class is final.
inferFinalAndDiagnoseIfNeeded(*this, SD, StaticSpellingKind::None);
inferFinalAndDiagnoseIfNeeded(*this, SD, SD->getStaticSpelling());
}

// Perform accessor-related validation.
Expand Down
10 changes: 10 additions & 0 deletions lib/Sema/TypeCheckDeclObjC.cpp
Expand Up @@ -827,6 +827,16 @@ bool swift::isRepresentableInObjC(const SubscriptDecl *SD, ObjCReason Reason) {
if (checkObjCInForeignClassContext(SD, Reason))
return false;

// ObjC doesn't support class subscripts.
if (!SD->isInstanceMember()) {
if (Diagnose) {
SD->diagnose(diag::objc_invalid_on_static_subscript,
SD->getDescriptiveKind(), Reason);
describeObjCReason(SD, Reason);
}
return true;
}

if (!SD->hasInterfaceType()) {
SD->getASTContext().getLazyResolver()->resolveDeclSignature(
const_cast<SubscriptDecl *>(SD));
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckDeclOverride.cpp
Expand Up @@ -1684,6 +1684,7 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) {
switch (baseKind) {
case DescriptiveDeclKind::StaticProperty:
case DescriptiveDeclKind::StaticMethod:
case DescriptiveDeclKind::StaticSubscript:
override->diagnose(diag::override_static, baseKind);
break;
default:
Expand Down
7 changes: 7 additions & 0 deletions test/ClangImporter/Inputs/custom-modules/ObjCSubscripts.h
Expand Up @@ -60,3 +60,10 @@
@interface KeySubscriptReversedOverrideSetter : KeySubscriptReversedBase
- (void)setObject:(id)object forKeyedSubscript:(NSString *)key;
@end

@interface NoClassSubscript : NSObject
+ (id)objectAtIndexedSubscript:(int)i;
+ (void)setObject:(id)obj atIndexedSubscript:(int)i;
+ (id)objectForKeyedSubscript:(NSString *)subscript;
+ (void)setObject:(id)object forKeyedSubscript:(NSString *)key;
@end
32 changes: 32 additions & 0 deletions test/ClangImporter/objc_class_subscript.swift
@@ -0,0 +1,32 @@
// RUN: %empty-directory(%t)
// RUN: %build-clang-importer-objc-overlays

// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -typecheck -I %S/Inputs/custom-modules %s -verify

// REQUIRES: objc_interop

import ObjCSubscripts

func testClass() {
_ = NoClassSubscript[0] // expected-error{{value of type 'NoClassSubscript.Type' has no subscripts}}
NoClassSubscript[0] = "" // expected-error{{value of type 'NoClassSubscript.Type' has no subscripts}}

_ = NoClassSubscript["foo"] // expected-error{{value of type 'NoClassSubscript.Type' has no subscripts}}
NoClassSubscript["foo"] = "" // expected-error{{value of type 'NoClassSubscript.Type' has no subscripts}}
}

func testInstance(x: NoClassSubscript) {
_ = x[0] // expected-error{{value of type 'NoClassSubscript' has no subscripts}}
x[0] = "" // expected-error{{value of type 'NoClassSubscript' has no subscripts}}

_ = x["foo"] // expected-error{{value of type 'NoClassSubscript' has no subscripts}}
x["foo"] = "" // expected-error{{value of type 'NoClassSubscript' has no subscripts}}
}

func testClassMethods() {
_ = NoClassSubscript.object(atIndexedSubscript: 0)
NoClassSubscript.setObject("", atIndexedSubscript: 0)

_ = NoClassSubscript.object(forKeyedSubscript: "foo")
NoClassSubscript.setObject("", forKeyedSubscript: "foo")
}
37 changes: 35 additions & 2 deletions test/Constraints/subscript.swift
Expand Up @@ -11,15 +11,15 @@ protocol IntToStringSubscript {
subscript (i : Int) -> String { get }
}

class LameDictionary {
class FauxDictionary {
subscript (i : Int) -> String {
get {
return String(i)
}
}
}

func archetypeSubscript<T : IntToStringSubscript, U : LameDictionary>(_ t: T, u: U)
func archetypeSubscript<T : IntToStringSubscript, U : FauxDictionary>(_ t: T, u: U)
-> String {
// Subscript an archetype.
if false { return t[17] }
Expand All @@ -33,6 +33,39 @@ func existentialSubscript(_ a: IntToStringSubscript) -> String {
return a[17]
}

// Static of above:

// Subscript of archetype.
protocol IntToStringStaticSubscript {
static subscript (i : Int) -> String { get }
}

class FauxStaticDictionary {
static subscript (i : Int) -> String {
get {
return String(i)
}
}
}

func archetypeStaticSubscript<
T : IntToStringStaticSubscript, U : FauxStaticDictionary
>(_ t: T.Type, u: U.Type) -> String {
// Subscript an archetype.
if false { return t[17] }

// Subscript an archetype for which the subscript operator is in a base class.
return u[17]
}

// Subscript of existential type.
func existentialStaticSubscript(
_ a: IntToStringStaticSubscript.Type
) -> String {
return a[17]
}


class MyDictionary<Key, Value> {
subscript (key : Key) -> Value {
get {}
Expand Down
7 changes: 7 additions & 0 deletions test/IDE/print_ast_tc_decls.swift
Expand Up @@ -172,6 +172,13 @@ struct d0100_FooStruct {
}
}
// PASS_COMMON-NEXT: {{^}} subscript(i: Int, j: Int) -> Double { get }{{$}}

static subscript(i: Int) -> Double {
get {
return Double(i)
}
}
// PASS_COMMON-NEXT: {{^}} static subscript(i: Int) -> Double { get }{{$}}

func bodyNameVoidFunc1(a: Int, b x: Float) {}
// PASS_COMMON-NEXT: {{^}} func bodyNameVoidFunc1(a: Int, b x: Float){{$}}
Expand Down

0 comments on commit def1af6

Please sign in to comment.