Resolver: Track storage class usages of structures
This will be used to validate layout rules, as well as preventing
illegal types from being used in a uniform / storage buffer.
Also: Cleanup logic around VariableDeclStatement
This was spread across 3 places, entirely unnecessarily.
Bug: tint:643
Change-Id: I9d309c3a5dfb5676984f49ce51763a97bcac93bb
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/45125
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index 53a69f9..6e16b2e 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -151,6 +151,11 @@
return false;
}
}
+
+ if (!ApplyStorageClassUsageToType(var->declared_storage_class(),
+ var->type())) {
+ return false;
+ }
}
if (!Functions(builder_->AST().Functions())) {
@@ -200,16 +205,6 @@
bool Resolver::Statements(const ast::StatementList& stmts) {
for (auto* stmt : stmts) {
- if (auto* decl = stmt->As<ast::VariableDeclStatement>()) {
- if (!VariableDeclStatement(decl)) {
- return false;
- }
- }
-
- if (!VariableStorageClass(stmt)) {
- return false;
- }
-
if (!Statement(stmt)) {
return false;
}
@@ -217,36 +212,6 @@
return true;
}
-bool Resolver::VariableStorageClass(ast::Statement* stmt) {
- auto* var_decl = stmt->As<ast::VariableDeclStatement>();
- if (var_decl == nullptr) {
- return true;
- }
-
- auto* var = var_decl->variable();
-
- auto* info = CreateVariableInfo(var);
- variable_to_info_.emplace(var, info);
-
- // Nothing to do for const
- if (var->is_const()) {
- return true;
- }
-
- if (info->storage_class == ast::StorageClass::kFunction) {
- return true;
- }
-
- if (info->storage_class != ast::StorageClass::kNone) {
- diagnostics_.add_error("function variable has a non-function storage class",
- stmt->source());
- return false;
- }
-
- info->storage_class = ast::StorageClass::kFunction;
- return true;
-}
-
bool Resolver::Statement(ast::Statement* stmt) {
auto* sem_statement = builder_->create<semantic::Statement>(stmt);
@@ -336,10 +301,7 @@
return true;
}
if (auto* v = stmt->As<ast::VariableDeclStatement>()) {
- variable_stack_.set(v->variable()->symbol(),
- variable_to_info_.at(v->variable()));
- current_block_->decls.push_back(v->variable());
- return Expression(v->variable()->constructor());
+ return VariableDeclStatement(v);
}
diagnostics_.add_error(
@@ -1118,21 +1080,44 @@
}
bool Resolver::VariableDeclStatement(const ast::VariableDeclStatement* stmt) {
- auto* ctor = stmt->variable()->constructor();
- if (!ctor) {
- return true;
- }
-
- if (auto* sce = ctor->As<ast::ScalarConstructorExpression>()) {
- auto* lhs_type = stmt->variable()->type()->UnwrapAliasIfNeeded();
- auto* rhs_type = sce->literal()->type()->UnwrapAliasIfNeeded();
-
- if (lhs_type != rhs_type) {
- diagnostics_.add_error(
- "constructor expression type does not match variable type",
- stmt->source());
+ if (auto* ctor = stmt->variable()->constructor()) {
+ if (!Expression(ctor)) {
return false;
}
+ if (auto* sce = ctor->As<ast::ScalarConstructorExpression>()) {
+ auto* lhs_type = stmt->variable()->type()->UnwrapAliasIfNeeded();
+ auto* rhs_type = sce->literal()->type()->UnwrapAliasIfNeeded();
+
+ if (lhs_type != rhs_type) {
+ diagnostics_.add_error(
+ "constructor expression type does not match variable type",
+ stmt->source());
+ return false;
+ }
+ }
+ }
+
+ auto* var = stmt->variable();
+
+ auto* info = CreateVariableInfo(var);
+ variable_to_info_.emplace(var, info);
+ variable_stack_.set(var->symbol(), info);
+ current_block_->decls.push_back(var);
+
+ if (!var->is_const()) {
+ if (info->storage_class != ast::StorageClass::kFunction) {
+ if (info->storage_class != ast::StorageClass::kNone) {
+ diagnostics_.add_error(
+ "function variable has a non-function storage class",
+ stmt->source());
+ return false;
+ }
+ info->storage_class = ast::StorageClass::kFunction;
+ }
+ }
+
+ if (!ApplyStorageClassUsageToType(info->storage_class, var->type())) {
+ return false;
}
return true;
@@ -1247,9 +1232,10 @@
for (auto it : struct_info_) {
auto* str = it.first;
auto* info = it.second;
- builder_->Sem().Add(str, builder_->create<semantic::Struct>(
- str, std::move(info->members), info->align,
- info->size, info->size_no_padding));
+ builder_->Sem().Add(
+ str, builder_->create<semantic::Struct>(
+ str, std::move(info->members), info->align, info->size,
+ info->size_no_padding, info->storage_class_usage));
}
}
@@ -1470,6 +1456,44 @@
return info;
}
+bool Resolver::ApplyStorageClassUsageToType(ast::StorageClass sc,
+ type::Type* ty) {
+ ty = ty->UnwrapAliasIfNeeded();
+
+ if (auto* str = ty->As<type::Struct>()) {
+ auto* info = Structure(str);
+ if (!info) {
+ return false;
+ }
+ if (info->storage_class_usage.count(sc)) {
+ return true; // Already applied
+ }
+ info->storage_class_usage.emplace(sc);
+ for (auto* member : str->impl()->members()) {
+ // TODO(amaiorano): Determine the host-sharable types
+ bool can_be_host_sharable = true;
+ if (ast::IsHostSharable(sc) && !can_be_host_sharable) {
+ std::stringstream err;
+ err << "Structure '" << str->FriendlyName(builder_->Symbols())
+ << "' is used by storage class " << sc
+ << " which contains a member of non-host-sharable type "
+ << member->type()->FriendlyName(builder_->Symbols());
+ diagnostics_.add_error(err.str(), member->source());
+ return false;
+ }
+ if (!ApplyStorageClassUsageToType(sc, member->type())) {
+ return false;
+ }
+ }
+ }
+
+ if (auto* arr = ty->As<type::Array>()) {
+ return ApplyStorageClassUsageToType(sc, arr->type());
+ }
+
+ return true;
+}
+
template <typename F>
bool Resolver::BlockScope(BlockInfo::Type type, F&& callback) {
BlockInfo block_info(type, current_block_);