Skip to content
Merged
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
25 changes: 25 additions & 0 deletions src/parser/cxx/name_lookup.cc
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,40 @@ auto Lookup::qualifiedLookup(Symbol* scopedSymbol, const Name* name) const
case SymbolKind::kNamespace:
return qualifiedLookup(
symbol_cast<NamespaceSymbol>(scopedSymbol)->scope(), name);

case SymbolKind::kClass:
return qualifiedLookup(symbol_cast<ClassSymbol>(scopedSymbol)->scope(),
name);

case SymbolKind::kEnum:
return qualifiedLookup(symbol_cast<EnumSymbol>(scopedSymbol)->scope(),
name);

case SymbolKind::kScopedEnum:
return qualifiedLookup(
symbol_cast<ScopedEnumSymbol>(scopedSymbol)->scope(), name);

case SymbolKind::kTypeAlias: {
auto alias = symbol_cast<TypeAliasSymbol>(scopedSymbol);

if (auto classType = type_cast<ClassType>(alias->type())) {
auto classSymbol = classType->symbol();
return qualifiedLookup(classSymbol->scope(), name);
}

if (auto enumType = type_cast<EnumType>(alias->type())) {
auto enumSymbol = enumType->symbol();
return qualifiedLookup(enumSymbol->scope(), name);
}

if (auto scopedEnumType = type_cast<ScopedEnumType>(alias->type())) {
auto scopedEnumSymbol = scopedEnumType->symbol();
return qualifiedLookup(scopedEnumSymbol->scope(), name);
}

return nullptr;
}

