Skip to content

Commit

Permalink
Make Self available to member functions (SE-0068?) (#22863)
Browse files Browse the repository at this point in the history
* Make Self available to instance member functions (SE-0068?)

* Works for value types and static functions.

* Further experiments with TypeExpr

* Move Self processing off diagnostic path

* diagnostic instead of assertion fail

* TypeExpr of DynamicSelfType now working.

* Update tests for availability of Self

* Cast to Self fixed!

* Self not available as type in classes except for return type

* Could it be this simple?

* Nearly there

* Fix function decls using Self inside methods.

* Fix validation-test/compiler_crashers_2_fixed/0164-sr7989.swift

* Fix of ./validation-test/compiler_crashers_2_fixed/0179-rdar44963974.swift

* "Un-fix" validation-test/compiler_crashers_2_fixed/0164-sr7989.swift

* CHANGELOG entry

* Update CHANGELOG.md

Co-Authored-By: johnno1962 <github@johnholdsworth.com>

* Update CHANGELOG.md
  • Loading branch information
johnno1962 authored and slavapestov committed Mar 16, 2019
1 parent 98d2d5d commit dbe99d7
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 16 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ CHANGELOG
Swift 5.1
---------

* [SE-0068][]:

`Self` can now be used inside member functions and for function arguments of structs and enums to refer to the containing type.

* [SR-7799][]:

Enum cases can now be matched against an optional enum without
Expand Down
5 changes: 2 additions & 3 deletions lib/AST/DeclContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,8 @@ bool DeclContext::isTypeContext() const {
DeclContext *DeclContext::getInnermostTypeContext() {
auto dc = this;
do {
if (auto decl = dc->getAsDecl())
if (isa<NominalTypeDecl>(decl) || isa<ExtensionDecl>(decl))
return dc;
if (dc->isTypeContext())
return dc;
} while ((dc = dc->getParent()));

return nullptr;
Expand Down
12 changes: 12 additions & 0 deletions lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,18 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) {
};

if (!isConfused) {
if (Name == Context.Id_Self) {
if (DeclContext *typeContext = DC->getInnermostTypeContext()){
Type SelfType = typeContext->getSelfInterfaceType();

if (typeContext->getSelfClassDecl())
SelfType = DynamicSelfType::get(SelfType, Context);
SelfType = DC->mapTypeIntoContext(SelfType);
return new (Context) TypeExpr(TypeLoc(new (Context)
FixedTypeRepr(SelfType, Loc)));
}
}

TypoCorrectionResults corrections(*this, Name, nameLoc);
performTypoCorrection(DC, UDRE->getRefKind(), Type(),
lookupOptions, corrections);
Expand Down
17 changes: 15 additions & 2 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1057,10 +1057,23 @@ static Type diagnoseUnknownType(TypeResolution resolution,
NominalTypeDecl *nominal = nullptr;
if ((nominalDC = dc->getInnermostTypeContext()) &&
(nominal = nominalDC->getSelfNominalTypeDecl())) {
// Attempt to refer to 'Self' within a non-protocol nominal
// type. Fix this by replacing 'Self' with the nominal type name.
assert(!isa<ProtocolDecl>(nominal) && "Cannot be a protocol");

bool insideClass = nominalDC->getSelfClassDecl() != nullptr;
AbstractFunctionDecl *methodDecl = dc->getInnermostMethodContext();
bool declaringMethod = methodDecl &&
methodDecl->getDeclContext() == dc->getParentForLookup();

if (((!insideClass || !declaringMethod) &&
!options.is(TypeResolverContext::GenericRequirement)) ||
options.is(TypeResolverContext::ExplicitCastExpr)) {
Type SelfType = nominal->getSelfInterfaceType();
if (insideClass)
SelfType = DynamicSelfType::get(SelfType, ctx);
return resolution.mapTypeIntoContext(SelfType);
}

// Attempt to refer to 'Self' within a non-protocol nominal type.
// Produce a Fix-It replacing 'Self' with the nominal type name.
auto name = getDeclNameFromContext(dc, nominal);
diags.diagnose(comp->getIdLoc(), diag::self_in_nominal, name)
Expand Down
14 changes: 7 additions & 7 deletions test/decl/func/dynamic_self.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ func inFunction() {
}

struct S0 {
func f() -> Self { } // expected-error{{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'S0'?}}{{15-19=S0}}
func f() -> Self { }

func g(_ ds: Self) { } // expected-error{{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'S0'?}}{{16-20=S0}}
func g(_ ds: Self) { }
}

enum E0 {
func f() -> Self { } // expected-error{{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'E0'?}}{{15-19=E0}}
func f() -> Self { }

func g(_ ds: Self) { } // expected-error{{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'E0'?}}{{16-20=E0}}
func g(_ ds: Self) { }
}

class C0 {
Expand Down Expand Up @@ -85,11 +85,11 @@ class C1 {
var x: Int = self // expected-error{{cannot convert value of type 'Self.Type' to specified type 'Int'}}

// Can't utter Self within the body of a method.
var c1 = C1(int: 5) as Self // expected-error{{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'C1'?}} {{28-32=C1}}
var c1 = C1(int: 5) as Self // expected-error{{'C1' is not convertible to 'Self'; did you mean to use 'as!' to force downcast?}}

if b { return self.init(int: 5) }

return Self() // expected-error{{use of unresolved identifier 'Self'; did you mean 'self'?}}
return Self() // expected-error{{non-nominal type 'Self' does not support explicit initialization}}
}

// This used to crash because metatype construction went down a
Expand Down Expand Up @@ -412,4 +412,4 @@ class SelfOperator {
func useSelfOperator() {
let s = SelfOperator()
_ = s + s
}
}
1 change: 0 additions & 1 deletion test/decl/nested/type_in_type.swift
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,6 @@ extension OuterGeneric.MidNonGeneric {
}

func doMoreStuffWrong() -> Self {
// expected-error@-1 {{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'OuterGeneric.MidNonGeneric'?}}

}
}
114 changes: 111 additions & 3 deletions test/type/self.swift
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// RUN: %target-typecheck-verify-swift -swift-version 5

struct S0<T> {
func foo(_ other: Self) { } // expected-error{{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'S0'?}}{{21-25=S0}}
func foo(_ other: Self) { }
}

class C0<T> {
func foo(_ other: Self) { } // expected-error{{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'C0'?}}{{21-25=C0}}
}

enum E0<T> {
func foo(_ other: Self) { } // expected-error{{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'E0'?}}{{21-25=E0}}
func foo(_ other: Self) { }
}

// rdar://problem/21745221
Expand All @@ -23,7 +23,7 @@ extension X {
}

extension X.Inner {
func foo(_ other: Self) { } // expected-error{{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'X.Inner'?}}{{21-25=X.Inner}}
func foo(_ other: Self) { }
}

// SR-695
Expand All @@ -43,3 +43,111 @@ final class FinalMario : Mario {
}
}

// These references to Self are now possible (SE-0068)

class A<T> {
let b: Int
required init(a: Int) {
print("\(Self.self).\(#function)")
Self.y()
b = a
}
static func z(n: Self? = nil) {
// expected-error@-1 {{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'A'?}}
print("\(Self.self).\(#function)")
}
class func y() {
print("\(Self.self).\(#function)")
Self.z()
}
func x() -> A? {
print("\(Self.self).\(#function)")
Self.y()
Self.z()
let _: Self = Self.init(a: 66)
// expected-error@-1 {{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'A'?}}
return Self.init(a: 77) as? Self as? A
// expected-warning@-1 {{conditional cast from 'Self' to 'Self' always succeeds}}
// expected-warning@-2 {{conditional downcast from 'Self?' to 'A<T>' is equivalent to an implicit conversion to an optional 'A<T>'}}
}
}

class B: A<Int> {
let a: Int
required convenience init(a: Int) {
print("\(Self.self).\(#function)")
self.init()
}
init() {
print("\(Self.self).\(#function)")
Self.y()
Self.z()
a = 99
super.init(a: 88)
}
override class func y() {
print("override \(Self.self).\(#function)")
}
}

class C {
required init() {
}
func f() {
func g(_: Self) {}
}
func g() {
_ = Self.init() as? Self
// expected-warning@-1 {{conditional cast from 'Self' to 'Self' always succeeds}}
}
}

struct S2 {
let x = 99
struct S3<T> {
let x = 99
static func x() {
Self.y()
}
func f() {
func g(_: Self) {}
}
static func y() {
print("HERE")
}
func foo(a: [Self]) -> Self? {
Self.x()
return Self.init() as? Self
// expected-warning@-1 {{conditional cast from 'S2.S3<T>' to 'S2.S3<T>' always succeeds}}
}
}
}

extension S2 {
static func x() {
Self.y()
}
static func y() {
print("HERE")
}
func f() {
func g(_: Self) {}
}
func foo(a: [Self]) -> Self? {
Self.x()
return Self.init() as? Self
// expected-warning@-1 {{conditional cast from 'S2' to 'S2' always succeeds}}
}
}

enum E {
static func f() {
func g(_: Self) {}
print("f()")
}
case e
func h(h: Self) -> Self {
Self.f()
return .e
}
}

0 comments on commit dbe99d7

Please sign in to comment.