# Changeset 61accc5

Ignore:
Timestamp:
Jun 11, 2018, 9:30:58 AM (4 years ago)
Branches:
aaron-thesis, arm-eh, cleanup-dtors, deferred_resn, demangler, enum, forall-pointer-decay, jacob/cs343-translation, jenkins-sandbox, master, new-ast, new-ast-unique-expr, new-env, no_list, persistent-indexer, pthread-emulation, qualifiedEnum, with_gc
Children:
85b2300
Parents:
c2b10fa (diff), f184ca3 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'master' of plg.uwaterloo.ca:/u/cforall/software/cfa/cfa-cc

Files:
17 edited

Unmodified
Removed
• ## doc/papers/concurrency/Paper.tex

• ## doc/proposals/user_conversions.md

 rc2b10fa There is also a set of _explicit_ conversions that are only allowed through a cast expression. Based on Glen's notes on conversions [1], I propose that safe and unsafe conversions be expressed as constructor variants, though I make explicit (cast) conversions a constructor variant as well rather than a dedicated operator. I propose that safe, unsafe, and explicit (cast) conversions be expressed as constructor variants. Throughout this article, I will use the following operator names for constructors and conversion functions from From to To: void ?{} ( To*, To );            // copy constructor void ?{} ( To*, From );          // explicit constructor void ?{explicit} ( To*, From );  // explicit cast conversion void ?{safe} ( To*, From );      // implicit safe conversion void ?{unsafe} ( To*, From );    // implicit unsafe conversion [1] http://plg.uwaterloo.ca/~cforall/Conversions/index.html Glen's design made no distinction between constructors and unsafe implicit void ?{} ( To&, To );            // copy constructor void ?{} ( To&, From );          // explicit constructor void ?{explicit} ( To&, From );  // explicit cast conversion void ?{safe} ( To&, From );      // implicit safe conversion void ?{unsafe} ( To&, From );    // implicit unsafe conversion It has been suggested that all constructors would define unsafe implicit conversions; this is elegant, but interacts poorly with tuples. Essentially, without making this distinction, a constructor like the following multiplying the space of possible interpretations of all functions: void ?{}( Coord *this, int x, int y ); void ?{}( Coord& this, int x, int y ); That said, it would certainly be possible to make a multiple-argument implicit used infrequently: void ?{unsafe}( Coord *this, int x, int y ); void ?{unsafe}( Coord& this, int x, int y ); An alternate possibility would be to only count two-arg constructors void ?{} ( To*, From ) as unsafe conversions; under this semantics, safe and void ?{} ( To&, From ) as unsafe conversions; under this semantics, safe and explicit conversions should also have a compiler-enforced restriction to ensure that they are two-arg functions (this restriction may be valuable is convertable to To. If user-defined conversions are not added to the language, void ?{} ( To*, From ) may be a suitable representation, relying on void ?{} ( To&, From ) may be a suitable representation, relying on conversions on the argument types to account for transitivity. On the other hand, To* should perhaps match its target type exactly, so another assertion syntax specific to conversions may be required, e.g. From -> To. Since To& should be an exact match on To, this should put all the implicit conversions on the RHS. On the other hand, under some models (like [1]), implicit conversions are not allowed in assertion parameters, so another assertion syntax specific to conversions may be required, e.g. From -> To. It has also been suggested that, for programmer control, no implicit conversions (except, possibly, for polymorphic specialization) should be allowed in resolution of cast operators. [1] ../working/assertion_resolution.md ### Constructor Idiom ### that we can use the full range of Cforall features for conversions, including polymorphism. Glen [1] defines a _constructor idiom_ that can be used to create chains of safe conversions without duplicating code; given a type Safe which members of another type From can be directly converted to, the constructor idiom allows us to write a conversion for any type To which Safe converts to: forall(otype To | { void ?{safe}( To*, Safe ) }) void ?{safe}( To *this, From that ) { In an earlier version of this proposal, Glen Ditchfield defines a _constructor idiom_ that can be used to create chains of safe conversions without duplicating code; given a type Safe which members of another type From can be directly converted to, the constructor idiom allows us to write a conversion for any type To which Safe converts to: forall(otype To | { void ?{safe}( To&, Safe ) }) void ?{safe}( To& this, From that ) { Safe tmp = /* some expression involving that */; *this = tmp; // uses assertion parameter this{ tmp }; // initialize from assertion parameter } unsafe conversions. Glen's original suggestion said the copy constructor for To should also be accepted as a resolution for void ?{safe}( To&, Safe ) (Safe == To), allowing this same code to be used for the single-step conversion as well. This proposal does come at the cost of an extra copy initialization of the target value, though. Contrariwise, if a monomorphic conversion from From to Safe is written, e.g: void ?{safe}( Safe& this, From that ) { this{ /* some parameters involving that */ }; } Then the code for a transitive conversion from From to any To type convertable from Safe is written: forall(otype To | { void ?{safe}( To&, Safe ) }) void ?{safe}( To& this, From that ) { Safe tmp = that;  // uses monomorphic conversion this{ tmp };      // initialize from assertion parameter } Given the entirely-boilerplate nature of this code, but negative performance implications of the unmodified constructor idiom, it might be fruitful to have transitive and single step conversion operators, and let CFA build the transitive conversions; some possible names: void ?{safe}  (To&, From);    void ?{final safe} (To&, From);  // single-step void ?{safe*} (To&, From);    void ?{safe}       (To&, From);  // transitive What selective non-use of the constructor idiom gives us is the ability to define a conversion that may only be the *last* conversion in a chain of such. Constructing a conversion graph able to unambiguously represent the full hierarchy of implicit conversions in C is provably impossible using only single-step conversions with no additional information (see Appendix A), but this mechanism is sufficiently powerful (see [1], though the design there has some minor bugs; the general idea is to use the constructor idiom to define two chains of conversions, one among the signed integral types, another among the unsigned, and to use monomorphic conversions to allow conversions between signed and unsigned integer types). One use for this is to solve the problem that explicit conversions were added to C++ for, that of conversions to bool chaining to become conversions to any arithmetic type. Another use is to unambiguously represent the full hierarchy of implicit conversions in C by making sign conversions non-transitive, allowing the compiler to resolve e.g. int -> unsigned long as int -> long -> unsigned long over int -> unsigned int -> unsigned long. See [2] for more details. [2] ../working/glen_conversions/index.html#usual ### Appendix A: Partial and Total Orders ### convert from int to unsigned long, so we just put in a direct conversion and make the compiler smart enough to figure out the costs" - this is the approach taken by the existing compipler, but given that in a user-defined approach taken by the existing compiler, but given that in a user-defined conversion proposal the users can build an arbitrary graph of conversions, this case still needs to be handled. exists a chain of conversions from a to b (see Appendix A for description of preorders and related constructs). This preorder corresponds roughly to a more usual type-theoretic concept of This preorder roughly corresponds to a more usual type-theoretic concept of subtyping ("if I can convert a to b, a is a more specific type than b"); however, since this graph is arbitrary, it may contain cycles, so if and so is considered to be the nearer type. By transitivity, then, the conversion from X to Y2 should be cheaper than the conversion from X to W, but in this case the X and W are the conversion from X to W, but in this case the Y2 and W are incomparable by the conversion preorder, so the tie is broken by the shorter path from X to W in favour of W, contradicting the transitivity property
