// generic_amb.h // generic ambiguity resolution; was in cc_tcheck.cc, but gnu.cc // wants to use it too #ifndef GENERIC_AMB_H #define GENERIC_AMB_H #include "cc_ast.h" // C++ AST #include "cc_env.h" // Env, DisambiguationErrorTrapper #include "objlist.h" // ObjList #include "trace.h" // TRACE // defined in cc_tcheck.cc bool noDisambErrors(ErrorList const &list); // when I print a message saying how an ambiguity was resolved, // I want to describe the selected node; in most cases the name // of that node's type suffices template string ambiguousNodeName(NODE const *n) { return n->kindName(); } // definitions in cc_tcheck.cc string ambiguousNodeName(Declarator const *n); string ambiguousNodeName(Expression const *e); // after a node has failed to typecheck, I may need to mark it // as "never been typechecked", so that if it's a shared subtree // in an ambiguous region we'll attempt to check it in each // context (since that's what's needed to put errors into the // environment); in the common case I don't need anything // // update: this doesn't work, because the problem may be one // level down from the top node.. // template // inline void markAsFailed(NODE *n) {} // // but for Expressions I need to nullify the 'type' field // inline void markAsFailed(Expression *n) // { // n->type = NULL; // } // Generic ambiguity resolution: We check all the alternatives, and // select the one which typechecks without errors. Complain if the // number of successful alternatives is not 1. template NODE *resolveAmbiguity( // pointer to the first alternative; we find other alternatives // by following the 'ambiguity' links NODE *ths, // typechecking context Env &env, // actual type name of 'NODE' (superclass type in the AST) char const *nodeTypeName, // when 'priority' is true, then the alternatives are considered to // be listed in order of preference, such that the first one to // successfully typecheck is immediately chosen bool priority, // This extra argument will be passed to NODE->mid_tcheck, and // whatever the succeeding tcheck puts back into its 'extra' will // be copied into the caller's version. (Currently, EXTRA is // always either 'Declarator::Tcheck' or it is 'int' (and ignored), // if that helps for concreteness.) EXTRA &callerExtra) { // grab location before checking the alternatives SourceLoc loc = env.loc(); // how many alternatives? int numAlts = 1; { for (NODE *a = ths->ambiguity; a != NULL; a = a->ambiguity) { numAlts++; } } TRACE("disamb", toString(loc) << ": ambiguous " << nodeTypeName << " with " << numAlts << " alternatives"); // make an array of lists to hold the errors generated by the // various alternatives enum { NOMINAL_MAX_ALT = 3 }; ErrorList altErrorsArray[NOMINAL_MAX_ALT]; ErrorList *altErrors = altErrorsArray; if (numAlts > NOMINAL_MAX_ALT) { // there are too many ambiguities to fit into the stack-allocated // array, so use an array on the heap instead; this is very // unusual, but not impossible (see e.g. in/t0328.cc) altErrors = new ErrorList[numAlts]; } // copy the caller's 'extra' so we can make untainted copies // for each iteration EXTRA origExtra(callerExtra); // check each one int altIndex = 0; int numOk = 0; NODE *lastOk = NULL; int lastOkIndex = -1; // save current error messages so we will only be dealing with // those generated by the current alternative { DisambiguationErrorTrapper trapper(env); for (NODE *alt = ths; alt != NULL; alt = alt->ambiguity, altIndex++) { int beforeChange = env.getChangeCount(); TRACE("disamb", toString(loc) << ": considering " << ambiguousNodeName(alt)); // tcheck 'alt' EXTRA extra(origExtra); try { alt->mid_tcheck(env, extra); } catch (x_assert &x) { HANDLER(); env.errors.markAllAsFromDisamb(); throw; } // move that run's errors into a per-alternative list altErrors[altIndex].takeMessages(env.errors); // did that alternative succeed? if (noDisambErrors(altErrors[altIndex])) { // yes; update our success trackers numOk++; lastOk = alt; lastOkIndex = altIndex; // copy that alternative's 'extra' to the caller's callerExtra = extra; if (priority) { // the alternatives are listed in priority order, so once an // alternative succeeds, stop and select it break; } } else { // if this NODE failed to check, then it had better not // have modified the environment if (beforeChange != env.getChangeCount()) { // 1/02/03: there used to be an assertion here, but // this actually happens sometimes, and it's unavoidable. // for example, // int foo(smanip &m); // when interpreted as an integer with an initializer has // both of its initializers fail to tcheck (unless TP is // a variable), but that's only after 'foo' has been added; // and the language requires that 'foo' be visible inside // its own initializer, so it has to be added first. // // what I really want here is to make sure this now-corrupt // environment can't participate in any eventually successful // parse, so I'll just insert one more (non-disambiguating) // error, so this entire interpretation line has to be rejected // one way or another altErrors[altIndex].addError(new ErrorMsg( loc, "a rejected alternative modified this environment", EF_NONE)); } // make sure we don't consider this subtree to be finished //markAsFailed(alt); } } // env's error messages restored at this point } if (numOk == 0) { // none of the alternatives checked out TRACE("disamb", toString(loc) << ": ambiguous " << nodeTypeName << ": all bad"); //breaker(); // nsFastLoadFile.i provokes this many times, but all benign (?) // add a note about the ambiguity env.errors.addError(new ErrorMsg( loc, "---- BEGIN: messages from an ambiguity ----", EF_NONE)); for (int i=0; i 0) { env.errors.addError(new ErrorMsg( loc, "---- SEPARATOR: messages from an ambiguity ----", EF_NONE)); } env.errors.takeMessages(altErrors[i]); } env.errors.addError(new ErrorMsg( loc, "---- END: messages from an ambiguity ----", EF_NONE)); } else if (numOk == 1) { // one alternative succeeds, which is what we want TRACE("disamb", toString(loc) << ": ambiguous " << nodeTypeName << ": selected " << ambiguousNodeName(lastOk)); // put back succeeding alternative's errors (non-disambiguating, // and warnings); errors associated with other alternatives will // be deleted automatically env.errors.takeMessages(altErrors[lastOkIndex]); // select 'lastOk' ths = lastOk; } else { // more than one alternative succeeds, not good TRACE("disamb", toString(loc) << ": ambiguous " << nodeTypeName << ": multiple good!"); // now complain; mark it 'disambiguating' so that we'll see // this show up even in template code env.error("more than one ambiguous alternative succeeds", EF_DISAMBIGUATES); } // 2005-03-27: I moved this down here so that we always resolve // the ambiguity, even when there is no basis for choosing. The // failed resolution will still generate an error, but this way // subsequent stages will find an unambiguous AST (in/t0459.cc). // // break the ambiguity link (if any) in 'lastOk', so if someone // comes along and tchecks this again we can skip the last part // of the ambiguity list const_cast(ths->ambiguity) = NULL; // cleanup the array if necessary if (numAlts > NOMINAL_MAX_ALT) { delete[] altErrors; } return ths; } // swap the order of the first two elements of the ambiguity link // list before passing to 'resolveAmbiguity' template NODE *swap_then_resolveAmbiguity( NODE *ths, Env &env, char const *nodeTypeName, bool priority, EXTRA &callerExtra) { // original configuration: a b rest NODE *a = ths; NODE *b = ths->ambiguity; // new configuration: b a rest a->ambiguity = b->ambiguity; b->ambiguity = a; // run with priority return resolveAmbiguity(b, env, nodeTypeName, priority, callerExtra); } #endif // GENERIC_AMB_H