From b41ae2b324cd76e9504e8609af519f010791e1de Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 5 Aug 2025 22:00:02 +0200 Subject: [PATCH 1/3] Inject the __func__ symbol during parsing in C mode --- src/parser/cxx/parser.cc | 21 +++++++++++++++++++++ tests/unit_tests/sema/struct_c_01.c | 1 + 2 files changed, 22 insertions(+) diff --git a/src/parser/cxx/parser.cc b/src/parser/cxx/parser.cc index 07a8f9be..054f3883 100644 --- a/src/parser/cxx/parser.cc +++ b/src/parser/cxx/parser.cc @@ -3426,10 +3426,31 @@ auto Parser::parse_compound_statement(CompoundStatementAST*& yyast, bool skip) if (!match(TokenKind::T_LBRACE, lbraceLoc)) return false; + FunctionSymbol* functionSymbol = + symbol_cast(scope()->owner()); + + if (!functionSymbol && scope()->isFunctionParametersScope()) { + functionSymbol = + symbol_cast(scope()->owner()->enclosingSymbol()); + } + auto _ = Binder::ScopeGuard{&binder_}; auto blockSymbol = binder_.enterBlock(lbraceLoc); + if (functionSymbol && is_parsing_c()) { + auto functionName = to_string(functionSymbol->name()); + + auto func = control()->newVariableSymbol(scope(), lbraceLoc); + func->setName(control()->getIdentifier("__func__")); + func->setType(functionSymbol->type()); + func->setType(control()->getBoundedArrayType(control()->getCharType(), + functionName.size() + 1)); + func->setConstexpr(true); + func->setConstValue(control()->stringLiteral(functionName)); + scope()->addSymbol(func); + } + auto ast = make_node(pool_); yyast = ast; diff --git a/tests/unit_tests/sema/struct_c_01.c b/tests/unit_tests/sema/struct_c_01.c index f5666c31..c812e7be 100644 --- a/tests/unit_tests/sema/struct_c_01.c +++ b/tests/unit_tests/sema/struct_c_01.c @@ -32,6 +32,7 @@ int main() { // CHECK-NEXT: field int x // CHECK-NEXT: function int main() // CHECK-NEXT: block +// CHECK-NEXT: variable constexpr char __func__[5] // CHECK-NEXT: enum K : int // CHECK-NEXT: enumerator K w = 0 // CHECK-NEXT: class B From abb9c8269b74f2775203be724d3e1bcdc2658f11 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 5 Aug 2025 22:33:21 +0200 Subject: [PATCH 2/3] Improve parsing of the builtin `offsetof` expression --- packages/cxx-frontend/src/AST.ts | 45 +++++++++-- .../cxx-frontend/src/RecursiveASTVisitor.ts | 4 +- src/parser/cxx/ast.cc | 6 +- src/parser/cxx/ast.fbs | 4 +- src/parser/cxx/ast.h | 4 +- src/parser/cxx/ast_interpreter.cc | 1 - src/parser/cxx/ast_pretty_printer.cc | 9 ++- src/parser/cxx/ast_printer.cc | 11 ++- src/parser/cxx/ast_rewriter.cc | 11 ++- src/parser/cxx/ast_slot.cc | 22 ++++-- src/parser/cxx/ast_visitor.cc | 4 +- src/parser/cxx/flatbuffers/ast_decoder.cc | 16 +++- src/parser/cxx/flatbuffers/ast_encoder.cc | 31 +++++++- src/parser/cxx/parser.cc | 16 +++- src/parser/cxx/type_checker.cc | 79 +++++++++++++++++-- 15 files changed, 226 insertions(+), 37 deletions(-) diff --git a/packages/cxx-frontend/src/AST.ts b/packages/cxx-frontend/src/AST.ts index 439e0f67..a400e236 100644 --- a/packages/cxx-frontend/src/AST.ts +++ b/packages/cxx-frontend/src/AST.ts @@ -6820,20 +6820,51 @@ export class BuiltinOffsetofExpressionAST extends ExpressionAST { } /** - * Returns the expression of this node + * Returns the location of the identifier token in this node */ - getExpression(): ExpressionAST | undefined { - return AST.from( - cxx.getASTSlot(this.getHandle(), 4), - this.parser, - ); + getIdentifierToken(): Token | undefined { + return Token.from(cxx.getASTSlot(this.getHandle(), 4), this.parser); + } + + /** + * Returns the designatorList of this node + */ + getDesignatorList(): Iterable { + let it = cxx.getASTSlot(this.getHandle(), 0); + let value: DesignatorAST | undefined; + let done = false; + const p = this.parser; + function advance() { + done = it === 0; + if (done) return; + const ast = cxx.getListValue(it); + value = AST.from(ast, p); + it = cxx.getListNext(it); + } + function next() { + advance(); + return { done, value }; + } + return { + [Symbol.iterator]() { + return { next }; + }, + }; } /** * Returns the location of the rparen token in this node */ getRparenToken(): Token | undefined { - return Token.from(cxx.getASTSlot(this.getHandle(), 5), this.parser); + return Token.from(cxx.getASTSlot(this.getHandle(), 6), this.parser); + } + + /** + * Returns the identifier attribute of this node + */ + getIdentifier(): string | undefined { + const slot = cxx.getASTSlot(this.getHandle(), 7); + return cxx.getIdentifierValue(slot); } } diff --git a/packages/cxx-frontend/src/RecursiveASTVisitor.ts b/packages/cxx-frontend/src/RecursiveASTVisitor.ts index a54793d6..c4b8ed77 100644 --- a/packages/cxx-frontend/src/RecursiveASTVisitor.ts +++ b/packages/cxx-frontend/src/RecursiveASTVisitor.ts @@ -1424,7 +1424,9 @@ export class RecursiveASTVisitor extends ASTVisitor { context: Context, ): void { this.accept(node.getTypeId(), context); - this.accept(node.getExpression(), context); + for (const element of node.getDesignatorList()) { + this.accept(element, context); + } } /** diff --git a/src/parser/cxx/ast.cc b/src/parser/cxx/ast.cc index 94a69993..0920dc50 100644 --- a/src/parser/cxx/ast.cc +++ b/src/parser/cxx/ast.cc @@ -1710,14 +1710,16 @@ auto BuiltinOffsetofExpressionAST::firstSourceLocation() -> SourceLocation { if (auto loc = cxx::firstSourceLocation(lparenLoc)) return loc; if (auto loc = cxx::firstSourceLocation(typeId)) return loc; if (auto loc = cxx::firstSourceLocation(commaLoc)) return loc; - if (auto loc = cxx::firstSourceLocation(expression)) return loc; + if (auto loc = cxx::firstSourceLocation(identifierLoc)) return loc; + if (auto loc = cxx::firstSourceLocation(designatorList)) return loc; if (auto loc = cxx::firstSourceLocation(rparenLoc)) return loc; return {}; } auto BuiltinOffsetofExpressionAST::lastSourceLocation() -> SourceLocation { if (auto loc = cxx::lastSourceLocation(rparenLoc)) return loc; - if (auto loc = cxx::lastSourceLocation(expression)) return loc; + if (auto loc = cxx::lastSourceLocation(designatorList)) return loc; + if (auto loc = cxx::lastSourceLocation(identifierLoc)) return loc; if (auto loc = cxx::lastSourceLocation(commaLoc)) return loc; if (auto loc = cxx::lastSourceLocation(typeId)) return loc; if (auto loc = cxx::lastSourceLocation(lparenLoc)) return loc; diff --git a/src/parser/cxx/ast.fbs b/src/parser/cxx/ast.fbs index 77e631ae..e12c3c02 100644 --- a/src/parser/cxx/ast.fbs +++ b/src/parser/cxx/ast.fbs @@ -1086,10 +1086,12 @@ table BuiltinBitCastExpression /* ExpressionAST */ { table BuiltinOffsetofExpression /* ExpressionAST */ { type_id: TypeId; - expression: Expression; + designator_list: [Designator]; + identifier: string; offsetof_loc: uint32; lparen_loc: uint32; comma_loc: uint32; + identifier_loc: uint32; rparen_loc: uint32; } diff --git a/src/parser/cxx/ast.h b/src/parser/cxx/ast.h index 2f6877bb..c5ec219d 100644 --- a/src/parser/cxx/ast.h +++ b/src/parser/cxx/ast.h @@ -2135,8 +2135,10 @@ class BuiltinOffsetofExpressionAST final : public ExpressionAST { SourceLocation lparenLoc; TypeIdAST* typeId = nullptr; SourceLocation commaLoc; - ExpressionAST* expression = nullptr; + SourceLocation identifierLoc; + List* designatorList = nullptr; SourceLocation rparenLoc; + const Identifier* identifier = nullptr; FieldSymbol* symbol = nullptr; void accept(ASTVisitor* visitor) override { visitor->visit(this); } diff --git a/src/parser/cxx/ast_interpreter.cc b/src/parser/cxx/ast_interpreter.cc index d260ebdc..020b24ac 100644 --- a/src/parser/cxx/ast_interpreter.cc +++ b/src/parser/cxx/ast_interpreter.cc @@ -2246,7 +2246,6 @@ auto ASTInterpreter::ExpressionVisitor::operator()( auto ASTInterpreter::ExpressionVisitor::operator()( BuiltinOffsetofExpressionAST* ast) -> ExpressionResult { auto typeIdResult = accept(ast->typeId); - auto expressionResult = accept(ast->expression); if (ast->symbol) return ast->symbol->offset(); diff --git a/src/parser/cxx/ast_pretty_printer.cc b/src/parser/cxx/ast_pretty_printer.cc index 3ec9d128..a24cf478 100644 --- a/src/parser/cxx/ast_pretty_printer.cc +++ b/src/parser/cxx/ast_pretty_printer.cc @@ -2751,7 +2751,14 @@ void ASTPrettyPrinter::ExpressionVisitor::operator()( nospace(); accept.writeToken(ast->commaLoc); } - accept(ast->expression); + if (ast->identifierLoc) { + accept.writeToken(ast->identifierLoc); + } + + for (auto it = ast->designatorList; it; it = it->next) { + accept(it->value); + } + if (ast->rparenLoc) { nospace(); accept.writeToken(ast->rparenLoc); diff --git a/src/parser/cxx/ast_printer.cc b/src/parser/cxx/ast_printer.cc index 50ff63ec..7780dc03 100644 --- a/src/parser/cxx/ast_printer.cc +++ b/src/parser/cxx/ast_printer.cc @@ -1493,8 +1493,17 @@ void ASTPrinter::visit(BuiltinOffsetofExpressionAST* ast) { to_string(ast->type)); } out_ << "\n"; + accept(ast->identifier, "identifier"); accept(ast->typeId, "type-id"); - accept(ast->expression, "expression"); + if (ast->designatorList) { + ++indent_; + out_ << std::format("{:{}}", "", indent_ * 2); + out_ << std::format("{}\n", "designator-list"); + for (auto node : ListView{ast->designatorList}) { + accept(node); + } + --indent_; + } } void ASTPrinter::visit(TypeidExpressionAST* ast) { diff --git a/src/parser/cxx/ast_rewriter.cc b/src/parser/cxx/ast_rewriter.cc index c84fbfe4..98ae84ec 100644 --- a/src/parser/cxx/ast_rewriter.cc +++ b/src/parser/cxx/ast_rewriter.cc @@ -2922,8 +2922,17 @@ auto ASTRewriter::ExpressionVisitor::operator()( copy->lparenLoc = ast->lparenLoc; copy->typeId = rewrite(ast->typeId); copy->commaLoc = ast->commaLoc; - copy->expression = rewrite(ast->expression); + copy->identifierLoc = ast->identifierLoc; + + for (auto designatorList = ©->designatorList; + auto node : ListView{ast->designatorList}) { + auto value = rewrite(node); + *designatorList = make_list_node(arena(), value); + designatorList = &(*designatorList)->next; + } + copy->rparenLoc = ast->rparenLoc; + copy->identifier = ast->identifier; copy->symbol = ast->symbol; return copy; diff --git a/src/parser/cxx/ast_slot.cc b/src/parser/cxx/ast_slot.cc index f757bf16..a6308f33 100644 --- a/src/parser/cxx/ast_slot.cc +++ b/src/parser/cxx/ast_slot.cc @@ -3405,19 +3405,29 @@ void ASTSlot::visit(BuiltinOffsetofExpressionAST* ast) { slotKind_ = ASTSlotKind::kToken; slotNameIndex_ = SlotNameIndex{38}; break; - case 4: // expression - value_ = reinterpret_cast(ast->expression); - slotKind_ = ASTSlotKind::kNode; - slotNameIndex_ = SlotNameIndex{79}; + case 4: // identifierLoc + value_ = ast->identifierLoc.index(); + slotKind_ = ASTSlotKind::kToken; + slotNameIndex_ = SlotNameIndex{102}; break; - case 5: // rparenLoc + case 5: // designatorList + value_ = reinterpret_cast(ast->designatorList); + slotKind_ = ASTSlotKind::kNodeList; + slotNameIndex_ = SlotNameIndex{62}; + break; + case 6: // rparenLoc value_ = ast->rparenLoc.index(); slotKind_ = ASTSlotKind::kToken; slotNameIndex_ = SlotNameIndex{185}; break; + case 7: // identifier + value_ = reinterpret_cast(ast->identifier); + slotKind_ = ASTSlotKind::kIdentifierAttribute; + slotNameIndex_ = SlotNameIndex{101}; + break; } // switch - slotCount_ = 6; + slotCount_ = 8; } void ASTSlot::visit(TypeidExpressionAST* ast) { diff --git a/src/parser/cxx/ast_visitor.cc b/src/parser/cxx/ast_visitor.cc index bde88bde..b2959c22 100644 --- a/src/parser/cxx/ast_visitor.cc +++ b/src/parser/cxx/ast_visitor.cc @@ -580,7 +580,9 @@ void ASTVisitor::visit(BuiltinBitCastExpressionAST* ast) { void ASTVisitor::visit(BuiltinOffsetofExpressionAST* ast) { accept(ast->typeId); - accept(ast->expression); + for (auto node : ListView{ast->designatorList}) { + accept(node); + } } void ASTVisitor::visit(TypeidExpressionAST* ast) { accept(ast->expression); } diff --git a/src/parser/cxx/flatbuffers/ast_decoder.cc b/src/parser/cxx/flatbuffers/ast_decoder.cc index 7e8368bf..9fb6a422 100644 --- a/src/parser/cxx/flatbuffers/ast_decoder.cc +++ b/src/parser/cxx/flatbuffers/ast_decoder.cc @@ -2717,9 +2717,21 @@ auto ASTDecoder::decodeBuiltinOffsetofExpression( ast->lparenLoc = SourceLocation(node->lparen_loc()); ast->typeId = decodeTypeId(node->type_id()); ast->commaLoc = SourceLocation(node->comma_loc()); - ast->expression = - decodeExpression(node->expression(), node->expression_type()); + ast->identifierLoc = SourceLocation(node->identifier_loc()); + if (node->designator_list()) { + auto* inserter = &ast->designatorList; + for (std::uint32_t i = 0; i < node->designator_list()->size(); ++i) { + *inserter = new (pool_) List(decodeDesignator( + node->designator_list()->Get(i), + io::Designator(node->designator_list_type()->Get(i)))); + inserter = &(*inserter)->next; + } + } ast->rparenLoc = SourceLocation(node->rparen_loc()); + if (node->identifier()) { + ast->identifier = + unit_->control()->getIdentifier(node->identifier()->str()); + } return ast; } diff --git a/src/parser/cxx/flatbuffers/ast_encoder.cc b/src/parser/cxx/flatbuffers/ast_encoder.cc index 3973538e..692b8bad 100644 --- a/src/parser/cxx/flatbuffers/ast_encoder.cc +++ b/src/parser/cxx/flatbuffers/ast_encoder.cc @@ -2955,16 +2955,41 @@ void ASTEncoder::visit(BuiltinBitCastExpressionAST* ast) { void ASTEncoder::visit(BuiltinOffsetofExpressionAST* ast) { const auto typeId = accept(ast->typeId); - const auto [expression, expressionType] = acceptExpression(ast->expression); + std::vector> designatorListOffsets; + std::vector> designatorListTypes; + + for (auto node : ListView{ast->designatorList}) { + if (!node) continue; + const auto [offset, type] = acceptDesignator(node); + designatorListOffsets.push_back(offset); + designatorListTypes.push_back(type); + } + + auto designatorListOffsetsVector = fbb_.CreateVector(designatorListOffsets); + auto designatorListTypesVector = fbb_.CreateVector(designatorListTypes); + + flatbuffers::Offset identifier; + if (ast->identifier) { + if (identifiers_.contains(ast->identifier)) { + identifier = identifiers_.at(ast->identifier); + } else { + identifier = fbb_.CreateString(ast->identifier->value()); + identifiers_.emplace(ast->identifier, identifier); + } + } io::BuiltinOffsetofExpression::Builder builder{fbb_}; builder.add_offsetof_loc(ast->offsetofLoc.index()); builder.add_lparen_loc(ast->lparenLoc.index()); builder.add_type_id(typeId.o); builder.add_comma_loc(ast->commaLoc.index()); - builder.add_expression(expression); - builder.add_expression_type(static_cast(expressionType)); + builder.add_identifier_loc(ast->identifierLoc.index()); + builder.add_designator_list(designatorListOffsetsVector); + builder.add_designator_list_type(designatorListTypesVector); builder.add_rparen_loc(ast->rparenLoc.index()); + if (ast->identifier) { + builder.add_identifier(identifier); + } offset_ = builder.Finish().Union(); type_ = io::Expression_BuiltinOffsetofExpression; diff --git a/src/parser/cxx/parser.cc b/src/parser/cxx/parser.cc index 054f3883..22b9d750 100644 --- a/src/parser/cxx/parser.cc +++ b/src/parser/cxx/parser.cc @@ -2105,7 +2105,21 @@ auto Parser::parse_builtin_offsetof_expression(ExpressionAST*& yyast, if (!parse_type_id(ast->typeId)) parse_error("expected a type id"); expect(TokenKind::T_COMMA, ast->commaLoc); - parse_expression(ast->expression, ctx); + expect(TokenKind::T_IDENTIFIER, ast->identifierLoc); + ast->identifier = unit->identifier(ast->identifierLoc); + + auto it = &ast->designatorList; + while (lookat_designator()) { + DesignatorAST* designator = nullptr; + + parse_designator(designator); + + if (!designator) continue; + + *it = make_list_node(pool_, designator); + it = &(*it)->next; + } + expect(TokenKind::T_RPAREN, ast->rparenLoc); check(ast); diff --git a/src/parser/cxx/type_checker.cc b/src/parser/cxx/type_checker.cc index 4423b12c..f3d8c573 100644 --- a/src/parser/cxx/type_checker.cc +++ b/src/parser/cxx/type_checker.cc @@ -371,6 +371,12 @@ void TypeChecker::Visitor::operator()(IdExpressionAST* ast) { ast->valueCategory = ValueCategory::kLValue; } } + } else { +#if false + auto name = get_name(control(), ast->unqualifiedId); + error(ast->firstSourceLocation(), + std::format("use of undefined name '{}'", to_string(name))); +#endif } } @@ -745,19 +751,76 @@ auto TypeChecker::Visitor::check_cast_to_derived(ExpressionAST* expression, void TypeChecker::Visitor::operator()(BuiltinBitCastExpressionAST* ast) {} void TypeChecker::Visitor::operator()(BuiltinOffsetofExpressionAST* ast) { + ast->type = control()->getSizeType(); + auto classType = ast->typeId ? type_cast(ast->typeId->type) : nullptr; - auto id = ast_cast(ast->expression); - if (classType && id && !id->nestedNameSpecifier) { - auto symbol = classType->symbol(); - auto name = get_name(control(), id->unqualifiedId); - auto member = Lookup{scope()}.qualifiedLookup(symbol->scope(), name); - auto field = symbol_cast(member); - ast->symbol = field; + if (!classType) { + error(ast->firstSourceLocation(), "expected a type"); + return; } - ast->type = control()->getSizeType(); + if (!ast->identifier) { + return; + } + + auto symbol = classType->symbol(); + auto member = + Lookup{scope()}.qualifiedLookup(symbol->scope(), ast->identifier); + + auto field = symbol_cast(member); + if (!field) { + error(ast->firstSourceLocation(), + std::format("no member named '{}'", ast->identifier->name())); + return; + } + + for (auto designator : ListView{ast->designatorList}) { + if (auto dot = ast_cast(designator); + dot && dot->identifier) { + // resolve the field in the current class scope + auto currentClass = + type_cast(control()->remove_cvref(field->type())); + + if (!currentClass) { + error(designator->firstSourceLocation(), + std::format("expected a class or union type, but got '{}'", + to_string(field->type()))); + break; + } + + auto member = Lookup{scope()}.qualifiedLookup( + currentClass->symbol()->scope(), dot->identifier); + + auto field = symbol_cast(member); + + if (!field) { + error(dot->firstSourceLocation(), + std::format("no member named '{}' in class '{}'", + dot->identifier->name(), + to_string(currentClass->symbol()->name()))); + } + + break; + } + + if (auto subscript = ast_cast(designator)) { + if (!control()->is_array(field->type()) && + !control()->is_pointer(field->type())) { + error(subscript->firstSourceLocation(), + std::format("cannot subscript a member of type '{}'", + to_string(field->type()))); + break; + } + + // todo update offset + + continue; + } + } + + ast->symbol = field; } void TypeChecker::Visitor::operator()(TypeidExpressionAST* ast) {} From 5d98b9d7285b4446150214a3f880bb1e8ed6ed11 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 5 Aug 2025 22:52:56 +0200 Subject: [PATCH 3/3] Override the name of anonymous symbols that have a type alias. --- src/parser/cxx/binder.cc | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/parser/cxx/binder.cc b/src/parser/cxx/binder.cc index 1874116e..a366edaa 100644 --- a/src/parser/cxx/binder.cc +++ b/src/parser/cxx/binder.cc @@ -412,6 +412,28 @@ auto Binder::declareTypeAlias(SourceLocation identifierLoc, TypeIdAST* typeId) if (typeId) symbol->setType(typeId->type); symbol->setTemplateParameters(currentTemplateParameters()); declaringScope()->addSymbol(symbol); + + if (auto classType = type_cast(symbol->type())) { + auto classSymbol = classType->symbol(); + if (!classSymbol->name()) { + classSymbol->setName(symbol->name()); + } + } + + if (auto enumType = type_cast(symbol->type())) { + auto enumSymbol = enumType->symbol(); + if (!enumSymbol->name()) { + enumSymbol->setName(symbol->name()); + } + } + + if (auto scopedEnumType = type_cast(symbol->type())) { + auto scopedEnumSymbol = scopedEnumType->symbol(); + if (!scopedEnumSymbol->name()) { + scopedEnumSymbol->setName(symbol->name()); + } + } + return symbol; } @@ -634,6 +656,28 @@ auto Binder::declareTypedef(DeclaratorAST* declarator, const Decl& decl) symbol->setName(name); symbol->setType(type); scope()->addSymbol(symbol); + + if (auto classType = type_cast(symbol->type())) { + auto classSymbol = classType->symbol(); + if (!classSymbol->name()) { + classSymbol->setName(symbol->name()); + } + } + + if (auto enumType = type_cast(symbol->type())) { + auto enumSymbol = enumType->symbol(); + if (!enumSymbol->name()) { + enumSymbol->setName(symbol->name()); + } + } + + if (auto scopedEnumType = type_cast(symbol->type())) { + auto scopedEnumSymbol = scopedEnumType->symbol(); + if (!scopedEnumSymbol->name()) { + scopedEnumSymbol->setName(symbol->name()); + } + } + return symbol; }