• ## src/Common/SemanticError.cc

 rc2b10fa // Created On       : Mon May 18 07:44:20 2015 // Last Modified By : Peter A. Buhr // Last Modified On : Wed May 16 15:01:20 2018 // Update Count     : 9 // Last Modified On : Thu Jun  7 08:05:26 2018 // Update Count     : 10 // void SemanticError( CodeLocation location, std::string error ) { SemanticErrorThrow = true; throw SemanticErrorException(location, error); throw SemanticErrorException( location, error ); }
• ## src/Parser/DeclarationNode.cc

 rc2b10fa // Created On       : Sat May 16 12:34:05 2015 // Last Modified By : Peter A. Buhr // Last Modified On : Wed Jun  6 15:57:50 2018 // Update Count     : 1076 // Last Modified On : Thu Jun  7 12:08:55 2018 // Update Count     : 1079 // type->aggregate.params->appendList( q->type->forall ); // augment forall qualifier } else {                                                                // not polymorphic type->aggregate.params = q->type->forall; // make polymorphic type // change implicit typedef from TYPEDEFname to TYPEGENname typedefTable.changeKind( *type->aggregate.name, TYPEGENname ); type->aggregate.params = q->type->forall; // set forall qualifier } // if } else {                                                                        // not polymorphic

• ## src/Parser/TypedefTable.h

 rc2b10fa // Created On       : Sat May 16 15:24:36 2015 // Last Modified By : Peter A. Buhr // Last Modified On : Thu May 31 23:23:47 2018 // Update Count     : 83 // Last Modified On : Thu Jun  7 12:10:17 2018 // Update Count     : 85 // bool exists( const std::string & identifier ); int isKind( const std::string & identifier ) const; void changeKind( const std::string & identifier, int kind ); void makeTypedef( const std::string & name ); void makeTypedef( const std::string & name, int kind = TYPEDEFname ); void addToScope( const std::string & identifier, int kind, const char * ); void addToEnclosingScope( const std::string & identifier, int kind, const char * );
