Skip to content
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

Add new syntax support for generic structs #1213

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
7 changes: 5 additions & 2 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1549,15 +1549,18 @@ def err_unbalanced_parentheses_in_where_clause : Error<
def err_expected_list_of_types_expr_for_generic_function : Error<
"expected a list of type arguments for a generic function">;

def err_type_function_comma_or_greater_expected : Error<
"expected , or >">;
def err_type_function_comma_or_closing_expected : Error<
"expected , or %0">;

def err_num_type_args_params_mistmatch : Error<
"generic struct takes %0 type arguments, but instantiation provided %1">;

def err_expected_type_argument_list_for_generic_instance : Error<
"expected a type argument list for a generic struct type">;

def err_mismatched_brackets : Error<
"found '<' after _TyArgs, use '(' instead">;

def err_expected_type_argument_list_for_itype_generic_instance : Error<
"expected a type argument list for a struct with a bounds-safe interface in a checked scope">;

Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -2085,7 +2085,7 @@ class Parser : public CodeCompletionHandler {
ExprResult ParseGenericFunctionApplication(ExprResult TypeFunc, SourceLocation Loc);

using TypeArgVector = SmallVector<TypeArgument, 4>;
std::pair<bool, TypeArgVector> ParseGenericTypeArgumentList(SourceLocation Loc);
std::pair<bool, TypeArgVector> ParseGenericTypeArgumentList(SourceLocation Loc, bool IsTyArgs=false);

QualType SubstituteTypeVariable(QualType QT,
SmallVector<TypeArgument, 4> &typeNames);
Expand Down
33 changes: 27 additions & 6 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2120,12 +2120,12 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
}

/// Checked C: parse an application of the 'Base' 'RecordDecl' to a number of type
/// arguments that are yet to be parsed.
/// arguments that are yet to be parsed. This function can handle both old and new syntax.
/// 'IsItypeGeneric' is true if we're parsing a type application where the base type
/// is an "itype generic" (as opposed to a regular generic). This is used when generatingq
/// error messages.
///
/// generic-struct-instantiation
/// generic-struct-typeargs-instantiation
/// '<' type-name-list '>'
///
/// type-name-list
Expand All @@ -2135,14 +2135,35 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
/// ',' type-name type-name-list-suffix [opt]
DeclResult Parser::ParseRecordTypeApplication(RecordDecl *Base, bool IsItypeGeneric) {
assert(Base->isGenericOrItypeGeneric() && "Instantiated record must be generic");
if (Tok.isNot(tok::less)) {
bool IsTyArgs = Tok.is(tok::kw__TyArgs);
if (Tok.isNot(tok::less) && !IsTyArgs) {
if (IsItypeGeneric) Diag(Tok.getLocation(), diag::err_expected_type_argument_list_for_itype_generic_instance);
else Diag(Tok.getLocation(), diag::err_expected_type_argument_list_for_generic_instance);
SkipUntil(tok::greater, StopAtSemi);
if (IsTyArgs)
SkipUntil(tok::r_paren, StopAtSemi);
else
SkipUntil(tok::greater, StopAtSemi);
return true;
}
ConsumeToken(); // eat '<'
auto ArgsRes = ParseGenericTypeArgumentList(SourceLocation());
if (IsTyArgs) {
// Skip the _TyArgs keyword.
ConsumeToken();
if (Tok.is(tok::greater)) {
Diag(Tok.getLocation(), diag::err_mismatched_brackets);
SkipUntil(tok::greater, StopAtSemi);
return true;
}
if (Tok.isNot(tok::l_paren)) {
Diag(Tok, diag::err_expected_lparen_after) << "_TyArgs";
SkipUntil(tok::r_paren, StopAtSemi);
return true;
}
}
if (IsTyArgs)
ConsumeParen(); // eat '('
else
ConsumeToken(); // eat '<'
auto ArgsRes = ParseGenericTypeArgumentList(SourceLocation(), IsTyArgs);
if (ArgsRes.first) {
// Problem while parsing the type arguments (error is produced by 'ParseGenericTypeArgumentList')
return true;
Expand Down
29 changes: 21 additions & 8 deletions clang/lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3816,18 +3816,24 @@ ExprResult Parser::ParseBoundsExpression() {
// This is re-used for both generic functions and generic structs.
// Return false if parsing succeeds, in which case the 'typeArgs' is also populated.
// Return true if parsing fails.
std::pair<bool, Parser::TypeArgVector> Parser::ParseGenericTypeArgumentList(SourceLocation Loc) {
std::pair<bool, Parser::TypeArgVector> Parser::ParseGenericTypeArgumentList(SourceLocation Loc, bool IsTyArgs) {
Parser::TypeArgVector typeArgumentInfos;
auto err = std::make_pair<>(true, Parser::TypeArgVector());
auto firstTypeArgument = true;
// Expect to see a list of type names, followed by a '>'.
while (Tok.getKind() != tok::greater) {
tok::TokenKind ExpectedEndToken = IsTyArgs ? tok::r_paren : tok::greater;
StringRef ExpectedEndTokenStr = IsTyArgs ? ")" : ">";
while (Tok.getKind() != ExpectedEndToken) {
if (!firstTypeArgument) {
if (ExpectAndConsume(tok::comma,
diag::err_type_function_comma_or_greater_expected)) {
// We want to consume greater, but not consume semi
SkipUntil(tok::greater, StopAtSemi | StopBeforeMatch);
diag::err_type_function_comma_or_closing_expected, ExpectedEndTokenStr)) {
// We want to consume greater or r_paren, but not consume semi
if (IsTyArgs)
SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch);
else
SkipUntil(tok::greater, StopAtSemi | StopBeforeMatch);
if (Tok.getKind() == tok::greater) ConsumeToken();
if (Tok.getKind() == tok::r_paren) ConsumeParen();
return err;
}
} else
Expand All @@ -3838,16 +3844,23 @@ std::pair<bool, Parser::TypeArgVector> Parser::ParseGenericTypeArgumentList(Sour
if (Ty.isInvalid()) {
// We do not need to write an error message since ParseTypeName does.
// We want to consume greater, but not consume semi
SkipUntil(tok::greater, StopAtSemi | StopBeforeMatch);
if (IsTyArgs)
SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch);
else
SkipUntil(tok::greater, StopAtSemi | StopBeforeMatch);
if (Tok.getKind() == tok::greater) ConsumeToken();
if (Tok.getKind() == tok::r_paren) ConsumeParen();
return err;
}

TypeSourceInfo *TInfo;
QualType realType = Actions.GetTypeFromParser(Ty.get(), &TInfo);
typeArgumentInfos.push_back({ realType, TInfo });
}
ConsumeToken(); // consume '>' token
if (IsTyArgs)
ConsumeParen(); // consume ')' token
else
ConsumeToken(); // consume '>' token

return std::make_pair(false, typeArgumentInfos);
}
Expand Down Expand Up @@ -3876,7 +3889,7 @@ Parser::ParseGenericMacroTypeArgumentList(SourceLocation Loc) {
if (Tok.getKind() == tok::comma) { // consume comma
ConsumeToken();
} else if (Tok.getKind() != tok::r_paren && !typeArgumentInfos.empty()) {
Diag(Tok, diag::err_type_function_comma_or_greater_expected);
Diag(Tok, diag::err_type_function_comma_or_closing_expected);
return handleError();
}
} else {
Expand Down