+void ExpressionValidator::visit(FunctionDeclaration &func)
+{
+ SetForScope<FunctionDeclaration *> set_func(current_function, &func);
+ TraversingVisitor::visit(func);
+}
+
+void ExpressionValidator::visit(Return &ret)
+{
+ if(current_function && current_function->return_type_declaration)
+ {
+ TypeDeclaration *return_type = current_function->return_type_declaration;
+ BasicTypeDeclaration *basic_return = dynamic_cast<BasicTypeDeclaration *>(return_type);
+ if(ret.expression)
+ {
+ if(ret.expression->type && ret.expression->type!=return_type)
+ error(ret, format("Return expression type '%s' is incompatible with declared return type '%s'",
+ ret.expression->type->name, return_type->name));
+ }
+ else if(!basic_return || basic_return->kind!=BasicTypeDeclaration::VOID)
+ error(ret, "Return statement without an expression in a function not returning 'void'");
+ }
+
+ TraversingVisitor::visit(ret);
+}
+
+
+int StageInterfaceValidator::get_location(const Layout &layout)
+{
+ for(vector<Layout::Qualifier>::const_iterator i=layout.qualifiers.begin(); i!=layout.qualifiers.end(); ++i)
+ if(i->name=="location")
+ return i->value;
+ return -1;
+}
+
+void StageInterfaceValidator::visit(VariableDeclaration &var)
+{
+ int location = (var.layout ? get_location(*var.layout) : -1);
+ if(var.interface=="in" && var.linked_declaration)
+ {
+ const Layout *linked_layout = var.linked_declaration->layout.get();
+ int linked_location = (linked_layout ? get_location(*linked_layout) : -1);
+ if(linked_location!=location)
+ {
+ error(var, format("Mismatched location %d for 'in %s'", location, var.name));
+ add_info(*var.linked_declaration, format("Linked to 'out %s' with location %d",
+ var.linked_declaration->name, linked_location));
+ }
+ if(var.type_declaration && var.linked_declaration->type_declaration)
+ {
+ const 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))
+ {
+ 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'",
+ var.linked_declaration->name, var.linked_declaration->type_declaration->name));
+ }
+ }
+ }
+
+ if(location>=0 && !var.interface.empty())
+ {
+ 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>();
+
+ for(unsigned i=0; i<loc_count; ++i)
+ {
+ map<unsigned, VariableDeclaration *>::const_iterator j = used.find(location+i);
+ if(j!=used.end())
+ {
+ error(var, format("Overlapping location %d for '%s %s'", location+i, var.interface, var.name));
+ add_info(*j->second, format("Previously used here for '%s %s'", j->second->interface, j->second->name));
+ }
+ else
+ used[location+i] = &var;
+ }
+ }
+}
+
+
+void GlobalInterfaceValidator::apply(Module &module)
+{
+ for(list<Stage>::iterator i=module.stages.begin(); i!=module.stages.end(); ++i)
+ {
+ stage = &*i;
+ i->content.visit(*this);
+ }
+}
+
+void GlobalInterfaceValidator::get_binding(const Layout &layout, unsigned &desc_set, int &binding)
+{
+ for(vector<Layout::Qualifier>::const_iterator i=layout.qualifiers.begin(); i!=layout.qualifiers.end(); ++i)
+ {
+ if(i->name=="set")
+ desc_set = i->value;
+ else if(i->name=="binding")
+ binding = i->value;
+ }
+}
+
+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;
+
+ map<unsigned, Binding> &used = used_bindings[desc_set];
+ map<unsigned, Binding>::const_iterator i = used.find(bind_point);
+ if(i!=used.end())
+ {
+ if(i->second.name!=binding.name)
+ {
+ 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(i->second.type && binding.type)
+ {
+ if(!is_same_type(*i->second.type, *binding.type))
+ {
+ 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);
+ }
+ }
+ }
+ else
+ used.insert(make_pair(bind_point, binding));
+}
+
+void GlobalInterfaceValidator::visit(VariableDeclaration &var)
+{
+ if(var.interface=="uniform" && var.layout)
+ check_binding(*var.layout, var);
+}
+
+void GlobalInterfaceValidator::visit(InterfaceBlock &iface)
+{
+ if(iface.interface=="uniform" && iface.layout)
+ check_binding(*iface.layout, iface);
+}
+