diff --git a/src/parser/cxx/ast.h b/src/parser/cxx/ast.h index 8532e880..92e9d11e 100644 --- a/src/parser/cxx/ast.h +++ b/src/parser/cxx/ast.h @@ -1640,6 +1640,7 @@ class BuiltinOffsetofExpressionAST final : public ExpressionAST { SourceLocation commaLoc; ExpressionAST* expression = nullptr; SourceLocation rparenLoc; + FieldSymbol* symbol = nullptr; void accept(ASTVisitor* visitor) override { visitor->visit(this); } @@ -3156,6 +3157,7 @@ class EnumSpecifierAST final : public SpecifierAST { SourceLocation commaLoc; List* enumeratorList = nullptr; SourceLocation rbraceLoc; + Symbol* symbol = nullptr; void accept(ASTVisitor* visitor) override { visitor->visit(this); } diff --git a/src/parser/cxx/const_expression_evaluator.cc b/src/parser/cxx/const_expression_evaluator.cc index f44bc447..193add8e 100644 --- a/src/parser/cxx/const_expression_evaluator.cc +++ b/src/parser/cxx/const_expression_evaluator.cc @@ -32,6 +32,7 @@ #include #include +#include namespace cxx { @@ -214,6 +215,7 @@ auto ConstExpressionEvaluator::operator()(BuiltinBitCastExpressionAST* ast) auto ConstExpressionEvaluator::operator()(BuiltinOffsetofExpressionAST* ast) -> std::optional { + if (ast->symbol) return ast->symbol->offset(); return std::nullopt; } diff --git a/src/parser/cxx/memory_layout.cc b/src/parser/cxx/memory_layout.cc index 551c82af..92746dfd 100644 --- a/src/parser/cxx/memory_layout.cc +++ b/src/parser/cxx/memory_layout.cc @@ -242,8 +242,13 @@ struct SizeOf { struct AlignmentOf { const MemoryLayout& memoryLayout; + auto operator()(const ClassType* type) const -> std::optional { + return type->symbol()->alignment(); + } + auto operator()(auto type) const -> std::optional { // ### TODO + if (!type) return std::nullopt; return memoryLayout.sizeOf(type); } }; @@ -297,11 +302,13 @@ void MemoryLayout::setSizeOfLongDouble(std::size_t sizeOfLongDouble) { auto MemoryLayout::sizeOf(const Type* type) const -> std::optional { + if (!type) return std::nullopt; return visit(SizeOf{*this}, type); } auto MemoryLayout::alignmentOf(const Type* type) const -> std::optional { + if (!type) return std::nullopt; return visit(AlignmentOf{*this}, type); } diff --git a/src/parser/cxx/name_lookup.h b/src/parser/cxx/name_lookup.h index a64ce5ee..4bf82190 100644 --- a/src/parser/cxx/name_lookup.h +++ b/src/parser/cxx/name_lookup.h @@ -49,12 +49,12 @@ class Lookup { [[nodiscard]] auto lookupType(NestedNameSpecifierAST* nestedNameSpecifier, const Identifier* id) const -> Symbol*; - private: - [[nodiscard]] auto unqualifiedLookup(const Name* name) const -> Symbol*; - [[nodiscard]] auto qualifiedLookup(Scope* scope, const Name* name) const -> Symbol*; + private: + [[nodiscard]] auto unqualifiedLookup(const Name* name) const -> Symbol*; + [[nodiscard]] auto qualifiedLookup(Symbol* scopedSymbol, const Name* name) const -> Symbol*; diff --git a/src/parser/cxx/parser.cc b/src/parser/cxx/parser.cc index 17fd5e83..7f8c5da6 100644 --- a/src/parser/cxx/parser.cc +++ b/src/parser/cxx/parser.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include #include #include @@ -42,12 +44,6 @@ #include #include -#include "cxx/cxx_fwd.h" -#include "cxx/parser_fwd.h" -#include "cxx/source_location.h" -#include "cxx/symbols_fwd.h" -#include "cxx/token_fwd.h" - namespace cxx { namespace { @@ -2652,6 +2648,19 @@ auto Parser::parse_builtin_offsetof_expression(ExpressionAST*& yyast, parse_expression(ast->expression, ctx); expect(TokenKind::T_RPAREN, ast->rparenLoc); + auto classType = type_cast(ast->typeId->type); + auto id = ast_cast(ast->expression); + + if (classType && id && !id->nestedNameSpecifier) { + auto symbol = classType->symbol(); + auto name = convertName(id->unqualifiedId); + auto member = Lookup{scope_}.qualifiedLookup(symbol->scope(), name); + auto field = symbol_cast(member); + ast->symbol = field; + } + + ast->type = control_->getSizeType(); + return true; } @@ -5631,7 +5640,10 @@ auto Parser::parse_defining_type_specifier( if (parse_enum_specifier(yyast, specs)) { lookahead.commit(); + auto enumSpec = ast_cast(yyast); + specs.setTypeSpecifier(yyast); + specs.type = enumSpec->symbol->type(); return true; } @@ -5641,6 +5653,7 @@ auto Parser::parse_defining_type_specifier( lookahead.commit(); specs.setTypeSpecifier(classSpecifier); + specs.type = classSpecifier->symbol->type(); yyast = classSpecifier; return true; @@ -6867,6 +6880,7 @@ auto Parser::parse_elaborated_type_specifier( } ast->symbol = classSymbol; + specs.type = classSymbol->type(); return true; } @@ -7971,6 +7985,7 @@ auto Parser::parse_enum_specifier(SpecifierAST*& yyast, DeclSpecs& specs) ast->colonLoc = colonLoc; ast->typeSpecifierList = typeSpecifierList; ast->lbraceLoc = lbraceLoc; + ast->symbol = symbol; if (!match(TokenKind::T_RBRACE, ast->rbraceLoc)) { parse_enumerator_list(ast->enumeratorList, symbol->type()); @@ -9416,7 +9431,69 @@ auto Parser::parse_class_specifier( expect(TokenKind::T_RBRACE, ast->rbraceLoc); } - ast->symbol->setComplete(true); + if (!is_template(classSymbol)) { + int offset = 0; + int alignment = 1; + + for (auto base : classSymbol->baseClasses()) { + auto baseClassSymbol = symbol_cast(base->symbol()); + + if (!baseClassSymbol) { + if (config_.checkTypes) { + parse_error(base->location(), std::format("base class '{}' not found", + to_string(base->name()))); + } + continue; + } + + offset = align_to(offset, baseClassSymbol->alignment()); + offset += baseClassSymbol->sizeInBytes(); + alignment = std::max(alignment, baseClassSymbol->alignment()); + } + + for (auto member : classSymbol->scope()->symbols()) { + auto field = symbol_cast(member); + if (!field) continue; + if (field->isStatic()) continue; + + if (!field->alignment()) { + if (config_.checkTypes) { + parse_error(field->location(), + std::format("alignment of incomplete type '{}'", + to_string(field->type(), field->name()))); + } + continue; + } + + auto size = control_->memoryLayout()->sizeOf(field->type()); + + if (!size.has_value()) { + if (config_.checkTypes) { + parse_error(field->location(), + std::format("size of incomplete type '{}'", + to_string(field->type(), field->name()))); + } + continue; + } + + if (classSymbol->isUnion()) { + offset = std::max(offset, int(size.value())); + } else { + offset = align_to(offset, field->alignment()); + field->setOffset(offset); + offset += size.value(); + } + + alignment = std::max(alignment, field->alignment()); + } + + offset = align_to(offset, alignment); + + classSymbol->setAlignment(alignment); + classSymbol->setSizeInBytes(offset); + } + + classSymbol->setComplete(true); return true; } @@ -9798,6 +9875,9 @@ auto Parser::declareField(DeclaratorAST* declarator, const Decl& decl) applySpecifiers(fieldSymbol, decl.specs); fieldSymbol->setName(name); fieldSymbol->setType(type); + if (auto alignment = control_->memoryLayout()->alignmentOf(type)) { + fieldSymbol->setAlignment(alignment.value()); + } scope_->addSymbol(fieldSymbol); return fieldSymbol; } diff --git a/src/parser/cxx/symbols.cc b/src/parser/cxx/symbols.cc index 1146344a..84fdc67c 100644 --- a/src/parser/cxx/symbols.cc +++ b/src/parser/cxx/symbols.cc @@ -178,7 +178,15 @@ auto ClassSymbol::isComplete() const -> bool { return isComplete_; } void ClassSymbol::setComplete(bool isComplete) { isComplete_ = isComplete; } -auto ClassSymbol::sizeInBytes() const -> std::size_t { return sizeInBytes_; } +auto ClassSymbol::sizeInBytes() const -> int { return sizeInBytes_; } + +void ClassSymbol::setSizeInBytes(int sizeInBytes) { + sizeInBytes_ = sizeInBytes; +} + +auto ClassSymbol::alignment() const -> int { return alignment_; } + +void ClassSymbol::setAlignment(int alignment) { alignment_ = alignment; } auto ClassSymbol::hasBaseClass(Symbol* symbol) const -> bool { std::unordered_set processed; @@ -493,6 +501,14 @@ auto FieldSymbol::isInline() const -> bool { return isInline_; } void FieldSymbol::setInline(bool isInline) { isInline_ = isInline; } +auto FieldSymbol::offset() const -> int { return offset_; } + +void FieldSymbol::setOffset(int offset) { offset_ = offset; } + +auto FieldSymbol::alignment() const -> int { return alignment_; } + +void FieldSymbol::setAlignment(int alignment) { alignment_ = alignment; } + ParameterSymbol::ParameterSymbol(Scope* enclosingScope) : Symbol(Kind, enclosingScope) {} diff --git a/src/parser/cxx/symbols.h b/src/parser/cxx/symbols.h index d2b96331..bb6c4cb2 100644 --- a/src/parser/cxx/symbols.h +++ b/src/parser/cxx/symbols.h @@ -251,7 +251,11 @@ class ClassSymbol final : public ScopedSymbol { [[nodiscard]] auto isComplete() const -> bool; void setComplete(bool isComplete); - [[nodiscard]] auto sizeInBytes() const -> std::size_t; + [[nodiscard]] auto sizeInBytes() const -> int; + void setSizeInBytes(int sizeInBytes); + + [[nodiscard]] auto alignment() const -> int; + void setAlignment(int alignment); [[nodiscard]] auto hasBaseClass(Symbol* symbol) const -> bool; @@ -305,7 +309,8 @@ class ClassSymbol final : public ScopedSymbol { std::unique_ptr> templateInfo_; ClassSymbol* templateClass_ = nullptr; std::size_t templateSepcializationIndex_ = 0; - std::size_t sizeInBytes_ = 0; + int sizeInBytes_ = 0; + int alignment_ = 0; union { std::uint32_t flags_{}; struct { @@ -566,6 +571,12 @@ class FieldSymbol final : public Symbol { [[nodiscard]] auto isInline() const -> bool; void setInline(bool isInline); + [[nodiscard]] auto offset() const -> int; + void setOffset(int offset); + + [[nodiscard]] auto alignment() const -> int; + void setAlignment(int alignment); + private: union { std::uint32_t flags_{}; @@ -577,6 +588,8 @@ class FieldSymbol final : public Symbol { std::uint32_t isInline_ : 1; }; }; + int offset_{}; + int alignment_{}; }; class ParameterSymbol final : public Symbol { diff --git a/tests/unit_tests/sema/wasm_stdlib_01.cc b/tests/unit_tests/sema/wasm_stdlib_01.cc new file mode 100644 index 00000000..238d9fdf --- /dev/null +++ b/tests/unit_tests/sema/wasm_stdlib_01.cc @@ -0,0 +1,6 @@ +// RUN: %cxx -toolchain wasm32 -ftemplates -fcheck %s + +#include +#include +#include +#include