diff --git a/CMakeLists.txt b/CMakeLists.txt index 57780808d..5555b42df 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 @@ -256,7 +256,7 @@ TESTSUITE ( aastep allowconnect-err and-or-not-synonyms 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 @@ -272,6 +272,7 @@ TESTSUITE ( aastep allowconnect-err and-or-not-synonyms 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-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.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 fd3facae0..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. @@ -346,25 +347,23 @@ 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. 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. + // 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 @@ -707,8 +706,41 @@ 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; + Symbol *codegen (Symbol *dest = NULL); + + ref args () const { return child (0); } + + // Typecheck construction of expected against args() + // 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, true); + } +}; + + +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"; } @@ -716,6 +748,16 @@ 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; } + + TypeSpec typecheck (TypeSpec expected) { + return typecheck(expected, 0); + } + + // Helper for typechecking an initlist or structure. + class TypeAdjuster; }; @@ -840,26 +882,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 ca9209536..39968d293 100644 --- a/src/liboslcomp/codegen.cpp +++ b/src/liboslcomp/codegen.cpp @@ -461,9 +461,18 @@ 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); + + 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; } @@ -661,7 +670,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 +797,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,13 +836,61 @@ 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); } } +Symbol* +ASTNode::codegen_aassign (TypeSpec elemtype, Symbol *src, Symbol *lval, + Symbol* ind, int i) +{ + 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) + ind = m_compiler->make_constant (i); + + emitcode ("aassign", lval, ind, src); + + return src; +} + void ASTNode::codegen_initlist (ref init, TypeSpec type, Symbol *sym) @@ -863,6 +924,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()); @@ -914,10 +981,39 @@ ASTNode::codegen_initlist (ref init, TypeSpec type, Symbol *sym) return; } } + else if (type.is_structure_array()) { + for (int i = 0; init && i < type.arraylength(); ++i) { + 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)); - // Loop over a list of initializers (it's just 1 if not an array)... - if (init->nodetype() == compound_initializer_node) + 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)... for (int i = 0; init; init = init->next(), ++i) { if (sym->typespec().is_structure() && init->nodetype() == compound_initializer_node) { @@ -931,17 +1027,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); @@ -959,7 +1046,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 @@ -968,21 +1055,21 @@ 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; } // 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()); @@ -992,9 +1079,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; } @@ -1012,15 +1103,27 @@ 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); - } 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) @@ -1177,19 +1280,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); @@ -1734,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 1f581920e..af4842b96 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 */); @@ -688,6 +692,10 @@ return_statement { $$ = new ASTreturn_statement (oslcompiler, $2); } + | RETURN compound_initializer ';' + { + $$ = new ASTreturn_statement (oslcompiler, $2); + } ; for_init_statement @@ -919,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 661e7befb..3589b9097 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,95 +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 - } - 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 { - error ("Can't use '{...}' for a struct field that is not an array"); - } - continue; - } - - // 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) { @@ -777,7 +681,7 @@ ASTtypecast_expression::typecheck (TypeSpec expected) TypeSpec -ASTtype_constructor::typecheck (TypeSpec expected) +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: @@ -791,23 +695,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); @@ -818,23 +723,300 @@ 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, + bind)) + 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. + 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 { +public: + // 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. + 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 + 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)); + } + } + return; + } + + if (cinit->ASTtype_constructor::typecheck(to, errors(), false) == to) + mark_type(cinit, to, true); + else + m_success = false; + } + + // 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(); + + int nelem = 0; + ASTcompound_initializer* cinit = init; + + if (init->initlist()->nodetype() != compound_initializer_node) { + if (! typecheck(init->initlist(), elemtype)) + return; + + 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); + } + } + + // 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 + 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 + typecheck_array(cinit, ftype); + + // Just leave if not reporting errors + if (!m_success && !errors()) + return; + + } else if (! typecheck(arg, ftype, structspec, &field)) + return; + + arg = arg->next(); + } + + // 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 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 type(); + } + + // Turn off the automatic binding on destruction + TypeSpec nobind() { + ASSERT (!m_success || m_adjust.size()); + m_debug_successful = m_success; + m_success = false; // Turn off the binding in destructor + return type(); + } + + // Turn automatic binding back on. + void bind() { + 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: + // 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, unsigned 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(m_compiler, mode).typecheck(this, expected); + } + return m_typespec; } @@ -867,7 +1049,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()) { @@ -897,7 +1079,30 @@ ASTNode::check_arglist (const char *funcname, ASTNode::ref arg, continue; // match anything } - if (! check_simple_arg (arg->typespec(), formals, coerce)) + 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); + + // 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. @@ -1160,10 +1365,14 @@ 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())); } - return typecheck_struct_initializers (args(), m_typespec, "this"); + + ASTcompound_initializer::TypeAdjuster(m_compiler) + .typecheck_fields(this, args().get(), m_typespec); + return m_typespec; } @@ -1212,9 +1421,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; @@ -1231,8 +1446,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) { @@ -1283,6 +1498,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()) { @@ -1321,10 +1537,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; @@ -1362,6 +1599,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; } @@ -1373,7 +1613,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()) { @@ -1400,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 += ")"; @@ -1432,6 +1676,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 @@ -1447,13 +1698,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) { @@ -1538,6 +1789,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) { @@ -1555,10 +1829,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; } }; @@ -1576,7 +1853,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) @@ -1587,7 +1864,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)) || @@ -1606,7 +1884,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) { } @@ -1664,22 +1942,29 @@ 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(); 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/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]); +} 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"); + } } diff --git a/testsuite/initlist/ref/out.txt b/testsuite/initlist/ref/out.txt new file mode 100644 index 000000000..3155dca0a --- /dev/null +++ b/testsuite/initlist/ref/out.txt @@ -0,0 +1,43 @@ +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) +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) +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)} +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)} +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/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..3fb9488b8 --- /dev/null +++ b/testsuite/initlist/test.osl @@ -0,0 +1,130 @@ + +struct custcolor4 +{ + color rgb; + float a; +}; + +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]; +}; + +struct nested1 { + color c3; +}; + +struct nested2 { + nested1 n1; + 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 }, + 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[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), + 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[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; + 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}}, 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) } }; + 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); + } + + 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..ff4e989d6 --- /dev/null +++ b/testsuite/oslc-err-initlist-args/ref/out.txt @@ -0,0 +1,29 @@ +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 (initializer-list)' + Candidates are: + test.osl:16 void failifnota (struct acolor) +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 (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 (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 (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 (initializer-list)' + 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/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..91914be92 --- /dev/null +++ b/testsuite/oslc-err-initlist-args/test.osl @@ -0,0 +1,75 @@ + +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; }; +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"); } + +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 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() +{ + // 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}); + + // 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); + bbb({1,2,3}, 1); + bbb({1,2,3}, 1.0); + ccc({1,2,3}, 1); + ccc({1,2,3}, 1.0); + (float) eee({1,2,3}, 1); + (float) ggg({1,2,3,4}); + noise({1,2,3,4}); +} + 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() +{ +} + 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 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