• ## src/Parser/lex.ll

 rc2b10fa * Created On       : Sat Sep 22 08:58:10 2001 * Last Modified By : Peter A. Buhr * Last Modified On : Wed Jun  6 17:31:09 2018 * Update Count     : 677 * Last Modified On : Thu Jun  7 08:27:40 2018 * Update Count     : 679 */ %% // ----end of lexer---- void yyerror( const char * errmsg ) { SemanticErrorThrow = true; cout << (yyfilename ? yyfilename : "*unknown file*") << ':' << yylineno << ':' << column - yyleng + 1 << ": " << ErrorHelpers::error_str() << errmsg << " at token \"" << (yytext[0] == '\0' ? "EOF" : yytext) << '"' << endl;

• ## src/tests/preempt_longrun/Makefile.in

 rc2b10fa clean-local: rm -f ${TESTS} rm -f${TESTS} core* out.log % : %.c \${CC}
• ## src/tests/preempt_longrun/enter.c

 rc2b10fa monitor mon_t {}; void foo( mon_t & mutex this ) {} mon_t mon; void foo( mon_t & mutex this ) {} thread worker_t {}; void main( worker_t & this ) { for( unsigned long i = 0; i < N; i++ ) { } extern "C" { static worker_t * workers; } int main(int argc, char * argv[] ) { processor p; { worker_t w[7]; workers = w; } }
• ## src/tests/preempt_longrun/processor.c

 rc2b10fa } static const unsigned long N = 5_000ul; static const unsigned long N = 50_000ul; int main(int argc, char* argv[]) { processor * p[15]; write(STDERR_FILENO, "Preparing\n", sizeof("Preparing\n")); write(STDOUT_FILENO, "Preparing\n", sizeof("Preparing\n")); for ( int pi = 0; pi < 15; pi++ ) { p[pi] = new(); } write(STDERR_FILENO, "Starting\n", sizeof("Starting\n")); write(STDOUT_FILENO, "Starting\n", sizeof("Starting\n")); for ( int i = 0; i < N; i++) { int pi = i % 15; p[pi] = new(); } write(STDERR_FILENO, "Stopping\n", sizeof("Stopping\n")); write(STDOUT_FILENO, "Stopping\n", sizeof("Stopping\n")); for ( int pi = 0; pi < 15; pi++ ) { delete( p[pi] ); } write(STDERR_FILENO, "Done\n", sizeof("Done\n")); write(STDOUT_FILENO, "Done\n", sizeof("Done\n")); }
Note: See TracChangeset for help on using the changeset viewer.