From aa11d44d8598e5e4ac15f2024a76251c250e0350 Mon Sep 17 00:00:00 2001 From: Frederich Munch Date: Mon, 18 Dec 2017 21:57:05 -0500 Subject: [PATCH 01/11] Refactor codegen of aassign into single method. --- src/liboslcomp/ast.h | 7 ++++++ src/liboslcomp/codegen.cpp | 50 ++++++++++++++++++++------------------ 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/liboslcomp/ast.h b/src/liboslcomp/ast.h index fd3facae0..303b6ac5c 100644 --- a/src/liboslcomp/ast.h +++ b/src/liboslcomp/ast.h @@ -366,6 +366,13 @@ class ASTNode : public OIIO::RefCnt { Symbol *codegen_struct_initializers (ref init, Symbol *sym, bool is_constructor=false); + // Codegen an array assignemnt: lval[index] = src + // If no index is provided the constant i is used. + // Will return either src or a temporary that was codegened. + Symbol* + codegen_aassign (TypeSpec elemtype, Symbol *src, Symbol *lval, + Symbol* index, int i = 0); + // Helper for param_default_literals: generate the string that gives // the initialization of the literal value (and/or the default, if // init==NULL) and append it to 'out'. Return whether the full diff --git a/src/liboslcomp/codegen.cpp b/src/liboslcomp/codegen.cpp index ca9209536..cd4a91f0b 100644 --- a/src/liboslcomp/codegen.cpp +++ b/src/liboslcomp/codegen.cpp @@ -830,6 +830,28 @@ ASTvariable_declaration::codegen_initializer (ref init, Symbol *sym) } +Symbol* +ASTNode::codegen_aassign (TypeSpec elemtype, Symbol *src, Symbol *lval, + Symbol* ind, int i) +{ + if (! equivalent (elemtype, src->typespec())) { + // Only allow A[ind] = x if the type of x is + // equivalent to that of A's elements. You can't, + // for example, do floatarray[ind] = int. So we + // convert through a temp. + Symbol *tmp = m_compiler->make_temporary (elemtype); + emitcode ("assign", tmp, src); + src = tmp; + } + + if (!ind) + ind = m_compiler->make_constant (i); + + emitcode ("aassign", lval, ind, src); + + return src; +} + void ASTNode::codegen_initlist (ref init, TypeSpec type, Symbol *sym) @@ -931,17 +953,8 @@ ASTNode::codegen_initlist (ref init, TypeSpec type, Symbol *sym) if (dest != sym) { if (sym->typespec().is_array()) { // Array variable -- assign to the i-th element - TypeSpec elemtype = sym->typespec().elementtype(); - if (! equivalent (elemtype, dest->typespec())) { - // We only allow A[ind] = x if the type of x is - // equivalent to that of A's elements. You can't, - // for example, do floatarray[ind] = int. So we - // convert through a temp. - Symbol *tmp = dest; - dest = m_compiler->make_temporary (elemtype); - emitcode ("assign", dest, tmp); - } - emitcode ("aassign", sym, m_compiler->make_constant(i), dest); + dest = codegen_aassign (sym->typespec().elementtype(), dest, + sym, nullptr, i); } else { // Non-array variable, just a simple assignment emitcode ("assign", sym, dest); @@ -1177,19 +1190,8 @@ ASTindex::codegen_assign (Symbol *src, Symbol *ind, emitcode ("compassign", temp, ind2, src); emitcode ("aassign", lv, ind, temp); } - else if (! equivalent (elemtype, src->typespec())) { - // Type conversion, e.g., colorarray[i] = float or - // floatarray[i] = int - // We only allow A[ind] = x if the type of x is equivalent - // to that of A's elements. You can't, for example, do - // floatarray[ind] = int. So we convert through a temp. - Symbol *tmp = src; - src = m_compiler->make_temporary (elemtype); - emitcode ("assign", src, tmp); - emitcode ("aassign", lv, ind, src); - } else { - // Simple Xarray[i] = X - emitcode ("aassign", lv, ind, src); + else { + src = codegen_aassign (elemtype, src, lv, ind); } } else if (lv->typespec().is_triple()) { emitcode ("compassign", lv, ind, src); From 831973f7276b653d8b1f55d98e4aa1719e853174 Mon Sep 17 00:00:00 2001 From: Frederich Munch Date: Mon, 18 Dec 2017 22:06:31 -0500 Subject: [PATCH 02/11] Allow float <=> int conversions using "aassign" without going through a temporary. --- CMakeLists.txt | 4 ++-- src/liboslcomp/codegen.cpp | 21 +++++++++++++-------- src/liboslexec/llvm_gen.cpp | 17 ++++++++++++++++- testsuite/array-aassign/ref/out.txt | 4 ++++ testsuite/array-aassign/run.py | 3 +++ testsuite/array-aassign/test.osl | 18 ++++++++++++++++++ 6 files changed, 56 insertions(+), 11 deletions(-) create mode 100644 testsuite/array-aassign/ref/out.txt create mode 100755 testsuite/array-aassign/run.py create mode 100644 testsuite/array-aassign/test.osl diff --git a/CMakeLists.txt b/CMakeLists.txt index 57780808d..fd85143c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -235,8 +235,8 @@ endmacro () if (OSL_BUILD_TESTS) # List all the individual testsuite tests here, except those that need # special installed tests. -TESTSUITE ( aastep allowconnect-err and-or-not-synonyms - arithmetic array array-derivs array-range +TESTSUITE ( aastep allowconnect-err and-or-not-synonyms arithmetic + array array-derivs array-range array-aassign blackbody blendmath breakcont bug-array-heapoffsets bug-locallifetime bug-outputinit bug-param-duplicate bug-peep diff --git a/src/liboslcomp/codegen.cpp b/src/liboslcomp/codegen.cpp index cd4a91f0b..bb1524785 100644 --- a/src/liboslcomp/codegen.cpp +++ b/src/liboslcomp/codegen.cpp @@ -834,14 +834,19 @@ Symbol* ASTNode::codegen_aassign (TypeSpec elemtype, Symbol *src, Symbol *lval, Symbol* ind, int i) { - if (! equivalent (elemtype, src->typespec())) { - // Only allow A[ind] = x if the type of x is - // equivalent to that of A's elements. You can't, - // for example, do floatarray[ind] = int. So we - // convert through a temp. - Symbol *tmp = m_compiler->make_temporary (elemtype); - emitcode ("assign", tmp, src); - src = tmp; + const TypeSpec& srctype = src->typespec(); + if (! equivalent (elemtype, srctype)) { + // Allow floatarray[ind] = int or intarray[ind] = float, but otherwise + // A[ind] = x is only allowed if the type of x is equivalent to that of + // A's elements. + // You can't for example, do colorarray[ind] = int. + if (elemtype.is_closure() || !elemtype.is_scalarnum() || + srctype.is_closure() || !srctype.is_scalarnum()) { + // Convert through a temp. + Symbol *tmp = m_compiler->make_temporary (elemtype); + emitcode ("assign", tmp, src); + src = tmp; + } } if (!ind) diff --git a/src/liboslexec/llvm_gen.cpp b/src/liboslexec/llvm_gen.cpp index c54e4900a..9001b1fe4 100644 --- a/src/liboslexec/llvm_gen.cpp +++ b/src/liboslexec/llvm_gen.cpp @@ -1336,9 +1336,24 @@ LLVMGEN (llvm_gen_aassign) } int num_components = Result.typespec().simpletype().aggregate; + + // Allow float <=> int casting + TypeDesc cast; + if (num_components == 1 && !Result.typespec().is_closure() && !Src.typespec().is_closure() && + (Result.typespec().is_int_based() || Result.typespec().is_float_based()) && + (Src.typespec().is_int_based() || Src.typespec().is_float_based())) { + cast = Result.typespec().simpletype(); + cast.arraylen = 0; + } else { + // Try to warn before llvm_fatal_error is called which provides little + // context as to what went wrong. + ASSERT (Result.typespec().simpletype().basetype == + Src.typespec().simpletype().basetype); + } + for (int d = 0; d <= 2; ++d) { for (int c = 0; c < num_components; ++c) { - llvm::Value *val = rop.loadLLVMValue (Src, c, d); + llvm::Value *val = rop.loadLLVMValue (Src, c, d, cast); rop.llvm_store_value (val, Result, d, index, c); } if (! Result.has_derivs()) diff --git a/testsuite/array-aassign/ref/out.txt b/testsuite/array-aassign/ref/out.txt new file mode 100644 index 000000000..236cf66c2 --- /dev/null +++ b/testsuite/array-aassign/ref/out.txt @@ -0,0 +1,4 @@ +Compiled test.osl -> test.oso +f[0]: 2.000000 +n.fa[0]: 4.000000 + diff --git a/testsuite/array-aassign/run.py b/testsuite/array-aassign/run.py new file mode 100755 index 000000000..431bb805c --- /dev/null +++ b/testsuite/array-aassign/run.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python + +command = testshade("test") diff --git a/testsuite/array-aassign/test.osl b/testsuite/array-aassign/test.osl new file mode 100644 index 000000000..20df252a0 --- /dev/null +++ b/testsuite/array-aassign/test.osl @@ -0,0 +1,18 @@ +// Test assigning to float from int without temporary works properly. +// Should really validate the generated oso too. + +struct nested { + float fa[3]; +}; + +shader test() +{ + int ival = 2; + float f[4]; + f[0] = ival++; + printf("f[0]: %f\n", f[0]); + + nested n; + n.fa[0] = ++ival; + printf("n.fa[0]: %f\n", n.fa[0]); +} From 1e89e1795034322711a19b5804cdaa0dfb260b23 Mon Sep 17 00:00:00 2001 From: Frederich Munch Date: Thu, 21 Dec 2017 11:33:52 -0500 Subject: [PATCH 03/11] C++11 style initializer lists. --- CMakeLists.txt | 2 +- src/liboslcomp/ast.cpp | 11 +- src/liboslcomp/ast.h | 56 +++-- src/liboslcomp/codegen.cpp | 76 +++++- src/liboslcomp/oslgram.y | 12 +- src/liboslcomp/typecheck.cpp | 271 +++++++++++++++++++++- testsuite/initlist/ref/out.txt | 29 +++ testsuite/initlist/run.py | 3 + testsuite/initlist/test.osl | 72 ++++++ testsuite/oslc-err-struct-dup/ref/out.txt | 1 + 10 files changed, 493 insertions(+), 40 deletions(-) create mode 100644 testsuite/initlist/ref/out.txt create mode 100755 testsuite/initlist/run.py create mode 100644 testsuite/initlist/test.osl diff --git a/CMakeLists.txt b/CMakeLists.txt index fd85143c3..dda597e3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -256,7 +256,7 @@ TESTSUITE ( aastep allowconnect-err and-or-not-synonyms arithmetic getsymbol-nonheap gettextureinfo group-outputs groupstring hash hashnoise hex hyperb - ieee_fp if incdec initops intbits isconnected isconstant + ieee_fp if incdec initlist initops intbits isconnected isconstant layers layers-Ciassign layers-entry layers-lazy layers-nonlazycopy layers-repeatedoutputs linearstep diff --git a/src/liboslcomp/ast.cpp b/src/liboslcomp/ast.cpp index 65027d6cc..4bf8195f7 100644 --- a/src/liboslcomp/ast.cpp +++ b/src/liboslcomp/ast.cpp @@ -498,6 +498,12 @@ ASTvariable_declaration::ASTvariable_declaration (OSLCompilerImpl *comp, m_isparam(isparam), m_isoutput(isoutput), m_ismetadata(ismeta), m_initlist(initlist) { + if (m_initlist && init) { + // Typecheck the init list early. + ASSERT (init->nodetype() == compound_initializer_node); + static_cast(init)->typecheck(type); + } + m_typespec = type; Symbol *f = comp->symtab().clash (name); if (f && ! m_ismetadata) { @@ -868,7 +874,8 @@ ASTreturn_statement::childname (size_t i) const ASTcompound_initializer::ASTcompound_initializer (OSLCompilerImpl *comp, ASTNode *exprlist) - : ASTNode (compound_initializer_node, comp, Nothing, exprlist) + : ASTtype_constructor (compound_initializer_node, comp, TypeSpec(), exprlist), + m_ctor(false) { } @@ -877,7 +884,7 @@ ASTcompound_initializer::ASTcompound_initializer (OSLCompilerImpl *comp, const char * ASTcompound_initializer::childname (size_t i) const { - return "expression_list"; + return canconstruct() ? "args" : "expression_list"; } diff --git a/src/liboslcomp/ast.h b/src/liboslcomp/ast.h index 303b6ac5c..dd979400a 100644 --- a/src/liboslcomp/ast.h +++ b/src/liboslcomp/ast.h @@ -714,8 +714,29 @@ class ASTreturn_statement : public ASTNode -class ASTcompound_initializer : public ASTNode +class ASTtype_constructor : public ASTNode { +protected: + ASTtype_constructor (NodeType n, OSLCompilerImpl *c, TypeSpec t, ASTNode *a) + : ASTNode (n, c, Nothing, a) { m_typespec = t; } + +public: + ASTtype_constructor (OSLCompilerImpl *comp, TypeSpec typespec, + ASTNode *args) + : ASTtype_constructor (type_constructor_node, comp, typespec, args) {} + + const char *nodetypename () const { return "type_constructor"; } + const char *childname (size_t i) const; + TypeSpec typecheck (TypeSpec expected); + Symbol *codegen (Symbol *dest = NULL); + + ref args () const { return child (0); } +}; + + +class ASTcompound_initializer : public ASTtype_constructor +{ + bool m_ctor; public: ASTcompound_initializer (OSLCompilerImpl *comp, ASTNode *exprlist); const char *nodetypename () const { return "compound_initializer"; } @@ -723,6 +744,19 @@ class ASTcompound_initializer : public ASTNode Symbol *codegen (Symbol *dest = NULL); ref initlist () const { return child (0); } + + bool canconstruct() const { return m_ctor; } + void canconstruct(bool b) { m_ctor = b; } + + enum Strictness { + typecheck_errors = 1, /// Report errors in typecheck calls + }; + + TypeSpec typecheck (TypeSpec expected, Strictness mode); + + TypeSpec typecheck (TypeSpec expected) { + return typecheck(expected, typecheck_errors); + } }; @@ -847,26 +881,6 @@ class ASTtypecast_expression : public ASTNode -class ASTtype_constructor : public ASTNode -{ -public: - ASTtype_constructor (OSLCompilerImpl *comp, TypeSpec typespec, - ASTNode *args) - : ASTNode (type_constructor_node, comp, 0, args) - { - m_typespec = typespec; - } - - const char *nodetypename () const { return "type_constructor"; } - const char *childname (size_t i) const; - TypeSpec typecheck (TypeSpec expected); - Symbol *codegen (Symbol *dest = NULL); - - ref args () const { return child (0); } -}; - - - class ASTfunction_call : public ASTNode { public: diff --git a/src/liboslcomp/codegen.cpp b/src/liboslcomp/codegen.cpp index bb1524785..46a54c1f4 100644 --- a/src/liboslcomp/codegen.cpp +++ b/src/liboslcomp/codegen.cpp @@ -461,9 +461,12 @@ ASTreturn_statement::codegen (Symbol *dest) Symbol * -ASTcompound_initializer::codegen (Symbol *dest) +ASTcompound_initializer::codegen (Symbol *sym) { - ASSERT(0 && "compound codegen"); + if (canconstruct()) + return ASTtype_constructor::codegen(sym); + + ASSERT(0 && "compound codegen"); return NULL; } @@ -661,7 +664,9 @@ ASTNode::one_default_literal (const Symbol *sym, ASTNode *init, float f = lit->floatval(); out += Strutil::format ("%.8g%s%.8g%s%.8g", f, sep, f, sep, f); } else if (init && init->typespec() == type && - init->nodetype() == ASTNode::type_constructor_node) { + (init->nodetype() == ASTNode::type_constructor_node || + (init->nodetype() == ASTNode::compound_initializer_node && + static_cast(init)->canconstruct()))) { ASTtype_constructor *ctr = (ASTtype_constructor *) init; ASTNode::ref val = ctr->args(); float f[3]; @@ -786,7 +791,9 @@ ASTvariable_declaration::param_default_literals (const Symbol *sym, bool compound = (init && init->nodetype() == compound_initializer_node); if (compound) { - init = ((ASTcompound_initializer *)init)->initlist().get(); + compound = !static_cast(init)->canconstruct(); + if (compound) + init = ((ASTcompound_initializer *)init)->initlist().get(); } bool completed = true; // have we output the full initialization? @@ -823,8 +830,29 @@ ASTvariable_declaration::codegen_initializer (ref init, Symbol *sym) init = ((ASTcompound_initializer *)init.get())->initlist(); codegen_initlist (init, typespec(), sym); } else { - if (init->nodetype() == compound_initializer_node) - init = ((ASTcompound_initializer *)init.get())->initlist(); + if (init->nodetype() == compound_initializer_node) { + ASTcompound_initializer* cinit = static_cast(init.get()); + init = cinit->initlist(); + if (cinit->canconstruct()) { + bool paraminit = (m_compiler->codegen_method() != m_compiler->main_method_name() && + (m_sym->symtype() == SymTypeParam || + m_sym->symtype() == SymTypeOutputParam)); + if (paraminit) { + // For parameter initialization, don't really generate ops if it + // can be statically initialized. + m_compiler->codegen_method (sym->name()); + sym->initbegin (m_compiler->next_op_label ()); + } + + Symbol* dest = cinit->codegen(sym); + if (dest != sym) + emitcode ("assign", sym, dest); + + if (paraminit) + sym->initend (m_compiler->next_op_label ()); + return; + } + } codegen_initlist (init, m_typespec, m_sym); } } @@ -890,6 +918,12 @@ ASTNode::codegen_initlist (ref init, TypeSpec type, Symbol *sym) } if (paraminit) { + // Warn early about struct array paramters. + // Handling this will likely need changes to oso format. + if (type.is_structure_array()) { + error ("array of struct are not allowed as parameters"); + return; + } // For parameter initialization, don't really generate ops if it // can be statically initialized. m_compiler->codegen_method (sym->name()); @@ -941,10 +975,31 @@ ASTNode::codegen_initlist (ref init, TypeSpec type, Symbol *sym) return; } } + else if (type.is_structure_array()) { + // Remove the array flag + TypeSpec elemtype = sym->typespec(); + elemtype.make_array(0); + // Make a temporary to construct into + Symbol* tmp = m_compiler->make_temporary(elemtype); + StructSpec *structspec = sym->typespec().structspec(); + ustring dstelem(sym->mangled()); + for (int i = 0; init && i < type.arraylength(); ++i) { + // Construct into the temporary + Symbol *ctmp = codegen_struct_initializers (init, tmp); + // Copy the temporary into the proper indexed element + codegen_assign_struct (structspec, dstelem, ustring(ctmp->mangled()), + m_compiler->make_constant(i), false, 0, + false); + init = init->next(); + } + if (paraminit) + sym->initend (m_compiler->next_op_label ()); + return; + } + else if (init->nodetype() == compound_initializer_node) + init = ((ASTcompound_initializer *)init.get())->initlist(); // Loop over a list of initializers (it's just 1 if not an array)... - if (init->nodetype() == compound_initializer_node) - init = ((ASTcompound_initializer *)init.get())->initlist(); for (int i = 0; init; init = init->next(), ++i) { if (sym->typespec().is_structure() && init->nodetype() == compound_initializer_node) { @@ -1000,7 +1055,7 @@ ASTNode::codegen_struct_initializers (ref init, Symbol *sym, } // General case -- per-field initializers - if (init->nodetype() == compound_initializer_node) { + if (!is_constructor && init->nodetype() == compound_initializer_node) { init = ((ASTcompound_initializer *)init.get())->initlist(); } StructSpec *structspec (sym->typespec().structspec()); @@ -1030,7 +1085,8 @@ ASTNode::codegen_struct_initializers (ref init, Symbol *sym, fieldsym->initbegin (m_compiler->next_op_label ()); } - if (init->nodetype() == compound_initializer_node) { + if (init->nodetype() == compound_initializer_node && + !((ASTcompound_initializer *)init.get())->canconstruct()) { // Initialize the field with a compound initializer codegen_initlist (((ASTcompound_initializer *)init.get())->initlist(), field.type, fieldsym); diff --git a/src/liboslcomp/oslgram.y b/src/liboslcomp/oslgram.y index 1f581920e..9a0946803 100644 --- a/src/liboslcomp/oslgram.y +++ b/src/liboslcomp/oslgram.y @@ -260,10 +260,12 @@ formal_param { // Grab the current declaration type, modify it to be array TypeSpec t = oslcompiler->current_typespec(); - if (! t.is_structure()) + if (! t.is_structure() && ! t.is_triple() && ! t.is_matrix()) oslcompiler->error (oslcompiler->filename(), oslcompiler->lineno(), - "Can't use '= {...}' initializer except with arrays or struct (%s)", $3); + "Can't use '= {...}' initializer " + "except with arrays, structs, vectors, " + "or matrix (%s)", $3); ASTvariable_declaration *var; var = new ASTvariable_declaration (oslcompiler, t, ustring($3), $4 /*init*/, @@ -455,10 +457,12 @@ def_expression | IDENTIFIER initializer_list { TypeSpec t = oslcompiler->current_typespec(); - if (! t.is_structure()) + if (! t.is_structure() && ! t.is_triple() && ! t.is_matrix()) oslcompiler->error (oslcompiler->filename(), oslcompiler->lineno(), - "Can't use '= {...}' initializer except with arrays or struct (%s)", $1); + "Can't use '= {...}' initializer " + "except with arrays, struct, vectors, " + "or matrix (%s)", $1); $$ = new ASTvariable_declaration (oslcompiler, t, ustring($1), $2, false, false, false, true /* initializer list */); diff --git a/src/liboslcomp/typecheck.cpp b/src/liboslcomp/typecheck.cpp index 661e7befb..318b58db6 100644 --- a/src/liboslcomp/typecheck.cpp +++ b/src/liboslcomp/typecheck.cpp @@ -157,6 +157,9 @@ ASTNode::typecheck_initlist (ref init, TypeSpec type, string_view name) ((ASTliteral *)init.get())->floatval() == 0.0f) { continue; // it's ok } + // Allow struct s = { ... }; vector v = { ... }; + if (type.is_structure_based() || (!type.is_closure() && type.is_triple())) + continue; if (! type.is_array() && i > 0) error ("Can't assign array initializers to non-array %s %s", type_c_str(type), name); @@ -202,8 +205,11 @@ ASTNode::typecheck_struct_initializers (ref init, TypeSpec type, } else if (field.type.is_structure()) { typecheck_struct_initializers (cinit->initlist(), field.type, fieldname); - } else { - error ("Can't use '{...}' for a struct field that is not an array"); + } else if (cinit->typecheck(field.type) != field.type) { + // Is this message still neccessary? typecheck above should have + // reported a more specific message. + error ("Can't use '{...}' for a struct field that is not an " + "array, vector, or constructable"); } continue; } @@ -840,6 +846,267 @@ ASTtype_constructor::typecheck (TypeSpec expected) +TypeSpec +ASTcompound_initializer::typecheck (TypeSpec expected, Strictness mode) +{ + if (m_ctor || m_typespec.is_structure_based() || + m_typespec.simpletype().basetype != TypeDesc::UNKNOWN ) { + if (m_typespec != expected) + error ("Cannot construct type '%s'", type_c_str(expected)); + return m_typespec; + } + + class TypeAdjuster { + enum { must_init_all = 1 << 1 }; /// All fields/elements must be inited + + // Only adjust the types on success of root initializer + // Oh for an llvm::SmallVector here! + std::vector> m_adjust; + const Strictness m_mode; + bool m_success; + + + void mark_type(ASTcompound_initializer* i, TypeSpec t, bool c = false) { + m_adjust.emplace_back(i, t, c); + } + + bool errors() const { return m_mode & typecheck_errors; } + + bool validate_size(int expected, int actual) const { + if (expected > actual) + return false; + return m_mode & must_init_all ? (expected == actual) : true; + } + + ASTcompound_initializer* + next_initlist(ASTNode *node, const TypeSpec& expected, int& nelem) const { + // Finished if already errored and not reporting them. + // Otherwise keep checking to report as many errors as possible + if (m_success || errors()) { + for (node = node->nextptr(); node; node = node->nextptr()) { + ++nelem; + if (node->nodetype() == compound_initializer_node) + return static_cast(node); + node->typecheck(expected); + } + } + return nullptr; + } + + public: + TypeAdjuster(Strictness m) : m_mode(m), m_success(true) {} + + ~TypeAdjuster() { + // Commit all ASTcompound_initializer types now. + if (m_success) { + for (auto&& initer : m_adjust) { + std::get<0>(initer)->m_typespec = std::get<1>(initer); + std::get<0>(initer)->m_ctor = std::get<2>(initer); + } + } + } + + // Adjust the type of an ASTcompound_initializer to the given type + void + basic_type(ASTcompound_initializer* cinit, const TypeSpec& to) { + // Handle nested initializer lists + // vector4 V = { {1}, {2}, {{3,4,5}, 6} }; + + if (!cinit->nchildren()) { + // Init all error's on + if (m_mode & must_init_all) { + m_success = false; + if (errors()) { + cinit->error ("Empty initializer list not allowed to" + "represent '%' here", cinit->type_c_str(to)); + } + } + return; + } + + // Count the number of arguments. + int a = 0; + for (ref c = cinit->child(0); c; c = c->next()) { + switch (c->nodetype()) { + case function_call_node: + case binary_expression_node: + if (c->typecheck().is_numeric()) + break; + + default: + m_success = false; + return; + + case literal_node: + case variable_ref_node: + case index_node: + break; + } + ++a; + } + + // Allow contruction by single element, or in case of triple + // an additional element (the space string). Let typechecking sort + // out any errors from mismatch/uncoercable types. + if (a == 1 || a == to.aggregate() || (to.is_triple() && a == 4)) { + mark_type(cinit, to, true); + return; + } + + m_success = false; + if (errors()) + cinit->error ("Cannot construct type '%s'", cinit->type_c_str(to)); + } + + // Adjust the type for every element of an array + void + array(ASTcompound_initializer* init, TypeSpec expected) { + ASSERT (expected.is_array()); + // Every element of the array is the same type + TypeSpec elemtype = expected.elementtype(); + + int nelem = 0; + ASTcompound_initializer* cinit = init; + + if (init->initlist()->nodetype() != compound_initializer_node) { + if (elemtype.is_closure() || elemtype.is_scalarnum()) { + // Array of scalars, init must be terminal list + for (ref c = init->child(0); c; c = c->next(), ++nelem) { + if ( c->typecheck (elemtype) != elemtype && + // Alllow assignment with comparable type + ! assignable(elemtype, c->typespec()) && + // Alllow closure assignments to '0' + ! (elemtype.is_closure() && + ! c->typespec().is_closure() && + c->typespec().is_int_or_float() && + c->nodetype() == literal_node && + ((ASTliteral *)c.get())->floatval() == 0.0f) ) { + + m_success = false; + if (!errors()) + return; + + c->error ("Can't assign '%s' to '%s'", + init->type_c_str(c->typespec()), + init->type_c_str(elemtype)); + } + } + cinit = nullptr; + } else { + // Skip over possible first literal filling a vector: + // vector v[] = { 1, {1,2,3} }; // ok + // struct s[] = { 1, {1,2,3} }; // illegal + if (elemtype.is_structure()) { + m_success = false; + if (!errors()) + return; + + init->error ("Can't assign '%s' to struct '%s'", + init->type_c_str(init->typespec()), + elemtype.structspec()->name()); + } + init->initlist()->typecheck (elemtype); + cinit = next_initlist(init->initlist().get(), elemtype, nelem); + } + } else { + // Remove the outer brackets: + // type a[3] = { {0}, {1}, {2} }; + cinit = static_cast(cinit->initlist().get()); + ASSERT (!cinit || cinit->nodetype() == compound_initializer_node); + } + + if (!elemtype.is_structure()) { + while (cinit) { + basic_type(cinit, elemtype); + cinit = next_initlist(cinit, elemtype, nelem); + } + } else { + // Every element of the array is the same StructSpec + while (cinit) { + fields(cinit, elemtype); + cinit = next_initlist(cinit, elemtype, nelem); + } + } + + // Match the number of elements unless expected is unsized. + if (m_success && (expected.is_unsized_array() || + validate_size(nelem, expected.arraylength()))) { + mark_type(init, expected); + return; + } + + m_success = false; + if (errors()) { + init->error ("Too %s initializers for a '%s'", + nelem < expected.arraylength() ? "few" : "many", + init->type_c_str(expected)); + } + } + + // Adjust the type for every field that has an initializer list + void + fields(ASTcompound_initializer* cinit, TypeSpec expected) { + ASSERT (expected.is_structure_based()); + StructSpec* structspec = expected.structspec(); + ASTNode* arg = cinit->initlist().get(); + int ninits = 0, nfields = structspec->numfields(); + while (arg && ninits < nfields) { + const TypeSpec& ftype = structspec->field(ninits++).type; + if (arg->nodetype() == compound_initializer_node) { + // Typecheck the nested initializer list + auto cinit = static_cast(arg); + if (!ftype.is_array()) { + if (!ftype.is_structure()) + basic_type(cinit, ftype); + else if (cinit->initlist()) + fields(cinit, ftype); + else if (m_mode & must_init_all) + m_success = false; // empty init list not allowd + } else + array(cinit, ftype); + + // Just leave if not reporting errors + if (!m_success && !errors()) + return; + + } else + arg->typecheck(ftype); + + arg = arg->nextptr(); + } + + // Can't have left over args, would mean ninits > nfields + if (m_success && !arg && validate_size(ninits, nfields)) { + mark_type(cinit, expected); + return; + } + + m_success = false; + if (errors()) { + cinit->error ("Too %s initializers for struct '%s'", + ninits < nfields ? "few" : "many", + structspec->name()); + } + } + }; + + // Scope to finalize/bind initializer list types before return m_typespec + { + TypeAdjuster ta(mode); + if (!expected.is_array()) { + if (expected.is_structure()) + ta.fields(this, expected); + else + ta.basic_type(this, expected); + } else + ta.array(this, expected); + } + + return m_typespec; +} + + + bool ASTNode::check_simple_arg (const TypeSpec &argtype, const char * &formals, bool coerce) diff --git a/testsuite/initlist/ref/out.txt b/testsuite/initlist/ref/out.txt new file mode 100644 index 000000000..f377015e6 --- /dev/null +++ b/testsuite/initlist/ref/out.txt @@ -0,0 +1,29 @@ +Compiled test.osl -> test.oso +Test vector initialization lists +vin: (1 2 3) +ci0: (5 6 7.5 8) +ci1: (8 7 6 5) +color: (3 2 2.5) +point: (4 5 6) +normal: (0 1 0) +vector: (10 20 30) +carray[0] = (1 1 1) +carray[1] = (2 2 2) +carray[2] = (3 3 3) +carray[3] = (4 0 0) +carray[4] = (0 5 0) +carray[5] = (0 0 6) +carray[6] = (7 8 9) +carray4[0] = (8 8 8 3) +carray4[1] = (10 10 10 10.5) +carray4[2] = (80 81 82 9) +ns0.c4 = {1 2 3 4} +ns0.ca = {(0 0 0), (1 2 3), (4 5 6)} +ns1[0] = {(0 1 2)} +ns1[1] = {(0.5 0.5 1)} +ns2 = {(5 6 7) (-1 -2 -3 -4)} +ns4[0] = {(5 6 7) (-1 -2 -3 -4)} +ns4[1] = {(1 2 3) (-5 -6 -7 -6)} +ns4[2] = {(11 12 13) (-10 -20 -30 -40)} +ns4[3] = {(7 7 7) (-5 -15 -20 -25)} + diff --git a/testsuite/initlist/run.py b/testsuite/initlist/run.py new file mode 100755 index 000000000..431bb805c --- /dev/null +++ b/testsuite/initlist/run.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python + +command = testshade("test") diff --git a/testsuite/initlist/test.osl b/testsuite/initlist/test.osl new file mode 100644 index 000000000..5204ff0d1 --- /dev/null +++ b/testsuite/initlist/test.osl @@ -0,0 +1,72 @@ + +struct custcolor4 +{ + color rgb; + float a; +}; + +struct nested { + custcolor4 c4; + color ca[3]; +}; + +struct nested1 { + color c3; +}; + +struct nested2 { + nested1 n1; + custcolor4 c4; +}; + +shader test( // FIXME: custcolor4 cina[2] = {{color(1),1}, {color(1),1}}, + vector vin = { 1, 2, 3 }, + custcolor4 ci0 = { {5, 6, 7 + u}, 8 }, + custcolor4 ci1 = { color(8,7,6), 5 } + ) +{ + printf("Test vector initialization lists\n"); + + printf("vin: (%g)\n", vin); + printf("ci0: (%g %g)\n", ci0.rgb, ci0.a); + printf("ci1: (%g %g)\n", ci1.rgb, ci1.a); + + color c0 = { 3, 2, 1 + u + 1}; + point p0 = { "common", 4, 5, 6 }; + normal n0 = { 0, 1, 0 }; + vector v0 = { 10, 20, 30 }; + + printf("color: (%g)\n", c0); + printf("point: (%g)\n", p0); + printf("normal: (%g)\n", n0); + printf("vector: (%g)\n", v0); + + color carray[7] = { {1}, {2}, {3}, {4,0,0}, {0,5,0}, {0,0,6}, {7,8,9} }; + for (int i = 0; i < 7; ++i) + printf ("carray[%d] = (%g)\n", i, carray[i]); + + custcolor4 carray4[3] = { {color(8), carray[2][0]}, {color(10), 10.5}, + {{80,81,82}, carray[6][2]} }; + for (int i = 0; i < 3; ++i) + printf ("carray4[%d] = (%g %g)\n", i, carray4[i].rgb, carray4[i].a); + + float a = 1, b = 4; + nested ns0 = { {{a,2,3},b}, { {0}, {1,2,3}, {4,5,6} } }; + printf ("ns0.c4 = {%g %g}\n", ns0.c4.rgb, ns0.c4.a); + printf ("ns0.ca = {(%g), (%g), (%g)}\n", ns0.ca[0], ns0.ca[1], ns0.ca[2]); + + nested1 ns1[2] = { {{0,1,2}}, {{u,v*1,u+v}} }; + printf ("ns1[0] = {(%g)}\n", ns1[0].c3); + printf ("ns1[1] = {(%g)}\n", ns1[1].c3); + + nested2 ns2 = { {{5,6,7}}, {{-1,-2,-3},-4} }; + printf ("ns2 = {(%g) (%g %g)}\n", ns2.n1.c3, ns2.c4.rgb, ns2.c4.a); + + int i = -4; + nested2 ns4[4] = { { {{5,6,7}}, {{-1,-2,-3}, i--} }, + { {{1,2,3}}, {{-5,-6,-7}, --i} }, + { {{11,12,13}}, {{-10,-20,-30},-40} }, + { {{7}}, custcolor4(color(-5,-15,-20),-25) } }; + for (int i = 0; i < 4; ++i) + printf ("ns4[%d] = {(%g) (%g %g)}\n", i, ns4[i].n1.c3, ns4[i].c4.rgb, ns4[i].c4.a); +} diff --git a/testsuite/oslc-err-struct-dup/ref/out.txt b/testsuite/oslc-err-struct-dup/ref/out.txt index 5fc4b31ed..710efba6d 100644 --- a/testsuite/oslc-err-struct-dup/ref/out.txt +++ b/testsuite/oslc-err-struct-dup/ref/out.txt @@ -1,4 +1,5 @@ test.osl:6: error: Field "c" already exists in struct "testStruct" test.osl:7: error: Field "i" already exists in struct "testStruct" test.osl:8: error: Field "f" already exists in struct "testStruct" +test.osl:14: error: Too many initializers for struct 'testStruct' FAILED test.osl From 15d487fc6306cd38684e49f6f1e260559f8bd32e Mon Sep 17 00:00:00 2001 From: Frederich Munch Date: Thu, 21 Dec 2017 16:10:20 -0500 Subject: [PATCH 04/11] Complete ASTcompound_initializer type check/binding. --- src/liboslcomp/ast.h | 12 +- src/liboslcomp/typecheck.cpp | 445 ++++++++++++++++----------------- testsuite/initlist/ref/out.txt | 4 + testsuite/initlist/test.osl | 10 + 4 files changed, 234 insertions(+), 237 deletions(-) diff --git a/src/liboslcomp/ast.h b/src/liboslcomp/ast.h index dd979400a..52da1e055 100644 --- a/src/liboslcomp/ast.h +++ b/src/liboslcomp/ast.h @@ -727,10 +727,17 @@ class ASTtype_constructor : public ASTNode const char *nodetypename () const { return "type_constructor"; } const char *childname (size_t i) const; - TypeSpec typecheck (TypeSpec expected); Symbol *codegen (Symbol *dest = NULL); ref args () const { return child (0); } + + // Typecheck construction of expected against args() + TypeSpec typecheck (TypeSpec expected, bool error); + + // Typecheck construction of m_typespec against args() + TypeSpec typecheck (TypeSpec expected) { + return typecheck (m_typespec, true); + } }; @@ -757,6 +764,9 @@ class ASTcompound_initializer : public ASTtype_constructor TypeSpec typecheck (TypeSpec expected) { return typecheck(expected, typecheck_errors); } + + // Helper for typechecking an initlist or structure. + class TypeAdjuster; }; diff --git a/src/liboslcomp/typecheck.cpp b/src/liboslcomp/typecheck.cpp index 318b58db6..f9f22a10f 100644 --- a/src/liboslcomp/typecheck.cpp +++ b/src/liboslcomp/typecheck.cpp @@ -783,7 +783,7 @@ ASTtypecast_expression::typecheck (TypeSpec expected) TypeSpec -ASTtype_constructor::typecheck (TypeSpec expected) +ASTtype_constructor::typecheck (TypeSpec expected, bool report) { // Hijack the usual function arg-checking routines. // So we have a set of valid patterns for each type constructor: @@ -797,23 +797,24 @@ ASTtype_constructor::typecheck (TypeSpec expected) // Select the pattern for the type of constructor we are... const char **patterns = NULL; TypeSpec argexpected; // default to unknown - if (typespec().is_float()) { + if (expected.is_float()) { patterns = float_patterns; - } else if (typespec().is_triple()) { + } else if (expected.is_triple()) { patterns = triple_patterns; // For triples, the constructor that takes just one argument is often // is used as a typecast, i.e. (vector)foo <==> vector(foo) // So pass on the expected type so it can resolve polymorphism in // the expected way. if (listlength(args()) == 1) - argexpected = m_typespec; - } else if (typespec().is_matrix()) { + argexpected = expected; + } else if (expected.is_matrix()) { patterns = matrix_patterns; - } else if (typespec().is_int()) { + } else if (expected.is_int()) { patterns = int_patterns; } else { - error ("Cannot construct type '%s'", type_c_str(typespec())); - return m_typespec; + if (report) + error ("Cannot construct type '%s'", type_c_str(expected)); + return TypeSpec(); } typecheck_children (argexpected); @@ -824,284 +825,256 @@ ASTtype_constructor::typecheck (TypeSpec expected) bool coerce = co; for (const char **pat = patterns; *pat; ++pat) { const char *code = *pat; - if (check_arglist (type_c_str(typespec()), args(), code + 1, coerce)) - return m_typespec; + if (check_arglist (type_c_str(expected), args(), code + 1, coerce)) + return expected; } } // If we made it this far, no match could be found. - std::string err = Strutil::format ("Cannot construct %s (", - type_c_str(typespec())); - for (ref a = args(); a; a = a->next()) { - err += a->typespec().string(); - if (a->next()) - err += ", "; - } - err += ")"; - error ("%s", err.c_str()); - // FIXME -- it might be nice here to enumerate for the user all the - // valid combinations. - return m_typespec; + if (report) { + std::string err = Strutil::format ("Cannot construct %s (", + type_c_str(expected)); + for (ref a = args(); a; a = a->next()) { + err += a->typespec().string(); + if (a->next()) + err += ", "; + } + err += ")"; + error ("%s", err.c_str()); + // FIXME -- it might be nice here to enumerate for the user all the + // valid combinations. + } + return TypeSpec(); } +class ASTcompound_initializer::TypeAdjuster { + enum { must_init_all = 1 << 1 }; /// All fields/elements must be inited -TypeSpec -ASTcompound_initializer::typecheck (TypeSpec expected, Strictness mode) -{ - if (m_ctor || m_typespec.is_structure_based() || - m_typespec.simpletype().basetype != TypeDesc::UNKNOWN ) { - if (m_typespec != expected) - error ("Cannot construct type '%s'", type_c_str(expected)); - return m_typespec; - } + // Only adjust the types on success of root initializer + // Oh for an llvm::SmallVector here! + std::vector> m_adjust; + OSLCompilerImpl* m_compiler; + const Strictness m_mode; + bool m_success; - class TypeAdjuster { - enum { must_init_all = 1 << 1 }; /// All fields/elements must be inited + void mark_type(ASTcompound_initializer* i, TypeSpec t, bool c = false) { + m_adjust.emplace_back(i, t, c); + } - // Only adjust the types on success of root initializer - // Oh for an llvm::SmallVector here! - std::vector> m_adjust; - const Strictness m_mode; - bool m_success; + // Should errors be reported? + bool errors() const { return m_mode & typecheck_errors; } + bool validate_size(int expected, int actual) const { + if (expected > actual) + return false; + return m_mode & must_init_all ? (expected == actual) : true; + } - void mark_type(ASTcompound_initializer* i, TypeSpec t, bool c = false) { - m_adjust.emplace_back(i, t, c); + ASTcompound_initializer* + next_initlist(ASTNode *node, const TypeSpec& expected, int& nelem) const { + // Finished if already errored and not reporting them. + // Otherwise keep checking to report as many errors as possible + if (m_success || errors()) { + for (node = node->nextptr(); node; node = node->nextptr()) { + ++nelem; + if (node->nodetype() == compound_initializer_node) + return static_cast(node); + node->typecheck(expected); + } } + return nullptr; + } - bool errors() const { return m_mode & typecheck_errors; } + // Typecheck node, reporting an error. + // Returns whether to continue iteration (not whether typecheck errored). + bool + typecheck (ref node, TypeSpec expected, const StructSpec* spec = nullptr, + const StructSpec::FieldSpec* field = nullptr) { + if ( node->typecheck (expected) != expected && + // Alllow assignment with comparable type + ! assignable(expected, node->typespec()) && + // Alllow closure assignments to '0' + ! (expected.is_closure() && + ! node->typespec().is_closure() && + node->typespec().is_int_or_float() && + node->nodetype() == literal_node && + ((ASTliteral *)node.get())->floatval() == 0.0f) ) { - bool validate_size(int expected, int actual) const { - if (expected > actual) + m_success = false; + if (!errors()) return false; - return m_mode & must_init_all ? (expected == actual) : true; - } - ASTcompound_initializer* - next_initlist(ASTNode *node, const TypeSpec& expected, int& nelem) const { - // Finished if already errored and not reporting them. - // Otherwise keep checking to report as many errors as possible - if (m_success || errors()) { - for (node = node->nextptr(); node; node = node->nextptr()) { - ++nelem; - if (node->nodetype() == compound_initializer_node) - return static_cast(node); - node->typecheck(expected); - } - } - return nullptr; + ASSERT (!spec || field); + node->error ("Can't assign '%s' to '%s%s'", + m_compiler->type_c_str(node->typespec()), + m_compiler->type_c_str(expected), + !spec ? "" : + Strutil::format(" %s.%s", spec->name(), field->name)); } + return true; + } - public: - TypeAdjuster(Strictness m) : m_mode(m), m_success(true) {} - - ~TypeAdjuster() { - // Commit all ASTcompound_initializer types now. - if (m_success) { - for (auto&& initer : m_adjust) { - std::get<0>(initer)->m_typespec = std::get<1>(initer); - std::get<0>(initer)->m_ctor = std::get<2>(initer); - } +public: + TypeAdjuster(OSLCompilerImpl* c, Strictness m = typecheck_errors) : + m_compiler(c), m_mode(m), m_success(true) {} + + ~TypeAdjuster () { + // Commit infered types of all ASTcompound_initializers scanned. + if (m_success) { + for (auto&& initer : m_adjust) { + std::get<0>(initer)->m_typespec = std::get<1>(initer); + std::get<0>(initer)->m_ctor = std::get<2>(initer); } } + } - // Adjust the type of an ASTcompound_initializer to the given type - void - basic_type(ASTcompound_initializer* cinit, const TypeSpec& to) { - // Handle nested initializer lists - // vector4 V = { {1}, {2}, {{3,4,5}, 6} }; - - if (!cinit->nchildren()) { - // Init all error's on - if (m_mode & must_init_all) { - m_success = false; - if (errors()) { - cinit->error ("Empty initializer list not allowed to" - "represent '%' here", cinit->type_c_str(to)); - } - } - return; - } - - // Count the number of arguments. - int a = 0; - for (ref c = cinit->child(0); c; c = c->next()) { - switch (c->nodetype()) { - case function_call_node: - case binary_expression_node: - if (c->typecheck().is_numeric()) - break; - - default: - m_success = false; - return; - - case literal_node: - case variable_ref_node: - case index_node: - break; + // Adjust the type of an ASTcompound_initializer to the given type + void + typecheck_init(ASTcompound_initializer* cinit, const TypeSpec& to) { + // Handle the ASTcompound_initializer as a constructor of type to + + if (!cinit->nchildren()) { + // Init all error's on + if (m_mode & must_init_all) { + m_success = false; + if (errors()) { + cinit->error ("Empty initializer list not allowed to" + "represent '%' here", cinit->type_c_str(to)); } - ++a; - } - - // Allow contruction by single element, or in case of triple - // an additional element (the space string). Let typechecking sort - // out any errors from mismatch/uncoercable types. - if (a == 1 || a == to.aggregate() || (to.is_triple() && a == 4)) { - mark_type(cinit, to, true); - return; } + return; + } + if (cinit->ASTtype_constructor::typecheck(to, errors()) == to) + mark_type(cinit, to, true); + else m_success = false; - if (errors()) - cinit->error ("Cannot construct type '%s'", cinit->type_c_str(to)); - } + } - // Adjust the type for every element of an array - void - array(ASTcompound_initializer* init, TypeSpec expected) { - ASSERT (expected.is_array()); - // Every element of the array is the same type - TypeSpec elemtype = expected.elementtype(); - - int nelem = 0; - ASTcompound_initializer* cinit = init; - - if (init->initlist()->nodetype() != compound_initializer_node) { - if (elemtype.is_closure() || elemtype.is_scalarnum()) { - // Array of scalars, init must be terminal list - for (ref c = init->child(0); c; c = c->next(), ++nelem) { - if ( c->typecheck (elemtype) != elemtype && - // Alllow assignment with comparable type - ! assignable(elemtype, c->typespec()) && - // Alllow closure assignments to '0' - ! (elemtype.is_closure() && - ! c->typespec().is_closure() && - c->typespec().is_int_or_float() && - c->nodetype() == literal_node && - ((ASTliteral *)c.get())->floatval() == 0.0f) ) { - - m_success = false; - if (!errors()) - return; - - c->error ("Can't assign '%s' to '%s'", - init->type_c_str(c->typespec()), - init->type_c_str(elemtype)); - } - } - cinit = nullptr; - } else { - // Skip over possible first literal filling a vector: - // vector v[] = { 1, {1,2,3} }; // ok - // struct s[] = { 1, {1,2,3} }; // illegal - if (elemtype.is_structure()) { - m_success = false; - if (!errors()) - return; - - init->error ("Can't assign '%s' to struct '%s'", - init->type_c_str(init->typespec()), - elemtype.structspec()->name()); - } - init->initlist()->typecheck (elemtype); - cinit = next_initlist(init->initlist().get(), elemtype, nelem); - } - } else { - // Remove the outer brackets: - // type a[3] = { {0}, {1}, {2} }; - cinit = static_cast(cinit->initlist().get()); - ASSERT (!cinit || cinit->nodetype() == compound_initializer_node); - } + // Adjust the type for every element of an array + void + typecheck_array(ASTcompound_initializer* init, TypeSpec expected) { + ASSERT (expected.is_array()); + // Every element of the array is the same type + TypeSpec elemtype = expected.elementtype(); - if (!elemtype.is_structure()) { - while (cinit) { - basic_type(cinit, elemtype); - cinit = next_initlist(cinit, elemtype, nelem); - } - } else { - // Every element of the array is the same StructSpec - while (cinit) { - fields(cinit, elemtype); - cinit = next_initlist(cinit, elemtype, nelem); - } - } + int nelem = 0; + ASTcompound_initializer* cinit = init; - // Match the number of elements unless expected is unsized. - if (m_success && (expected.is_unsized_array() || - validate_size(nelem, expected.arraylength()))) { - mark_type(init, expected); + if (init->initlist()->nodetype() != compound_initializer_node) { + if (! typecheck(init->initlist(), elemtype)) return; - } - m_success = false; - if (errors()) { - init->error ("Too %s initializers for a '%s'", - nelem < expected.arraylength() ? "few" : "many", - init->type_c_str(expected)); + cinit = next_initlist(init->initlist().get(), elemtype, nelem); + } else { + // Remove the outer brackets: + // type a[3] = { {0}, {1}, {2} }; + cinit = static_cast(cinit->initlist().get()); + ASSERT (!cinit || cinit->nodetype() == compound_initializer_node); + } + + if (!elemtype.is_structure()) { + while (cinit) { + typecheck_init(cinit, elemtype); + cinit = next_initlist(cinit, elemtype, nelem); + } + } else { + // Every element of the array is the same StructSpec + while (cinit) { + typecheck_fields(cinit, cinit->initlist(), elemtype); + cinit = next_initlist(cinit, elemtype, nelem); } } - // Adjust the type for every field that has an initializer list - void - fields(ASTcompound_initializer* cinit, TypeSpec expected) { - ASSERT (expected.is_structure_based()); - StructSpec* structspec = expected.structspec(); - ASTNode* arg = cinit->initlist().get(); - int ninits = 0, nfields = structspec->numfields(); - while (arg && ninits < nfields) { - const TypeSpec& ftype = structspec->field(ninits++).type; - if (arg->nodetype() == compound_initializer_node) { - // Typecheck the nested initializer list - auto cinit = static_cast(arg); - if (!ftype.is_array()) { - if (!ftype.is_structure()) - basic_type(cinit, ftype); - else if (cinit->initlist()) - fields(cinit, ftype); - else if (m_mode & must_init_all) - m_success = false; // empty init list not allowd - } else - array(cinit, ftype); + // Match the number of elements unless expected is unsized. + if (m_success && (expected.is_unsized_array() || + validate_size(nelem, expected.arraylength()))) { + mark_type(init, expected); + return; + } - // Just leave if not reporting errors - if (!m_success && !errors()) - return; + m_success = false; + if (errors()) { + init->error ("Too %s initializers for a '%s'", + nelem < expected.arraylength() ? "few" : "many", + init->type_c_str(expected)); + } + } + // Adjust the type for every field that has an initializer list + void + typecheck_fields(ASTNode* parent, ref arg, TypeSpec expected) { + ASSERT (expected.is_structure_based()); + StructSpec* structspec = expected.structspec(); + int ninits = 0, nfields = structspec->numfields(); + while (arg && ninits < nfields) { + const auto& field = structspec->field(ninits++); + const auto& ftype = field.type; + if (arg->nodetype() == compound_initializer_node) { + // Typecheck the nested initializer list + auto cinit = static_cast(arg.get()); + if (!field.type.is_array()) { + if (!field.type.is_structure()) + typecheck_init(cinit, field.type); + else if (cinit->initlist()) + typecheck_fields(cinit, cinit->initlist().get(), ftype); + else if (m_mode & must_init_all) + m_success = false; // empty init list not allowd } else - arg->typecheck(ftype); + typecheck_array(cinit, ftype); - arg = arg->nextptr(); - } + // Just leave if not reporting errors + if (!m_success && !errors()) + return; - // Can't have left over args, would mean ninits > nfields - if (m_success && !arg && validate_size(ninits, nfields)) { - mark_type(cinit, expected); + } else if (! typecheck(arg, ftype, structspec, &field)) return; - } - m_success = false; - if (errors()) { - cinit->error ("Too %s initializers for struct '%s'", - ninits < nfields ? "few" : "many", - structspec->name()); - } + arg = arg->next(); } - }; - // Scope to finalize/bind initializer list types before return m_typespec + // Can't have left over args, would mean ninits > nfields + if (m_success && !arg && validate_size(ninits, nfields)) { + if (parent->nodetype() == compound_initializer_node) + mark_type(static_cast(parent), expected); + return; + } + + m_success = false; + if (errors() && (arg || nfields > ninits)) { + parent->error ("Too %s initializers for struct '%s'", + ninits < nfields ? "few" : "many", + structspec->name()); + } + } +}; + + + +TypeSpec +ASTcompound_initializer::typecheck (TypeSpec expected, Strictness mode) +{ + if (m_ctor || m_typespec.is_structure_based() || + m_typespec.simpletype().basetype != TypeDesc::UNKNOWN ) { + if (m_typespec != expected) + error ("Cannot construct type '%s'", type_c_str(expected)); + return m_typespec; + } + + // Scoped so ~TypeAdjuster() will bind m_typespec before return m_typespec { - TypeAdjuster ta(mode); + TypeAdjuster ta(m_compiler, mode); if (!expected.is_array()) { if (expected.is_structure()) - ta.fields(this, expected); + ta.typecheck_fields(this, initlist(), expected); else - ta.basic_type(this, expected); + ta.typecheck_init(this, expected); } else - ta.array(this, expected); + ta.typecheck_array(this, expected); } + return m_typespec; } diff --git a/testsuite/initlist/ref/out.txt b/testsuite/initlist/ref/out.txt index f377015e6..b93edb4c9 100644 --- a/testsuite/initlist/ref/out.txt +++ b/testsuite/initlist/ref/out.txt @@ -14,6 +14,10 @@ carray[3] = (4 0 0) carray[4] = (0 5 0) carray[5] = (0 0 6) carray[6] = (7 8 9) +c4ctors[0]: (1 1 1 2) +c4ctors[1]: (3 3 3 4) +c4ctors[2]: (5 6 7 8) +c4ctors[3]: (2 2 2 1) carray4[0] = (8 8 8 3) carray4[1] = (10 10 10 10.5) carray4[2] = (80 81 82 9) diff --git a/testsuite/initlist/test.osl b/testsuite/initlist/test.osl index 5204ff0d1..18ea4b216 100644 --- a/testsuite/initlist/test.osl +++ b/testsuite/initlist/test.osl @@ -5,6 +5,11 @@ struct custcolor4 float a; }; +custcolor4 __operator__neg__(custcolor4 a) +{ + return custcolor4(-a.rgb, -a.a); +} + struct nested { custcolor4 c4; color ca[3]; @@ -45,6 +50,11 @@ shader test( // FIXME: custcolor4 cina[2] = {{color(1),1}, {color(1),1}}, for (int i = 0; i < 7; ++i) printf ("carray[%d] = (%g)\n", i, carray[i]); + custcolor4 c4ctors[4] = { custcolor4(1,2), custcolor4(3,4), + custcolor4(color(5,6,7), 8), -custcolor4(-2,-1) }; + for (int i = 0; i < 4; ++i) + printf("c4ctors[%d]: (%g %g)\n", i, c4ctors[i].rgb, c4ctors[i].a); + custcolor4 carray4[3] = { {color(8), carray[2][0]}, {color(10), 10.5}, {{80,81,82}, carray[6][2]} }; for (int i = 0; i < 3; ++i) From 198e80e18dafea8d1d8af99565920fb6516d3fbb Mon Sep 17 00:00:00 2001 From: Frederich Munch Date: Fri, 15 Dec 2017 00:14:01 -0500 Subject: [PATCH 05/11] Initialize struct array elements without going through a temporary. --- src/liboslcomp/ast.h | 3 +- src/liboslcomp/codegen.cpp | 67 +++++++++++++++++++++++++------------- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/liboslcomp/ast.h b/src/liboslcomp/ast.h index 52da1e055..63bd74547 100644 --- a/src/liboslcomp/ast.h +++ b/src/liboslcomp/ast.h @@ -364,7 +364,8 @@ class ASTNode : public OIIO::RefCnt { // It's in the ASTNode base class because it's used from mutiple // subclasses. Symbol *codegen_struct_initializers (ref init, Symbol *sym, - bool is_constructor=false); + bool is_constructor=false, + Symbol *arrayindex = nullptr); // Codegen an array assignemnt: lval[index] = src // If no index is provided the constant i is used. diff --git a/src/liboslcomp/codegen.cpp b/src/liboslcomp/codegen.cpp index 46a54c1f4..24f35ac3e 100644 --- a/src/liboslcomp/codegen.cpp +++ b/src/liboslcomp/codegen.cpp @@ -976,20 +976,28 @@ ASTNode::codegen_initlist (ref init, TypeSpec type, Symbol *sym) } } else if (type.is_structure_array()) { - // Remove the array flag - TypeSpec elemtype = sym->typespec(); - elemtype.make_array(0); - // Make a temporary to construct into - Symbol* tmp = m_compiler->make_temporary(elemtype); - StructSpec *structspec = sym->typespec().structspec(); - ustring dstelem(sym->mangled()); for (int i = 0; init && i < type.arraylength(); ++i) { - // Construct into the temporary - Symbol *ctmp = codegen_struct_initializers (init, tmp); - // Copy the temporary into the proper indexed element - codegen_assign_struct (structspec, dstelem, ustring(ctmp->mangled()), - m_compiler->make_constant(i), false, 0, - false); + ASTNode* expr = init.get(); + bool ctor = false; + Symbol* dest = sym; + switch (expr->nodetype()) { + case function_call_node: { + ASTfunction_call* fcall = static_cast(expr); + if ((ctor = fcall->is_struct_ctr())) { + expr = fcall->args().get(); + ASSERT (expr != nullptr); + } + } + break; + case compound_initializer_node: + ctor = static_cast(expr)->canconstruct(); + break; + default: + break; + } + codegen_struct_initializers (expr, dest, ctor, + m_compiler->make_constant(i)); + init = init->next(); } if (paraminit) @@ -1032,7 +1040,7 @@ ASTNode::codegen_initlist (ref init, TypeSpec type, Symbol *sym) Symbol * ASTNode::codegen_struct_initializers (ref init, Symbol *sym, - bool is_constructor) + bool is_constructor, Symbol *arrayindex) { // If we're doing this initialization for shader params for their // init ops, we need to take care to set the codegen method names @@ -1041,15 +1049,15 @@ ASTNode::codegen_struct_initializers (ref init, Symbol *sym, (sym->symtype() == SymTypeParam || sym->symtype() == SymTypeOutputParam)); - ASSERT (sym->typespec().is_structure()); + ASSERT (sym->typespec().is_structure_based()); if (init->nodetype() != compound_initializer_node && !is_constructor) { // Just one initializer, it's a whole struct of the right type. Symbol *initsym = init->codegen (sym); if (initsym != sym) { StructSpec *structspec (sym->typespec().structspec()); codegen_assign_struct (structspec, ustring(sym->mangled()), - ustring(initsym->mangled()), NULL, true, 0, - paraminit); + ustring(initsym->mangled()), arrayindex, + true, 0, paraminit); } return sym; } @@ -1065,9 +1073,13 @@ ASTNode::codegen_struct_initializers (ref init, Symbol *sym, ustring fieldname = ustring::format ("%s.%s", sym->mangled().c_str(), field.name.c_str()); Symbol *fieldsym = m_compiler->symtab().find_exact (fieldname); - if (fieldsym->typespec().is_structure()) { + if (fieldsym->typespec().is_structure_based() && + (init->nodetype() == type_constructor_node || + init->nodetype() == compound_initializer_node)) { + bool ctor = init->nodetype() == type_constructor_node ? true : + static_cast(init.get())->canconstruct(); // The field is itself a nested struct, so recurse - codegen_struct_initializers (init, fieldsym); + codegen_struct_initializers (init, fieldsym, ctor, arrayindex); continue; } @@ -1090,11 +1102,22 @@ ASTNode::codegen_struct_initializers (ref init, Symbol *sym, // Initialize the field with a compound initializer codegen_initlist (((ASTcompound_initializer *)init.get())->initlist(), field.type, fieldsym); - } else { + } else if (init->nodetype() == function_call_node && + static_cast(init.get())->is_struct_ctr()) { + codegen_struct_initializers (static_cast(init.get())->args(), + fieldsym, true, arrayindex); + } + else { // Initialize the field with a scalar initializer Symbol *dest = init->codegen (fieldsym); - if (dest != fieldsym) - emitcode ("assign", fieldsym, dest); + if (dest != fieldsym) { + if (!arrayindex) + emitcode ("assign", fieldsym, dest); + else { + dest = codegen_aassign (fieldsym->typespec().elementtype(), + dest, fieldsym, arrayindex); + } + } } if (paraminit) From d918af2cd891646baa9c1c1bb5f6dba1eb1c239b Mon Sep 17 00:00:00 2001 From: Frederich Munch Date: Wed, 20 Dec 2017 02:38:43 -0500 Subject: [PATCH 06/11] Allow initializer lists as return types. --- CMakeLists.txt | 1 + src/liboslcomp/codegen.cpp | 8 ++- src/liboslcomp/oslgram.y | 4 ++ testsuite/initlist/ref/out.txt | 5 ++ testsuite/initlist/test.osl | 50 +++++++++++++++++-- .../oslc-err-initlist-return/ref/out.txt | 8 +++ testsuite/oslc-err-initlist-return/run.py | 6 +++ testsuite/oslc-err-initlist-return/test.osl | 22 ++++++++ 8 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 testsuite/oslc-err-initlist-return/ref/out.txt create mode 100755 testsuite/oslc-err-initlist-return/run.py create mode 100644 testsuite/oslc-err-initlist-return/test.osl diff --git a/CMakeLists.txt b/CMakeLists.txt index dda597e3a..fd9036cde 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -272,6 +272,7 @@ TESTSUITE ( aastep allowconnect-err and-or-not-synonyms arithmetic oslc-err-arrayindex oslc-err-closuremul oslc-err-field oslc-err-format oslc-err-funcoverload oslc-err-intoverflow oslc-err-noreturn oslc-err-notfunc + oslc-err-initlist-return oslc-err-outputparamvararray oslc-err-paramdefault oslc-err-struct-array-init oslc-err-struct-ctr oslc-err-struct-dup oslc-err-struct-print diff --git a/src/liboslcomp/codegen.cpp b/src/liboslcomp/codegen.cpp index 24f35ac3e..47085d7ec 100644 --- a/src/liboslcomp/codegen.cpp +++ b/src/liboslcomp/codegen.cpp @@ -463,9 +463,15 @@ ASTreturn_statement::codegen (Symbol *dest) Symbol * ASTcompound_initializer::codegen (Symbol *sym) { - if (canconstruct()) + if (canconstruct()) return ASTtype_constructor::codegen(sym); + if (m_typespec.is_structure_based()) { + if (!sym) + sym = m_compiler->make_temporary (m_typespec); + return codegen_struct_initializers (initlist(), sym, true /*is_constructor*/); + } + ASSERT(0 && "compound codegen"); return NULL; } diff --git a/src/liboslcomp/oslgram.y b/src/liboslcomp/oslgram.y index 9a0946803..35c0df7c9 100644 --- a/src/liboslcomp/oslgram.y +++ b/src/liboslcomp/oslgram.y @@ -692,6 +692,10 @@ return_statement { $$ = new ASTreturn_statement (oslcompiler, $2); } + | RETURN compound_initializer ';' + { + $$ = new ASTreturn_statement (oslcompiler, $2); + } ; for_init_statement diff --git a/testsuite/initlist/ref/out.txt b/testsuite/initlist/ref/out.txt index b93edb4c9..a6b8487a9 100644 --- a/testsuite/initlist/ref/out.txt +++ b/testsuite/initlist/ref/out.txt @@ -14,6 +14,8 @@ carray[3] = (4 0 0) carray[4] = (0 5 0) carray[5] = (0 0 6) carray[6] = (7 8 9) +carray[7] = (10 11 12) +carray[8] = (2 2 2) c4ctors[0]: (1 1 1 2) c4ctors[1]: (3 3 3 4) c4ctors[2]: (5 6 7 8) @@ -21,6 +23,7 @@ c4ctors[3]: (2 2 2 1) carray4[0] = (8 8 8 3) carray4[1] = (10 10 10 10.5) carray4[2] = (80 81 82 9) +carray4[3] = (0.1 0.2 0.3 0.4) ns0.c4 = {1 2 3 4} ns0.ca = {(0 0 0), (1 2 3), (4 5 6)} ns1[0] = {(0 1 2)} @@ -30,4 +33,6 @@ ns4[0] = {(5 6 7) (-1 -2 -3 -4)} ns4[1] = {(1 2 3) (-5 -6 -7 -6)} ns4[2] = {(11 12 13) (-10 -20 -30 -40)} ns4[3] = {(7 7 7) (-5 -15 -20 -25)} +ar: {1, {2 3 4}} +br: {{6 7 8}, 5} diff --git a/testsuite/initlist/test.osl b/testsuite/initlist/test.osl index 18ea4b216..09658b8b9 100644 --- a/testsuite/initlist/test.osl +++ b/testsuite/initlist/test.osl @@ -10,6 +10,10 @@ custcolor4 __operator__neg__(custcolor4 a) return custcolor4(-a.rgb, -a.a); } +custcolor4 color4value() { + return { {.1, .2, .3}, .4 }; +} + struct nested { custcolor4 c4; color ca[3]; @@ -24,6 +28,33 @@ struct nested2 { custcolor4 c4; }; +struct acolor { + float a; + color rgb; +}; + +acolor acolorvalue() { + return { 22, {23,33,43} }; +} + +acolor acolorcopy(acolor a) { + return a; +} + +struct bcolor { + color rgb; + float a; +}; + +acolor colorvalue() { + return { 1, {2,3,4} }; +} + +bcolor colorvalue() { + return { {6,7,8}, 5 }; +} + + shader test( // FIXME: custcolor4 cina[2] = {{color(1),1}, {color(1),1}}, vector vin = { 1, 2, 3 }, custcolor4 ci0 = { {5, 6, 7 + u}, 8 }, @@ -46,8 +77,9 @@ shader test( // FIXME: custcolor4 cina[2] = {{color(1),1}, {color(1),1}}, printf("normal: (%g)\n", n0); printf("vector: (%g)\n", v0); - color carray[7] = { {1}, {2}, {3}, {4,0,0}, {0,5,0}, {0,0,6}, {7,8,9} }; - for (int i = 0; i < 7; ++i) + color carray[9] = { {1}, {2}, {3}, {4,0,0}, {0,5,0}, {0,0,6}, {7,8,9}, + color(10,11,12), 2 }; + for (int i = 0; i < 9; ++i) printf ("carray[%d] = (%g)\n", i, carray[i]); custcolor4 c4ctors[4] = { custcolor4(1,2), custcolor4(3,4), @@ -55,9 +87,9 @@ shader test( // FIXME: custcolor4 cina[2] = {{color(1),1}, {color(1),1}}, for (int i = 0; i < 4; ++i) printf("c4ctors[%d]: (%g %g)\n", i, c4ctors[i].rgb, c4ctors[i].a); - custcolor4 carray4[3] = { {color(8), carray[2][0]}, {color(10), 10.5}, - {{80,81,82}, carray[6][2]} }; - for (int i = 0; i < 3; ++i) + custcolor4 carray4[4] = { {color(8), carray[2][0]}, {color(10), 10.5}, + {{80,81,82}, carray[6][2]}, color4value() }; + for (int i = 0; i < 4; ++i) printf ("carray4[%d] = (%g %g)\n", i, carray4[i].rgb, carray4[i].a); float a = 1, b = 4; @@ -79,4 +111,12 @@ shader test( // FIXME: custcolor4 cina[2] = {{color(1),1}, {color(1),1}}, { {{7}}, custcolor4(color(-5,-15,-20),-25) } }; for (int i = 0; i < 4; ++i) printf ("ns4[%d] = {(%g) (%g %g)}\n", i, ns4[i].n1.c3, ns4[i].c4.rgb, ns4[i].c4.a); + + { + acolor ar = colorvalue(); + bcolor br = colorvalue(); + + printf("ar: {%g, {%g}}\n", ar.a, ar.rgb); + printf("br: {{%g}, %g}\n", br.rgb, br.a); + } } diff --git a/testsuite/oslc-err-initlist-return/ref/out.txt b/testsuite/oslc-err-initlist-return/ref/out.txt new file mode 100644 index 000000000..99a3530fd --- /dev/null +++ b/testsuite/oslc-err-initlist-return/ref/out.txt @@ -0,0 +1,8 @@ +test.osl:13: error: Cannot construct float (int, int, int) +test.osl:13: error: Cannot return a 'unknown' from 'struct bcolor badreturn()' +test.osl:14: warning: Function 'struct bcolor badreturn ()' redefined in the same scope + Previous definitions: + test.osl:13 +test.osl:14: error: Cannot return a 'struct acolor' from 'struct bcolor badreturn()' +test.osl:17: error: Syntax error: syntax error +FAILED test.osl diff --git a/testsuite/oslc-err-initlist-return/run.py b/testsuite/oslc-err-initlist-return/run.py new file mode 100755 index 000000000..6a72436dc --- /dev/null +++ b/testsuite/oslc-err-initlist-return/run.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python + +# command = oslc("test.osl") +# don't even need that -- it's automatic +failureok = 1 # this test is expected to have oslc errors + diff --git a/testsuite/oslc-err-initlist-return/test.osl b/testsuite/oslc-err-initlist-return/test.osl new file mode 100644 index 000000000..fb9ab8ca0 --- /dev/null +++ b/testsuite/oslc-err-initlist-return/test.osl @@ -0,0 +1,22 @@ + +struct acolor { + float a; + color rgb; +}; + +struct bcolor { + color rgb; + float a; +}; + +bcolor okreturn() { return { 1 }; } +bcolor badreturn() { return { 1, {1,2,3} }; } +bcolor badreturn() { return acolor(1,2); } + +// This seems like it might be useful? +bcolor badreturn() { return {}; } + +shader test() +{ +} + From a1be36b7d7edc2b551d254f0c94b9b4732845737 Mon Sep 17 00:00:00 2001 From: Frederich Munch Date: Wed, 20 Dec 2017 04:51:18 -0500 Subject: [PATCH 07/11] Allow initializer lists as function/constructor arguments. --- CMakeLists.txt | 2 +- src/liboslcomp/ast.h | 10 ++++ src/liboslcomp/codegen.cpp | 1 + src/liboslcomp/oslgram.y | 2 + src/liboslcomp/typecheck.cpp | 23 ++++++-- testsuite/initlist/ref/out.txt | 5 ++ testsuite/initlist/test.osl | 10 +++- testsuite/oslc-err-initlist-args/ref/out.txt | 17 ++++++ testsuite/oslc-err-initlist-args/run.py | 6 +++ testsuite/oslc-err-initlist-args/test.osl | 56 ++++++++++++++++++++ 10 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 testsuite/oslc-err-initlist-args/ref/out.txt create mode 100755 testsuite/oslc-err-initlist-args/run.py create mode 100644 testsuite/oslc-err-initlist-args/test.osl diff --git a/CMakeLists.txt b/CMakeLists.txt index fd9036cde..5555b42df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -272,7 +272,7 @@ TESTSUITE ( aastep allowconnect-err and-or-not-synonyms arithmetic oslc-err-arrayindex oslc-err-closuremul oslc-err-field oslc-err-format oslc-err-funcoverload oslc-err-intoverflow oslc-err-noreturn oslc-err-notfunc - oslc-err-initlist-return + oslc-err-initlist-args oslc-err-initlist-return oslc-err-outputparamvararray oslc-err-paramdefault oslc-err-struct-array-init oslc-err-struct-ctr oslc-err-struct-dup oslc-err-struct-print diff --git a/src/liboslcomp/ast.h b/src/liboslcomp/ast.h index 63bd74547..e878efd7f 100644 --- a/src/liboslcomp/ast.h +++ b/src/liboslcomp/ast.h @@ -756,8 +756,18 @@ class ASTcompound_initializer : public ASTtype_constructor bool canconstruct() const { return m_ctor; } void canconstruct(bool b) { m_ctor = b; } + // It is legal to have an incomplete initializer list in some contexts: + // struct custom { float x, y, z, w }; + // custom c = { 0, 1 }; + // color c[3] = { {1}, {2} }; + // + // Others (function calls) should initialize all elements to avoid ambiguity + // color subproc(color a, color b) + // subproc({0, 1}, {2, 3}) -> error, otherwise there may be subtle changes + // in behaviour if overloads added later. enum Strictness { typecheck_errors = 1, /// Report errors in typecheck calls + must_init_all = 1 << 1, /// All fields/elements must be inited }; TypeSpec typecheck (TypeSpec expected, Strictness mode); diff --git a/src/liboslcomp/codegen.cpp b/src/liboslcomp/codegen.cpp index 47085d7ec..39968d293 100644 --- a/src/liboslcomp/codegen.cpp +++ b/src/liboslcomp/codegen.cpp @@ -1826,6 +1826,7 @@ ASTfunction_call::codegen (Symbol *dest) // alias each of the fields if (a->nodetype() == variable_ref_node || a->nodetype() == function_call_node || + a->nodetype() == compound_initializer_node || a->nodetype() == binary_expression_node || a->nodetype() == unary_expression_node) { // Passed a variable that is a struct ; make the struct diff --git a/src/liboslcomp/oslgram.y b/src/liboslcomp/oslgram.y index 35c0df7c9..af4842b96 100644 --- a/src/liboslcomp/oslgram.y +++ b/src/liboslcomp/oslgram.y @@ -927,7 +927,9 @@ function_args_opt function_args : expression + | compound_initializer | function_args ',' expression { $$ = concat ($1, $3); } + | function_args ',' compound_initializer { $$ = concat ($1, $3); } ; assign_expression diff --git a/src/liboslcomp/typecheck.cpp b/src/liboslcomp/typecheck.cpp index f9f22a10f..a06990878 100644 --- a/src/liboslcomp/typecheck.cpp +++ b/src/liboslcomp/typecheck.cpp @@ -214,6 +214,8 @@ ASTNode::typecheck_struct_initializers (ref init, TypeSpec type, continue; } + init->typecheck(field.type); + // Ok to assign a literal 0 to a closure to initialize it. if (field.type.is_closure() && ! init->typespec().is_closure() && (init->typespec().is_float() || init->typespec().is_int()) && @@ -849,8 +851,6 @@ ASTtype_constructor::typecheck (TypeSpec expected, bool report) class ASTcompound_initializer::TypeAdjuster { - enum { must_init_all = 1 << 1 }; /// All fields/elements must be inited - // Only adjust the types on success of root initializer // Oh for an llvm::SmallVector here! std::vector> m_adjust; @@ -1137,6 +1137,14 @@ ASTNode::check_arglist (const char *funcname, ASTNode::ref arg, continue; // match anything } + if (arg->nodetype() == compound_initializer_node) { + int advance; + TypeSpec formaltype = m_compiler->type_from_code (formals, &advance); + //formals += advance; + ((ASTcompound_initializer*)arg.get())->typecheck (formaltype, + ASTcompound_initializer::must_init_all); + } + if (! check_simple_arg (arg->typespec(), formals, coerce)) return false; // If check_simple_arg succeeded, it advanced formals, and we @@ -1400,6 +1408,7 @@ ASTfunction_call::typecheck_struct_constructor () ASSERT (structspec); m_typespec = m_sym->typespec(); if (structspec->numfields() != (int)listlength(args())) { + // Support a single argument which is an init-list of proper type? error ("Constructor for '%s' has the wrong number of arguments (expected %d, got %d)", structspec->name(), structspec->numfields(), listlength(args())); } @@ -1904,13 +1913,19 @@ class LegacyOverload TypeSpec ASTfunction_call::typecheck (TypeSpec expected) { - typecheck_children (); - if (is_struct_ctr()) { // Looks like function call, but is actually struct constructor return typecheck_struct_constructor (); } + // Instead of typecheck_children, typecheck all arguments except for + // initializer lists, who will be checked against each overload's formal + // specification or each field's known type if is_struct_ctr. + for (ref arg = args(); arg; arg = arg->next()) { + if (arg->nodetype() != compound_initializer_node) + typecheck_list (arg, expected); + } + // Save the currently choosen symbol for error reporting later FunctionSymbol* poly = func(); diff --git a/testsuite/initlist/ref/out.txt b/testsuite/initlist/ref/out.txt index a6b8487a9..3155dca0a 100644 --- a/testsuite/initlist/ref/out.txt +++ b/testsuite/initlist/ref/out.txt @@ -35,4 +35,9 @@ ns4[2] = {(11 12 13) (-10 -20 -30 -40)} ns4[3] = {(7 7 7) (-5 -15 -20 -25)} ar: {1, {2 3 4}} br: {{6 7 8}, 5} +ac[0] = {(2 3 4 1)} +ac[1] = {(6 7 8 5)} +ac[2] = {(23 33 43 22)} +ac[3] = {(1 6 1 1)} +AC: ({11 12 13}) diff --git a/testsuite/initlist/test.osl b/testsuite/initlist/test.osl index 09658b8b9..3fb9488b8 100644 --- a/testsuite/initlist/test.osl +++ b/testsuite/initlist/test.osl @@ -105,7 +105,7 @@ shader test( // FIXME: custcolor4 cina[2] = {{color(1),1}, {color(1),1}}, printf ("ns2 = {(%g) (%g %g)}\n", ns2.n1.c3, ns2.c4.rgb, ns2.c4.a); int i = -4; - nested2 ns4[4] = { { {{5,6,7}}, {{-1,-2,-3}, i--} }, + nested2 ns4[4] = { { {{5,6,7}}, custcolor4({-1,-2,-3}, i--) }, { {{1,2,3}}, {{-5,-6,-7}, --i} }, { {{11,12,13}}, {{-10,-20,-30},-40} }, { {{7}}, custcolor4(color(-5,-15,-20),-25) } }; @@ -119,4 +119,12 @@ shader test( // FIXME: custcolor4 cina[2] = {{color(1),1}, {color(1),1}}, printf("ar: {%g, {%g}}\n", ar.a, ar.rgb); printf("br: {{%g}, %g}\n", br.rgb, br.a); } + + acolor ac[4] = { {1, {2,3,4}}, acolor(5, {6,7,8}), acolorvalue(), + acolorcopy({1, {1, 6, 1}}) }; + for (int i = 0; i < 4; ++i) + printf ("ac[%d] = {(%g %g)}\n", i, ac[i].rgb, ac[i].a); + + acolor cc = acolorcopy({10, {11,12,13}}); + printf("AC: ({%g})\n", cc.rgb); } diff --git a/testsuite/oslc-err-initlist-args/ref/out.txt b/testsuite/oslc-err-initlist-args/ref/out.txt new file mode 100644 index 000000000..e8972488e --- /dev/null +++ b/testsuite/oslc-err-initlist-args/ref/out.txt @@ -0,0 +1,17 @@ +test.osl:43: error: No matching function call to 'failifnota (unknown)' + Candidates are: + test.osl:16 void failifnota (struct acolor) +test.osl:44: error: No matching function call to 'failifnota (unknown)' + Candidates are: + test.osl:16 void failifnota (struct acolor) +test.osl:53: error: Ambiguous call to 'ddd (unknown, int)' + Candidates are: + test.osl:33 void ddd (struct B, int) + test.osl:32 void ddd (struct A, int) +test.osl:54: error: Ambiguous call to 'noise (unknown)' + Candidates are: + test.osl:35 float noise (struct A) + float noise (point) + color noise (point) + vector noise (point) +FAILED test.osl diff --git a/testsuite/oslc-err-initlist-args/run.py b/testsuite/oslc-err-initlist-args/run.py new file mode 100755 index 000000000..6a72436dc --- /dev/null +++ b/testsuite/oslc-err-initlist-args/run.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python + +# command = oslc("test.osl") +# don't even need that -- it's automatic +failureok = 1 # this test is expected to have oslc errors + diff --git a/testsuite/oslc-err-initlist-args/test.osl b/testsuite/oslc-err-initlist-args/test.osl new file mode 100644 index 000000000..0ee27ba1c --- /dev/null +++ b/testsuite/oslc-err-initlist-args/test.osl @@ -0,0 +1,56 @@ + +struct acolor { + float a; + color rgb; +}; + +struct bcolor { + color rgb; + float a; +}; + +void colorvalue(acolor a) {} + +void colorvalue(bcolor b) {} + +void failifnota(acolor a) {} + + +// No float a, b, c; ??? +struct A { float a; float b; float c; }; +struct B { float a; float b; float c; }; + +void aaa(A a, float b) { printf("A.1\n"); } +void aaa(B b, int c) { printf("B.1\n"); } + +void bbb(B b, int c) { printf("B.2\n"); } +void bbb(A a, float b) { printf("A.2\n"); } + +void ccc(A a, int b) { printf("A.3\n"); } +void ccc(B b, float c) { printf("B.3\n"); } + +void ddd(A a, int b) { printf("A.4\n"); } +void ddd(B b, int c) { printf("B.4\n"); } + +float noise(A a) { return 1; } + +shader test() +{ + colorvalue({ 1, {2,3,4} }); // ok + colorvalue({ {6,7,8}, 5 }); // ok + + failifnota({ 1, {2,3,4} }); // ok + failifnota({6,7,8}); // fail + failifnota({ {6,7,8}, 5 }); // fail + + aaa({1,2,3}, 1); + aaa({1,2,3}, 1.0); + bbb({1,2,3}, 1); + bbb({1,2,3}, 1.0); + ccc({1,2,3}, 1); + ccc({1,2,3}, 1.0); + + ddd({1,2,3}, 1); + noise({1,2,3}); +} + From 51059cf5e1218bd42656a2462d09a08f09999035 Mon Sep 17 00:00:00 2001 From: Frederich Munch Date: Thu, 21 Dec 2017 19:33:03 -0500 Subject: [PATCH 08/11] Remove ASTNode typecheck_initlist & typecheck_struct_initializers. Everything now flows properly through ASTcompound_initializer. --- src/liboslcomp/ast.h | 10 -- src/liboslcomp/typecheck.cpp | 109 +--------------------- testsuite/oslc-err-struct-ctr/ref/out.txt | 6 +- 3 files changed, 7 insertions(+), 118 deletions(-) diff --git a/src/liboslcomp/ast.h b/src/liboslcomp/ast.h index e878efd7f..0565ee773 100644 --- a/src/liboslcomp/ast.h +++ b/src/liboslcomp/ast.h @@ -346,20 +346,10 @@ class ASTNode : public OIIO::RefCnt { bool copywholearrays, int intindex, bool paraminit); - // Helper: type check an initializer list -- either a single item to - // a scalar, or a list to an array. - void typecheck_initlist (ref init, TypeSpec type, string_view name); - // Helper: generate code for an initializer list -- either a single // item to a scalar, or a list to an array. void codegen_initlist (ref init, TypeSpec type, Symbol *sym); - // Special type checking for structure member initializers. - // It's in the ASTNode base class because it's used from mutiple - // subclasses. - TypeSpec typecheck_struct_initializers (ref init, TypeSpec type, - string_view name); - // Special code generation for structure initializers. // It's in the ASTNode base class because it's used from mutiple // subclasses. diff --git a/src/liboslcomp/typecheck.cpp b/src/liboslcomp/typecheck.cpp index a06990878..d60867827 100644 --- a/src/liboslcomp/typecheck.cpp +++ b/src/liboslcomp/typecheck.cpp @@ -113,13 +113,6 @@ ASTvariable_declaration::typecheck (TypeSpec expected) init = ((ASTcompound_initializer *)init.get())->initlist(); } - if (m_typespec.is_structure()) { - // struct initialization handled separately - return typecheck_struct_initializers (init, m_typespec, m_name.c_str()); - } - - typecheck_initlist (init, m_typespec, m_name.c_str()); - // Warning to catch confusing comma operator in variable initializers. // One place this comes up is when somebody forgets the proper syntax // for constructors, for example @@ -138,103 +131,6 @@ ASTvariable_declaration::typecheck (TypeSpec expected) -void -ASTNode::typecheck_initlist (ref init, TypeSpec type, string_view name) -{ - // Loop over a list of initializers (it's just 1 if not an array)... - for (int i = 0; init; init = init->next(), ++i) { - // Check for too many initializers for an array - if (type.is_array() && !type.is_unsized_array() && i >= type.arraylength()) { - error ("Too many initializers for a '%s'", type_c_str(type)); - break; - } - // Special case: ok to assign a literal 0 to a closure to - // initialize it. - if ((type.is_closure() || type.is_closure_array()) && - ! init->typespec().is_closure() && - init->typespec().is_int_or_float() && - init->nodetype() == literal_node && - ((ASTliteral *)init.get())->floatval() == 0.0f) { - continue; // it's ok - } - // Allow struct s = { ... }; vector v = { ... }; - if (type.is_structure_based() || (!type.is_closure() && type.is_triple())) - continue; - if (! type.is_array() && i > 0) - error ("Can't assign array initializers to non-array %s %s", - type_c_str(type), name); - if (! assignable(type.elementtype(), init->typespec())) - error ("Can't assign '%s' to %s %s", type_c_str(init->typespec()), - type_c_str(type), name); - } -} - - - -TypeSpec -ASTNode::typecheck_struct_initializers (ref init, TypeSpec type, - string_view name) -{ - ASSERT (type.is_structure()); - - if (! init->next() && init->typespec() == type) { - // Special case: just one initializer, it's a whole struct of - // the right type. - return type; - } - - // General case -- per-field initializers - - const StructSpec *structspec (type.structspec()); - int numfields = (int)structspec->numfields(); - for (int i = 0; init; init = init->next(), ++i) { - if (i >= numfields) { - error ("Too many initializers for '%s %s'", - type_c_str(type), name); - break; - } - const StructSpec::FieldSpec &field (structspec->field(i)); - - if (init->nodetype() == compound_initializer_node) { - // Initializer is itself a compound, it ought to be initializing - // a field that is an array. - ASTcompound_initializer *cinit = (ASTcompound_initializer *)init.get(); - ustring fieldname = ustring::format ("%s.%s", name, field.name); - if (field.type.is_array ()) { - typecheck_initlist (cinit->initlist(), field.type, fieldname); - } else if (field.type.is_structure()) { - typecheck_struct_initializers (cinit->initlist(), field.type, - fieldname); - } else if (cinit->typecheck(field.type) != field.type) { - // Is this message still neccessary? typecheck above should have - // reported a more specific message. - error ("Can't use '{...}' for a struct field that is not an " - "array, vector, or constructable"); - } - continue; - } - - init->typecheck(field.type); - - // Ok to assign a literal 0 to a closure to initialize it. - if (field.type.is_closure() && ! init->typespec().is_closure() && - (init->typespec().is_float() || init->typespec().is_int()) && - init->nodetype() == literal_node && - ((ASTliteral *)init.get())->floatval() == 0.0f) { - continue; // it's ok - } - - // Normal initializer, normal field. - if (! assignable(field.type, init->typespec())) - error ("Can't assign '%s' to '%s %s.%s'", - type_c_str(init->typespec()), - type_c_str(field.type), name, field.name); - } - return type; -} - - - TypeSpec ASTvariable_ref::typecheck (TypeSpec expected) { @@ -1412,7 +1308,10 @@ ASTfunction_call::typecheck_struct_constructor () error ("Constructor for '%s' has the wrong number of arguments (expected %d, got %d)", structspec->name(), structspec->numfields(), listlength(args())); } - return typecheck_struct_initializers (args(), m_typespec, "this"); + + ASTcompound_initializer::TypeAdjuster(m_compiler) + .typecheck_fields(this, args().get(), m_typespec); + return m_typespec; } diff --git a/testsuite/oslc-err-struct-ctr/ref/out.txt b/testsuite/oslc-err-struct-ctr/ref/out.txt index 6ea9e12a6..0f80ee06e 100644 --- a/testsuite/oslc-err-struct-ctr/ref/out.txt +++ b/testsuite/oslc-err-struct-ctr/ref/out.txt @@ -1,7 +1,7 @@ test.osl:10: error: Cannot initialize structure 'A' with a scalar value test.osl:13: error: Constructor for 'rgba' has the wrong number of arguments (expected 2, got 1) test.osl:16: error: Constructor for 'rgba' has the wrong number of arguments (expected 2, got 3) -test.osl:16: error: Too many initializers for 'struct rgba this' -test.osl:19: error: Can't assign 'string' to 'color this.rgb' -test.osl:19: error: Can't assign 'string' to 'float this.a' +test.osl:16: error: Too many initializers for struct 'rgba' +test.osl:19: error: Can't assign 'string' to 'color rgba.rgb' +test.osl:19: error: Can't assign 'string' to 'float rgba.a' FAILED test.osl From 8d3fbd4a00d915418dcf9d5892a379b91b848c2f Mon Sep 17 00:00:00 2001 From: Frederich Munch Date: Thu, 8 Feb 2018 11:58:28 -0500 Subject: [PATCH 09/11] Adapt to recent function-overload changes. Hide some implementation details that don't need to be public. --- src/liboslcomp/ast.h | 30 +-- src/liboslcomp/typecheck.cpp | 293 +++++++++++++++-------- testsuite/function-overloads/ref/out.txt | 29 ++- testsuite/function-overloads/test.osl | 21 ++ 4 files changed, 246 insertions(+), 127 deletions(-) diff --git a/src/liboslcomp/ast.h b/src/liboslcomp/ast.h index 0565ee773..de6965771 100644 --- a/src/liboslcomp/ast.h +++ b/src/liboslcomp/ast.h @@ -300,7 +300,8 @@ class ASTNode : public OIIO::RefCnt { /// Type check a list (whose head is given by 'arg' against the list /// of expected types given in encoded form by 'formals'. bool check_arglist (const char *funcname, ref arg, - const char *formals, bool coerce=false); + const char *formals, bool coerce=false, + bool bind = true); /// Follow a list of nodes, generating code for each in turn, and return /// the Symbol* for the last thing generated. @@ -723,11 +724,13 @@ class ASTtype_constructor : public ASTNode ref args () const { return child (0); } // Typecheck construction of expected against args() - TypeSpec typecheck (TypeSpec expected, bool error); + // Optionally ignoring errors and binding any init-list arguments to the + // required type. + TypeSpec typecheck (TypeSpec expected, bool error, bool bind = true); // Typecheck construction of m_typespec against args() TypeSpec typecheck (TypeSpec expected) { - return typecheck (m_typespec, true); + return typecheck (m_typespec, true, true); } }; @@ -735,6 +738,9 @@ class ASTtype_constructor : public ASTNode class ASTcompound_initializer : public ASTtype_constructor { bool m_ctor; + + TypeSpec typecheck (TypeSpec expected, unsigned mode); + public: ASTcompound_initializer (OSLCompilerImpl *comp, ASTNode *exprlist); const char *nodetypename () const { return "compound_initializer"; } @@ -746,24 +752,8 @@ class ASTcompound_initializer : public ASTtype_constructor bool canconstruct() const { return m_ctor; } void canconstruct(bool b) { m_ctor = b; } - // It is legal to have an incomplete initializer list in some contexts: - // struct custom { float x, y, z, w }; - // custom c = { 0, 1 }; - // color c[3] = { {1}, {2} }; - // - // Others (function calls) should initialize all elements to avoid ambiguity - // color subproc(color a, color b) - // subproc({0, 1}, {2, 3}) -> error, otherwise there may be subtle changes - // in behaviour if overloads added later. - enum Strictness { - typecheck_errors = 1, /// Report errors in typecheck calls - must_init_all = 1 << 1, /// All fields/elements must be inited - }; - - TypeSpec typecheck (TypeSpec expected, Strictness mode); - TypeSpec typecheck (TypeSpec expected) { - return typecheck(expected, typecheck_errors); + return typecheck(expected, 0); } // Helper for typechecking an initlist or structure. diff --git a/src/liboslcomp/typecheck.cpp b/src/liboslcomp/typecheck.cpp index d60867827..1aa865ccb 100644 --- a/src/liboslcomp/typecheck.cpp +++ b/src/liboslcomp/typecheck.cpp @@ -681,7 +681,7 @@ ASTtypecast_expression::typecheck (TypeSpec expected) TypeSpec -ASTtype_constructor::typecheck (TypeSpec expected, bool report) +ASTtype_constructor::typecheck (TypeSpec expected, bool report, bool bind) { // Hijack the usual function arg-checking routines. // So we have a set of valid patterns for each type constructor: @@ -723,7 +723,8 @@ ASTtype_constructor::typecheck (TypeSpec expected, bool report) bool coerce = co; for (const char **pat = patterns; *pat; ++pat) { const char *code = *pat; - if (check_arglist (type_c_str(expected), args(), code + 1, coerce)) + if (check_arglist (type_c_str(expected), args(), code + 1, coerce, + bind)) return expected; } } @@ -747,73 +748,26 @@ ASTtype_constructor::typecheck (TypeSpec expected, bool report) class ASTcompound_initializer::TypeAdjuster { - // Only adjust the types on success of root initializer - // Oh for an llvm::SmallVector here! - std::vector> m_adjust; - OSLCompilerImpl* m_compiler; - const Strictness m_mode; - bool m_success; - - void mark_type(ASTcompound_initializer* i, TypeSpec t, bool c = false) { - m_adjust.emplace_back(i, t, c); - } - - // Should errors be reported? - bool errors() const { return m_mode & typecheck_errors; } - - bool validate_size(int expected, int actual) const { - if (expected > actual) - return false; - return m_mode & must_init_all ? (expected == actual) : true; - } - - ASTcompound_initializer* - next_initlist(ASTNode *node, const TypeSpec& expected, int& nelem) const { - // Finished if already errored and not reporting them. - // Otherwise keep checking to report as many errors as possible - if (m_success || errors()) { - for (node = node->nextptr(); node; node = node->nextptr()) { - ++nelem; - if (node->nodetype() == compound_initializer_node) - return static_cast(node); - node->typecheck(expected); - } - } - return nullptr; - } - - // Typecheck node, reporting an error. - // Returns whether to continue iteration (not whether typecheck errored). - bool - typecheck (ref node, TypeSpec expected, const StructSpec* spec = nullptr, - const StructSpec::FieldSpec* field = nullptr) { - if ( node->typecheck (expected) != expected && - // Alllow assignment with comparable type - ! assignable(expected, node->typespec()) && - // Alllow closure assignments to '0' - ! (expected.is_closure() && - ! node->typespec().is_closure() && - node->typespec().is_int_or_float() && - node->nodetype() == literal_node && - ((ASTliteral *)node.get())->floatval() == 0.0f) ) { - - m_success = false; - if (!errors()) - return false; - - ASSERT (!spec || field); - node->error ("Can't assign '%s' to '%s%s'", - m_compiler->type_c_str(node->typespec()), - m_compiler->type_c_str(expected), - !spec ? "" : - Strutil::format(" %s.%s", spec->name(), field->name)); - } - return true; - } - public: - TypeAdjuster(OSLCompilerImpl* c, Strictness m = typecheck_errors) : - m_compiler(c), m_mode(m), m_success(true) {} + // It is legal to have an incomplete initializer list in some contexts: + // struct custom { float x, y, z, w }; + // custom c = { 0, 1 }; + // color c[3] = { {1}, {2} }; + // + // Others (function calls) should initialize all elements to avoid ambiguity + // color subproc(color a, color b) + // subproc({0, 1}, {2, 3}) -> error, otherwise there may be subtle changes + // in behaviour if overloads added later. + enum Strictness { + default_flags = 0, + no_errors = 1, /// Don't report errors in typecheck calls + must_init_all = 1 << 1, /// All fields/elements must be inited + function_arg = no_errors | must_init_all + }; + + TypeAdjuster(OSLCompilerImpl* c, unsigned m = default_flags) : + m_compiler(c), m_mode(Strictness(m)), m_success(true), + m_debug_successful(false) {} ~TypeAdjuster () { // Commit infered types of all ASTcompound_initializers scanned. @@ -842,7 +796,7 @@ class ASTcompound_initializer::TypeAdjuster { return; } - if (cinit->ASTtype_constructor::typecheck(to, errors()) == to) + if (cinit->ASTtype_constructor::typecheck(to, errors(), false) == to) mark_type(cinit, to, true); else m_success = false; @@ -944,12 +898,109 @@ class ASTcompound_initializer::TypeAdjuster { structspec->name()); } } + + TypeSpec typecheck(ASTcompound_initializer* ilist, TypeSpec expected) { + if (!expected.is_array()) { + if (expected.is_structure()) + typecheck_fields(ilist, ilist->initlist(), expected); + else + typecheck_init(ilist, expected); + } else + typecheck_array(ilist, expected); + + return m_success ? (std::get<1>(m_adjust.back())) + : TypeSpec(TypeDesc::NONE); + } + + // Turn off the automatic binding on destruction + TypeSpec nobind() { + ASSERT (!m_success || m_adjust.size()); + // If succeeded, root type is at end of m_adjust + bool val = m_success; + m_debug_successful = m_success; + m_success = false; // Turn off the binding in destructor + return val ? (std::get<1>(m_adjust.back())) : TypeSpec(TypeDesc::NONE); + } + + // Turn automatic binding back on. + void bind() { + ASSERT (m_debug_successful); + m_success = true; + } + + bool success() const { return m_success; } + +private: + // Only adjust the types on success of root initializer + // Oh for an llvm::SmallVector here! + std::vector> m_adjust; + OSLCompilerImpl* m_compiler; + const Strictness m_mode; + bool m_success; + bool m_debug_successful; // Only for nobind() & bind() cycle assertion. + + void mark_type(ASTcompound_initializer* i, TypeSpec t, bool c = false) { + m_adjust.emplace_back(i, t, c); + } + + // Should errors be reported? + bool errors() const { return !(m_mode & no_errors); } + + bool validate_size(int expected, int actual) const { + if (expected > actual) + return false; + return m_mode & must_init_all ? (expected == actual) : true; + } + + ASTcompound_initializer* + next_initlist(ASTNode *node, const TypeSpec& expected, int& nelem) const { + // Finished if already errored and not reporting them. + // Otherwise keep checking to report as many errors as possible + if (m_success || errors()) { + for (node = node->nextptr(); node; node = node->nextptr()) { + ++nelem; + if (node->nodetype() == compound_initializer_node) + return static_cast(node); + node->typecheck(expected); + } + } + return nullptr; + } + + // Typecheck node, reporting an error. + // Returns whether to continue iteration (not whether typecheck errored). + bool + typecheck (ref node, TypeSpec expected, const StructSpec* spec = nullptr, + const StructSpec::FieldSpec* field = nullptr) { + if ( node->typecheck (expected) != expected && + // Alllow assignment with comparable type + ! assignable(expected, node->typespec()) && + // Alllow closure assignments to '0' + ! (expected.is_closure() && + ! node->typespec().is_closure() && + node->typespec().is_int_or_float() && + node->nodetype() == literal_node && + ((ASTliteral *)node.get())->floatval() == 0.0f) ) { + + m_success = false; + if (!errors()) + return false; + + ASSERT (!spec || field); + node->error ("Can't assign '%s' to '%s%s'", + m_compiler->type_c_str(node->typespec()), + m_compiler->type_c_str(expected), + !spec ? "" : + Strutil::format(" %s.%s", spec->name(), field->name)); + } + return true; + } }; TypeSpec -ASTcompound_initializer::typecheck (TypeSpec expected, Strictness mode) +ASTcompound_initializer::typecheck (TypeSpec expected, unsigned mode) { if (m_ctor || m_typespec.is_structure_based() || m_typespec.simpletype().basetype != TypeDesc::UNKNOWN ) { @@ -960,17 +1011,9 @@ ASTcompound_initializer::typecheck (TypeSpec expected, Strictness mode) // Scoped so ~TypeAdjuster() will bind m_typespec before return m_typespec { - TypeAdjuster ta(m_compiler, mode); - if (!expected.is_array()) { - if (expected.is_structure()) - ta.typecheck_fields(this, initlist(), expected); - else - ta.typecheck_init(this, expected); - } else - ta.typecheck_array(this, expected); + TypeAdjuster(m_compiler, mode).typecheck(this, expected); } - return m_typespec; } @@ -1003,7 +1046,7 @@ ASTNode::check_simple_arg (const TypeSpec &argtype, bool ASTNode::check_arglist (const char *funcname, ASTNode::ref arg, - const char *formals, bool coerce) + const char *formals, bool coerce, bool bind) { // std::cerr << "ca " << funcname << " formals='" << formals << "\n"; for ( ; arg; arg = arg->next()) { @@ -1033,15 +1076,30 @@ ASTNode::check_arglist (const char *funcname, ASTNode::ref arg, continue; // match anything } + TypeSpec formaltype; if (arg->nodetype() == compound_initializer_node) { int advance; + // Get the TypeSpec from the argument string. TypeSpec formaltype = m_compiler->type_from_code (formals, &advance); - //formals += advance; - ((ASTcompound_initializer*)arg.get())->typecheck (formaltype, - ASTcompound_initializer::must_init_all); - } - if (! check_simple_arg (arg->typespec(), formals, coerce)) + // See if the initlist can be used to construct a formaltype. + ASTcompound_initializer::TypeAdjuster ta( + m_compiler, ASTcompound_initializer::TypeAdjuster::no_errors); + + TypeSpec itype = ta.typecheck( + static_cast(arg.get()), formaltype); + + ASSERT (!ta.success() || (formaltype == itype)); + + // ~TypeAdjuster will set the proper type for the list on success. + // This will overwrite the prior binding simillar to how legacy + // function ambiguity resolution took the last definition. + if (!bind) + ta.nobind(); + } else + formaltype = arg->typespec(); + + if (! check_simple_arg (formaltype, formals, coerce)) return false; // If check_simple_arg succeeded, it advanced formals, and we // repeat for the next argument. @@ -1360,9 +1418,15 @@ class CandidateFunctions { kSpatialCoerce = kCoercable + 9, // prefer vector/point/normal conversion over color kTripleCoerce = kCoercable + 4, // prefer triple conversion over float promotion }; + + typedef std::vector> + InitBindings; + struct Candidate { FunctionSymbol* sym; TypeSpec rtype; + InitBindings bindings; int ascore; int rscore; @@ -1379,8 +1443,8 @@ class CandidateFunctions { TypeSpec m_rval; ASTNode::ref m_args; size_t m_nargs; - FunctionSymbol* m_called; // Function called by name (can be NULL!) + bool m_had_initlist; const char* scoreWildcard(int& argscore, size_t& fargs, const char* args) const { while (fargs < m_nargs) { @@ -1431,6 +1495,7 @@ class CandidateFunctions { TypeSpec rtype = m_compiler->type_from_code (formals, &advance); formals += advance; + InitBindings bindings; int argscore = 0; size_t fargs = 0; for (ASTNode::ref arg = m_args; *formals && arg; ++fargs, arg = arg->next()) { @@ -1469,10 +1534,31 @@ class CandidateFunctions { if (fargs >= m_nargs) return kNoMatch; + TypeSpec argtype; TypeSpec formaltype = m_compiler->type_from_code (formals, &advance); formals += advance; - int score = scoreType(formaltype, arg->typespec()); + if (arg->nodetype() == ASTNode::compound_initializer_node) { + m_had_initlist = true; + auto ilist = static_cast(arg.get()); + bindings.emplace_back(ilist, ASTcompound_initializer::TypeAdjuster + (m_compiler, + ASTcompound_initializer::TypeAdjuster::function_arg)); + + // Typecheck the init list can construct the formal type. + bindings.back().second.typecheck(ilist, formaltype); + + // Don't bind the type yet, that only occurs when the final + // candidate is chosen. + argtype = bindings.back().second.nobind(); + + // Couldn't create the formaltype from this list...no match. + if (argtype.simpletype().basetype == TypeDesc::NONE) + return kNoMatch; + } else + argtype = arg->typespec(); + + int score = scoreType(formaltype, argtype); if (score == kNoMatch) return kNoMatch; @@ -1510,6 +1596,9 @@ class CandidateFunctions { m_candidates.emplace_back(func, rtype, argscore, scoreType(m_rval, rtype)); + // save the initializer list types + m_candidates.back().bindings.swap(bindings); + return argscore; } @@ -1521,7 +1610,7 @@ class CandidateFunctions { CandidateFunctions(OSLCompilerImpl* compiler, TypeSpec rval, ASTNode::ref args, FunctionSymbol* func) : m_compiler(compiler), m_rval(rval), m_args(args), m_nargs(0), - m_called(func) { + m_called(func), m_had_initlist(false) { //std::cerr << "Matching " << func->name() << " formals='" << (rval.simpletype().basetype != TypeDesc::UNKNOWN ? compiler->code_from_type (rval) : " "); for (ASTNode::ref arg = m_args; arg; arg = arg->next()) { @@ -1580,6 +1669,13 @@ class CandidateFunctions { best(ASTNode* caller, const ustring& funcname) { ASSERT (caller); // Assertion that passed ASTNode::ref was not empty + // When successful, bind all the initializer list types. + auto best = [](Candidate* c) -> std::pair { + for (auto&& t : c->bindings) + t.second.bind(); + return {c->sym, c->rtype }; + }; + switch (m_candidates.size()) { case 0: // Nothing at all, Error @@ -1595,13 +1691,13 @@ class CandidateFunctions { return { nullptr, TypeSpec() }; case 1: // Success - return { m_candidates[0].sym, m_candidates[0].rtype }; + return best(&m_candidates[0]); default: break; } int ambiguity = -1; - std::pair c = { nullptr, -1 }; + std::pair c = { nullptr, -1 }; for (auto& candidate : m_candidates) { // re-score based on matching return value if (candidate.rscore > c.second) { @@ -1703,10 +1799,13 @@ class CandidateFunctions { } } - return {c.first->sym, c.first->rtype}; + return best(c.first); } bool empty() const { return m_candidates.empty(); } + + // Remove when LegacyOverload checking is removed. + bool hadinitlist() const { return m_had_initlist; } }; @@ -1724,7 +1823,7 @@ class LegacyOverload ASTfunction_call* m_func; FunctionSymbol* m_root; bool (ASTNode::*m_check_arglist) (const char *funcname, ASTNode::ref arg, - const char *formals, bool coerce); + const char *frmls, bool coerce, bool bnd); std::pair typecheck_polys (TypeSpec expected, bool coerceargs, bool equivreturn) @@ -1735,7 +1834,8 @@ class LegacyOverload int advance; TypeSpec returntype = m_compiler->type_from_code (code, &advance); code += advance; - if ((m_func->*m_check_arglist) (name, m_func->args(), code, coerceargs)) { + if ((m_func->*m_check_arglist) (name, m_func->args(), code, + coerceargs, false)) { // Return types also must match if not coercible if (expected == returntype || (equivreturn && equivalent(expected, returntype)) || @@ -1754,7 +1854,7 @@ class LegacyOverload bool (ASTNode::*checkfunc) (const char *funcname, ASTNode::ref arg, const char *formals, - bool coerce)) + bool coerce, bool bind)) : m_compiler(comp), m_func(func), m_root(root), m_check_arglist(checkfunc) { } @@ -1831,9 +1931,10 @@ ASTfunction_call::typecheck (TypeSpec expected) CandidateFunctions candidates(m_compiler, expected, args(), poly); std::tie(m_sym, m_typespec) = candidates.best(this, m_name); - // Check resolution against prior versions of OSL + // Check resolution against prior versions of OSL. + // Skip the check if any arguments used initializer list syntax. static const char* OSL_LEGACY = ::getenv("OSL_LEGACY_FUNCTION_RESOLUTION"); - if (OSL_LEGACY && strcmp(OSL_LEGACY, "0")) { + if (candidates.hadinitlist() && OSL_LEGACY && strcmp(OSL_LEGACY, "0")) { auto* legacy = LegacyOverload(m_compiler, this, poly, &ASTfunction_call::check_arglist)(expected); if (m_sym != legacy) { diff --git a/testsuite/function-overloads/ref/out.txt b/testsuite/function-overloads/ref/out.txt index 90fc7c3b1..32d372d5d 100644 --- a/testsuite/function-overloads/ref/out.txt +++ b/testsuite/function-overloads/ref/out.txt @@ -1,4 +1,4 @@ -test.osl:215: warning: Ambiguous call to 'freturn ()' +test.osl:226: warning: Ambiguous call to 'freturn ()' Chosen function is: test.osl:73 float freturn () Other candidates are: @@ -12,7 +12,7 @@ test.osl:215: warning: Ambiguous call to 'freturn ()' test.osl:75 closure color freturn () test.osl:80 struct A freturn () test.osl:79 void freturn () -test.osl:216: warning: Ambiguous call to 'ireturn ()' +test.osl:227: warning: Ambiguous call to 'ireturn ()' Chosen function is: test.osl:87 int ireturn () Other candidates are: @@ -25,7 +25,7 @@ test.osl:216: warning: Ambiguous call to 'ireturn ()' test.osl:83 closure color ireturn () test.osl:82 struct A ireturn () test.osl:90 void ireturn () -test.osl:217: warning: Ambiguous call to 'creturn ()' +test.osl:228: warning: Ambiguous call to 'creturn ()' Chosen function is: test.osl:98 color creturn () Other candidates are: @@ -37,7 +37,7 @@ test.osl:217: warning: Ambiguous call to 'creturn ()' test.osl:101 closure color creturn () test.osl:96 struct A creturn () test.osl:95 void creturn () -test.osl:218: warning: Ambiguous call to 'vreturn ()' +test.osl:229: warning: Ambiguous call to 'vreturn ()' Chosen function is: test.osl:105 vector vreturn () Other candidates are: @@ -48,7 +48,7 @@ test.osl:218: warning: Ambiguous call to 'vreturn ()' test.osl:104 closure color vreturn () test.osl:107 struct A vreturn () test.osl:108 void vreturn () -test.osl:219: warning: Ambiguous call to 'preturn ()' +test.osl:230: warning: Ambiguous call to 'preturn ()' Chosen function is: test.osl:118 point preturn () Other candidates are: @@ -58,7 +58,7 @@ test.osl:219: warning: Ambiguous call to 'preturn ()' test.osl:115 closure color preturn () test.osl:117 struct A preturn () test.osl:113 void preturn () -test.osl:220: warning: Ambiguous call to 'nreturn ()' +test.osl:231: warning: Ambiguous call to 'nreturn ()' Chosen function is: test.osl:122 normal nreturn () Other candidates are: @@ -67,7 +67,7 @@ test.osl:220: warning: Ambiguous call to 'nreturn ()' test.osl:124 closure color nreturn () test.osl:121 struct A nreturn () test.osl:125 void nreturn () -test.osl:221: warning: Ambiguous call to 'mreturn ()' +test.osl:232: warning: Ambiguous call to 'mreturn ()' Chosen function is: test.osl:130 matrix mreturn () Other candidates are: @@ -75,25 +75,25 @@ test.osl:221: warning: Ambiguous call to 'mreturn ()' test.osl:128 closure color mreturn () test.osl:129 struct A mreturn () test.osl:127 void mreturn () -test.osl:222: warning: Ambiguous call to 'strreturn ()' +test.osl:233: warning: Ambiguous call to 'strreturn ()' Chosen function is: test.osl:136 string strreturn () Other candidates are: test.osl:134 closure color strreturn () test.osl:133 struct A strreturn () test.osl:135 void strreturn () -test.osl:223: warning: Ambiguous call to 'ccreturn ()' +test.osl:234: warning: Ambiguous call to 'ccreturn ()' Chosen function is: test.osl:138 closure color ccreturn () Other candidates are: test.osl:140 struct A ccreturn () test.osl:139 void ccreturn () -test.osl:224: warning: Ambiguous call to 'structreturn ()' +test.osl:235: warning: Ambiguous call to 'structreturn ()' Chosen function is: test.osl:142 struct A structreturn () Other candidates are: test.osl:143 void structreturn () -test.osl:225: warning: Ambiguous call to 'structreturn1 ()' +test.osl:236: warning: Ambiguous call to 'structreturn1 ()' Chosen function is: test.osl:146 struct A structreturn1 () Other candidates are: @@ -160,4 +160,11 @@ ccreturn.ccolor structreturn.struct structreturn1.struct +aaa.Bint +aaa.Afloat +bbb.Bint +bbb.Afloat +ccc.Aint +ccc.Bfloat + diff --git a/testsuite/function-overloads/test.osl b/testsuite/function-overloads/test.osl index aa7d150fa..7d77c56fe 100644 --- a/testsuite/function-overloads/test.osl +++ b/testsuite/function-overloads/test.osl @@ -145,6 +145,17 @@ void structreturn() { printf("structreturn.void\n"); } void structreturn1() { printf("structreturn1.void\n"); } A structreturn1() { printf("structreturn1.struct\n"); A a; return a; } +struct B { float b; }; + +void aaa(A a, float b) { printf("aaa.Afloat\n"); } +void aaa(B b, int c) { printf("aaa.Bint\n"); } + +void bbb(B b, int c) { printf("bbb.Bint\n"); } +void bbb(A a, float b) { printf("bbb.Afloat\n"); } + +void ccc(A a, int b) { printf("ccc.Aint\n"); } +void ccc(B b, float c) { printf("ccc.Bfloat\n"); } + shader test () { { @@ -225,4 +236,14 @@ shader test () structreturn1(); printf("\n"); } + + { + aaa({1}, 1); + aaa({1}, 1.0); + bbb({1}, 1); + bbb({1}, 1.0); + ccc({1}, 1); + ccc({1}, 1.0); + printf("\n"); + } } From d9be60467f519910a177564a7249fa9b1ea6fa9c Mon Sep 17 00:00:00 2001 From: Frederich Munch Date: Thu, 8 Feb 2018 15:23:32 -0500 Subject: [PATCH 10/11] Error on ambiguity of arguments using init-lists. --- src/liboslcomp/typecheck.cpp | 38 ++++++++++++++++---- testsuite/oslc-err-initlist-args/ref/out.txt | 26 ++++++++++---- testsuite/oslc-err-initlist-args/test.osl | 35 +++++++++++++----- 3 files changed, 78 insertions(+), 21 deletions(-) diff --git a/src/liboslcomp/typecheck.cpp b/src/liboslcomp/typecheck.cpp index 1aa865ccb..776c202cf 100644 --- a/src/liboslcomp/typecheck.cpp +++ b/src/liboslcomp/typecheck.cpp @@ -908,26 +908,29 @@ class ASTcompound_initializer::TypeAdjuster { } else typecheck_array(ilist, expected); - return m_success ? (std::get<1>(m_adjust.back())) - : TypeSpec(TypeDesc::NONE); + return type(); } // Turn off the automatic binding on destruction TypeSpec nobind() { ASSERT (!m_success || m_adjust.size()); - // If succeeded, root type is at end of m_adjust - bool val = m_success; m_debug_successful = m_success; m_success = false; // Turn off the binding in destructor - return val ? (std::get<1>(m_adjust.back())) : TypeSpec(TypeDesc::NONE); + return type(); } // Turn automatic binding back on. void bind() { - ASSERT (m_debug_successful); + ASSERT (m_success || m_debug_successful); m_success = true; } + TypeSpec type() const { + // If succeeded, root type is at end of m_adjust + return (m_success || m_debug_successful) ? std::get<1>(m_adjust.back()) + : TypeSpec(TypeDesc::NONE); + } + bool success() const { return m_success; } private: @@ -1782,6 +1785,29 @@ class CandidateFunctions { } bool warn = userstructs < 2; + + // Also force an error for ambiguous init-lists. + if (warn && m_had_initlist) { + const InitBindings* bindings = nullptr; + for (auto& candidate : m_candidates) { + // If no bindings, nothing to compare against. + if (!bindings) { + bindings = &candidate.bindings; + continue; + } + // Number of bindings should match, otherwise score shouldn't + ASSERT (candidate.bindings.size() == bindings->size()); + for (auto a = bindings->cbegin(), + b = candidate.bindings.cbegin(), + e = bindings->cend(); a != e; ++a, ++b) { + if (a->second.type() != b->second.type()) { + warn = false; + break; + } + } + } + } + reportAmbiguity(caller, funcname, !warn /* "Candidates are" msg*/, "Ambiguous call to", warn /* As warning */); if (warn) { diff --git a/testsuite/oslc-err-initlist-args/ref/out.txt b/testsuite/oslc-err-initlist-args/ref/out.txt index e8972488e..9eb0a96ef 100644 --- a/testsuite/oslc-err-initlist-args/ref/out.txt +++ b/testsuite/oslc-err-initlist-args/ref/out.txt @@ -1,17 +1,29 @@ -test.osl:43: error: No matching function call to 'failifnota (unknown)' +test.osl:52: error: No matching function call to 'failifnota (unknown)' Candidates are: test.osl:16 void failifnota (struct acolor) -test.osl:44: error: No matching function call to 'failifnota (unknown)' +test.osl:53: error: No matching function call to 'failifnota (unknown)' Candidates are: test.osl:16 void failifnota (struct acolor) -test.osl:53: error: Ambiguous call to 'ddd (unknown, int)' +test.osl:54: error: Ambiguous call to 'ddd (unknown, int)' Candidates are: - test.osl:33 void ddd (struct B, int) - test.osl:32 void ddd (struct A, int) -test.osl:54: error: Ambiguous call to 'noise (unknown)' + test.osl:35 void ddd (struct B, int) + test.osl:34 void ddd (struct A, int) +test.osl:55: error: Ambiguous call to 'eee (unknown, int)' Candidates are: - test.osl:35 float noise (struct A) + test.osl:37 float eee (struct A, int) + test.osl:38 void eee (struct B, int) +test.osl:56: error: Ambiguous call to 'noise (unknown)' + Candidates are: + test.osl:46 float noise (struct A) float noise (point) color noise (point) vector noise (point) +test.osl:57: error: Ambiguous call to 'fff (unknown)' + Candidates are: + test.osl:41 void fff (point) + test.osl:40 void fff (struct A) +test.osl:58: error: Ambiguous call to 'ggg (unknown)' + Candidates are: + test.osl:44 float ggg (struct D) + test.osl:43 void ggg (struct C) FAILED test.osl diff --git a/testsuite/oslc-err-initlist-args/test.osl b/testsuite/oslc-err-initlist-args/test.osl index 0ee27ba1c..91914be92 100644 --- a/testsuite/oslc-err-initlist-args/test.osl +++ b/testsuite/oslc-err-initlist-args/test.osl @@ -19,6 +19,8 @@ void failifnota(acolor a) {} // No float a, b, c; ??? struct A { float a; float b; float c; }; struct B { float a; float b; float c; }; +struct C { float a; float b; float c; float d; }; +struct D { float a; float b; float c; float d; }; void aaa(A a, float b) { printf("A.1\n"); } void aaa(B b, int c) { printf("B.1\n"); } @@ -32,16 +34,33 @@ void ccc(B b, float c) { printf("B.3\n"); } void ddd(A a, int b) { printf("A.4\n"); } void ddd(B b, int c) { printf("B.4\n"); } +float eee(A a, int b) { printf("A.5\n"); return 1; } +void eee(B b, int c) { printf("B.5\n"); } + +void fff(A a) {} +void fff(point p) {} + +void ggg(C c) {} +float ggg(D d) { return 0; } + float noise(A a) { return 1; } +float noise(C a) { return 1; } shader test() { - colorvalue({ 1, {2,3,4} }); // ok - colorvalue({ {6,7,8}, 5 }); // ok + // Failures + failifnota({6,7,8}); + failifnota({ {6,7,8}, 5 }); + ddd({1,2,3}, 1); + eee({1,2,3}, 1); + noise({1,2,3}); + fff({1,2,3}); + ggg({1,2,3,4}); - failifnota({ 1, {2,3,4} }); // ok - failifnota({6,7,8}); // fail - failifnota({ {6,7,8}, 5 }); // fail + // Success + colorvalue({ 1, {2,3,4} }); + colorvalue({ {6,7,8}, 5 }); + failifnota({ 1, {2,3,4} }); aaa({1,2,3}, 1); aaa({1,2,3}, 1.0); @@ -49,8 +68,8 @@ shader test() bbb({1,2,3}, 1.0); ccc({1,2,3}, 1); ccc({1,2,3}, 1.0); - - ddd({1,2,3}, 1); - noise({1,2,3}); + (float) eee({1,2,3}, 1); + (float) ggg({1,2,3,4}); + noise({1,2,3,4}); } From fcbdb6cfa54d3d17ccbfdebaf7037159ac9798c4 Mon Sep 17 00:00:00 2001 From: Frederich Munch Date: Thu, 8 Feb 2018 15:29:34 -0500 Subject: [PATCH 11/11] Make warnings & errors involving initializer list arguments more obvious. --- src/liboslcomp/typecheck.cpp | 6 +++++- testsuite/oslc-err-initlist-args/ref/out.txt | 14 +++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/liboslcomp/typecheck.cpp b/src/liboslcomp/typecheck.cpp index 776c202cf..3589b9097 100644 --- a/src/liboslcomp/typecheck.cpp +++ b/src/liboslcomp/typecheck.cpp @@ -1640,7 +1640,11 @@ class CandidateFunctions { const char *comma = ""; for (ASTNode::ref arg = m_args; arg; arg = arg->next()) { argstr += comma; - argstr += arg->typespec().string(); + if (arg->typespec().simpletype().is_unknown() && + arg->nodetype() == ASTNode::compound_initializer_node) { + argstr += "initializer-list"; + } else + argstr += arg->typespec().string(); comma = ", "; } argstr += ")"; diff --git a/testsuite/oslc-err-initlist-args/ref/out.txt b/testsuite/oslc-err-initlist-args/ref/out.txt index 9eb0a96ef..ff4e989d6 100644 --- a/testsuite/oslc-err-initlist-args/ref/out.txt +++ b/testsuite/oslc-err-initlist-args/ref/out.txt @@ -1,28 +1,28 @@ -test.osl:52: error: No matching function call to 'failifnota (unknown)' +test.osl:52: error: No matching function call to 'failifnota (initializer-list)' Candidates are: test.osl:16 void failifnota (struct acolor) -test.osl:53: error: No matching function call to 'failifnota (unknown)' +test.osl:53: error: No matching function call to 'failifnota (initializer-list)' Candidates are: test.osl:16 void failifnota (struct acolor) -test.osl:54: error: Ambiguous call to 'ddd (unknown, int)' +test.osl:54: error: Ambiguous call to 'ddd (initializer-list, int)' Candidates are: test.osl:35 void ddd (struct B, int) test.osl:34 void ddd (struct A, int) -test.osl:55: error: Ambiguous call to 'eee (unknown, int)' +test.osl:55: error: Ambiguous call to 'eee (initializer-list, int)' Candidates are: test.osl:37 float eee (struct A, int) test.osl:38 void eee (struct B, int) -test.osl:56: error: Ambiguous call to 'noise (unknown)' +test.osl:56: error: Ambiguous call to 'noise (initializer-list)' Candidates are: test.osl:46 float noise (struct A) float noise (point) color noise (point) vector noise (point) -test.osl:57: error: Ambiguous call to 'fff (unknown)' +test.osl:57: error: Ambiguous call to 'fff (initializer-list)' Candidates are: test.osl:41 void fff (point) test.osl:40 void fff (struct A) -test.osl:58: error: Ambiguous call to 'ggg (unknown)' +test.osl:58: error: Ambiguous call to 'ggg (initializer-list)' Candidates are: test.osl:44 float ggg (struct D) test.osl:43 void ggg (struct C)