Skip to content

Commit b79fa44

Browse files
authored
Merge pull request #3878 from CodaFi/decltype
[SE-0096] Implement type(of:)
2 parents cda5aef + 80fb5c1 commit b79fa44

File tree

88 files changed

+374
-274
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+374
-274
lines changed

benchmark/single-source/DictionaryBridge.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import TestsUtils
1919
class Thing : NSObject {
2020

2121
required override init() {
22-
let c = self.dynamicType.col()
22+
let c = type(of: self).col()
2323
CheckResults(c!.count == 10, "The rules of the universe apply")
2424
}
2525

docs/archive/LangRef.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1255,7 +1255,7 @@ <h3 id="type-metatype">Metatype Types</h3>
12551255
type-metatype ::= type-simple '.' 'Type'
12561256
</pre>
12571257

1258-
<p>Every type has an associated metatype <tt>T.dynamicType</tt>. A value of the metatype
1258+
<p>Every type has an associated metatype <tt>type(of: T)</tt>. A value of the metatype
12591259
type is a reference to a global object which describes the type.
12601260
Most metatype types are singleton and therefore require no
12611261
storage, but metatypes associated with <a href="#decl-class">class

include/swift/AST/DiagnosticsParse.def

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,18 @@ ERROR(expr_selector_expected_property_expr,PointsToFirstBadToken,
11051105
ERROR(expr_selector_expected_rparen,PointsToFirstBadToken,
11061106
"expected ')' to complete '#selector' expression", ())
11071107

1108+
// Type-of expressions.
1109+
ERROR(expr_typeof_expected_lparen,PointsToFirstBadToken,
1110+
"expected '(' following 'type'", ())
1111+
ERROR(expr_typeof_expected_label_of,PointsToFirstBadToken,
1112+
"expected argument label 'of:' within 'type(...)'", ())
1113+
ERROR(expr_typeof_expected_expr,PointsToFirstBadToken,
1114+
"expected an expression within 'type(of: ...)'", ())
1115+
ERROR(expr_typeof_expected_rparen,PointsToFirstBadToken,
1116+
"expected ')' to complete 'type(of: ...)' expression", ())
1117+
WARNING(expr_dynamictype_deprecated,PointsToFirstBadToken,
1118+
"'.dynamicType' is deprecated. Use 'type(of: ...)' instead", ())
1119+
11081120
//------------------------------------------------------------------------------
11091121
// Attribute-parsing diagnostics
11101122
//------------------------------------------------------------------------------

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,7 @@ ERROR(did_not_call_method,none,
691691
(Identifier))
692692

693693
ERROR(init_not_instance_member,none,
694-
"'init' is a member of the type; insert '.dynamicType' to initialize "
694+
"'init' is a member of the type; use 'type(of: ...)' to initialize "
695695
"a new object of the same dynamic type", ())
696696
ERROR(super_initializer_not_in_initializer,none,
697697
"'super.init' cannot be called outside of an initializer", ())
@@ -2271,8 +2271,6 @@ WARNING(optional_pattern_match_promotion,none,
22712271
(Type, Type))
22722272

22732273

2274-
ERROR(type_of_metatype,none,
2275-
"'.dynamicType' is not allowed after a type name", ())
22762274
ERROR(invalid_noescape_use,none,
22772275
"non-escaping %select{value|parameter}1 %0 may only be called",
22782276
(Identifier, bool))

include/swift/AST/Expr.h

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3517,30 +3517,36 @@ class AutoClosureExpr : public AbstractClosureExpr {
35173517
}
35183518
};
35193519

3520-
/// DynamicTypeExpr - "base.dynamicType" - Produces a metatype value.
3520+
/// DynamicTypeExpr - "type(of: base)" - Produces a metatype value.
35213521
///
3522-
/// The metatype value can comes from evaluating an expression and then
3523-
/// getting its metatype.
3522+
/// The metatype value comes from evaluating an expression then retrieving the
3523+
/// metatype of the result.
35243524
class DynamicTypeExpr : public Expr {
3525+
SourceLoc KeywordLoc;
3526+
SourceLoc LParenLoc;
35253527
Expr *Base;
3526-
SourceLoc MetatypeLoc;
3528+
SourceLoc RParenLoc;
35273529

35283530
public:
3529-
explicit DynamicTypeExpr(Expr *Base, SourceLoc MetatypeLoc, Type Ty)
3531+
explicit DynamicTypeExpr(SourceLoc KeywordLoc, SourceLoc LParenLoc,
3532+
Expr *Base, SourceLoc RParenLoc, Type Ty)
35303533
: Expr(ExprKind::DynamicType, /*Implicit=*/false, Ty),
3531-
Base(Base), MetatypeLoc(MetatypeLoc) { }
3534+
KeywordLoc(KeywordLoc), LParenLoc(LParenLoc), Base(Base),
3535+
RParenLoc(RParenLoc) { }
35323536

35333537
Expr *getBase() const { return Base; }
35343538
void setBase(Expr *base) { Base = base; }
35353539

3536-
SourceLoc getLoc() const { return MetatypeLoc; }
3537-
SourceLoc getMetatypeLoc() const { return MetatypeLoc; }
3538-
3540+
SourceLoc getLoc() const { return KeywordLoc; }
3541+
SourceRange getSourceRange() const {
3542+
return SourceRange(KeywordLoc, RParenLoc);
3543+
}
3544+
35393545
SourceLoc getStartLoc() const {
3540-
return getBase()->getStartLoc();
3546+
return KeywordLoc;
35413547
}
35423548
SourceLoc getEndLoc() const {
3543-
return (MetatypeLoc.isValid() ? MetatypeLoc : getBase()->getEndLoc());
3549+
return RParenLoc;
35443550
}
35453551

35463552
static bool classof(const Expr *E) {

include/swift/Parse/Parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,6 +1118,7 @@ class Parser {
11181118
ParserResult<Expr> parseExprSuper(bool isExprBasic);
11191119
ParserResult<Expr> parseExprConfiguration();
11201120
ParserResult<Expr> parseExprStringLiteral();
1121+
ParserResult<Expr> parseExprTypeOf();
11211122

11221123
/// If the token is an escaped identifier being used as an argument
11231124
/// label, but doesn't need to be, diagnose it.

include/swift/Parse/Tokens.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ STMT_KEYWORD(catch)
130130
// Expression keywords.
131131
KEYWORD(as)
132132
KEYWORD(Any)
133-
KEYWORD(dynamicType)
134133
KEYWORD(false)
135134
KEYWORD(is)
136135
KEYWORD(nil)
@@ -148,6 +147,9 @@ KEYWORD(__COLUMN__)
148147
KEYWORD(__FUNCTION__)
149148
KEYWORD(__DSO_HANDLE__)
150149

150+
// FIXME(SE-0096): REMOVE ME
151+
KEYWORD(dynamicType)
152+
151153
// Pattern keywords.
152154
KEYWORD(_)
153155

lib/FrontendTool/FrontendTool.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ class JSONFixitWriter : public DiagnosticConsumer {
585585
Info.ID == diag::invalid_ibinspectable.ID ||
586586
Info.ID == diag::invalid_ibaction_decl.ID)
587587
return false;
588-
// Adding .dynamicType interacts poorly with the swift migrator by
588+
// Adding type(of:) interacts poorly with the swift migrator by
589589
// invalidating some inits with type errors.
590590
if (Info.ID == diag::init_not_instance_member.ID)
591591
return false;

lib/Parse/ParseExpr.cpp

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,12 @@ ParserResult<Expr> Parser::parseExprPostfix(Diag<> ID, bool isExprBasic) {
10951095
}
10961096

10971097
case tok::identifier: // foo
1098+
// If starts with 'type(', parse the 'type(of: ...)' metatype expression
1099+
if (Tok.getText() == "type" && peekToken().is(tok::l_paren)) {
1100+
Result = parseExprTypeOf();
1101+
break;
1102+
}
1103+
10981104
// If we are parsing a refutable pattern and are inside a let/var pattern,
10991105
// the identifiers change to be value bindings instead of decl references.
11001106
// Parse and return this as an UnresolvedPatternExpr around a binding. This
@@ -1413,12 +1419,27 @@ ParserResult<Expr> Parser::parseExprPostfix(Diag<> ID, bool isExprBasic) {
14131419
}
14141420

14151421
// Handle "x.dynamicType" - A metatype expr.
1422+
// Deprecated in SE-0096: `x.dynamicType` becomes `type(of: x)`
1423+
//
1424+
// FIXME(SE-0096): This will go away along with the keyword soon.
14161425
if (Tok.is(tok::kw_dynamicType)) {
1417-
Result = makeParserResult(
1418-
new (Context) DynamicTypeExpr(Result.get(), consumeToken(), Type()));
1426+
// Fix-it
1427+
auto range = Result.get()->getSourceRange();
1428+
auto dynamicTypeExprRange = SourceRange(TokLoc, consumeToken());
1429+
diagnose(TokLoc, diag::expr_dynamictype_deprecated)
1430+
.highlight(dynamicTypeExprRange)
1431+
.fixItReplace(dynamicTypeExprRange, ")")
1432+
.fixItInsert(range.Start, "type(of: ");
1433+
1434+
// HACK: Arbitrary.
1435+
auto loc = range.Start;
1436+
auto dt = new (Context) DynamicTypeExpr(loc, loc, Result.get(), loc, Type());
1437+
dt->setImplicit();
1438+
Result = makeParserResult(dt);
14191439
continue;
14201440
}
14211441

1442+
14221443
// Handle "x.self" expr.
14231444
if (Tok.is(tok::kw_self)) {
14241445
Result = makeParserResult(
@@ -2995,3 +3016,76 @@ Parser::parseVersionConstraintSpec() {
29953016
return makeParserResult(new (Context) VersionConstraintAvailabilitySpec(
29963017
Platform.getValue(), PlatformLoc, Version, VersionRange));
29973018
}
3019+
3020+
/// parseExprTypeOf
3021+
///
3022+
/// expr-dynamictype:
3023+
/// 'type' '(' 'of:' expr ')'
3024+
///
3025+
ParserResult<Expr> Parser::parseExprTypeOf() {
3026+
// Consume 'type'
3027+
SourceLoc keywordLoc = consumeToken();
3028+
3029+
// Parse the leading '('.
3030+
SourceLoc lParenLoc = consumeToken(tok::l_paren);
3031+
3032+
// Parse `of` label.
3033+
auto ofRange = Tok.getRange();
3034+
if (Tok.canBeArgumentLabel() && peekToken().is(tok::colon)) {
3035+
bool hasOf = Tok.getText() == "of";
3036+
if (!hasOf) {
3037+
// User mis-spelled the 'of' label.
3038+
diagnose(Tok, diag::expr_typeof_expected_label_of)
3039+
.fixItReplace({ ofRange.getStart(), ofRange.getEnd() }, "of");
3040+
}
3041+
3042+
// Consume either 'of' or the misspelling.
3043+
consumeToken();
3044+
consumeToken(tok::colon);
3045+
3046+
if (!hasOf) {
3047+
return makeParserError();
3048+
}
3049+
} else {
3050+
// No label at all; insert it.
3051+
diagnose(Tok, diag::expr_typeof_expected_label_of)
3052+
.fixItInsert(ofRange.getStart(), "of: ");
3053+
}
3054+
3055+
// Parse the subexpression.
3056+
ParserResult<Expr> subExpr = parseExpr(diag::expr_typeof_expected_expr);
3057+
if (subExpr.hasCodeCompletion())
3058+
return makeParserCodeCompletionResult<Expr>();
3059+
3060+
// Parse the closing ')'
3061+
SourceLoc rParenLoc;
3062+
if (subExpr.isParseError()) {
3063+
skipUntilDeclStmtRBrace(tok::r_paren);
3064+
if (Tok.is(tok::r_paren))
3065+
rParenLoc = consumeToken();
3066+
else
3067+
rParenLoc = Tok.getLoc();
3068+
} else {
3069+
parseMatchingToken(tok::r_paren, rParenLoc,
3070+
diag::expr_typeof_expected_rparen, lParenLoc);
3071+
}
3072+
3073+
// If the subexpression was in error, just propagate the error.
3074+
if (subExpr.isParseError()) {
3075+
if (subExpr.hasCodeCompletion()) {
3076+
auto res = makeParserResult(
3077+
new (Context) DynamicTypeExpr(keywordLoc, lParenLoc,
3078+
subExpr.get(), rParenLoc,
3079+
Type()));
3080+
res.setHasCodeCompletion();
3081+
return res;
3082+
} else {
3083+
return makeParserResult<Expr>(
3084+
new (Context) ErrorExpr(SourceRange(keywordLoc, rParenLoc)));
3085+
}
3086+
}
3087+
3088+
return makeParserResult(
3089+
new (Context) DynamicTypeExpr(keywordLoc, lParenLoc,
3090+
subExpr.get(), rParenLoc, Type()));
3091+
}

lib/Sema/CSDiag.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2267,14 +2267,14 @@ bool FailureDiagnosis::diagnoseGeneralMemberFailure(Constraint *constraint) {
22672267
return true;
22682268
}
22692269

2270-
// Suggest inserting '.dynamicType' to construct another object of the
2271-
// same dynamic type.
2272-
SourceLoc fixItLoc
2273-
= ctorRef->getNameLoc().getBaseNameLoc().getAdvancedLoc(-1);
2270+
// Suggest inserting a call to 'type(of:)' to construct another object
2271+
// of the same dynamic type.
2272+
SourceRange fixItRng = ctorRef->getNameLoc().getSourceRange();
22742273

2275-
// Place the '.dynamicType' right before the init.
2274+
// Surround the caller in `type(of:)`.
22762275
diagnose(anchor->getLoc(), diag::init_not_instance_member)
2277-
.fixItInsert(fixItLoc, ".dynamicType");
2276+
.fixItInsert(fixItRng.Start, "type(of: ")
2277+
.fixItInsertAfter(fixItRng.End, ")");
22782278
return true;
22792279
}
22802280
}

lib/Sema/MiscDiagnostics.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -486,17 +486,9 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E,
486486
return;
487487

488488
// Allow references to types as a part of:
489-
// - member references T.foo, T.Type, T.self, etc. (but *not* T.type)
489+
// - member references T.foo, T.Type, T.self, etc.
490490
// - constructor calls T()
491491
if (auto *ParentExpr = Parent.getAsExpr()) {
492-
// Reject use of "T.dynamicType", it should be written as "T.self".
493-
if (auto metaExpr = dyn_cast<DynamicTypeExpr>(ParentExpr)) {
494-
// Add a fixit to replace '.dynamicType' with '.self'.
495-
TC.diagnose(E->getStartLoc(), diag::type_of_metatype)
496-
.fixItReplace(metaExpr->getMetatypeLoc(), "self");
497-
return;
498-
}
499-
500492
// This is the white-list of accepted syntactic forms.
501493
if (isa<ErrorExpr>(ParentExpr) ||
502494
isa<DotSelfExpr>(ParentExpr) || // T.self

stdlib/private/StdlibCollectionUnittest/CheckMutableCollectionType.swift.gyb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -491,10 +491,10 @@ self.test("\(testNamePrefix).sorted/DispatchesThrough_withUnsafeMutableBufferPoi
491491
// This sort operation is not in-place.
492492
// The collection is copied into an array before sorting.
493493
expectEqual(
494-
0, lc.log._withUnsafeMutableBufferPointerIfSupported[lc.dynamicType])
494+
0, lc.log._withUnsafeMutableBufferPointerIfSupported[type(of: lc)])
495495
expectEqual(
496496
0,
497-
lc.log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[lc.dynamicType])
497+
lc.log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[type(of: lc)])
498498

499499
expectEqualSequence([ 1, 2, 3, 4, 5 ], extractedResult.map { $0.value })
500500
}
@@ -808,10 +808,10 @@ self.test("\(testNamePrefix).partition/DispatchesThrough_withUnsafeMutableBuffer
808808
})
809809

810810
expectEqual(
811-
1, lc.log._withUnsafeMutableBufferPointerIfSupported[lc.dynamicType])
811+
1, lc.log._withUnsafeMutableBufferPointerIfSupported[type(of: lc)])
812812
expectEqual(
813813
withUnsafeMutableBufferPointerIsSupported ? 1 : 0,
814-
lc.log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[lc.dynamicType])
814+
lc.log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[type(of: lc)])
815815

816816
expectEqual(4, lc.distance(from: lc.startIndex, to: pivot))
817817
expectEqualsUnordered([1, 2, 3, 4], lc.prefix(upTo: pivot).map { extractValue($0).value })

stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ extension LoggingType {
3232
}
3333

3434
public var selfType: Any.Type {
35-
return self.dynamicType
35+
return type(of: self)
3636
}
3737
}
3838

stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift.gyb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ public struct ${Self}<T> : ${SelfProtocols} {
555555
self.underestimatedCount = 0
556556
self._elements = []
557557
self._collectionState =
558-
_CollectionState(name: "\(self.dynamicType)", elementCount: 0)
558+
_CollectionState(name: "\(type(of: self))", elementCount: 0)
559559
}
560560

561561
public init<S : Sequence>(_ elements: S) where S.Iterator.Element == T {

0 commit comments

Comments
 (0)