blob: 750b5f7a33458c5ba60a7dea701cbf8b7048ccd2 [file] [log] [blame]
//===--- ParseGeneric.cpp - Swift Language Parser for Generics ------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 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
//
//===----------------------------------------------------------------------===//
//
// Generic Parsing
//
//===----------------------------------------------------------------------===//
#include "swift/Parse/Parser.h"
#include "swift/AST/DiagnosticsParse.h"
#include "swift/AST/TypeRepr.h"
#include "swift/Parse/ParsedSyntaxBuilders.h"
#include "swift/Parse/ParsedSyntaxRecorder.h"
#include "swift/Parse/SyntaxParsingContext.h"
using namespace swift;
using namespace swift::syntax;
/// Parse a list of generic parameters.
///
/// generic-parameter-clause-list:
/// generic-parameter-clause generic-parameter-clause*
ParserStatus Parser::parseSILGenericParamsSyntax(
Optional<ParsedGenericParameterClauseListSyntax> &result) {
assert(isInSILMode());
ParserStatus status;
if (!startsWithLess(Tok))
return status;
SmallVector<ParsedGenericParameterClauseSyntax, 1> clauses;
do {
auto result = parseGenericParameterClauseSyntax();
status |= result.getStatus();
if (!result.isNull())
clauses.push_back(result.get());
} while (startsWithLess(Tok));
result = ParsedSyntaxRecorder::makeGenericParameterClauseList(clauses,
*SyntaxContext);
return status;
}
/// Parse a sequence of generic parameters, e.g. '<T: Comparable, U: Container>'
/// along with an optional requires clause.
///
/// generic-parameter-clause:
/// '<' generic-paramter (',' generic-parameter)* where-clause? '>'
///
/// generic-parameter:
/// identifier
/// identifier ':' type
ParsedSyntaxResult<ParsedGenericParameterClauseSyntax>
Parser::parseGenericParameterClauseSyntax() {
assert(startsWithLess(Tok) && "Generic parameter list must start with '<'");
ParsedGenericParameterClauseSyntaxBuilder builder(*SyntaxContext);
ParserStatus status;
// Parse '<'.
SourceLoc LAngleLoc = Tok.getLoc();
builder.useLeftAngleBracket(consumeStartingLessSyntax());
// Parse parameters.
bool hasNext = true;
do {
ParsedGenericParameterSyntaxBuilder paramBuilder(*SyntaxContext);
// Parse attributes.
// TODO: Implement syntax attribute parsing.
Optional<ParsedAttributeListSyntax> attrs;
if (Tok.is(tok::at_sign)) {
SyntaxParsingContext TmpCtxt(SyntaxContext);
TmpCtxt.setTransparent();
auto AttrsLoc = Tok.getLoc();
DeclAttributes attrsAST;
parseDeclAttributeList(attrsAST);
if (!attrsAST.isEmpty())
Generator.addDeclAttributes(attrsAST, AttrsLoc);
attrs = SyntaxContext->popIf<ParsedAttributeListSyntax>();
}
// Parse the name of the parameter.
auto ident = Context.getIdentifier(Tok.getText());
auto name = parseIdentifierSyntax(diag::expected_generics_parameter_name);
if (!name) {
if (attrs) {
paramBuilder.useAttributes(std::move(*attrs));
builder.addGenericParameterListMember(paramBuilder.build());
}
status.setIsParseError();
break;
}
if (attrs)
paramBuilder.useAttributes(std::move(*attrs));
paramBuilder.useName(std::move(*name));
// Parse the ':' followed by a type.
if (Tok.is(tok::colon)) {
paramBuilder.useColon(consumeTokenSyntax(tok::colon));
if (Tok.isAny(tok::identifier, tok::code_complete, tok::kw_protocol,
tok::kw_Any)) {
auto tyResult = parseTypeSyntax();
status |= tyResult.getStatus();
if (auto ty = tyResult.getOrNull())
paramBuilder.useInheritedType(std::move(*ty));
} else {
if (Tok.is(tok::kw_class)) {
diagnose(Tok, diag::unexpected_class_constraint);
diagnose(Tok, diag::suggest_anyobject)
.fixItReplace(Tok.getLoc(), "AnyObject");
auto ty = ParsedSyntaxRecorder::makeClassRestrictionType(
consumeTokenSyntax(tok::kw_class), *SyntaxContext);
paramBuilder.useInheritedType(std::move(ty));
} else {
diagnose(Tok, diag::expected_generics_type_restriction, ident);
paramBuilder.useInheritedType(
ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext));
}
status.setIsParseError();
}
}
// Parse ','
hasNext = Tok.is(tok::comma);
if (hasNext)
paramBuilder.useTrailingComma(consumeTokenSyntax(tok::comma));
builder.addGenericParameterListMember(paramBuilder.build());
} while (hasNext);
// Parse optional where clause.
SourceLoc whereLoc;
if (Tok.is(tok::kw_where)) {
SmallVector<RequirementRepr, 2> requirementAST;
bool FirstTypeInComplete = false;
auto where = parseGenericWhereClauseSyntax(FirstTypeInComplete);
builder.useObsoletedWhereClause(where.get());
}
// Parse the closing '>'.
if (startsWithGreater(Tok)) {
builder.useRightAngleBracket(consumeStartingGreaterSyntax());
} else {
if (!status.isError()) {
diagnose(Tok, diag::expected_rangle_generics_param);
diagnose(LAngleLoc, diag::opening_angle);
}
// Skip until we hit the '>'.
if (ignoreUntilGreaterInTypeList())
builder.useRightAngleBracket(consumeStartingGreaterSyntax());
status.setIsParseError();
}
return makeParsedResult(builder.build(), status);
}
ParserResult<GenericParamList> Parser::parseGenericParameters() {
auto loc = leadingTriviaLoc();
auto syntaxResult = parseGenericParameterClauseSyntax();
if (syntaxResult.isNull())
return syntaxResult.getStatus();
SyntaxContext->addSyntax(syntaxResult.get());
auto clause = SyntaxContext->topNode<GenericParameterClauseSyntax>();
if (clause.getGenericParameterList().empty())
return nullptr;
return makeParserResult(syntaxResult.getStatus(),
Generator.generate(clause, loc));
}
ParserResult<GenericParamList> Parser::maybeParseGenericParams() {
if (!startsWithLess(Tok))
return nullptr;
return parseGenericParameters();
}
ParserResult<GenericParamList> Parser::parseSILGenericParams() {
assert(isInSILMode());
auto loc = leadingTriviaLoc();
Optional<ParsedGenericParameterClauseListSyntax> result;
auto status = parseSILGenericParamsSyntax(result);
if (!result.hasValue()) {
status.setIsParseError();
return status;
}
SyntaxContext->addSyntax(std::move(*result));
auto list = SyntaxContext->topNode<GenericParameterClauseListSyntax>();
auto ret = Generator.generate(list, loc);
if (!ret)
return nullptr;
return makeParserResult(status, ret);
}
void Parser::diagnoseWhereClauseInGenericParamList(
const GenericParamList *GenericParams, SourceLoc whereLoc) {
if (GenericParams == nullptr || GenericParams->getWhereLoc().isInvalid())
return;
auto WhereRangeInsideBrackets = GenericParams->getWhereClauseSourceRange();
// Move everything immediately following the last generic parameter
// as written all the way to the right angle bracket (">")
auto LastGenericParam = GenericParams->getParams().back();
auto EndOfLastGenericParam =
Lexer::getLocForEndOfToken(SourceMgr, LastGenericParam->getEndLoc());
CharSourceRange RemoveWhereRange { SourceMgr,
EndOfLastGenericParam,
GenericParams->getRAngleLoc()
};
auto WhereCharRange =
Lexer::getCharSourceRangeFromSourceRange(SourceMgr,
GenericParams->getWhereClauseSourceRange());
SmallString<64> Buffer;
llvm::raw_svector_ostream WhereClauseText(Buffer);
WhereClauseText << SourceMgr.extractText(whereLoc.isValid()
? WhereCharRange
: RemoveWhereRange);
// If, for some reason, there was a where clause in both locations, we're
// adding to the list of requirements, so tack on a comma here before
// inserting it at the head of the later where clause.
if (Tok.is(tok::kw_where))
WhereClauseText << ',';
auto Diag = diagnose(WhereRangeInsideBrackets.Start,
diag::where_inside_brackets);
Diag.fixItRemoveChars(RemoveWhereRange.getStart(),
RemoveWhereRange.getEnd());
if (whereLoc.isValid()) {
Diag.fixItReplace(whereLoc, WhereClauseText.str());
} else {
Diag.fixItInsert(Lexer::getLocForEndOfToken(SourceMgr, PreviousLoc),
WhereClauseText.str());
}
}
void Parser::diagnoseWhereClauseInGenericParamList(
const GenericParamList *GenericParams) {
SourceLoc whereLoc;
if (Tok.is(tok::kw_where))
whereLoc = Tok.getLoc();
diagnoseWhereClauseInGenericParamList(GenericParams, whereLoc);
}
/// Parse a 'where' clause, which places additional constraints on generic
/// parameters or types based on them.
///
/// where-clause:
/// 'where' generic-requirement (',' generic-requirement) *
///
/// generic-requirement:
/// conformance-requirement
/// same-type-requirement
/// layout-requirement
///
/// conformance-requirement:
/// type ':' type
///
/// same-type-requirement:
/// type '==' type
///
/// layout-requirement:
/// type ':' layout-constraint
ParsedSyntaxResult<ParsedGenericWhereClauseSyntax>
Parser::parseGenericWhereClauseSyntax(bool &FirstTypeInComplete,
bool allowLayoutConstraints) {
ParsedGenericWhereClauseSyntaxBuilder builder(*SyntaxContext);
ParserStatus status;
// Parse 'where'.
builder.useWhereKeyword(consumeTokenSyntax(tok::kw_where));
bool hasNext = true;
do {
auto firstType = parseTypeSyntax();
status |= firstType.getStatus();
FirstTypeInComplete = firstType.hasCodeCompletion();
if (firstType.isNull())
break;
ParsedGenericRequirementSyntaxBuilder elementBuilder(*SyntaxContext);
if (Tok.is(tok::colon)) {
auto colon = consumeTokenSyntax(tok::colon);
if (Tok.is(tok::identifier) &&
getLayoutConstraint(Context.getIdentifier(Tok.getText()), Context)
->isKnownLayout()) {
// Layout constraint.
ParsedLayoutRequirementSyntaxBuilder layoutReqBuilder(*SyntaxContext);
layoutReqBuilder.useLeftTypeIdentifier(firstType.get());
layoutReqBuilder.useColon(std::move(colon));
SourceLoc layoutLoc = Tok.getLoc();
auto layout = parseLayoutConstraintSyntax();
status |= layout.getStatus();
if (!allowLayoutConstraints && !isInSILMode())
diagnose(layoutLoc,
diag::layout_constraints_only_inside_specialize_attr);
assert(!layout.isNull());
layoutReqBuilder.useLayoutConstraint(layout.get());
elementBuilder.useBody(layoutReqBuilder.build());
} else {
// Conformance requirement.
ParsedConformanceRequirementSyntaxBuilder conformanceReqBuilder(
*SyntaxContext);
conformanceReqBuilder.useLeftTypeIdentifier(firstType.get());
conformanceReqBuilder.useColon(std::move(colon));
auto secondType = parseTypeSyntax();
status |= secondType.getStatus();
if (!secondType.isNull())
conformanceReqBuilder.useRightTypeIdentifier(secondType.get());
else
conformanceReqBuilder.useRightTypeIdentifier(
ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext));
elementBuilder.useBody(conformanceReqBuilder.build());
}
} else if ((Tok.isAnyOperator() && Tok.getText() == "==") ||
Tok.is(tok::equal)) {
// Same type requirement.
ParsedSameTypeRequirementSyntaxBuilder sametypeReqBuilder(*SyntaxContext);
sametypeReqBuilder.useLeftTypeIdentifier(firstType.get());
if (Tok.is(tok::equal)) {
diagnose(Tok, diag::requires_single_equal)
.fixItReplace(SourceRange(Tok.getLoc()), "==");
ignoreToken();
} else {
sametypeReqBuilder.useEqualityToken(consumeTokenSyntax());
}
auto secondType = parseTypeSyntax();
status |= secondType.getStatus();
if (!secondType.isNull())
sametypeReqBuilder.useRightTypeIdentifier(secondType.get());
else
sametypeReqBuilder.useRightTypeIdentifier(
ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext));
elementBuilder.useBody(sametypeReqBuilder.build());
} else {
diagnose(Tok, diag::expected_requirement_delim);
status.setIsParseError();
// Fallback to conformance requirement with missing right type.
ParsedConformanceRequirementSyntaxBuilder conformanceReqBuilder(
*SyntaxContext);
conformanceReqBuilder.useLeftTypeIdentifier(firstType.get());
conformanceReqBuilder.useRightTypeIdentifier(
ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext));
elementBuilder.useBody(conformanceReqBuilder.build());
}
// Parse ','.
hasNext = (status.isSuccess() && Tok.is(tok::comma));
if (hasNext)
elementBuilder.useTrailingComma(consumeTokenSyntax());
builder.addRequirementListMember(elementBuilder.build());
} while (hasNext && status.isSuccess());
return makeParsedResult(builder.build(), status);
}
ParserStatus Parser::parseGenericWhereClause(
SourceLoc &whereLoc, SmallVectorImpl<RequirementRepr> &requirements,
bool &FirstTypeInComplete, bool AllowLayoutConstraints) {
auto loc = leadingTriviaLoc();
auto syntaxResult = parseGenericWhereClauseSyntax(FirstTypeInComplete,
AllowLayoutConstraints);
if (syntaxResult.isNull())
return syntaxResult.getStatus();
SyntaxContext->addSyntax(syntaxResult.get());
auto clause = SyntaxContext->topNode<GenericWhereClauseSyntax>();
whereLoc = Generator.generate(clause.getWhereKeyword(), loc);
requirements.reserve(clause.getRequirementList().size());
for (auto elem : clause.getRequirementList()) {
if (auto req = Generator.generate(elem, loc))
requirements.push_back(*req);
}
return syntaxResult.getStatus();
}
/// Parse a free-standing where clause attached to a declaration, adding it to
/// a generic parameter list that may (or may not) already exist.
ParserStatus Parser::
parseFreestandingGenericWhereClause(GenericParamList *genericParams,
WhereClauseKind kind) {
assert(Tok.is(tok::kw_where) && "Shouldn't call this without a where");
// Push the generic arguments back into a local scope so that references will
// find them.
Scope S(this, ScopeKind::Generics);
if (genericParams)
for (auto pd : genericParams->getParams())
addToScope(pd);
SmallVector<RequirementRepr, 4> Requirements;
SourceLoc WhereLoc;
bool FirstTypeInComplete;
auto result = parseGenericWhereClause(WhereLoc, Requirements,
FirstTypeInComplete);
if (result.shouldStopParsing() || Requirements.empty())
return result;
if (!genericParams)
diagnose(WhereLoc, diag::where_without_generic_params, unsigned(kind));
else
genericParams->addTrailingWhereClause(Context, WhereLoc, Requirements);
return ParserStatus();
}
/// Parse a where clause after a protocol or associated type declaration.
ParserStatus Parser::parseProtocolOrAssociatedTypeWhereClause(
TrailingWhereClause *&trailingWhere, bool isProtocol) {
assert(Tok.is(tok::kw_where) && "Shouldn't call this without a where");
SourceLoc whereLoc;
SmallVector<RequirementRepr, 4> requirements;
bool firstTypeInComplete;
auto whereStatus =
parseGenericWhereClause(whereLoc, requirements, firstTypeInComplete);
if (whereStatus.isSuccess()) {
trailingWhere =
TrailingWhereClause::create(Context, whereLoc, requirements);
} else if (whereStatus.hasCodeCompletion()) {
return whereStatus;
}
return ParserStatus();
}