Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add static and class subscripts #23358

Merged
merged 9 commits into from
Apr 11, 2019
2 changes: 2 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -3638,6 +3638,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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading