#include <msp/core/raii.h>
#include <msp/strings/format.h>
#include <msp/strings/utils.h>
+#include "reflect.h"
#include "validate.h"
using namespace std;
allowed = (variable && scope==GLOBAL);
else if(i->name=="binding" || i->name=="set")
{
+ if(i->name=="set")
+ {
+ error(layout, "Layout qualifier 'set' not allowed when targeting OpenGL");
+ continue;
+ }
+
if(variable)
{
TypeDeclaration *type = variable->type_declaration;
while(BasicTypeDeclaration *basic = dynamic_cast<BasicTypeDeclaration *>(type))
type = basic->base_type;
- allowed = (scope==GLOBAL && dynamic_cast<ImageTypeDeclaration *>(type));
- err_descr = "variable of non-opaque type";
+ bool uniform = (variable->interface=="uniform");
+ allowed = (scope==GLOBAL && uniform && dynamic_cast<ImageTypeDeclaration *>(type));
+ err_descr = (uniform ? "variable of non-opaque type" : "non-uniform variable");
}
else if(iface_block)
- allowed = true;
+ {
+ allowed = (iface_block->interface=="uniform");
+ err_descr = "non-uniform interface block";
+ }
}
else if(i->name=="constant_id")
{
}
}
else if(i->name=="offset")
- allowed = (variable && scope==INTERFACE_BLOCK);
+ allowed = (variable && scope==INTERFACE_BLOCK && iface_block->interface=="uniform");
+ else if(i->name=="align")
+ allowed = (scope==INTERFACE_BLOCK && iface_block->interface=="uniform");
else if(i->name=="points")
{
allowed = (stage->type==Stage::GEOMETRY && iface_layout && (iface_layout->interface=="in" || iface_layout->interface=="out"));
allowed = (iface_block && !variable && iface_block->interface=="uniform");
value = false;
}
+ else if(i->name=="column_major" || i->name=="row_major")
+ {
+ allowed = (variable && scope==INTERFACE_BLOCK);
+ if(allowed)
+ {
+ BasicTypeDeclaration *basic = dynamic_cast<BasicTypeDeclaration *>(variable->type_declaration);
+ while(basic && basic->kind==BasicTypeDeclaration::ARRAY)
+ basic = dynamic_cast<BasicTypeDeclaration *>(basic->base_type);
+ allowed = (basic && basic->kind==BasicTypeDeclaration::MATRIX);
+ err_descr = "non-matrix variable";
+ }
+ }
if(!allowed)
{
{
if(scope==STRUCT || scope==INTERFACE_BLOCK)
error(var, format("Constant qualifier not allowed on %s", descr));
+ if(!var.init_expression)
+ error(var, "Constant variable must have an initializer");
}
if(!var.interpolation.empty() || !var.sampling.empty())
}
TypeDeclaration *type = var.type_declaration;
+ BasicTypeDeclaration::Kind kind = BasicTypeDeclaration::ALIAS;
while(BasicTypeDeclaration *basic = dynamic_cast<BasicTypeDeclaration *>(type))
+ {
+ kind = basic->kind;
type = basic->base_type;
+ }
if(dynamic_cast<ImageTypeDeclaration *>(type))
{
if(scope!=GLOBAL && scope!=FUNCTION_PARAM)
else if(scope==GLOBAL && var.interface!="uniform")
error(var, format("Type '%s' only allowed with uniform interface", type->name));
}
+ else if(kind==BasicTypeDeclaration::VOID)
+ error(var, "Type 'void' not allowed on variable");
+ else if(kind==BasicTypeDeclaration::BOOL && !var.interface.empty() && var.source!=BUILTIN_SOURCE)
+ error(var, "Type 'bool' not allowed on interface variable");
if(var.init_expression)
{
void IdentifierValidator::visit(InterfaceBlock &iface)
{
- string key = iface.interface+iface.block_name;
+ string key = format("%s %s", iface.interface, iface.block_name);
map<string, InterfaceBlock *>::const_iterator i = interface_blocks.find(key);
if(i!=interface_blocks.end())
multiple_definition(format("interface block '%s %s'", iface.interface, iface.block_name), iface, *i->second);
TraversingVisitor::visit(func);
}
+void ExpressionValidator::visit(Conditional &cond)
+{
+ if(cond.condition->type)
+ {
+ BasicTypeDeclaration *basic_cond = dynamic_cast<BasicTypeDeclaration *>(cond.condition->type);
+ if(!basic_cond || basic_cond->kind!=BasicTypeDeclaration::BOOL)
+ error(cond, "Condition is not a boolean");
+ }
+ TraversingVisitor::visit(cond);
+}
+
+void ExpressionValidator::visit(Iteration &iter)
+{
+ if(iter.condition->type)
+ {
+ BasicTypeDeclaration *basic_cond = dynamic_cast<BasicTypeDeclaration *>(iter.condition->type);
+ if(!basic_cond || basic_cond->kind!=BasicTypeDeclaration::BOOL)
+ error(iter, "Loop condition is not a boolean");
+ }
+ TraversingVisitor::visit(iter);
+}
+
void ExpressionValidator::visit(Return &ret)
{
if(current_function && current_function->return_type_declaration)
}
if(var.type_declaration && var.linked_declaration->type_declaration)
{
- const TypeDeclaration *type = var.type_declaration;
+ TypeDeclaration *type = var.type_declaration;
if(stage->type==Stage::GEOMETRY)
{
if(const BasicTypeDeclaration *basic = dynamic_cast<const BasicTypeDeclaration *>(type))
if(basic->kind==BasicTypeDeclaration::ARRAY && basic->base_type)
type = basic->base_type;
}
- if(!is_same_type(*type, *var.linked_declaration->type_declaration))
+ if(!TypeComparer().apply(*type, *var.linked_declaration->type_declaration))
{
error(var, format("Mismatched type '%s' for 'in %s'", type->name, var.name));
add_info(*var.linked_declaration, format("Linked to 'out %s' with type '%s'",
{
map<unsigned, VariableDeclaration *> &used = used_locations[var.interface];
- unsigned loc_count = 1;
- if(var.array)
- if(const Literal *literal = dynamic_cast<const Literal *>(var.array_size.get()))
- if(literal->value.check_type<int>())
- loc_count = literal->value.value<int>();
-
+ unsigned loc_count = LocationCounter().apply(var);
for(unsigned i=0; i<loc_count; ++i)
{
map<unsigned, VariableDeclaration *>::const_iterator j = used.find(location+i);
}
}
-void GlobalInterfaceValidator::get_binding(const Layout &layout, unsigned &desc_set, int &binding)
+void GlobalInterfaceValidator::check_uniform(const Uniform &uni)
{
- for(vector<Layout::Qualifier>::const_iterator i=layout.qualifiers.begin(); i!=layout.qualifiers.end(); ++i)
+ map<std::string, const Uniform *>::const_iterator i = used_names.find(uni.name);
+ if(i!=used_names.end())
{
- if(i->name=="set")
- desc_set = i->value;
- else if(i->name=="binding")
- binding = i->value;
+ if(uni.location>=0 && i->second->location>=0 && i->second->location!=uni.location)
+ {
+ error(*uni.node, format("Mismatched location %d for uniform '%s'", uni.location, uni.name));
+ add_info(*i->second->node, format("Previously declared here with location %d", i->second->location));
+ }
+ if(uni.bind_point>=0 && i->second->bind_point>=0 && i->second->bind_point!=uni.bind_point)
+ {
+ error(*uni.node, format("Mismatched binding %d for uniform '%s'", uni.bind_point, uni.name));
+ add_info(*i->second->node, format("Previously declared here with binding %d", i->second->bind_point));
+ }
+ if(uni.type && i->second->type && !TypeComparer().apply(*uni.type, *i->second->type))
+ {
+ string type_name = (dynamic_cast<const StructDeclaration *>(uni.type) ?
+ "structure" : format("type '%s'", uni.type->name));
+ error(*uni.node, format("Mismatched %s for uniform '%s'", type_name, uni.name));
+
+ string message = "Previously declared here";
+ if(!dynamic_cast<const StructDeclaration *>(i->second->type))
+ message += format(" with type '%s'", i->second->type->name);
+ add_info(*i->second->node, message);
+ }
}
-}
-
-void GlobalInterfaceValidator::check_binding(const Layout &layout, const Binding &binding)
-{
- unsigned desc_set = 0;
- int bind_point = -1;
- get_binding(layout, desc_set, bind_point);
- if(bind_point<0)
- return;
+ else
+ used_names.insert(make_pair(uni.name, &uni));
- map<unsigned, Binding> &used = used_bindings[desc_set];
- map<unsigned, Binding>::const_iterator i = used.find(bind_point);
- if(i!=used.end())
+ if(uni.location>=0)
{
- if(i->second.name!=binding.name)
+ map<unsigned, const Uniform *>::const_iterator j = used_locations.find(uni.location);
+ if(j!=used_locations.end())
{
- error(*binding.node, format("Overlapping binding %d for '%s'", bind_point, binding.name));
- add_info(*i->second.node, format("Previously used here for '%s'", i->second.name));
+ if(j->second->name!=uni.name)
+ {
+ error(*uni.node, format("Overlapping location %d for '%s'", uni.location, uni.name));
+ add_info(*j->second->node, format("Previously used here for '%s'", j->second->name));
+ }
}
- if(i->second.type && binding.type)
+ else
{
- if(!is_same_type(*i->second.type, *binding.type))
+ for(unsigned k=0; k<uni.loc_count; ++k)
+ used_locations.insert(make_pair(uni.location+k, &uni));
+ }
+ }
+
+ if(uni.bind_point>=0)
+ {
+ map<unsigned, const Uniform *> &used = used_bindings[uni.desc_set];
+ map<unsigned, const Uniform *>::const_iterator j = used.find(uni.bind_point);
+ if(j!=used.end())
+ {
+ if(j->second->name!=uni.name)
{
- string type_name = (dynamic_cast<const StructDeclaration *>(binding.type) ? "struct type" :
- format("type '%s'", binding.type->name));
- error(*binding.node, format("Mismatched %s for binding %d '%s'", type_name, bind_point, binding.name));
-
- string message = "Previously used here";
- if(!dynamic_cast<const StructDeclaration *>(i->second.type))
- message += format(" with type '%s'", i->second.type->name);
- add_info(*i->second.node, message);
+ error(*uni.node, format("Overlapping binding %d for '%s'", uni.bind_point, uni.name));
+ add_info(*j->second->node, format("Previously used here for '%s'", j->second->name));
}
}
+ else
+ used.insert(make_pair(uni.bind_point, &uni));
}
- else
- used.insert(make_pair(bind_point, binding));
}
void GlobalInterfaceValidator::visit(VariableDeclaration &var)
{
- if(var.interface=="uniform" && var.layout)
- check_binding(*var.layout, var);
+ if(var.interface=="uniform")
+ {
+ Uniform uni;
+ uni.node = &var;
+ uni.type = var.type_declaration;
+ uni.name = var.name;
+ if(var.layout)
+ {
+ uni.location = get_layout_value(*var.layout, "location");
+ uni.loc_count = LocationCounter().apply(var);
+ uni.desc_set = get_layout_value(*var.layout, "set", 0);
+ uni.bind_point = get_layout_value(*var.layout, "binding");
+ }
+
+ uniforms.push_back(uni);
+ check_uniform(uniforms.back());
+ }
}
void GlobalInterfaceValidator::visit(InterfaceBlock &iface)
{
- if(iface.interface=="uniform" && iface.layout)
- check_binding(*iface.layout, iface);
+ if(iface.interface=="uniform")
+ {
+ Uniform uni;
+ uni.node = &iface;
+ uni.type = iface.struct_declaration;
+ uni.name = iface.block_name;
+ if(iface.layout)
+ {
+ uni.desc_set = get_layout_value(*iface.layout, "set", 0);
+ uni.bind_point = get_layout_value(*iface.layout, "binding");
+ }
+
+ uniforms.push_back(uni);
+ check_uniform(uniforms.back());
+ }
}
} // namespace SL