Skip to content

[SE-0096] Implement type(of:) #3878

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

Merged
merged 4 commits into from
Jul 30, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion benchmark/single-source/DictionaryBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import TestsUtils
class Thing : NSObject {

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

Expand Down
2 changes: 1 addition & 1 deletion docs/archive/LangRef.html
Original file line number Diff line number Diff line change
Expand Up @@ -1255,7 +1255,7 @@ <h3 id="type-metatype">Metatype Types</h3>
type-metatype ::= type-simple '.' 'Type'
</pre>

<p>Every type has an associated metatype <tt>T.dynamicType</tt>. A value of the metatype
<p>Every type has an associated metatype <tt>type(of: T)</tt>. A value of the metatype
type is a reference to a global object which describes the type.
Most metatype types are singleton and therefore require no
storage, but metatypes associated with <a href="#decl-class">class
Expand Down
12 changes: 12 additions & 0 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,18 @@ ERROR(expr_selector_expected_property_expr,PointsToFirstBadToken,
ERROR(expr_selector_expected_rparen,PointsToFirstBadToken,
"expected ')' to complete '#selector' expression", ())

// Type-of expressions.
ERROR(expr_typeof_expected_lparen,PointsToFirstBadToken,
"expected '(' following 'type'", ())
ERROR(expr_typeof_expected_label_of,PointsToFirstBadToken,
"expected argument label 'of:' within 'type(...)'", ())
ERROR(expr_typeof_expected_expr,PointsToFirstBadToken,
"expected an expression within 'type(of: ...)'", ())
ERROR(expr_typeof_expected_rparen,PointsToFirstBadToken,
"expected ')' to complete 'type(of: ...)' expression", ())
WARNING(expr_dynamictype_deprecated,PointsToFirstBadToken,
"'.dynamicType' is deprecated. Use 'type(of: ...)' instead", ())

//------------------------------------------------------------------------------
// Attribute-parsing diagnostics
//------------------------------------------------------------------------------
Expand Down
4 changes: 1 addition & 3 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,7 @@ ERROR(did_not_call_method,none,
(Identifier))

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


ERROR(type_of_metatype,none,
"'.dynamicType' is not allowed after a type name", ())
ERROR(invalid_noescape_use,none,
"non-escaping %select{value|parameter}1 %0 may only be called",
(Identifier, bool))
Expand Down
28 changes: 17 additions & 11 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -3517,30 +3517,36 @@ class AutoClosureExpr : public AbstractClosureExpr {
}
};

/// DynamicTypeExpr - "base.dynamicType" - Produces a metatype value.
/// DynamicTypeExpr - "type(of: base)" - Produces a metatype value.
///
/// The metatype value can comes from evaluating an expression and then
/// getting its metatype.
/// The metatype value comes from evaluating an expression then retrieving the
/// metatype of the result.
class DynamicTypeExpr : public Expr {
SourceLoc KeywordLoc;
SourceLoc LParenLoc;
Expr *Base;
SourceLoc MetatypeLoc;
SourceLoc RParenLoc;

public:
explicit DynamicTypeExpr(Expr *Base, SourceLoc MetatypeLoc, Type Ty)
explicit DynamicTypeExpr(SourceLoc KeywordLoc, SourceLoc LParenLoc,
Expr *Base, SourceLoc RParenLoc, Type Ty)
: Expr(ExprKind::DynamicType, /*Implicit=*/false, Ty),
Base(Base), MetatypeLoc(MetatypeLoc) { }
KeywordLoc(KeywordLoc), LParenLoc(LParenLoc), Base(Base),
RParenLoc(RParenLoc) { }

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

SourceLoc getLoc() const { return MetatypeLoc; }
SourceLoc getMetatypeLoc() const { return MetatypeLoc; }

SourceLoc getLoc() const { return KeywordLoc; }
SourceRange getSourceRange() const {
return SourceRange(KeywordLoc, RParenLoc);
}

SourceLoc getStartLoc() const {
return getBase()->getStartLoc();
return KeywordLoc;
}
SourceLoc getEndLoc() const {
return (MetatypeLoc.isValid() ? MetatypeLoc : getBase()->getEndLoc());
return RParenLoc;
}

static bool classof(const Expr *E) {
Expand Down
1 change: 1 addition & 0 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1118,6 +1118,7 @@ class Parser {
ParserResult<Expr> parseExprSuper(bool isExprBasic);
ParserResult<Expr> parseExprConfiguration();
ParserResult<Expr> parseExprStringLiteral();
ParserResult<Expr> parseExprTypeOf();

/// If the token is an escaped identifier being used as an argument
/// label, but doesn't need to be, diagnose it.
Expand Down
4 changes: 3 additions & 1 deletion include/swift/Parse/Tokens.def
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ STMT_KEYWORD(catch)
// Expression keywords.
KEYWORD(as)
KEYWORD(Any)
KEYWORD(dynamicType)
KEYWORD(false)
KEYWORD(is)
KEYWORD(nil)
Expand All @@ -148,6 +147,9 @@ KEYWORD(__COLUMN__)
KEYWORD(__FUNCTION__)
KEYWORD(__DSO_HANDLE__)

// FIXME(SE-0096): REMOVE ME
KEYWORD(dynamicType)

// Pattern keywords.
KEYWORD(_)

Expand Down
2 changes: 1 addition & 1 deletion lib/FrontendTool/FrontendTool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ class JSONFixitWriter : public DiagnosticConsumer {
Info.ID == diag::invalid_ibinspectable.ID ||
Info.ID == diag::invalid_ibaction_decl.ID)
return false;
// Adding .dynamicType interacts poorly with the swift migrator by
// Adding type(of:) interacts poorly with the swift migrator by
// invalidating some inits with type errors.
if (Info.ID == diag::init_not_instance_member.ID)
return false;
Expand Down
98 changes: 96 additions & 2 deletions lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,12 @@ ParserResult<Expr> Parser::parseExprPostfix(Diag<> ID, bool isExprBasic) {
}

case tok::identifier: // foo
// If starts with 'type(', parse the 'type(of: ...)' metatype expression
if (Tok.getText() == "type" && peekToken().is(tok::l_paren)) {
Result = parseExprTypeOf();
break;
}

// If we are parsing a refutable pattern and are inside a let/var pattern,
// the identifiers change to be value bindings instead of decl references.
// Parse and return this as an UnresolvedPatternExpr around a binding. This
Expand Down Expand Up @@ -1413,12 +1419,27 @@ ParserResult<Expr> Parser::parseExprPostfix(Diag<> ID, bool isExprBasic) {
}

// Handle "x.dynamicType" - A metatype expr.
// Deprecated in SE-0096: `x.dynamicType` becomes `type(of: x)`
//
// FIXME(SE-0096): This will go away along with the keyword soon.
if (Tok.is(tok::kw_dynamicType)) {
Result = makeParserResult(
new (Context) DynamicTypeExpr(Result.get(), consumeToken(), Type()));
// Fix-it
auto range = Result.get()->getSourceRange();
auto dynamicTypeExprRange = SourceRange(TokLoc, consumeToken());
diagnose(TokLoc, diag::expr_dynamictype_deprecated)
.highlight(dynamicTypeExprRange)
.fixItReplace(dynamicTypeExprRange, ")")
.fixItInsert(range.Start, "type(of: ");

// HACK: Arbitrary.
auto loc = range.Start;
auto dt = new (Context) DynamicTypeExpr(loc, loc, Result.get(), loc, Type());
dt->setImplicit();
Result = makeParserResult(dt);
continue;
}


// Handle "x.self" expr.
if (Tok.is(tok::kw_self)) {
Result = makeParserResult(
Expand Down Expand Up @@ -2995,3 +3016,76 @@ Parser::parseVersionConstraintSpec() {
return makeParserResult(new (Context) VersionConstraintAvailabilitySpec(
Platform.getValue(), PlatformLoc, Version, VersionRange));
}

/// parseExprTypeOf
///
/// expr-dynamictype:
/// 'type' '(' 'of:' expr ')'
///
ParserResult<Expr> Parser::parseExprTypeOf() {
// Consume 'type'
SourceLoc keywordLoc = consumeToken();

// Parse the leading '('.
SourceLoc lParenLoc = consumeToken(tok::l_paren);

// Parse `of` label.
auto ofRange = Tok.getRange();
if (Tok.canBeArgumentLabel() && peekToken().is(tok::colon)) {
bool hasOf = Tok.getText() == "of";
if (!hasOf) {
// User mis-spelled the 'of' label.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean it's impossible for user code to define a function named type (with any argument label)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does. It is a limitation of the SE that deserves to be discussed. One solution would be to make this a "magic hash-prefix" function like #selector.

Copy link
Contributor Author

@CodaFi CodaFi Jul 30, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally we wanted this function defined in stdlib and not in the parser. But that can't happen for bureaucratic reasons related to protocols and other nominal types having differing expressions allowed after these dynamicType contexts.

Copy link
Contributor Author

@CodaFi CodaFi Jul 30, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevertheless, I think I'm going to disable this diagnostic when I do the next patch to properly remove the keyword and make the parser fall back to find user-defined functions.

diagnose(Tok, diag::expr_typeof_expected_label_of)
.fixItReplace({ ofRange.getStart(), ofRange.getEnd() }, "of");
}

// Consume either 'of' or the misspelling.
consumeToken();
consumeToken(tok::colon);

if (!hasOf) {
return makeParserError();
}
} else {
// No label at all; insert it.
diagnose(Tok, diag::expr_typeof_expected_label_of)
.fixItInsert(ofRange.getStart(), "of: ");
}

// Parse the subexpression.
ParserResult<Expr> subExpr = parseExpr(diag::expr_typeof_expected_expr);
if (subExpr.hasCodeCompletion())
return makeParserCodeCompletionResult<Expr>();

// Parse the closing ')'
SourceLoc rParenLoc;
if (subExpr.isParseError()) {
skipUntilDeclStmtRBrace(tok::r_paren);
if (Tok.is(tok::r_paren))
rParenLoc = consumeToken();
else
rParenLoc = Tok.getLoc();
} else {
parseMatchingToken(tok::r_paren, rParenLoc,
diag::expr_typeof_expected_rparen, lParenLoc);
}

// If the subexpression was in error, just propagate the error.
if (subExpr.isParseError()) {
if (subExpr.hasCodeCompletion()) {
auto res = makeParserResult(
new (Context) DynamicTypeExpr(keywordLoc, lParenLoc,
subExpr.get(), rParenLoc,
Type()));
res.setHasCodeCompletion();
return res;
} else {
return makeParserResult<Expr>(
new (Context) ErrorExpr(SourceRange(keywordLoc, rParenLoc)));
}
}

return makeParserResult(
new (Context) DynamicTypeExpr(keywordLoc, lParenLoc,
subExpr.get(), rParenLoc, Type()));
}
12 changes: 6 additions & 6 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2267,14 +2267,14 @@ bool FailureDiagnosis::diagnoseGeneralMemberFailure(Constraint *constraint) {
return true;
}

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

// Place the '.dynamicType' right before the init.
// Surround the caller in `type(of:)`.
diagnose(anchor->getLoc(), diag::init_not_instance_member)
.fixItInsert(fixItLoc, ".dynamicType");
.fixItInsert(fixItRng.Start, "type(of: ")
.fixItInsertAfter(fixItRng.End, ")");
return true;
}
}
Expand Down
10 changes: 1 addition & 9 deletions lib/Sema/MiscDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,17 +486,9 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E,
return;

// Allow references to types as a part of:
// - member references T.foo, T.Type, T.self, etc. (but *not* T.type)
// - member references T.foo, T.Type, T.self, etc.
// - constructor calls T()
if (auto *ParentExpr = Parent.getAsExpr()) {
// Reject use of "T.dynamicType", it should be written as "T.self".
if (auto metaExpr = dyn_cast<DynamicTypeExpr>(ParentExpr)) {
// Add a fixit to replace '.dynamicType' with '.self'.
TC.diagnose(E->getStartLoc(), diag::type_of_metatype)
.fixItReplace(metaExpr->getMetatypeLoc(), "self");
return;
}

// This is the white-list of accepted syntactic forms.
if (isa<ErrorExpr>(ParentExpr) ||
isa<DotSelfExpr>(ParentExpr) || // T.self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -491,10 +491,10 @@ self.test("\(testNamePrefix).sorted/DispatchesThrough_withUnsafeMutableBufferPoi
// This sort operation is not in-place.
// The collection is copied into an array before sorting.
expectEqual(
0, lc.log._withUnsafeMutableBufferPointerIfSupported[lc.dynamicType])
0, lc.log._withUnsafeMutableBufferPointerIfSupported[type(of: lc)])
expectEqual(
0,
lc.log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[lc.dynamicType])
lc.log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[type(of: lc)])

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

expectEqual(
1, lc.log._withUnsafeMutableBufferPointerIfSupported[lc.dynamicType])
1, lc.log._withUnsafeMutableBufferPointerIfSupported[type(of: lc)])
expectEqual(
withUnsafeMutableBufferPointerIsSupported ? 1 : 0,
lc.log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[lc.dynamicType])
lc.log._withUnsafeMutableBufferPointerIfSupportedNonNilReturns[type(of: lc)])

expectEqual(4, lc.distance(from: lc.startIndex, to: pivot))
expectEqualsUnordered([1, 2, 3, 4], lc.prefix(upTo: pivot).map { extractValue($0).value })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ extension LoggingType {
}

public var selfType: Any.Type {
return self.dynamicType
return type(of: self)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ public struct ${Self}<T> : ${SelfProtocols} {
self.underestimatedCount = 0
self._elements = []
self._collectionState =
_CollectionState(name: "\(self.dynamicType)", elementCount: 0)
_CollectionState(name: "\(type(of: self))", elementCount: 0)
}

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