blob: d252ad73b12d24d141237746cb2f7987638314c0 [file] [log] [blame]
//===--- ParseType.cpp - Swift Language Parser for Types ------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Type Parsing and AST Building
//
//===----------------------------------------------------------------------===//
#include "swift/Parse/Parser.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/Attr.h"
#include "swift/AST/TypeLoc.h"
#include "swift/Parse/Lexer.h"
#include "swift/Parse/CodeCompletionCallbacks.h"
#include "swift/Syntax/SyntaxBuilders.h"
#include "swift/Syntax/SyntaxFactory.h"
#include "swift/Syntax/TokenSyntax.h"
#include "swift/Syntax/SyntaxNodes.h"
#include "swift/Syntax/SyntaxParsingContext.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/SaveAndRestore.h"
using namespace swift;
using namespace swift::syntax;
TypeRepr *Parser::applyAttributeToType(TypeRepr *ty,
const TypeAttributes &attrs,
VarDecl::Specifier specifier,
SourceLoc specifierLoc) {
// Apply those attributes that do apply.
if (!attrs.empty())
ty = new (Context) AttributedTypeRepr(attrs, ty);
// Apply 'inout' or '__shared'
if (specifierLoc.isValid()) {
if (auto *fnTR = dyn_cast<FunctionTypeRepr>(ty)) {
// If the input to the function isn't parenthesized, apply the inout
// to the first (only) parameter, as we would in Swift 2. (This
// syntax is deprecated in Swift 3.)
TypeRepr *argsTR = fnTR->getArgsTypeRepr();
if (!isa<TupleTypeRepr>(argsTR)) {
auto *newArgsTR =
new (Context) InOutTypeRepr(argsTR, specifierLoc);
auto *newTR =
new (Context) FunctionTypeRepr(fnTR->getGenericParams(),
newArgsTR,
fnTR->getThrowsLoc(),
fnTR->getArrowLoc(),
fnTR->getResultTypeRepr());
newTR->setGenericEnvironment(fnTR->getGenericEnvironment());
return newTR;
}
}
switch (specifier) {
case VarDecl::Specifier::Owned:
break;
case VarDecl::Specifier::InOut:
ty = new (Context) InOutTypeRepr(ty, specifierLoc);
break;
case VarDecl::Specifier::Shared:
ty = new (Context) SharedTypeRepr(ty, specifierLoc);
break;
case VarDecl::Specifier::Var:
llvm_unreachable("cannot have var as specifier");
break;
}
}
return ty;
}
LayoutConstraint Parser::parseLayoutConstraint(Identifier LayoutConstraintID) {
LayoutConstraint layoutConstraint =
getLayoutConstraint(LayoutConstraintID, Context);
assert(layoutConstraint->isKnownLayout() &&
"Expected layout constraint definition");
if (!layoutConstraint->isTrivial())
return layoutConstraint;
SourceLoc LParenLoc;
if (!consumeIf(tok::l_paren, LParenLoc)) {
// It is a trivial without any size constraints.
return LayoutConstraint::getLayoutConstraint(LayoutConstraintKind::Trivial,
Context);
}
int size = 0;
int alignment = 0;
auto ParseTrivialLayoutConstraintBody = [&] () -> bool {
// Parse the size and alignment.
if (Tok.is(tok::integer_literal)) {
if (Tok.getText().getAsInteger(10, size)) {
diagnose(Tok.getLoc(), diag::layout_size_should_be_positive);
return true;
}
consumeToken();
if (consumeIf(tok::comma)) {
// parse alignment.
if (Tok.is(tok::integer_literal)) {
if (Tok.getText().getAsInteger(10, alignment)) {
diagnose(Tok.getLoc(), diag::layout_alignment_should_be_positive);
return true;
}
consumeToken();
} else {
diagnose(Tok.getLoc(), diag::layout_alignment_should_be_positive);
return true;
}
}
} else {
diagnose(Tok.getLoc(), diag::layout_size_should_be_positive);
return true;
}
return false;
};
if (ParseTrivialLayoutConstraintBody()) {
// There was an error during parsing.
skipUntil(tok::r_paren);
consumeIf(tok::r_paren);
return LayoutConstraint::getUnknownLayout();
}
if (!consumeIf(tok::r_paren)) {
// Expected a closing r_paren.
diagnose(Tok.getLoc(), diag::expected_rparen_layout_constraint);
consumeToken();
return LayoutConstraint::getUnknownLayout();
}
if (size < 0) {
diagnose(Tok.getLoc(), diag::layout_size_should_be_positive);
return LayoutConstraint::getUnknownLayout();
}
if (alignment < 0) {
diagnose(Tok.getLoc(), diag::layout_alignment_should_be_positive);
return LayoutConstraint::getUnknownLayout();
}
// Otherwise it is a trivial layout constraint with
// provided size and alignment.
return LayoutConstraint::getLayoutConstraint(layoutConstraint->getKind(), size,
alignment, Context);
}
/// parseTypeSimple
/// type-simple:
/// type-identifier
/// type-tuple
/// type-composition-deprecated
/// 'Any'
/// type-simple '.Type'
/// type-simple '.Protocol'
/// type-simple '?'
/// type-simple '!'
/// type-collection
/// type-array
ParserResult<TypeRepr> Parser::parseTypeSimple(Diag<> MessageID,
bool HandleCodeCompletion) {
ParserResult<TypeRepr> ty;
if (Tok.is(tok::kw_inout) ||
(Tok.is(tok::identifier) && (Tok.getRawText().equals("__shared") ||
Tok.getRawText().equals("__owned")))) {
// Type specifier should already be parsed before here. This only happens
// for construct like 'P1 & inout P2'.
diagnose(Tok.getLoc(), diag::attr_only_on_parameters_parse, Tok.getText());
consumeToken();
}
switch (Tok.getKind()) {
case tok::kw_Self:
case tok::kw_Any:
case tok::identifier: {
ty = parseTypeIdentifier();
break;
}
case tok::l_paren:
ty = parseTypeTupleBody();
break;
case tok::code_complete:
if (!HandleCodeCompletion)
break;
if (CodeCompletion)
CodeCompletion->completeTypeSimpleBeginning();
// Eat the code completion token because we handled it.
consumeToken(tok::code_complete);
return makeParserCodeCompletionResult<TypeRepr>();
case tok::l_square: {
auto Result = parseTypeCollection();
if (Result.hasSyntax())
SyntaxContext->addSyntax(Result.getSyntax());
ty = Result.getASTResult();
break;
}
case tok::kw_protocol:
if (startsWithLess(peekToken())) {
ty = parseOldStyleProtocolComposition();
break;
}
LLVM_FALLTHROUGH;
default:
{
auto diag = diagnose(Tok, MessageID);
// If the next token is closing or separating, the type was likely forgotten
if (Tok.isAny(tok::r_paren, tok::r_brace, tok::r_square, tok::arrow,
tok::equal, tok::comma, tok::semi))
diag.fixItInsert(getEndOfPreviousLoc(), " <#type#>");
}
if (Tok.isKeyword() && !Tok.isAtStartOfLine()) {
ty = makeParserErrorResult(new (Context) ErrorTypeRepr(Tok.getLoc()));
consumeToken();
return ty;
}
checkForInputIncomplete();
return nullptr;
}
auto makeMetatypeTypeSyntax = [&]() {
if (!SyntaxContext->isEnabled())
return;
MetatypeTypeSyntaxBuilder Builder;
Builder
.useTypeOrProtocol(SyntaxContext->popToken())
.usePeriod(SyntaxContext->popToken())
.useBaseType(SyntaxContext->popIf<TypeSyntax>().getValue());
SyntaxContext->addSyntax(Builder.build());
};
// '.Type', '.Protocol', '?', '!', and '[]' still leave us with type-simple.
while (ty.isNonNull()) {
if ((Tok.is(tok::period) || Tok.is(tok::period_prefix))) {
if (peekToken().isContextualKeyword("Type")) {
consumeToken();
SourceLoc metatypeLoc = consumeToken(tok::identifier);
ty = makeParserResult(ty,
new (Context) MetatypeTypeRepr(ty.get(), metatypeLoc));
makeMetatypeTypeSyntax();
continue;
}
if (peekToken().isContextualKeyword("Protocol")) {
consumeToken();
SourceLoc protocolLoc = consumeToken(tok::identifier);
ty = makeParserResult(ty,
new (Context) ProtocolTypeRepr(ty.get(), protocolLoc));
makeMetatypeTypeSyntax();
continue;
}
}
if (!Tok.isAtStartOfLine()) {
if (isOptionalToken(Tok)) {
auto Result = parseTypeOptional(ty.get());
if (Result.hasSyntax())
SyntaxContext->addSyntax(Result.getSyntax());
ty = Result.getASTResult();
continue;
}
if (isImplicitlyUnwrappedOptionalToken(Tok)) {
auto Result = parseTypeImplicitlyUnwrappedOptional(ty.get());
if (Result.hasSyntax())
SyntaxContext->addSyntax(Result.getSyntax());
ty = Result.getASTResult();
continue;
}
// Parse legacy array types for migration.
if (Tok.is(tok::l_square)) {
ty = parseTypeArray(ty.get());
continue;
}
}
break;
}
return ty;
}
ParserResult<TypeRepr> Parser::parseType() {
return parseType(diag::expected_type);
}
ParserResult<TypeRepr> Parser::parseSILBoxType(GenericParamList *generics,
const TypeAttributes &attrs,
Optional<Scope> &GenericsScope) {
auto LBraceLoc = consumeToken(tok::l_brace);
SmallVector<SILBoxTypeRepr::Field, 4> Fields;
if (!Tok.is(tok::r_brace)) {
for (;;) {
bool Mutable;
if (Tok.is(tok::kw_var)) {
Mutable = true;
} else if (Tok.is(tok::kw_let)) {
Mutable = false;
} else {
diagnose(Tok, diag::sil_box_expected_var_or_let);
return makeParserError();
}
SourceLoc VarOrLetLoc = consumeToken();
auto fieldTy = parseType();
if (!fieldTy.getPtrOrNull())
return makeParserError();
Fields.push_back({VarOrLetLoc, Mutable, fieldTy.get()});
if (consumeIf(tok::comma))
continue;
break;
}
}
if (!Tok.is(tok::r_brace)) {
diagnose(Tok, diag::sil_box_expected_r_brace);
return makeParserError();
}
auto RBraceLoc = consumeToken(tok::r_brace);
// The generic arguments are taken from the enclosing scope. Pop the
// box layout's scope now.
GenericsScope.reset();
SourceLoc LAngleLoc, RAngleLoc;
SmallVector<TypeRepr*, 4> Args;
if (Tok.isContextualPunctuator("<")) {
LAngleLoc = consumeToken();
for (;;) {
auto argTy = parseType();
if (!argTy.getPtrOrNull())
return makeParserError();
Args.push_back(argTy.get());
if (consumeIf(tok::comma))
continue;
break;
}
if (!Tok.isContextualPunctuator(">")) {
diagnose(Tok, diag::sil_box_expected_r_angle);
return makeParserError();
}
RAngleLoc = consumeToken();
}
auto repr = SILBoxTypeRepr::create(Context, generics,
LBraceLoc, Fields, RBraceLoc,
LAngleLoc, Args, RAngleLoc);
return makeParserResult(applyAttributeToType(repr, attrs,
VarDecl::Specifier::Owned,
SourceLoc()));
}
/// parseType
/// type:
/// attribute-list type-composition
/// attribute-list type-function
///
/// type-function:
/// type-composition '->' type
/// type-composition 'throws' '->' type
///
ParserResult<TypeRepr> Parser::parseType(Diag<> MessageID,
bool HandleCodeCompletion,
bool IsSILFuncDecl) {
// Start a context for creating type syntax.
SyntaxParsingContext TypeParsingContext(SyntaxContext,
SyntaxContextKind::Type);
// Parse attributes.
VarDecl::Specifier specifier;
SourceLoc specifierLoc;
TypeAttributes attrs;
parseTypeAttributeList(specifier, specifierLoc, attrs);
Optional<Scope> GenericsScope;
// Parse generic parameters in SIL mode.
GenericParamList *generics = nullptr;
if (isInSILMode()) {
// If this is part of a sil function decl, generic parameters are visible in
// the function body; otherwise, they are visible when parsing the type.
if (!IsSILFuncDecl)
GenericsScope.emplace(this, ScopeKind::Generics);
generics = maybeParseGenericParams().getPtrOrNull();
}
// In SIL mode, parse box types { ... }.
if (isInSILMode() && Tok.is(tok::l_brace)) {
return parseSILBoxType(generics, attrs, GenericsScope);
}
ParserResult<TypeRepr> ty =
parseTypeSimpleOrComposition(MessageID, HandleCodeCompletion);
if (ty.hasCodeCompletion())
return makeParserCodeCompletionResult<TypeRepr>();
if (ty.isNull())
return nullptr;
auto tyR = ty.get();
// Parse a throws specifier.
// Don't consume 'throws', if the next token is not '->', so we can emit a
// more useful diagnostic when parsing a function decl.
SourceLoc throwsLoc;
if (Tok.isAny(tok::kw_throws, tok::kw_rethrows, tok::kw_throw) &&
peekToken().is(tok::arrow)) {
if (Tok.isNot(tok::kw_throws)) {
// 'rethrows' is only allowed on function declarations for now.
// 'throw' is probably a typo for 'throws'.
Diag<> DiagID = Tok.is(tok::kw_rethrows) ?
diag::rethrowing_function_type : diag::throw_in_function_type;
diagnose(Tok.getLoc(), DiagID)
.fixItReplace(Tok.getLoc(), "throws");
if (Tok.is(tok::kw_throw))
Tok.setKind(tok::kw_throws);
}
throwsLoc = consumeToken();
}
if (Tok.is(tok::arrow)) {
// Handle type-function if we have an arrow.
SourceLoc arrowLoc = consumeToken();
ParserResult<TypeRepr> SecondHalf =
parseType(diag::expected_type_function_result);
if (SecondHalf.hasCodeCompletion())
return makeParserCodeCompletionResult<TypeRepr>();
if (SecondHalf.isNull())
return nullptr;
if (SyntaxContext->isEnabled()) {
FunctionTypeSyntaxBuilder Builder;
Builder.useReturnType(SyntaxContext->popIf<TypeSyntax>().getValue());
Builder.useArrow(SyntaxContext->popToken());
if (throwsLoc.isValid())
Builder.useThrowsOrRethrowsKeyword(SyntaxContext->popToken());
auto InputNode = SyntaxContext->popIf<TypeSyntax>().getValue();
if (auto TupleTypeNode = InputNode.getAs<TupleTypeSyntax>()) {
// Decompose TupleTypeSyntax and repack into FunctionType.
Builder
.useLeftParen(TupleTypeNode->getLeftParen())
.useArguments(TupleTypeNode->getElements())
.useRightParen(TupleTypeNode->getRightParen());
} else {
Builder
.addTupleTypeElement(SyntaxFactory::makeTupleTypeElement(InputNode));
}
SyntaxContext->addSyntax(Builder.build());
}
tyR = new (Context) FunctionTypeRepr(generics, tyR, throwsLoc, arrowLoc,
SecondHalf.get());
} else if (generics) {
// Only function types may be generic.
auto brackets = generics->getSourceRange();
diagnose(brackets.Start, diag::generic_non_function);
GenericsScope.reset();
// Forget any generic parameters we saw in the type.
class EraseTypeParamWalker : public ASTWalker {
public:
bool walkToTypeReprPre(TypeRepr *T) override {
if (auto ident = dyn_cast<ComponentIdentTypeRepr>(T)) {
if (auto decl = ident->getBoundDecl()) {
if (auto genericParam = dyn_cast<GenericTypeParamDecl>(decl))
ident->overwriteIdentifier(genericParam->getName());
}
}
return true;
}
} walker;
if (tyR)
tyR->walk(walker);
}
if (specifierLoc.isValid() || !attrs.empty())
SyntaxContext->setCreateSyntax(SyntaxKind::AttributedType);
return makeParserResult(applyAttributeToType(tyR, attrs, specifier,
specifierLoc));
}
bool Parser::parseGenericArguments(SmallVectorImpl<TypeRepr*> &Args,
SourceLoc &LAngleLoc,
SourceLoc &RAngleLoc) {
SyntaxParsingContext GenericArgumentsContext(
SyntaxContext, SyntaxKind::GenericArgumentClause);
// Parse the opening '<'.
assert(startsWithLess(Tok) && "Generic parameter list must start with '<'");
LAngleLoc = consumeStartingLess();
{
SyntaxParsingContext ListContext(SyntaxContext,
SyntaxKind::GenericArgumentList);
while (true) {
SyntaxParsingContext ElementContext(SyntaxContext,
SyntaxKind::GenericArgument);
ParserResult<TypeRepr> Ty = parseType(diag::expected_type);
if (Ty.isNull() || Ty.hasCodeCompletion()) {
// Skip until we hit the '>'.
RAngleLoc = skipUntilGreaterInTypeList();
return true;
}
Args.push_back(Ty.get());
// Parse the comma, if the list continues.
if (!consumeIf(tok::comma))
break;
}
}
if (!startsWithGreater(Tok)) {
checkForInputIncomplete();
diagnose(Tok, diag::expected_rangle_generic_arg_list);
diagnose(LAngleLoc, diag::opening_angle);
// Skip until we hit the '>'.
RAngleLoc = skipUntilGreaterInTypeList();
return true;
} else {
RAngleLoc = consumeStartingGreater();
}
return false;
}
/// parseTypeIdentifier
///
/// type-identifier:
/// identifier generic-args? ('.' identifier generic-args?)*
///
ParserResult<TypeRepr> Parser::parseTypeIdentifier() {
if (Tok.isNot(tok::identifier) && Tok.isNot(tok::kw_Self)) {
// is this the 'Any' type
if (Tok.is(tok::kw_Any)) {
return parseAnyType();
} else if (Tok.is(tok::code_complete)) {
if (CodeCompletion)
CodeCompletion->completeTypeSimpleBeginning();
// Eat the code completion token because we handled it.
consumeToken(tok::code_complete);
return makeParserCodeCompletionResult<IdentTypeRepr>();
}
diagnose(Tok, diag::expected_identifier_for_type);
// If there is a keyword at the start of a new line, we won't want to
// skip it as a recovery but rather keep it.
if (Tok.isKeyword() && !Tok.isAtStartOfLine())
consumeToken();
return nullptr;
}
SyntaxParsingContext IdentTypeCtxt(SyntaxContext, SyntaxContextKind::Type);
ParserStatus Status;
SmallVector<ComponentIdentTypeRepr *, 4> ComponentsR;
SourceLoc EndLoc;
while (true) {
SourceLoc Loc;
Identifier Name;
if (Tok.is(tok::kw_Self)) {
Loc = consumeIdentifier(&Name);
} else {
// FIXME: specialize diagnostic for 'Type': type cannot start with
// 'metatype'
// FIXME: offer a fixit: 'self' -> 'Self'
if (parseIdentifier(Name, Loc, diag::expected_identifier_in_dotted_type))
Status.setIsParseError();
}
if (Loc.isValid()) {
SourceLoc LAngle, RAngle;
SmallVector<TypeRepr*, 8> GenericArgs;
if (startsWithLess(Tok)) {
if (parseGenericArguments(GenericArgs, LAngle, RAngle))
return nullptr;
}
EndLoc = Loc;
ComponentIdentTypeRepr *CompT;
if (!GenericArgs.empty())
CompT = new (Context) GenericIdentTypeRepr(Loc, Name,
Context.AllocateCopy(GenericArgs),
SourceRange(LAngle, RAngle));
else
CompT = new (Context) SimpleIdentTypeRepr(Loc, Name);
ComponentsR.push_back(CompT);
}
SyntaxContext->createNodeInPlace(ComponentsR.size() == 1
? SyntaxKind::SimpleTypeIdentifier
: SyntaxKind::MemberTypeIdentifier);
// Treat 'Foo.<anything>' as an attempt to write a dotted type
// unless <anything> is 'Type'.
if ((Tok.is(tok::period) || Tok.is(tok::period_prefix))) {
if (peekToken().is(tok::code_complete)) {
Status.setHasCodeCompletion();
break;
}
if (!peekToken().isContextualKeyword("Type")
&& !peekToken().isContextualKeyword("Protocol")) {
consumeToken();
continue;
}
} else if (Tok.is(tok::code_complete)) {
if (!Tok.isAtStartOfLine())
Status.setHasCodeCompletion();
break;
}
break;
}
IdentTypeRepr *ITR = nullptr;
if (!ComponentsR.empty()) {
// Lookup element #0 through our current scope chains in case it is some
// thing local (this returns null if nothing is found).
if (auto Entry = lookupInScope(ComponentsR[0]->getIdentifier()))
if (auto *TD = dyn_cast<TypeDecl>(Entry))
ComponentsR[0]->setValue(TD, nullptr);
ITR = IdentTypeRepr::create(Context, ComponentsR);
}
if (Status.hasCodeCompletion() && CodeCompletion) {
if (Tok.isNot(tok::code_complete)) {
// We have a dot.
consumeToken();
CodeCompletion->completeTypeIdentifierWithDot(ITR);
} else {
CodeCompletion->completeTypeIdentifierWithoutDot(ITR);
}
// Eat the code completion token because we handled it.
consumeToken(tok::code_complete);
}
return makeParserResult(Status, ITR);
}
/// parseTypeSimpleOrComposition
///
/// type-composition:
/// type-simple
/// type-composition '&' type-simple
ParserResult<TypeRepr>
Parser::parseTypeSimpleOrComposition(Diag<> MessageID,
bool HandleCodeCompletion) {
SyntaxParsingContext CompositionContext(SyntaxContext, SyntaxContextKind::Type);
// Parse the first type
ParserResult<TypeRepr> FirstType = parseTypeSimple(MessageID,
HandleCodeCompletion);
if (FirstType.hasCodeCompletion())
return makeParserCodeCompletionResult<TypeRepr>();
if (FirstType.isNull() || !Tok.isContextualPunctuator("&"))
return FirstType;
SmallVector<TypeRepr *, 4> Types;
ParserStatus Status(FirstType);
SourceLoc FirstTypeLoc = FirstType.get()->getStartLoc();
SourceLoc FirstAmpersandLoc = Tok.getLoc();
auto addType = [&](TypeRepr *T) {
if (!T) return;
if (auto Comp = dyn_cast<CompositionTypeRepr>(T)) {
// Accept protocol<P1, P2> & P3; explode it.
auto TyRs = Comp->getTypes();
if (!TyRs.empty()) // If empty, is 'Any'; ignore.
Types.append(TyRs.begin(), TyRs.end());
return;
}
Types.push_back(T);
};
addType(FirstType.get());
SyntaxContext->setCreateSyntax(SyntaxKind::CompositionType);
assert(Tok.isContextualPunctuator("&"));
do {
consumeToken(); // consume '&'
if (SyntaxContext->isEnabled() && Status.isSuccess()) {
CompositionTypeElementSyntaxBuilder Builder;
Builder
.useAmpersand(SyntaxContext->popToken())
.useType(SyntaxContext->popIf<TypeSyntax>().getValue());
SyntaxContext->addSyntax(Builder.build());
}
// Parse next type.
ParserResult<TypeRepr> ty =
parseTypeSimple(diag::expected_identifier_for_type, HandleCodeCompletion);
if (ty.hasCodeCompletion())
return makeParserCodeCompletionResult<TypeRepr>();
Status |= ty;
addType(ty.getPtrOrNull());
} while (Tok.isContextualPunctuator("&"));
if (SyntaxContext->isEnabled() && Status.isSuccess()) {
auto LastNode = SyntaxFactory::makeCompositionTypeElement(
SyntaxContext->popIf<TypeSyntax>().getValue(), None);
SyntaxContext->addSyntax(LastNode);
}
SyntaxContext->collectNodesInPlace(SyntaxKind::CompositionTypeElementList);
return makeParserResult(Status, CompositionTypeRepr::create(
Context, Types, FirstTypeLoc, {FirstAmpersandLoc, PreviousLoc}));
}
ParserResult<CompositionTypeRepr>
Parser::parseAnyType() {
SyntaxParsingContext IdentTypeCtxt(SyntaxContext,
SyntaxKind::SimpleTypeIdentifier);
auto Loc = consumeToken(tok::kw_Any);
auto TyR = CompositionTypeRepr::createEmptyComposition(Context, Loc);
return makeParserResult(TyR);
}
/// parseOldStyleProtocolComposition
/// type-composition-deprecated:
/// 'protocol' '<' '>'
/// 'protocol' '<' type-composition-list-deprecated '>'
///
/// type-composition-list-deprecated:
/// type-identifier
/// type-composition-list-deprecated ',' type-identifier
ParserResult<TypeRepr> Parser::parseOldStyleProtocolComposition() {
assert(Tok.is(tok::kw_protocol) && startsWithLess(peekToken()));
// Start a context for creating type syntax.
SyntaxParsingContext TypeParsingContext(SyntaxContext,
SyntaxContextKind::Type);
SourceLoc ProtocolLoc = consumeToken();
SourceLoc LAngleLoc = consumeStartingLess();
// Parse the type-composition-list.
ParserStatus Status;
SmallVector<TypeRepr *, 4> Protocols;
bool IsEmpty = startsWithGreater(Tok);
if (!IsEmpty) {
do {
// Parse the type-identifier.
ParserResult<TypeRepr> Protocol = parseTypeIdentifier();
Status |= Protocol;
if (auto *ident =
dyn_cast_or_null<IdentTypeRepr>(Protocol.getPtrOrNull()))
Protocols.push_back(ident);
} while (consumeIf(tok::comma));
}
// Check for the terminating '>'.
SourceLoc RAngleLoc = PreviousLoc;
if (startsWithGreater(Tok)) {
RAngleLoc = consumeStartingGreater();
} else {
if (Status.isSuccess()) {
diagnose(Tok, diag::expected_rangle_protocol);
diagnose(LAngleLoc, diag::opening_angle);
Status.setIsParseError();
}
// Skip until we hit the '>'.
RAngleLoc = skipUntilGreaterInTypeList(/*protocolComposition=*/true);
}
auto composition = CompositionTypeRepr::create(
Context, Protocols, ProtocolLoc, {LAngleLoc, RAngleLoc});
if (Status.isSuccess()) {
// Only if we have complete protocol<...> construct, diagnose deprecated.
SmallString<32> replacement;
if (Protocols.empty()) {
replacement = "Any";
} else {
auto extractText = [&](TypeRepr *Ty) -> StringRef {
auto SourceRange = Ty->getSourceRange();
return SourceMgr.extractText(
Lexer::getCharSourceRangeFromSourceRange(SourceMgr, SourceRange));
};
auto Begin = Protocols.begin();
replacement += extractText(*Begin);
while (++Begin != Protocols.end()) {
replacement += " & ";
replacement += extractText(*Begin);
}
}
if (Protocols.size() > 1) {
// Need parenthesis if the next token looks like postfix TypeRepr.
// i.e. '?', '!', '.Type', '.Protocol'
bool needParen = false;
needParen |= !Tok.isAtStartOfLine() &&
(isOptionalToken(Tok) || isImplicitlyUnwrappedOptionalToken(Tok));
needParen |= Tok.isAny(tok::period, tok::period_prefix);
if (needParen) {
replacement.insert(replacement.begin(), '(');
replacement += ")";
}
}
// Copy split token after '>' to the replacement string.
// FIXME: lexer should smartly separate '>' and trailing contents like '?'.
StringRef TrailingContent = L->getTokenAt(RAngleLoc).getRange().str().
substr(1);
if (!TrailingContent.empty()) {
replacement += TrailingContent;
}
auto isThree = Context.isSwiftVersion3();
#define THREEIFY(MESSAGE) (isThree ? diag::swift3_##MESSAGE : diag::MESSAGE)
// Replace 'protocol<T1, T2>' with 'T1 & T2'
diagnose(ProtocolLoc,
IsEmpty ? THREEIFY(deprecated_any_composition) :
Protocols.size() > 1 ? THREEIFY(deprecated_protocol_composition) :
THREEIFY(deprecated_protocol_composition_single))
.highlight(composition->getSourceRange())
.fixItReplace(composition->getSourceRange(), replacement);
#undef THREEIFY
}
return makeParserResult(Status, composition);
}
/// parseTypeTupleBody
/// type-tuple:
/// '(' type-tuple-body? ')'
/// type-tuple-body:
/// type-tuple-element (',' type-tuple-element)* '...'?
/// type-tuple-element:
/// identifier ':' type
/// type
ParserResult<TupleTypeRepr> Parser::parseTypeTupleBody() {
SyntaxParsingContext TypeContext(SyntaxContext, SyntaxKind::TupleType);
Parser::StructureMarkerRAII ParsingTypeTuple(*this, Tok);
SourceLoc RPLoc, LPLoc = consumeToken(tok::l_paren);
SourceLoc EllipsisLoc;
unsigned EllipsisIdx;
SmallVector<TupleTypeReprElement, 8> ElementsR;
ParserStatus Status = parseList(tok::r_paren, LPLoc, RPLoc,
/*AllowSepAfterLast=*/false,
diag::expected_rparen_tuple_type_list,
SyntaxKind::TupleTypeElementList,
[&] () -> ParserStatus {
TupleTypeReprElement element;
// 'inout' here can be a obsoleted use of the marker in an argument list,
// consume it in backtracking context so we can determine it's really a
// deprecated use of it.
llvm::Optional<BacktrackingScope> Backtracking;
SourceLoc ObsoletedInOutLoc;
if (Tok.is(tok::kw_inout)) {
Backtracking.emplace(*this);
ObsoletedInOutLoc = consumeToken(tok::kw_inout);
}
// If the tuple element starts with a potential argument label followed by a
// ':' or another potential argument label, then the identifier is an
// element tag, and it is followed by a type annotation.
if (Tok.canBeArgumentLabel() &&
(peekToken().is(tok::colon) || peekToken().canBeArgumentLabel())) {
if (Backtracking)
// Found obsoleted 'inout' use.
Backtracking->cancelBacktrack();
// Consume a name.
if (!Tok.is(tok::kw__))
element.Name = Context.getIdentifier(Tok.getText());
element.NameLoc = consumeToken();
// If there is a second name, consume it as well.
if (Tok.canBeArgumentLabel()) {
if (!Tok.is(tok::kw__))
element.SecondName = Context.getIdentifier(Tok.getText());
element.SecondNameLoc = consumeToken();
}
// Consume the ':'.
if (!consumeIf(tok::colon, element.ColonLoc))
diagnose(Tok, diag::expected_parameter_colon);
} else if (Backtracking) {
// If we don't have labels, 'inout' is not a obsoleted use.
ObsoletedInOutLoc = SourceLoc();
}
Backtracking.reset();
// Parse the type annotation.
auto type = parseType(diag::expected_type);
if (type.hasCodeCompletion())
return makeParserCodeCompletionStatus();
if (type.isNull())
return makeParserError();
element.Type = type.get();
// Complain obsoleted 'inout' position; (inout name: Ty)
if (ObsoletedInOutLoc.isValid()) {
if (isa<InOutTypeRepr>(element.Type) ||
isa<SharedTypeRepr>(element.Type)) {
// If the parsed type is already a inout type et al, just remove it.
diagnose(Tok, diag::parameter_specifier_repeated)
.fixItRemove(ObsoletedInOutLoc);
} else {
diagnose(ObsoletedInOutLoc, diag::inout_as_attr_disallowed, "'inout'")
.fixItRemove(ObsoletedInOutLoc)
.fixItInsert(element.Type->getStartLoc(), "inout ");
// Build inout type. Note that we bury the inout locator within the
// named locator. This is weird but required by Sema apparently.
element.Type =
new (Context) InOutTypeRepr(element.Type, ObsoletedInOutLoc);
}
}
// Parse optional '...'.
if (Tok.isEllipsis()) {
auto ElementEllipsisLoc = consumeToken();
if (EllipsisLoc.isInvalid()) {
EllipsisLoc = ElementEllipsisLoc;
EllipsisIdx = ElementsR.size();
} else {
diagnose(ElementEllipsisLoc, diag::multiple_ellipsis_in_tuple)
.highlight(EllipsisLoc)
.fixItRemove(ElementEllipsisLoc);
}
}
// Parse '= expr' here so we can complain about it directly, rather
// than dying when we see it.
if (Tok.is(tok::equal)) {
SyntaxParsingContext InitContext(SyntaxContext,
SyntaxKind::InitializerClause);
SourceLoc equalLoc = consumeToken(tok::equal);
auto init = parseExpr(diag::expected_init_value);
auto inFlight = diagnose(equalLoc, diag::tuple_type_init);
if (init.isNonNull())
inFlight.fixItRemove(SourceRange(equalLoc, init.get()->getEndLoc()));
}
// Record the ',' location.
if (Tok.is(tok::comma))
element.TrailingCommaLoc = Tok.getLoc();
ElementsR.push_back(element);
return makeParserSuccess();
});
if (EllipsisLoc.isInvalid())
EllipsisIdx = ElementsR.size();
bool isFunctionType = Tok.isAny(tok::arrow, tok::kw_throws,
tok::kw_rethrows);
// If there were any labels, figure out which labels should go into the type
// representation.
for (auto &element : ElementsR) {
// True tuples have labels.
if (!isFunctionType) {
// If there were two names, complain.
if (element.NameLoc.isValid() && element.SecondNameLoc.isValid()) {
auto diag = diagnose(element.NameLoc, diag::tuple_type_multiple_labels);
if (element.Name.empty()) {
diag.fixItRemoveChars(element.NameLoc,
element.Type->getStartLoc());
} else {
diag.fixItRemove(
SourceRange(Lexer::getLocForEndOfToken(SourceMgr, element.NameLoc),
element.SecondNameLoc));
}
}
continue;
}
// If there was a first name, complain; arguments in function types are
// always unlabeled.
if (element.NameLoc.isValid() && !element.Name.empty()) {
auto diag = diagnose(element.NameLoc, diag::function_type_argument_label,
element.Name);
if (element.SecondNameLoc.isInvalid())
diag.fixItInsert(element.NameLoc, "_ ");
else if (element.SecondName.empty())
diag.fixItRemoveChars(element.NameLoc,
element.Type->getStartLoc());
else
diag.fixItReplace(SourceRange(element.NameLoc), "_");
}
if (element.SecondNameLoc.isValid()) {
// Form the named parameter type representation.
element.UnderscoreLoc = element.NameLoc;
element.Name = element.SecondName;
element.NameLoc = element.SecondNameLoc;
}
}
return makeParserResult(Status,
TupleTypeRepr::create(Context, ElementsR,
SourceRange(LPLoc, RPLoc),
EllipsisLoc, EllipsisIdx));
}
/// parseTypeArray - Parse the type-array production, given that we
/// are looking at the initial l_square. Note that this index
/// clause is actually the outermost (first-indexed) clause.
///
/// type-array:
/// type-simple
/// type-array '[' ']'
/// type-array '[' expr ']'
///
ParserResult<TypeRepr> Parser::parseTypeArray(TypeRepr *Base) {
assert(Tok.isFollowingLSquare());
Parser::StructureMarkerRAII ParsingArrayBound(*this, Tok);
SourceLoc lsquareLoc = consumeToken();
ArrayTypeRepr *ATR = nullptr;
// Handle a postfix [] production, a common typo for a C-like array.
// If we have something that might be an array size expression, parse it as
// such, for better error recovery.
if (Tok.isNot(tok::r_square)) {
auto sizeEx = parseExprBasic(diag::expected_expr);
if (sizeEx.hasCodeCompletion())
return makeParserCodeCompletionStatus();
if (sizeEx.isNull())
return makeParserErrorResult(Base);
}
SourceLoc rsquareLoc;
if (parseMatchingToken(tok::r_square, rsquareLoc,
diag::expected_rbracket_array_type, lsquareLoc))
return makeParserErrorResult(Base);
// If we parsed something valid, diagnose it with a fixit to rewrite it to
// Swift syntax.
diagnose(lsquareLoc, diag::new_array_syntax)
.fixItInsert(Base->getStartLoc(), "[")
.fixItRemove(lsquareLoc);
// Build a normal array slice type for recovery.
ATR = new (Context) ArrayTypeRepr(Base,
SourceRange(Base->getStartLoc(), rsquareLoc));
return makeParserResult(ATR);
}
SyntaxParserResult<TypeSyntax, TypeRepr> Parser::parseTypeCollection() {
ParserStatus Status;
// Parse the leading '['.
assert(Tok.is(tok::l_square));
Parser::StructureMarkerRAII parsingCollection(*this, Tok);
SourceLoc lsquareLoc = consumeToken();
// Parse the element type.
ParserResult<TypeRepr> firstTy = parseType(diag::expected_element_type);
Status |= firstTy;
// If there is a ':', this is a dictionary type.
SourceLoc colonLoc;
ParserResult<TypeRepr> secondTy;
if (Tok.is(tok::colon)) {
colonLoc = consumeToken();
// Parse the second type.
secondTy = parseType(diag::expected_dictionary_value_type);
Status |= secondTy;
}
// Parse the closing ']'.
SourceLoc rsquareLoc;
if (parseMatchingToken(tok::r_square, rsquareLoc,
colonLoc.isValid()
? diag::expected_rbracket_dictionary_type
: diag::expected_rbracket_array_type,
lsquareLoc))
Status.setIsParseError();
if (Status.hasCodeCompletion())
return Status;
// If we couldn't parse anything for one of the types, propagate the error.
if (Status.isError())
return makeParserError();
TypeRepr *TyR;
llvm::Optional<TypeSyntax> SyntaxNode;
SourceRange brackets(lsquareLoc, rsquareLoc);
if (colonLoc.isValid()) {
// Form the dictionary type.
TyR = new (Context)
DictionaryTypeRepr(firstTy.get(), secondTy.get(), colonLoc, brackets);
if (SyntaxContext->isEnabled()) {
DictionaryTypeSyntaxBuilder Builder;
Builder
.useRightSquareBracket(SyntaxContext->popToken())
.useValueType(SyntaxContext->popIf<TypeSyntax>().getValue())
.useColon(SyntaxContext->popToken())
.useKeyType(SyntaxContext->popIf<TypeSyntax>().getValue())
.useLeftSquareBracket(SyntaxContext->popToken());
SyntaxNode.emplace(Builder.build());
}
} else {
// Form the array type.
TyR = new (Context) ArrayTypeRepr(firstTy.get(), brackets);
if (SyntaxContext->isEnabled()) {
ArrayTypeSyntaxBuilder Builder;
Builder
.useRightSquareBracket(SyntaxContext->popToken())
.useElementType(SyntaxContext->popIf<TypeSyntax>().getValue())
.useLeftSquareBracket(SyntaxContext->popToken());
SyntaxNode.emplace(Builder.build());
}
}
return makeSyntaxResult(Status, SyntaxNode, TyR);
}
bool Parser::isOptionalToken(const Token &T) const {
// A postfix '?' by itself is obviously optional.
if (T.is(tok::question_postfix))
return true;
// A postfix or bound infix operator token that begins with '?' can be
// optional too. We'll munch off the '?', so long as it is left-bound with
// the type (i.e., parsed as a postfix or unspaced binary operator).
if ((T.is(tok::oper_postfix) || T.is(tok::oper_binary_unspaced)) &&
T.getText().startswith("?"))
return true;
return false;
}
bool Parser::isImplicitlyUnwrappedOptionalToken(const Token &T) const {
// A postfix '!' by itself, or a '!' in SIL mode, is obviously implicitly
// unwrapped optional.
if (T.is(tok::exclaim_postfix) || T.is(tok::sil_exclamation))
return true;
// A postfix or bound infix operator token that begins with '!' can be
// implicitly unwrapped optional too. We'll munch off the '!', so long as it
// is left-bound with the type (i.e., parsed as a postfix or unspaced binary
// operator).
if ((T.is(tok::oper_postfix) || T.is(tok::oper_binary_unspaced)) &&
T.getText().startswith("!"))
return true;
return false;
}
SourceLoc Parser::consumeOptionalToken() {
assert(isOptionalToken(Tok) && "not a '?' token?!");
return consumeStartingCharacterOfCurrentToken(tok::question_postfix);
}
SourceLoc Parser::consumeImplicitlyUnwrappedOptionalToken() {
assert(isImplicitlyUnwrappedOptionalToken(Tok) && "not a '!' token?!");
// If the text of the token is just '!', grab the next token.
return consumeStartingCharacterOfCurrentToken(tok::exclaim_postfix);
}
/// Parse a single optional suffix, given that we are looking at the
/// question mark.
SyntaxParserResult<TypeSyntax, OptionalTypeRepr>
Parser::parseTypeOptional(TypeRepr *base) {
SourceLoc questionLoc = consumeOptionalToken();
auto TyR = new (Context) OptionalTypeRepr(base, questionLoc);
llvm::Optional<TypeSyntax> SyntaxNode;
if (SyntaxContext->isEnabled()) {
OptionalTypeSyntaxBuilder Builder;
Builder
.useQuestionMark(SyntaxContext->popToken())
.useWrappedType(SyntaxContext->popIf<TypeSyntax>().getValue());
SyntaxNode.emplace(Builder.build());
}
return makeSyntaxResult(SyntaxNode, TyR);
}
/// Parse a single implicitly unwrapped optional suffix, given that we
/// are looking at the exclamation mark.
SyntaxParserResult<TypeSyntax, ImplicitlyUnwrappedOptionalTypeRepr>
Parser::parseTypeImplicitlyUnwrappedOptional(TypeRepr *base) {
SourceLoc exclamationLoc = consumeImplicitlyUnwrappedOptionalToken();
auto TyR =
new (Context) ImplicitlyUnwrappedOptionalTypeRepr(base, exclamationLoc);
llvm::Optional<TypeSyntax> SyntaxNode;
if (SyntaxContext->isEnabled()) {
ImplicitlyUnwrappedOptionalTypeSyntaxBuilder Builder;
Builder
.useExclamationMark(SyntaxContext->popToken())
.useWrappedType(SyntaxContext->popIf<TypeSyntax>().getValue());
SyntaxNode.emplace(Builder.build());
}
return makeSyntaxResult(SyntaxNode, TyR);
}
//===----------------------------------------------------------------------===//
// Speculative type list parsing
//===----------------------------------------------------------------------===//
static bool isGenericTypeDisambiguatingToken(Parser &P) {
auto &tok = P.Tok;
switch (tok.getKind()) {
default:
return false;
case tok::r_paren:
case tok::r_square:
case tok::l_brace:
case tok::r_brace:
case tok::period:
case tok::period_prefix:
case tok::comma:
case tok::semi:
case tok::eof:
case tok::code_complete:
case tok::exclaim_postfix:
case tok::question_postfix:
case tok::colon:
return true;
case tok::oper_binary_spaced:
if (tok.getText() == "&")
return true;
LLVM_FALLTHROUGH;
case tok::oper_binary_unspaced:
case tok::oper_postfix:
// These might be '?' or '!' type modifiers.
return P.isOptionalToken(tok) || P.isImplicitlyUnwrappedOptionalToken(tok);
case tok::l_paren:
case tok::l_square:
// These only apply to the generic type if they don't start a new line.
return !tok.isAtStartOfLine();
}
}
bool Parser::canParseAsGenericArgumentList() {
if (!Tok.isAnyOperator() || !Tok.getText().equals("<"))
return false;
BacktrackingScope backtrack(*this);
if (canParseGenericArguments())
return isGenericTypeDisambiguatingToken(*this);
return false;
}
bool Parser::canParseGenericArguments() {
// Parse the opening '<'.
if (!startsWithLess(Tok))
return false;
consumeStartingLess();
do {
if (!canParseType())
return false;
// Parse the comma, if the list continues.
} while (consumeIf(tok::comma));
if (!startsWithGreater(Tok)) {
return false;
} else {
consumeStartingGreater();
return true;
}
}
bool Parser::canParseType() {
// Accept 'inout' at for better recovery.
consumeIf(tok::kw_inout);
switch (Tok.getKind()) {
case tok::kw_Self:
case tok::kw_Any:
if (!canParseTypeIdentifier())
return false;
break;
case tok::kw_protocol: // Deprecated composition syntax
case tok::identifier:
if (!canParseTypeIdentifierOrTypeComposition())
return false;
break;
case tok::l_paren: {
consumeToken();
if (!canParseTypeTupleBody())
return false;
break;
}
case tok::at_sign: {
consumeToken();
if (!canParseTypeAttribute())
return false;
return canParseType();
}
case tok::l_square:
consumeToken();
if (!canParseType())
return false;
if (consumeIf(tok::colon)) {
if (!canParseType())
return false;
}
if (!consumeIf(tok::r_square))
return false;
break;
default:
return false;
}
// '.Type', '.Protocol', '?', and '!' still leave us with type-simple.
while (true) {
if ((Tok.is(tok::period) || Tok.is(tok::period_prefix)) &&
(peekToken().isContextualKeyword("Type")
|| peekToken().isContextualKeyword("Protocol"))) {
consumeToken();
consumeToken(tok::identifier);
continue;
}
if (isOptionalToken(Tok)) {
consumeOptionalToken();
continue;
}
if (isImplicitlyUnwrappedOptionalToken(Tok)) {
consumeImplicitlyUnwrappedOptionalToken();
continue;
}
break;
}
// Handle type-function if we have an arrow or 'throws'/'rethrows' modifier.
if (Tok.isAny(tok::kw_throws, tok::kw_rethrows)) {
consumeToken();
// "throws" or "rethrows" isn't a valid type without being followed by
// a return.
if (!Tok.is(tok::arrow))
return false;
}
if (consumeIf(tok::arrow)) {
if (!canParseType())
return false;
return true;
}
return true;
}
bool Parser::canParseTypeIdentifierOrTypeComposition() {
if (Tok.is(tok::kw_protocol))
return canParseOldStyleProtocolComposition();
while (true) {
if (!canParseTypeIdentifier())
return false;
if (Tok.isContextualPunctuator("&")) {
consumeToken();
continue;
} else {
return true;
}
}
}
bool Parser::canParseTypeIdentifier() {
while (true) {
if (!Tok.isAny(tok::identifier, tok::kw_Self, tok::kw_Any))
return false;
consumeToken();
if (startsWithLess(Tok)) {
if (!canParseGenericArguments())
return false;
}
// Treat 'Foo.<anything>' as an attempt to write a dotted type
// unless <anything> is 'Type'.
if ((Tok.is(tok::period) || Tok.is(tok::period_prefix)) &&
!peekToken().isContextualKeyword("Type") &&
!peekToken().isContextualKeyword("Protocol")) {
consumeToken();
} else {
return true;
}
}
}
bool Parser::canParseOldStyleProtocolComposition() {
consumeToken(tok::kw_protocol);
// Check for the starting '<'.
if (!startsWithLess(Tok)) {
return false;
}
consumeStartingLess();
// Check for empty protocol composition.
if (startsWithGreater(Tok)) {
consumeStartingGreater();
return true;
}
// Parse the type-composition-list.
do {
if (!canParseTypeIdentifier()) {
return false;
}
} while (consumeIf(tok::comma));
// Check for the terminating '>'.
if (!startsWithGreater(Tok)) {
return false;
}
consumeStartingGreater();
return true;
}
bool Parser::canParseTypeTupleBody() {
if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::r_brace) &&
Tok.isNotEllipsis() && !isStartOfDecl()) {
do {
// The contextual inout marker is part of argument lists.
consumeIf(tok::kw_inout);
// If the tuple element starts with "ident :", then it is followed
// by a type annotation.
if (Tok.canBeArgumentLabel() &&
(peekToken().is(tok::colon) || peekToken().canBeArgumentLabel())) {
consumeToken();
if (Tok.canBeArgumentLabel()) {
consumeToken();
if (!Tok.is(tok::colon)) return false;
}
consumeToken(tok::colon);
// Parse a type.
if (!canParseType())
return false;
// Parse default values. This aren't actually allowed, but we recover
// better if we skip over them.
if (consumeIf(tok::equal)) {
while (Tok.isNot(tok::eof) && Tok.isNot(tok::r_paren) &&
Tok.isNot(tok::r_brace) && Tok.isNotEllipsis() &&
Tok.isNot(tok::comma) &&
!isStartOfDecl()) {
skipSingle();
}
}
continue;
}
// Otherwise, this has to be a type.
if (!canParseType())
return false;
if (Tok.isEllipsis())
consumeToken();
} while (consumeIf(tok::comma));
}
return consumeIf(tok::r_paren);
}