default:
return nullptr;
} // switch
Expand Down
116 changes: 38 additions & 78 deletions src/parser/cxx/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5272,29 +5272,33 @@ auto Parser::parse_static_assert_declaration(DeclarationAST*& yyast) -> bool {

expect(TokenKind::T_SEMICOLON, ast->semicolonLoc);

bool value = false;
if (!inTemplate_) {
// not in a template context

if (constValue.has_value()) {
value = visit(to_bool, *constValue);
}
bool value = false;

if (!value && config_.staticAssert) {
SourceLocation loc = ast->firstSourceLocation();
if (constValue.has_value()) {
value = visit(to_bool, *constValue);
}

if (!ast->expression || !constValue.has_value()) {
parse_error(
loc,
"static assertion expression is not an integral constant expression");
} else {
if (ast->literalLoc)
loc = ast->literalLoc;
else if (ast->expression)
ast->expression->firstSourceLocation();
if (!value && config_.staticAssert) {
SourceLocation loc = ast->firstSourceLocation();

if (!ast->expression || !constValue.has_value()) {
parse_error(loc,
"static assertion expression is not an integral constant "
"expression");
} else {
if (ast->literalLoc)
loc = ast->literalLoc;
else if (ast->expression)
ast->expression->firstSourceLocation();

std::string message =
ast->literal ? ast->literal->value() : "static assert failed";
std::string message =
ast->literal ? ast->literal->value() : "static assert failed";

unit->error(loc, std::move(message));
unit->error(loc, std::move(message));
}
}
}

Expand Down Expand Up @@ -9431,66 +9435,11 @@ auto Parser::parse_class_specifier(
expect(TokenKind::T_RBRACE, ast->rbraceLoc);
}

if (!is_template(classSymbol)) {
int offset = 0;
int alignment = 1;

for (auto base : classSymbol->baseClasses()) {
auto baseClassSymbol = symbol_cast<ClassSymbol>(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<FieldSymbol>(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());
if (!inTemplate_) {
auto status = classSymbol->buildClassLayout(control_);
if (!status.has_value() && config_.checkTypes) {
parse_error(classSymbol->location(), status.error());
}

offset = align_to(offset, alignment);

classSymbol->setAlignment(alignment);
classSymbol->setSizeInBytes(offset);
}

classSymbol->setComplete(true);
Expand Down Expand Up @@ -11506,7 +11455,18 @@ void Parser::completePendingFunctionDefinitions() {
}
}

void Parser::setScope(Scope* scope) { scope_ = scope; }
void Parser::setScope(Scope* scope) {
scope_ = scope;

inTemplate_ = false;

for (auto current = scope_; current; current = current->parent()) {
if (current->isTemplateParametersScope()) {
inTemplate_ = true;
break;
}
}
}

void Parser::setScope(ScopedSymbol* symbol) { setScope(symbol->scope()); }

Expand Down
1 change: 1 addition & 0 deletions src/parser/cxx/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,7 @@ class Parser final {
int templateParameterCount_ = 0;
bool didAcceptCompletionToken_ = false;
std::vector<FunctionDefinitionAST*> pendingFunctionDefinitions_;
bool inTemplate_ = false;

template <typename T>
class CachedAST {
Expand Down
22 changes: 22 additions & 0 deletions src/parser/cxx/symbol_instantiation.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
// cxx
#include <cxx/ast.h>
#include <cxx/control.h>
#include <cxx/memory_layout.h>
#include <cxx/name_printer.h>
#include <cxx/scope.h>
#include <cxx/symbols.h>
#include <cxx/translation_unit.h>
#include <cxx/type_printer.h>
#include <cxx/types.h>

#include <format>
Expand Down Expand Up @@ -185,9 +188,19 @@ auto SymbolInstantiation::findOrCreateReplacement(Symbol* symbol) -> Symbol* {
replacements_[symbol] = newSymbol;

auto enclosingSymbol = replacement(symbol->enclosingSymbol());

newSymbol->setEnclosingScope(enclosingSymbol->scope());

if (symbol->type()) {
newSymbol->setType(visit(VisitType{*this}, symbol->type()));

auto field = symbol_cast<FieldSymbol>(newSymbol);

if (field) {
auto memoryLayout = control()->memoryLayout();
auto alignment = memoryLayout->alignmentOf(newSymbol->type());
field->setAlignment(alignment.value_or(0));
}
}

newSymbol->setName(symbol->name());
Expand Down Expand Up @@ -217,6 +230,10 @@ auto SymbolInstantiation::VisitSymbol::operator()(ClassSymbol* symbol)
auto newSymbol = self.replacement(symbol);
newSymbol->setFlags(symbol->flags());

if (symbol->isComplete()) {
newSymbol->setComplete(true);
}

if (symbol != self.current_) {
newSymbol->setTemplateParameters(
self.instantiate(symbol->templateParameters()));
Expand All @@ -226,14 +243,19 @@ auto SymbolInstantiation::VisitSymbol::operator()(ClassSymbol* symbol)
auto newBaseClass = self.instantiate(baseClass);
newSymbol->addBaseClass(newBaseClass);
}

for (auto ctor : symbol->constructors()) {
auto newCtor = self.instantiate(ctor);
newSymbol->addConstructor(newCtor);
}

for (auto member : views::members(symbol)) {
auto newMember = self.instantiate(member);
newSymbol->addMember(newMember);
}

auto status = newSymbol->buildClassLayout(self.control());

return newSymbol;
}

Expand Down
67 changes: 66 additions & 1 deletion src/parser/cxx/symbols.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,15 @@

// cxx
#include <cxx/ast.h>
#include <cxx/control.h>
#include <cxx/memory_layout.h>
#include <cxx/name_printer.h>
#include <cxx/scope.h>
#include <cxx/type_printer.h>
#include <cxx/types.h>
#include <cxx/util.h>

#include <format>

namespace cxx {

Expand Down Expand Up @@ -184,7 +191,7 @@ void ClassSymbol::setSizeInBytes(int sizeInBytes) {
sizeInBytes_ = sizeInBytes;
}

auto ClassSymbol::alignment() const -> int { return alignment_; }
auto ClassSymbol::alignment() const -> int { return std::max(alignment_, 1); }

void ClassSymbol::setAlignment(int alignment) { alignment_ = alignment; }

Expand Down Expand Up @@ -241,6 +248,64 @@ void ClassSymbol::addSpecialization(std::vector<TemplateArgument> arguments,
templateInfo_->addSpecialization(std::move(arguments), specialization);
}

auto ClassSymbol::buildClassLayout(Control* control)
-> std::expected<bool, std::string> {
int offset = 0;
int alignment = 1;

auto memoryLayout = control->memoryLayout();

for (auto base : baseClasses()) {
auto baseClassSymbol = symbol_cast<ClassSymbol>(base->symbol());

if (!baseClassSymbol) {
return std::unexpected(
std::format("base class '{}' not found", to_string(base->name())));
}

offset = align_to(offset, baseClassSymbol->alignment());
offset += baseClassSymbol->sizeInBytes();
alignment = std::max(alignment, baseClassSymbol->alignment());
}

for (auto member : scope()->symbols()) {
auto field = symbol_cast<FieldSymbol>(member);
if (!field) continue;
if (field->isStatic()) continue;

if (!field->alignment()) {
return std::unexpected(
std::format("alignment of incomplete type '{}'",
to_string(field->type(), field->name())));
}

auto size = memoryLayout->sizeOf(field->type());

if (!size.has_value()) {
return std::unexpected(
std::format("size of incomplete type '{}'",
to_string(field->type(), field->name())));
}

if (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);

setAlignment(alignment);
setSizeInBytes(offset);

return true;
}

EnumSymbol::EnumSymbol(Scope* enclosingScope)
: ScopedSymbol(Kind, enclosingScope) {}

Expand Down
4 changes: 4 additions & 0 deletions src/parser/cxx/symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <cxx/symbols_fwd.h>
#include <cxx/types_fwd.h>

#include <expected>
#include <memory>
#include <optional>
#include <ranges>
Expand Down Expand Up @@ -298,6 +299,9 @@ class ClassSymbol final : public ScopedSymbol {
return templateSepcializationIndex_;
}

[[nodiscard]] auto buildClassLayout(Control* control)
-> std::expected<bool, std::string>;

private:
[[nodiscard]] auto hasBaseClass(Symbol* symbol,
std::unordered_set<const ClassSymbol*>&) const
Expand Down
28 changes: 28 additions & 0 deletions tests/unit_tests/sema/template_class_04.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RUN: %cxx -verify -ftemplates -fcheck %s

template <typename Key, typename Value>
struct X {
Key key;
Value value;

void check() { static_assert(sizeof(*this)); }
};

auto main() -> int {
using U = X<char, int>;
U u;

static_assert(sizeof(U) == 8);
static_assert(sizeof(u) == 8);

static_assert(__builtin_offsetof(U, key) == 0);
static_assert(__builtin_offsetof(U, value) == 4);

static_assert(sizeof(X<char, int>::key) == 1);
static_assert(sizeof(X<char, int>::value) == 4);

static_assert(sizeof(U::key) == 1);
static_assert(sizeof(U::value) == 4);

return 0;
}