resolver: Use Switch() for type-dispatch
Bug: tint:1383 Change-Id: I9efbe6b3e7c0314a76f65b5e8969f1f20bcecf93 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/79771 Reviewed-by: David Neto <dneto@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Antonio Maiorano <amaiorano@google.com> Commit-Queue: Ben Clayton <bclayton@chromium.org>
This commit is contained in:
parent
6f9ac3524a
commit
f95f11417e
|
@ -119,24 +119,23 @@ bool Resolver::ResolveInternal() {
|
||||||
|
|
||||||
// Process all module-scope declarations in dependency order.
|
// Process all module-scope declarations in dependency order.
|
||||||
for (auto* decl : dependencies_.ordered_globals) {
|
for (auto* decl : dependencies_.ordered_globals) {
|
||||||
if (auto* td = decl->As<ast::TypeDecl>()) {
|
Mark(decl);
|
||||||
Mark(td);
|
if (!Switch(
|
||||||
if (!TypeDecl(td)) {
|
decl, //
|
||||||
return false;
|
[&](const ast::TypeDecl* td) { //
|
||||||
}
|
return TypeDecl(td) != nullptr;
|
||||||
} else if (auto* func = decl->As<ast::Function>()) {
|
},
|
||||||
Mark(func);
|
[&](const ast::Function* func) {
|
||||||
if (!Function(func)) {
|
return Function(func) != nullptr;
|
||||||
return false;
|
},
|
||||||
}
|
[&](const ast::Variable* var) {
|
||||||
} else if (auto* var = decl->As<ast::Variable>()) {
|
return GlobalVariable(var) != nullptr;
|
||||||
Mark(var);
|
},
|
||||||
if (!GlobalVariable(var)) {
|
[&](Default) {
|
||||||
return false;
|
TINT_UNREACHABLE(Resolver, diagnostics_)
|
||||||
}
|
<< "unhandled global declaration: " << decl->TypeInfo().name;
|
||||||
} else {
|
return false;
|
||||||
TINT_UNREACHABLE(Resolver, diagnostics_)
|
})) {
|
||||||
<< "unhandled global declaration: " << decl->TypeInfo().name;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,131 +164,137 @@ bool Resolver::ResolveInternal() {
|
||||||
|
|
||||||
sem::Type* Resolver::Type(const ast::Type* ty) {
|
sem::Type* Resolver::Type(const ast::Type* ty) {
|
||||||
Mark(ty);
|
Mark(ty);
|
||||||
auto* s = [&]() -> sem::Type* {
|
auto* s = Switch(
|
||||||
if (ty->Is<ast::Void>()) {
|
ty,
|
||||||
return builder_->create<sem::Void>();
|
[&](const ast::Void*) -> sem::Type* {
|
||||||
}
|
return builder_->create<sem::Void>();
|
||||||
if (ty->Is<ast::Bool>()) {
|
},
|
||||||
return builder_->create<sem::Bool>();
|
[&](const ast::Bool*) -> sem::Type* {
|
||||||
}
|
return builder_->create<sem::Bool>();
|
||||||
if (ty->Is<ast::I32>()) {
|
},
|
||||||
return builder_->create<sem::I32>();
|
[&](const ast::I32*) -> sem::Type* {
|
||||||
}
|
return builder_->create<sem::I32>();
|
||||||
if (ty->Is<ast::U32>()) {
|
},
|
||||||
return builder_->create<sem::U32>();
|
[&](const ast::U32*) -> sem::Type* {
|
||||||
}
|
return builder_->create<sem::U32>();
|
||||||
if (ty->Is<ast::F32>()) {
|
},
|
||||||
return builder_->create<sem::F32>();
|
[&](const ast::F32*) -> sem::Type* {
|
||||||
}
|
return builder_->create<sem::F32>();
|
||||||
if (auto* t = ty->As<ast::Vector>()) {
|
},
|
||||||
if (!t->type) {
|
[&](const ast::Vector* t) -> sem::Type* {
|
||||||
AddError("missing vector element type", t->source.End());
|
if (!t->type) {
|
||||||
return nullptr;
|
AddError("missing vector element type", t->source.End());
|
||||||
}
|
return nullptr;
|
||||||
if (auto* el = Type(t->type)) {
|
|
||||||
if (auto* vector = builder_->create<sem::Vector>(el, t->width)) {
|
|
||||||
if (ValidateVector(vector, t->source)) {
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
if (auto* el = Type(t->type)) {
|
||||||
return nullptr;
|
if (auto* vector = builder_->create<sem::Vector>(el, t->width)) {
|
||||||
}
|
if (ValidateVector(vector, t->source)) {
|
||||||
if (auto* t = ty->As<ast::Matrix>()) {
|
return vector;
|
||||||
if (!t->type) {
|
|
||||||
AddError("missing matrix element type", t->source.End());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (auto* el = Type(t->type)) {
|
|
||||||
if (auto* column_type = builder_->create<sem::Vector>(el, t->rows)) {
|
|
||||||
if (auto* matrix =
|
|
||||||
builder_->create<sem::Matrix>(column_type, t->columns)) {
|
|
||||||
if (ValidateMatrix(matrix, t->source)) {
|
|
||||||
return matrix;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return nullptr;
|
||||||
return nullptr;
|
},
|
||||||
}
|
[&](const ast::Matrix* t) -> sem::Type* {
|
||||||
if (auto* t = ty->As<ast::Array>()) {
|
if (!t->type) {
|
||||||
return Array(t);
|
AddError("missing matrix element type", t->source.End());
|
||||||
}
|
|
||||||
if (auto* t = ty->As<ast::Atomic>()) {
|
|
||||||
if (auto* el = Type(t->type)) {
|
|
||||||
auto* a = builder_->create<sem::Atomic>(el);
|
|
||||||
if (!ValidateAtomic(t, a)) {
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return a;
|
if (auto* el = Type(t->type)) {
|
||||||
}
|
if (auto* column_type = builder_->create<sem::Vector>(el, t->rows)) {
|
||||||
return nullptr;
|
if (auto* matrix =
|
||||||
}
|
builder_->create<sem::Matrix>(column_type, t->columns)) {
|
||||||
if (auto* t = ty->As<ast::Pointer>()) {
|
if (ValidateMatrix(matrix, t->source)) {
|
||||||
if (auto* el = Type(t->type)) {
|
return matrix;
|
||||||
auto access = t->access;
|
}
|
||||||
if (access == ast::kUndefined) {
|
}
|
||||||
access = DefaultAccessForStorageClass(t->storage_class);
|
}
|
||||||
}
|
}
|
||||||
return builder_->create<sem::Pointer>(el, t->storage_class, access);
|
return nullptr;
|
||||||
}
|
},
|
||||||
return nullptr;
|
[&](const ast::Array* t) -> sem::Type* { return Array(t); },
|
||||||
}
|
[&](const ast::Atomic* t) -> sem::Type* {
|
||||||
if (auto* t = ty->As<ast::Sampler>()) {
|
if (auto* el = Type(t->type)) {
|
||||||
return builder_->create<sem::Sampler>(t->kind);
|
auto* a = builder_->create<sem::Atomic>(el);
|
||||||
}
|
if (!ValidateAtomic(t, a)) {
|
||||||
if (auto* t = ty->As<ast::SampledTexture>()) {
|
return nullptr;
|
||||||
if (auto* el = Type(t->type)) {
|
}
|
||||||
return builder_->create<sem::SampledTexture>(t->dim, el);
|
return a;
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (auto* t = ty->As<ast::MultisampledTexture>()) {
|
|
||||||
if (auto* el = Type(t->type)) {
|
|
||||||
return builder_->create<sem::MultisampledTexture>(t->dim, el);
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (auto* t = ty->As<ast::DepthTexture>()) {
|
|
||||||
return builder_->create<sem::DepthTexture>(t->dim);
|
|
||||||
}
|
|
||||||
if (auto* t = ty->As<ast::DepthMultisampledTexture>()) {
|
|
||||||
return builder_->create<sem::DepthMultisampledTexture>(t->dim);
|
|
||||||
}
|
|
||||||
if (auto* t = ty->As<ast::StorageTexture>()) {
|
|
||||||
if (auto* el = Type(t->type)) {
|
|
||||||
if (!ValidateStorageTexture(t)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
return builder_->create<sem::StorageTexture>(t->dim, t->format,
|
return nullptr;
|
||||||
t->access, el);
|
},
|
||||||
}
|
[&](const ast::Pointer* t) -> sem::Type* {
|
||||||
return nullptr;
|
if (auto* el = Type(t->type)) {
|
||||||
}
|
auto access = t->access;
|
||||||
if (ty->As<ast::ExternalTexture>()) {
|
if (access == ast::kUndefined) {
|
||||||
return builder_->create<sem::ExternalTexture>();
|
access = DefaultAccessForStorageClass(t->storage_class);
|
||||||
}
|
}
|
||||||
return Switch(
|
return builder_->create<sem::Pointer>(el, t->storage_class, access);
|
||||||
ResolvedSymbol(ty), //
|
}
|
||||||
[&](sem::Type* type) { return type; },
|
return nullptr;
|
||||||
[&](sem::Variable* var) {
|
},
|
||||||
auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
|
[&](const ast::Sampler* t) -> sem::Type* {
|
||||||
AddError("cannot use variable '" + name + "' as type", ty->source);
|
return builder_->create<sem::Sampler>(t->kind);
|
||||||
AddNote("'" + name + "' declared here", var->Declaration()->source);
|
},
|
||||||
return nullptr;
|
[&](const ast::SampledTexture* t) -> sem::Type* {
|
||||||
},
|
if (auto* el = Type(t->type)) {
|
||||||
[&](sem::Function* func) {
|
return builder_->create<sem::SampledTexture>(t->dim, el);
|
||||||
auto name = builder_->Symbols().NameFor(func->Declaration()->symbol);
|
}
|
||||||
AddError("cannot use function '" + name + "' as type", ty->source);
|
return nullptr;
|
||||||
AddNote("'" + name + "' declared here", func->Declaration()->source);
|
},
|
||||||
return nullptr;
|
[&](const ast::MultisampledTexture* t) -> sem::Type* {
|
||||||
},
|
if (auto* el = Type(t->type)) {
|
||||||
[&](Default) {
|
return builder_->create<sem::MultisampledTexture>(t->dim, el);
|
||||||
TINT_UNREACHABLE(Resolver, diagnostics_)
|
}
|
||||||
<< "Unhandled ast::Type: " << ty->TypeInfo().name;
|
return nullptr;
|
||||||
return nullptr;
|
},
|
||||||
});
|
[&](const ast::DepthTexture* t) -> sem::Type* {
|
||||||
}();
|
return builder_->create<sem::DepthTexture>(t->dim);
|
||||||
|
},
|
||||||
|
[&](const ast::DepthMultisampledTexture* t) -> sem::Type* {
|
||||||
|
return builder_->create<sem::DepthMultisampledTexture>(t->dim);
|
||||||
|
},
|
||||||
|
[&](const ast::StorageTexture* t) -> sem::Type* {
|
||||||
|
if (auto* el = Type(t->type)) {
|
||||||
|
if (!ValidateStorageTexture(t)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return builder_->create<sem::StorageTexture>(t->dim, t->format,
|
||||||
|
t->access, el);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
},
|
||||||
|
[&](const ast::ExternalTexture*) -> sem::Type* {
|
||||||
|
return builder_->create<sem::ExternalTexture>();
|
||||||
|
},
|
||||||
|
[&](Default) -> sem::Type* {
|
||||||
|
return Switch(
|
||||||
|
ResolvedSymbol(ty), //
|
||||||
|
[&](sem::Type* type) { return type; },
|
||||||
|
[&](sem::Variable* var) {
|
||||||
|
auto name =
|
||||||
|
builder_->Symbols().NameFor(var->Declaration()->symbol);
|
||||||
|
AddError("cannot use variable '" + name + "' as type",
|
||||||
|
ty->source);
|
||||||
|
AddNote("'" + name + "' declared here",
|
||||||
|
var->Declaration()->source);
|
||||||
|
return nullptr;
|
||||||
|
},
|
||||||
|
[&](sem::Function* func) {
|
||||||
|
auto name =
|
||||||
|
builder_->Symbols().NameFor(func->Declaration()->symbol);
|
||||||
|
AddError("cannot use function '" + name + "' as type",
|
||||||
|
ty->source);
|
||||||
|
AddNote("'" + name + "' declared here",
|
||||||
|
func->Declaration()->source);
|
||||||
|
return nullptr;
|
||||||
|
},
|
||||||
|
[&](Default) {
|
||||||
|
TINT_UNREACHABLE(Resolver, diagnostics_)
|
||||||
|
<< "Unhandled ast::Type: " << ty->TypeInfo().name;
|
||||||
|
return nullptr;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
if (s) {
|
if (s) {
|
||||||
builder_->Sem().Add(ty, s);
|
builder_->Sem().Add(ty, s);
|
||||||
|
@ -520,30 +525,27 @@ void Resolver::AllocateOverridableConstantIds() {
|
||||||
|
|
||||||
void Resolver::SetShadows() {
|
void Resolver::SetShadows() {
|
||||||
for (auto it : dependencies_.shadows) {
|
for (auto it : dependencies_.shadows) {
|
||||||
auto* var = Sem(it.first);
|
Switch(
|
||||||
if (auto* local = var->As<sem::LocalVariable>()) {
|
Sem(it.first), //
|
||||||
local->SetShadows(Sem(it.second));
|
[&](sem::LocalVariable* local) { local->SetShadows(Sem(it.second)); },
|
||||||
}
|
[&](sem::Parameter* param) { param->SetShadows(Sem(it.second)); });
|
||||||
if (auto* param = var->As<sem::Parameter>()) {
|
|
||||||
param->SetShadows(Sem(it.second));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} // namespace resolver
|
} // namespace resolver
|
||||||
|
|
||||||
bool Resolver::GlobalVariable(const ast::Variable* var) {
|
sem::GlobalVariable* Resolver::GlobalVariable(const ast::Variable* var) {
|
||||||
auto* sem = Variable(var, VariableKind::kGlobal);
|
auto* sem = Variable(var, VariableKind::kGlobal);
|
||||||
if (!sem) {
|
if (!sem) {
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto storage_class = sem->StorageClass();
|
auto storage_class = sem->StorageClass();
|
||||||
if (!var->is_const && storage_class == ast::StorageClass::kNone) {
|
if (!var->is_const && storage_class == ast::StorageClass::kNone) {
|
||||||
AddError("global variables must have a storage class", var->source);
|
AddError("global variables must have a storage class", var->source);
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (var->is_const && storage_class != ast::StorageClass::kNone) {
|
if (var->is_const && storage_class != ast::StorageClass::kNone) {
|
||||||
AddError("global constants shouldn't have a storage class", var->source);
|
AddError("global constants shouldn't have a storage class", var->source);
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto* attr : var->attributes) {
|
for (auto* attr : var->attributes) {
|
||||||
|
@ -558,20 +560,20 @@ bool Resolver::GlobalVariable(const ast::Variable* var) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ValidateNoDuplicateAttributes(var->attributes)) {
|
if (!ValidateNoDuplicateAttributes(var->attributes)) {
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ValidateGlobalVariable(sem)) {
|
if (!ValidateGlobalVariable(sem)) {
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(bclayton): Call this at the end of resolve on all uniform and storage
|
// TODO(bclayton): Call this at the end of resolve on all uniform and storage
|
||||||
// referenced structs
|
// referenced structs
|
||||||
if (!ValidateStorageClassLayout(sem)) {
|
if (!ValidateStorageClassLayout(sem)) {
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return sem->As<sem::GlobalVariable>();
|
||||||
}
|
}
|
||||||
|
|
||||||
sem::Function* Resolver::Function(const ast::Function* decl) {
|
sem::Function* Resolver::Function(const ast::Function* decl) {
|
||||||
|
@ -858,66 +860,71 @@ bool Resolver::Statements(const ast::StatementList& stmts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
sem::Statement* Resolver::Statement(const ast::Statement* stmt) {
|
sem::Statement* Resolver::Statement(const ast::Statement* stmt) {
|
||||||
if (stmt->Is<ast::CaseStatement>()) {
|
return Switch(
|
||||||
AddError("case statement can only be used inside a switch statement",
|
stmt,
|
||||||
stmt->source);
|
// Compound statements. These create their own sem::CompoundStatement
|
||||||
return nullptr;
|
// bindings.
|
||||||
}
|
[&](const ast::BlockStatement* b) -> sem::Statement* {
|
||||||
if (stmt->Is<ast::ElseStatement>()) {
|
return BlockStatement(b);
|
||||||
TINT_ICE(Resolver, diagnostics_)
|
},
|
||||||
<< "Resolver::Statement() encountered an Else statement. Else "
|
[&](const ast::ForLoopStatement* l) -> sem::Statement* {
|
||||||
"statements are embedded in If statements, so should never be "
|
return ForLoopStatement(l);
|
||||||
"encountered as top-level statements";
|
},
|
||||||
return nullptr;
|
[&](const ast::LoopStatement* l) -> sem::Statement* {
|
||||||
}
|
return LoopStatement(l);
|
||||||
|
},
|
||||||
|
[&](const ast::IfStatement* i) -> sem::Statement* {
|
||||||
|
return IfStatement(i);
|
||||||
|
},
|
||||||
|
[&](const ast::SwitchStatement* s) -> sem::Statement* {
|
||||||
|
return SwitchStatement(s);
|
||||||
|
},
|
||||||
|
|
||||||
// Compound statements. These create their own sem::CompoundStatement
|
// Non-Compound statements
|
||||||
// bindings.
|
[&](const ast::AssignmentStatement* a) -> sem::Statement* {
|
||||||
if (auto* b = stmt->As<ast::BlockStatement>()) {
|
return AssignmentStatement(a);
|
||||||
return BlockStatement(b);
|
},
|
||||||
}
|
[&](const ast::BreakStatement* b) -> sem::Statement* {
|
||||||
if (auto* l = stmt->As<ast::ForLoopStatement>()) {
|
return BreakStatement(b);
|
||||||
return ForLoopStatement(l);
|
},
|
||||||
}
|
[&](const ast::CallStatement* c) -> sem::Statement* {
|
||||||
if (auto* l = stmt->As<ast::LoopStatement>()) {
|
return CallStatement(c);
|
||||||
return LoopStatement(l);
|
},
|
||||||
}
|
[&](const ast::ContinueStatement* c) -> sem::Statement* {
|
||||||
if (auto* i = stmt->As<ast::IfStatement>()) {
|
return ContinueStatement(c);
|
||||||
return IfStatement(i);
|
},
|
||||||
}
|
[&](const ast::DiscardStatement* d) -> sem::Statement* {
|
||||||
if (auto* s = stmt->As<ast::SwitchStatement>()) {
|
return DiscardStatement(d);
|
||||||
return SwitchStatement(s);
|
},
|
||||||
}
|
[&](const ast::FallthroughStatement* f) -> sem::Statement* {
|
||||||
|
return FallthroughStatement(f);
|
||||||
|
},
|
||||||
|
[&](const ast::ReturnStatement* r) -> sem::Statement* {
|
||||||
|
return ReturnStatement(r);
|
||||||
|
},
|
||||||
|
[&](const ast::VariableDeclStatement* v) -> sem::Statement* {
|
||||||
|
return VariableDeclStatement(v);
|
||||||
|
},
|
||||||
|
|
||||||
// Non-Compound statements
|
// Error cases
|
||||||
if (auto* a = stmt->As<ast::AssignmentStatement>()) {
|
[&](const ast::CaseStatement*) -> sem::Statement* {
|
||||||
return AssignmentStatement(a);
|
AddError("case statement can only be used inside a switch statement",
|
||||||
}
|
stmt->source);
|
||||||
if (auto* b = stmt->As<ast::BreakStatement>()) {
|
return nullptr;
|
||||||
return BreakStatement(b);
|
},
|
||||||
}
|
[&](const ast::ElseStatement*) -> sem::Statement* {
|
||||||
if (auto* c = stmt->As<ast::CallStatement>()) {
|
TINT_ICE(Resolver, diagnostics_)
|
||||||
return CallStatement(c);
|
<< "Resolver::Statement() encountered an Else statement. Else "
|
||||||
}
|
"statements are embedded in If statements, so should never be "
|
||||||
if (auto* c = stmt->As<ast::ContinueStatement>()) {
|
"encountered as top-level statements";
|
||||||
return ContinueStatement(c);
|
return nullptr;
|
||||||
}
|
},
|
||||||
if (auto* d = stmt->As<ast::DiscardStatement>()) {
|
[&](Default) -> sem::Statement* {
|
||||||
return DiscardStatement(d);
|
AddError(
|
||||||
}
|
"unknown statement type: " + std::string(stmt->TypeInfo().name),
|
||||||
if (auto* f = stmt->As<ast::FallthroughStatement>()) {
|
stmt->source);
|
||||||
return FallthroughStatement(f);
|
return nullptr;
|
||||||
}
|
});
|
||||||
if (auto* r = stmt->As<ast::ReturnStatement>()) {
|
|
||||||
return ReturnStatement(r);
|
|
||||||
}
|
|
||||||
if (auto* v = stmt->As<ast::VariableDeclStatement>()) {
|
|
||||||
return VariableDeclStatement(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
AddError("unknown statement type: " + std::string(stmt->TypeInfo().name),
|
|
||||||
stmt->source);
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sem::CaseStatement* Resolver::CaseStatement(const ast::CaseStatement* stmt) {
|
sem::CaseStatement* Resolver::CaseStatement(const ast::CaseStatement* stmt) {
|
||||||
|
@ -1137,32 +1144,42 @@ sem::Expression* Resolver::Expression(const ast::Expression* root) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto* expr : utils::Reverse(sorted)) {
|
for (auto* expr : utils::Reverse(sorted)) {
|
||||||
sem::Expression* sem_expr = nullptr;
|
auto* sem_expr = Switch(
|
||||||
if (auto* array = expr->As<ast::IndexAccessorExpression>()) {
|
expr,
|
||||||
sem_expr = IndexAccessor(array);
|
[&](const ast::IndexAccessorExpression* array) -> sem::Expression* {
|
||||||
} else if (auto* bin_op = expr->As<ast::BinaryExpression>()) {
|
return IndexAccessor(array);
|
||||||
sem_expr = Binary(bin_op);
|
},
|
||||||
} else if (auto* bitcast = expr->As<ast::BitcastExpression>()) {
|
[&](const ast::BinaryExpression* bin_op) -> sem::Expression* {
|
||||||
sem_expr = Bitcast(bitcast);
|
return Binary(bin_op);
|
||||||
} else if (auto* call = expr->As<ast::CallExpression>()) {
|
},
|
||||||
sem_expr = Call(call);
|
[&](const ast::BitcastExpression* bitcast) -> sem::Expression* {
|
||||||
} else if (auto* ident = expr->As<ast::IdentifierExpression>()) {
|
return Bitcast(bitcast);
|
||||||
sem_expr = Identifier(ident);
|
},
|
||||||
} else if (auto* literal = expr->As<ast::LiteralExpression>()) {
|
[&](const ast::CallExpression* call) -> sem::Expression* {
|
||||||
sem_expr = Literal(literal);
|
return Call(call);
|
||||||
} else if (auto* member = expr->As<ast::MemberAccessorExpression>()) {
|
},
|
||||||
sem_expr = MemberAccessor(member);
|
[&](const ast::IdentifierExpression* ident) -> sem::Expression* {
|
||||||
} else if (auto* unary = expr->As<ast::UnaryOpExpression>()) {
|
return Identifier(ident);
|
||||||
sem_expr = UnaryOp(unary);
|
},
|
||||||
} else if (expr->Is<ast::PhonyExpression>()) {
|
[&](const ast::LiteralExpression* literal) -> sem::Expression* {
|
||||||
sem_expr = builder_->create<sem::Expression>(
|
return Literal(literal);
|
||||||
expr, builder_->create<sem::Void>(), current_statement_,
|
},
|
||||||
sem::Constant{}, /* has_side_effects */ false);
|
[&](const ast::MemberAccessorExpression* member) -> sem::Expression* {
|
||||||
} else {
|
return MemberAccessor(member);
|
||||||
TINT_ICE(Resolver, diagnostics_)
|
},
|
||||||
<< "unhandled expression type: " << expr->TypeInfo().name;
|
[&](const ast::UnaryOpExpression* unary) -> sem::Expression* {
|
||||||
return nullptr;
|
return UnaryOp(unary);
|
||||||
}
|
},
|
||||||
|
[&](const ast::PhonyExpression*) -> sem::Expression* {
|
||||||
|
return builder_->create<sem::Expression>(
|
||||||
|
expr, builder_->create<sem::Void>(), current_statement_,
|
||||||
|
sem::Constant{}, /* has_side_effects */ false);
|
||||||
|
},
|
||||||
|
[&](Default) {
|
||||||
|
TINT_ICE(Resolver, diagnostics_)
|
||||||
|
<< "unhandled expression type: " << expr->TypeInfo().name;
|
||||||
|
return nullptr;
|
||||||
|
});
|
||||||
if (!sem_expr) {
|
if (!sem_expr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -1183,15 +1200,23 @@ sem::Expression* Resolver::IndexAccessor(
|
||||||
auto* obj = Sem(expr->object);
|
auto* obj = Sem(expr->object);
|
||||||
auto* obj_raw_ty = obj->Type();
|
auto* obj_raw_ty = obj->Type();
|
||||||
auto* obj_ty = obj_raw_ty->UnwrapRef();
|
auto* obj_ty = obj_raw_ty->UnwrapRef();
|
||||||
const sem::Type* ty = nullptr;
|
auto* ty = Switch(
|
||||||
if (auto* arr = obj_ty->As<sem::Array>()) {
|
obj_ty, //
|
||||||
ty = arr->ElemType();
|
[&](const sem::Array* arr) -> const sem::Type* {
|
||||||
} else if (auto* vec = obj_ty->As<sem::Vector>()) {
|
return arr->ElemType();
|
||||||
ty = vec->type();
|
},
|
||||||
} else if (auto* mat = obj_ty->As<sem::Matrix>()) {
|
[&](const sem::Vector* vec) -> const sem::Type* { //
|
||||||
ty = builder_->create<sem::Vector>(mat->type(), mat->rows());
|
return vec->type();
|
||||||
} else {
|
},
|
||||||
AddError("cannot index type '" + TypeNameOf(obj_ty) + "'", expr->source);
|
[&](const sem::Matrix* mat) -> const sem::Type* {
|
||||||
|
return builder_->create<sem::Vector>(mat->type(), mat->rows());
|
||||||
|
},
|
||||||
|
[&](Default) -> const sem::Type* {
|
||||||
|
AddError("cannot index type '" + TypeNameOf(obj_ty) + "'",
|
||||||
|
expr->source);
|
||||||
|
return nullptr;
|
||||||
|
});
|
||||||
|
if (ty == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1528,24 +1553,30 @@ sem::Call* Resolver::TypeConversion(const ast::CallExpression* expr,
|
||||||
// Now that the argument types have been determined, make sure that
|
// Now that the argument types have been determined, make sure that
|
||||||
// they obey the conversion rules laid out in
|
// they obey the conversion rules laid out in
|
||||||
// https://gpuweb.github.io/gpuweb/wgsl/#conversion-expr.
|
// https://gpuweb.github.io/gpuweb/wgsl/#conversion-expr.
|
||||||
bool ok = true;
|
bool ok = Switch(
|
||||||
if (auto* vec_type = target->As<sem::Vector>()) {
|
target,
|
||||||
ok = ValidateVectorConstructorOrCast(expr, vec_type);
|
[&](const sem::Vector* vec_type) {
|
||||||
} else if (auto* mat_type = target->As<sem::Matrix>()) {
|
return ValidateVectorConstructorOrCast(expr, vec_type);
|
||||||
// Note: Matrix types currently cannot be converted (the element
|
},
|
||||||
// type must only be f32). We implement this for the day we support
|
[&](const sem::Matrix* mat_type) {
|
||||||
// other matrix element types.
|
// Note: Matrix types currently cannot be converted (the element
|
||||||
ok = ValidateMatrixConstructorOrCast(expr, mat_type);
|
// type must only be f32). We implement this for the day we
|
||||||
} else if (target->is_scalar()) {
|
// support other matrix element types.
|
||||||
ok = ValidateScalarConstructorOrCast(expr, target);
|
return ValidateMatrixConstructorOrCast(expr, mat_type);
|
||||||
} else if (auto* arr_type = target->As<sem::Array>()) {
|
},
|
||||||
ok = ValidateArrayConstructorOrCast(expr, arr_type);
|
[&](const sem::Array* arr_type) {
|
||||||
} else if (auto* struct_type = target->As<sem::Struct>()) {
|
return ValidateArrayConstructorOrCast(expr, arr_type);
|
||||||
ok = ValidateStructureConstructorOrCast(expr, struct_type);
|
},
|
||||||
} else {
|
[&](const sem::Struct* struct_type) {
|
||||||
AddError("type is not constructible", expr->source);
|
return ValidateStructureConstructorOrCast(expr, struct_type);
|
||||||
return nullptr;
|
},
|
||||||
}
|
[&](Default) {
|
||||||
|
if (target->is_scalar()) {
|
||||||
|
return ValidateScalarConstructorOrCast(expr, target);
|
||||||
|
}
|
||||||
|
AddError("type is not constructible", expr->source);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -1588,21 +1619,27 @@ sem::Call* Resolver::TypeConstructor(
|
||||||
// Now that the argument types have been determined, make sure that
|
// Now that the argument types have been determined, make sure that
|
||||||
// they obey the constructor type rules laid out in
|
// they obey the constructor type rules laid out in
|
||||||
// https://gpuweb.github.io/gpuweb/wgsl/#type-constructor-expr.
|
// https://gpuweb.github.io/gpuweb/wgsl/#type-constructor-expr.
|
||||||
bool ok = true;
|
bool ok = Switch(
|
||||||
if (auto* vec_type = ty->As<sem::Vector>()) {
|
ty,
|
||||||
ok = ValidateVectorConstructorOrCast(expr, vec_type);
|
[&](const sem::Vector* vec_type) {
|
||||||
} else if (auto* mat_type = ty->As<sem::Matrix>()) {
|
return ValidateVectorConstructorOrCast(expr, vec_type);
|
||||||
ok = ValidateMatrixConstructorOrCast(expr, mat_type);
|
},
|
||||||
} else if (ty->is_scalar()) {
|
[&](const sem::Matrix* mat_type) {
|
||||||
ok = ValidateScalarConstructorOrCast(expr, ty);
|
return ValidateMatrixConstructorOrCast(expr, mat_type);
|
||||||
} else if (auto* arr_type = ty->As<sem::Array>()) {
|
},
|
||||||
ok = ValidateArrayConstructorOrCast(expr, arr_type);
|
[&](const sem::Array* arr_type) {
|
||||||
} else if (auto* struct_type = ty->As<sem::Struct>()) {
|
return ValidateArrayConstructorOrCast(expr, arr_type);
|
||||||
ok = ValidateStructureConstructorOrCast(expr, struct_type);
|
},
|
||||||
} else {
|
[&](const sem::Struct* struct_type) {
|
||||||
AddError("type is not constructible", expr->source);
|
return ValidateStructureConstructorOrCast(expr, struct_type);
|
||||||
return nullptr;
|
},
|
||||||
}
|
[&](Default) {
|
||||||
|
if (ty->is_scalar()) {
|
||||||
|
return ValidateScalarConstructorOrCast(expr, ty);
|
||||||
|
}
|
||||||
|
AddError("type is not constructible", expr->source);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -2155,21 +2192,25 @@ std::string Resolver::RawTypeNameOf(const sem::Type* ty) {
|
||||||
}
|
}
|
||||||
|
|
||||||
sem::Type* Resolver::TypeOf(const ast::LiteralExpression* lit) {
|
sem::Type* Resolver::TypeOf(const ast::LiteralExpression* lit) {
|
||||||
if (lit->Is<ast::SintLiteralExpression>()) {
|
return Switch(
|
||||||
return builder_->create<sem::I32>();
|
lit,
|
||||||
}
|
[&](const ast::SintLiteralExpression*) -> sem::Type* {
|
||||||
if (lit->Is<ast::UintLiteralExpression>()) {
|
return builder_->create<sem::I32>();
|
||||||
return builder_->create<sem::U32>();
|
},
|
||||||
}
|
[&](const ast::UintLiteralExpression*) -> sem::Type* {
|
||||||
if (lit->Is<ast::FloatLiteralExpression>()) {
|
return builder_->create<sem::U32>();
|
||||||
return builder_->create<sem::F32>();
|
},
|
||||||
}
|
[&](const ast::FloatLiteralExpression*) -> sem::Type* {
|
||||||
if (lit->Is<ast::BoolLiteralExpression>()) {
|
return builder_->create<sem::F32>();
|
||||||
return builder_->create<sem::Bool>();
|
},
|
||||||
}
|
[&](const ast::BoolLiteralExpression*) -> sem::Type* {
|
||||||
TINT_UNREACHABLE(Resolver, diagnostics_)
|
return builder_->create<sem::Bool>();
|
||||||
<< "Unhandled literal type: " << lit->TypeInfo().name;
|
},
|
||||||
return nullptr;
|
[&](Default) -> sem::Type* {
|
||||||
|
TINT_UNREACHABLE(Resolver, diagnostics_)
|
||||||
|
<< "Unhandled literal type: " << lit->TypeInfo().name;
|
||||||
|
return nullptr;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sem::Array* Resolver::Array(const ast::Array* arr) {
|
sem::Array* Resolver::Array(const ast::Array* arr) {
|
||||||
|
@ -2770,30 +2811,23 @@ bool Resolver::IsPlain(const sem::Type* type) const {
|
||||||
|
|
||||||
// https://gpuweb.github.io/gpuweb/wgsl/#fixed-footprint-types
|
// https://gpuweb.github.io/gpuweb/wgsl/#fixed-footprint-types
|
||||||
bool Resolver::IsFixedFootprint(const sem::Type* type) const {
|
bool Resolver::IsFixedFootprint(const sem::Type* type) const {
|
||||||
if (type->is_scalar()) {
|
return Switch(
|
||||||
return true;
|
type, //
|
||||||
}
|
[&](const sem::Vector*) { return true; }, //
|
||||||
if (type->Is<sem::Vector>()) {
|
[&](const sem::Matrix*) { return true; }, //
|
||||||
return true;
|
[&](const sem::Atomic*) { return true; },
|
||||||
}
|
[&](const sem::Array* arr) {
|
||||||
if (type->Is<sem::Matrix>()) {
|
return !arr->IsRuntimeSized() && IsFixedFootprint(arr->ElemType());
|
||||||
return true;
|
},
|
||||||
}
|
[&](const sem::Struct* str) {
|
||||||
if (type->Is<sem::Atomic>()) {
|
for (auto* member : str->Members()) {
|
||||||
return true;
|
if (!IsFixedFootprint(member->Type())) {
|
||||||
}
|
return false;
|
||||||
if (auto* arr = type->As<sem::Array>()) {
|
}
|
||||||
return !arr->IsRuntimeSized() && IsFixedFootprint(arr->ElemType());
|
}
|
||||||
}
|
return true;
|
||||||
if (auto* str = type->As<sem::Struct>()) {
|
},
|
||||||
for (auto* member : str->Members()) {
|
[&](Default) { return type->is_scalar(); });
|
||||||
if (!IsFixedFootprint(member->Type())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://gpuweb.github.io/gpuweb/wgsl.html#storable-types
|
// https://gpuweb.github.io/gpuweb/wgsl.html#storable-types
|
||||||
|
@ -2806,27 +2840,22 @@ bool Resolver::IsHostShareable(const sem::Type* type) const {
|
||||||
if (type->IsAnyOf<sem::I32, sem::U32, sem::F32>()) {
|
if (type->IsAnyOf<sem::I32, sem::U32, sem::F32>()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (auto* vec = type->As<sem::Vector>()) {
|
return Switch(
|
||||||
return IsHostShareable(vec->type());
|
type, //
|
||||||
}
|
[&](const sem::Vector* vec) { return IsHostShareable(vec->type()); },
|
||||||
if (auto* mat = type->As<sem::Matrix>()) {
|
[&](const sem::Matrix* mat) { return IsHostShareable(mat->type()); },
|
||||||
return IsHostShareable(mat->type());
|
[&](const sem::Array* arr) { return IsHostShareable(arr->ElemType()); },
|
||||||
}
|
[&](const sem::Struct* str) {
|
||||||
if (auto* arr = type->As<sem::Array>()) {
|
for (auto* member : str->Members()) {
|
||||||
return IsHostShareable(arr->ElemType());
|
if (!IsHostShareable(member->Type())) {
|
||||||
}
|
return false;
|
||||||
if (auto* str = type->As<sem::Struct>()) {
|
}
|
||||||
for (auto* member : str->Members()) {
|
}
|
||||||
if (!IsHostShareable(member->Type())) {
|
return true;
|
||||||
return false;
|
},
|
||||||
}
|
[&](const sem::Atomic* atomic) {
|
||||||
}
|
return IsHostShareable(atomic->Type());
|
||||||
return true;
|
});
|
||||||
}
|
|
||||||
if (auto* atomic = type->As<sem::Atomic>()) {
|
|
||||||
return IsHostShareable(atomic->Type());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resolver::IsBuiltin(Symbol symbol) const {
|
bool Resolver::IsBuiltin(Symbol symbol) const {
|
||||||
|
|
|
@ -219,6 +219,7 @@ class Resolver {
|
||||||
sem::ElseStatement* ElseStatement(const ast::ElseStatement*);
|
sem::ElseStatement* ElseStatement(const ast::ElseStatement*);
|
||||||
sem::Statement* FallthroughStatement(const ast::FallthroughStatement*);
|
sem::Statement* FallthroughStatement(const ast::FallthroughStatement*);
|
||||||
sem::ForLoopStatement* ForLoopStatement(const ast::ForLoopStatement*);
|
sem::ForLoopStatement* ForLoopStatement(const ast::ForLoopStatement*);
|
||||||
|
sem::GlobalVariable* GlobalVariable(const ast::Variable*);
|
||||||
sem::Statement* Parameter(const ast::Variable*);
|
sem::Statement* Parameter(const ast::Variable*);
|
||||||
sem::IfStatement* IfStatement(const ast::IfStatement*);
|
sem::IfStatement* IfStatement(const ast::IfStatement*);
|
||||||
sem::LoopStatement* LoopStatement(const ast::LoopStatement*);
|
sem::LoopStatement* LoopStatement(const ast::LoopStatement*);
|
||||||
|
@ -228,8 +229,6 @@ class Resolver {
|
||||||
sem::Statement* VariableDeclStatement(const ast::VariableDeclStatement*);
|
sem::Statement* VariableDeclStatement(const ast::VariableDeclStatement*);
|
||||||
bool Statements(const ast::StatementList&);
|
bool Statements(const ast::StatementList&);
|
||||||
|
|
||||||
bool GlobalVariable(const ast::Variable*);
|
|
||||||
|
|
||||||
// AST and Type validation methods
|
// AST and Type validation methods
|
||||||
// Each return true on success, false on failure.
|
// Each return true on success, false on failure.
|
||||||
bool ValidateAlias(const ast::Alias*);
|
bool ValidateAlias(const ast::Alias*);
|
||||||
|
|
Loading…
Reference in New Issue