diff --git a/compiler/AST/AggregateType.cpp b/compiler/AST/AggregateType.cpp index 7546bf80a218..9cd0c6faeab6 100644 --- a/compiler/AST/AggregateType.cpp +++ b/compiler/AST/AggregateType.cpp @@ -1275,6 +1275,9 @@ void AggregateType::resolveConcreteType() { this->resolveStatus = RESOLVING; this->symbol->instantiationPoint = getInstantiationPoint(this->symbol->defPoint); + if (this->symbol->instantiationPoint) + this->symbol->userInstantiationPointLoc = + getUserInstantiationPoint(this->symbol); if (isClass() == true && symbol->hasFlag(FLAG_NO_OBJECT) == false) { AggregateType* parent = dispatchParents.v[0]; @@ -1348,6 +1351,9 @@ AggregateType* AggregateType::instantiationWithParent(AggregateType* parent, Exp if (retval->symbol->instantiationPoint == NULL) { retval->symbol->instantiationPoint = toBlockStmt(insnPoint); + if (retval->symbol->instantiationPoint) + retval->symbol->userInstantiationPointLoc = + getUserInstantiationPoint(retval->symbol); } // Update the type of the 'super' field @@ -1702,6 +1708,9 @@ AggregateType* AggregateType::getNewInstantiation(Symbol* sym, Type* symType, Ex retval->instantiatedFrom = this; if (retval->symbol->instantiationPoint == NULL) { retval->symbol->instantiationPoint = toBlockStmt(insnPoint); + if (retval->symbol->instantiationPoint != NULL) + retval->symbol->userInstantiationPointLoc = + getUserInstantiationPoint(retval->symbol); } retval->symbol->copyFlags(symbol); diff --git a/compiler/AST/FnSymbol.cpp b/compiler/AST/FnSymbol.cpp index c1986450dd07..eb02d3c696d0 100644 --- a/compiler/AST/FnSymbol.cpp +++ b/compiler/AST/FnSymbol.cpp @@ -43,7 +43,8 @@ FnSymbol* gGenericTupleDestroy = NULL; std::map ftableMap; std::vector ftableVec; -FnSymbol::FnSymbol(const char* initName) : Symbol(E_FnSymbol, initName) { +FnSymbol::FnSymbol(const char* initName) + : Symbol(E_FnSymbol, initName), userInstantiationPointLoc(0, NULL) { retType = dtUnknown; where = NULL; lifetimeConstraints= NULL; @@ -536,6 +537,8 @@ void FnSymbol::setInstantiationPoint(Expr* expr) { this->_instantiationPoint = block; this->_backupInstantiationPoint = block->getFunction(); } + + userInstantiationPointLoc = getUserInstantiationPoint(this); } BlockStmt* FnSymbol::instantiationPoint() const { diff --git a/compiler/AST/baseAST.cpp b/compiler/AST/baseAST.cpp index 06faf6c155e8..b802f1464884 100644 --- a/compiler/AST/baseAST.cpp +++ b/compiler/AST/baseAST.cpp @@ -631,9 +631,6 @@ void BaseAST::printDocsDescription(const char *doc, std::ostream *file, unsigned } } - -astlocT currentAstLoc(0,NULL); - void registerModule(ModuleSymbol* mod) { switch (mod->modTag) { case MOD_USER: @@ -814,40 +811,3 @@ bool isCForLoop(const BaseAST* a) return (stmt != 0 && stmt->isCForLoop()) ? true : false; } - -/* Create a throw-away ast with a given filename and line number. - This can be used e.g. to pass a line and filename to USR_FATAL - since it only takes those from an AST, not directly. */ -VarSymbol* createASTforLineNumber(const char* filename, int line) { - astlocT astloc(line, filename); - astlocMarker markAstLoc(astloc); - VarSymbol* lineTemp = newTemp(); - return lineTemp; -} - -/************************************* | ************************************** -* * -* Definitions for astlocMarker * -* * -************************************** | *************************************/ - -// constructor, invoked upon SET_LINENO -astlocMarker::astlocMarker(astlocT newAstLoc) - : previousAstLoc(currentAstLoc) -{ - //previousAstLoc = currentAstLoc; - currentAstLoc = newAstLoc; -} - -// constructor, for special occasions -astlocMarker::astlocMarker(int lineno, const char* filename) - : previousAstLoc(currentAstLoc) -{ - currentAstLoc.lineno = lineno; - currentAstLoc.filename = astr(filename); -} - -// destructor, invoked upon leaving SET_LINENO's scope -astlocMarker::~astlocMarker() { - currentAstLoc = previousAstLoc; -} diff --git a/compiler/AST/symbol.cpp b/compiler/AST/symbol.cpp index 154757b61a63..949242281e09 100644 --- a/compiler/AST/symbol.cpp +++ b/compiler/AST/symbol.cpp @@ -1205,7 +1205,8 @@ TypeSymbol::TypeSymbol(const char* init_name, Type* init_type) : llvmTbaaStructCopyNode(NULL), llvmConstTbaaStructCopyNode(NULL), llvmDIType(NULL), doc(NULL), - instantiationPoint(NULL) + instantiationPoint(NULL), + userInstantiationPointLoc(0, NULL) { addFlag(FLAG_TYPE_VARIABLE); if (!type) diff --git a/compiler/include/AggregateType.h b/compiler/include/AggregateType.h index ce14646c0894..e599b6a2225d 100644 --- a/compiler/include/AggregateType.h +++ b/compiler/include/AggregateType.h @@ -112,12 +112,18 @@ class AggregateType : public Type { bool setFirstGenericField(); - AggregateType* getInstantiation(Symbol* sym, int index, Expr* insnPoint = NULL); + AggregateType* getInstantiation(Symbol* sym, int index, + Expr* insnPoint); AggregateType* getInstantiationParent(AggregateType* pt); - AggregateType* generateType(CallExpr* call, const char* callString); - AggregateType* generateType(SymbolMap& subs, CallExpr* call, const char* callString, bool evalDefaults, Expr* insnPoint = NULL); + AggregateType* generateType(CallExpr* call, + const char* callString); + AggregateType* generateType(SymbolMap& subs, + CallExpr* call, + const char* callString, + bool evalDefaults, + Expr* insnPoint = NULL); void resolveConcreteType(); bool isInstantiatedFrom(const AggregateType* base) diff --git a/compiler/include/FnSymbol.h b/compiler/include/FnSymbol.h index a63fc0f0104b..4f28d4bd1576 100644 --- a/compiler/include/FnSymbol.h +++ b/compiler/include/FnSymbol.h @@ -65,6 +65,8 @@ class FnSymbol : public Symbol { FnSymbol* instantiatedFrom; SymbolMap substitutions; + astlocT userInstantiationPointLoc; + private: BlockStmt* _instantiationPoint; FnSymbol* _backupInstantiationPoint; diff --git a/compiler/include/astlocs.h b/compiler/include/astlocs.h new file mode 100644 index 000000000000..3372684749ba --- /dev/null +++ b/compiler/include/astlocs.h @@ -0,0 +1,75 @@ +/* + * Copyright 2004-2020 Hewlett Packard Enterprise Development LP + * Other additional copyright holders may be indicated within. + * + * The entirety of this work is licensed under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ASTLOCS_H_ +#define _ASTLOCS_H_ + +class BaseAST; +class Expr; + +// how an AST node knows its location in the source code +// (assumed to get copied upon assignment and parameter passing) +class astlocT { +public: + astlocT(int linenoArg, const char* filenameArg) : + filename(filenameArg), lineno(linenoArg) + {} + + const char* filename; // filename of location + int lineno; // line number of location + + inline bool operator==(const astlocT other) const { + return this->filename == other.filename && this->lineno == other.lineno; + } + inline bool operator!=(const astlocT other) const { + return this->filename != other.filename || this->lineno != other.lineno; + } +}; + +// +// macro to update the global line number used to set the line number +// of an AST node when it is constructed - or to print out the line +// number of code related to a core dump. +// +// This should be used before constructing new nodes to make sure the +// line number is correctly set. The global line number reverts to +// its previous value upon leaving the scope where the macro is used. +// The fixed variable name ensures a single macro per scope. +// Users of the macro are to create additional scopes when needed. +// todo - should we add it to DECLARE_COPY/DECLARE_SYMBOL_COPY ? +// +#define SET_LINENO(ast) astlocMarker markAstLoc(ast->astloc) + +extern astlocT currentAstLoc; + +class astlocMarker { +public: + astlocMarker(astlocT newAstLoc); + astlocMarker(int lineno, const char* filename); + ~astlocMarker(); + + astlocT previousAstLoc; +}; + +Expr* findLocationIgnoringInternalInlining(Expr* cur); +bool printsUserLocation(const BaseAST* astIn); + +astlocT getUserInstantiationPoint(const BaseAST* ast); + +#endif diff --git a/compiler/include/baseAST.h b/compiler/include/baseAST.h index cf6ce3829f7e..af5c467e28d8 100644 --- a/compiler/include/baseAST.h +++ b/compiler/include/baseAST.h @@ -36,6 +36,7 @@ #include #include +#include "astlocs.h" #include "map.h" #include "vec.h" @@ -138,25 +139,6 @@ foreach_ast(decl_gvecs); typedef Map SymbolMap; typedef MapElem SymbolMapElem; -// how an AST node knows its location in the source code -// (assumed to get copied upon assignment and parameter passing) -class astlocT { -public: - astlocT(int linenoArg, const char* filenameArg) : - filename(filenameArg), lineno(linenoArg) - {} - - const char* filename; // filename of location - int lineno; // line number of location - - inline bool operator==(const astlocT other) const { - return this->filename == other.filename && this->lineno == other.lineno; - } - inline bool operator!=(const astlocT other) const { - return this->filename != other.filename || this->lineno != other.lineno; - } -}; - // // enumerated type of all AST node types // @@ -306,34 +288,6 @@ void trace_remove(BaseAST* ast, char flag); void verifyInTree(BaseAST* ast, const char* msg); - -VarSymbol* createASTforLineNumber(const char* filename, int line); - -// -// macro to update the global line number used to set the line number -// of an AST node when it is constructed - or to print out the line -// number of code related to a core dump. -// -// This should be used before constructing new nodes to make sure the -// line number is correctly set. The global line number reverts to -// its previous value upon leaving the scope where the macro is used. -// The fixed variable name ensures a single macro per scope. -// Users of the macro are to create additional scopes when needed. -// todo - should we add it to DECLARE_COPY/DECLARE_SYMBOL_COPY ? -// -#define SET_LINENO(ast) astlocMarker markAstLoc(ast->astloc) - -extern astlocT currentAstLoc; - -class astlocMarker { -public: - astlocMarker(astlocT newAstLoc); - astlocMarker(int lineno, const char* filename); - ~astlocMarker(); - - astlocT previousAstLoc; -}; - // // class test inlines: determine the dynamic type of a BaseAST* // diff --git a/compiler/include/flags_list.h b/compiler/include/flags_list.h index 62c3f1f3a5ff..2c53cd825460 100644 --- a/compiler/include/flags_list.h +++ b/compiler/include/flags_list.h @@ -121,8 +121,7 @@ symbolFlag( FLAG_DOMAIN , ypr, "domain" , ncm ) symbolFlag( FLAG_DONT_DISABLE_REMOTE_VALUE_FORWARDING , ypr, "dont disable remote value forwarding" , ncm ) symbolFlag( FLAG_DOWN_END_COUNT_FN , ypr, "down end count fn" , ncm ) symbolFlag( FLAG_END_COUNT , ypr, "end count" , ncm ) -symbolFlag( FLAG_ERRONEOUS_AUTOCOPY, ypr, "erroneous autocopy", ncm) -symbolFlag( FLAG_ERRONEOUS_INITCOPY, ypr, "erroneous initcopy", ncm) +symbolFlag( FLAG_ERRONEOUS_COPY, ypr, "erroneous copy", ncm) symbolFlag( FLAG_ERROR_MODE_FATAL, ypr, "error mode fatal", ncm) symbolFlag( FLAG_ERROR_MODE_RELAXED, ypr, "error mode relaxed", ncm) symbolFlag( FLAG_ERROR_MODE_STRICT, ypr, "error mode strict", ncm) diff --git a/compiler/include/misc.h b/compiler/include/misc.h index 96bb11502f34..61636cb3f55e 100644 --- a/compiler/include/misc.h +++ b/compiler/include/misc.h @@ -23,6 +23,10 @@ #include #include +#include "baseAST.h" + +#include "astlocs.h" + #ifdef HAVE_LLVM #define exit(x) clean_exit(x) #else @@ -88,6 +92,7 @@ void setupError(const char* subdir, const char* filename, int lineno, int void handleError(const char* fmt, ...); void handleError(const BaseAST* ast, const char* fmt, ...); +void handleError(astlocT astloc, const char* fmt, ...); void exitIfFatalErrorsEncountered(); @@ -104,6 +109,8 @@ void printCallStackCalls(); bool fatalErrorsEncountered(); void clearFatalErrors(); +astlocT getUserInstantiationLocation(const BaseAST* ast); + // Returns true if an error/warning at this location // (e.g. with USR_FATAL(ast, ...)) would print out // a user line number. diff --git a/compiler/include/resolution.h b/compiler/include/resolution.h index abf7ac5e8764..f395c8e2a335 100644 --- a/compiler/include/resolution.h +++ b/compiler/include/resolution.h @@ -141,7 +141,7 @@ bool canDispatch(Type* actualType, void parseExplainFlag(char* flag, int* line, ModuleSymbol** module); -FnSymbol* findCopyInitFn(AggregateType* ct); +FnSymbol* findCopyInitFn(AggregateType* ct, const char*& err); FnSymbol* findAssignFn(AggregateType* at); FnSymbol* findZeroArgInitFn(AggregateType* at); @@ -224,7 +224,8 @@ void getAutoCopyTypeKeys(Vec& keys); FnSymbol* getAutoCopy(Type* t); // returns NULL if there are none FnSymbol* getAutoDestroy(Type* t); // " FnSymbol* getUnalias(Type* t); - +const char* getErroneousCopyError(FnSymbol* fn); +void markCopyErroneous(FnSymbol* fn, const char* err); bool isPOD(Type* t); diff --git a/compiler/include/symbol.h b/compiler/include/symbol.h index 090cfe61d4c9..7efc20c5930d 100644 --- a/compiler/include/symbol.h +++ b/compiler/include/symbol.h @@ -538,7 +538,7 @@ class TypeSymbol : public Symbol { const char* doc; BlockStmt* instantiationPoint; - + astlocT userInstantiationPointLoc; }; /************************************* | ************************************** diff --git a/compiler/resolution/callDestructors.cpp b/compiler/resolution/callDestructors.cpp index 61378d088641..45d6dc6e23a2 100644 --- a/compiler/resolution/callDestructors.cpp +++ b/compiler/resolution/callDestructors.cpp @@ -321,7 +321,7 @@ void ReturnByRef::updateAssignmentsFromRefArgToValue(FnSymbol* fn) if (symLhs != NULL && symRhs != NULL) { - // check if rhs is actually a formal + // check if rhs is actually a formal bool rhsIsFormal = false; for_formals(formal, fn) { if(formal == symRhs) { @@ -1077,7 +1077,7 @@ static void insertCopiesForYields() // Function resolution adds "dummy" initCopy functions for types // that cannot be copied. These "dummy" initCopy functions are marked -// with the flag FLAG_ERRONEOUS_INITCOPY. This pattern enables +// with the flag FLAG_ERRONEOUS_COPY. This pattern enables // the compiler to continue to operate with its current structure // even for types that cannot be copied. In particular, this pass // has the ability to remove initCopy calls in some cases. @@ -1086,33 +1086,38 @@ static void insertCopiesForYields() // flag is ever called and raises an error if so. static void checkForErroneousInitCopies() { + std::map errors; + + // Store errors in local map + forv_Vec(FnSymbol, fn, gFnSymbols) { + if (fn->hasFlag(FLAG_ERRONEOUS_COPY)) { + // Store the error in the local map + if (const char* err = getErroneousCopyError(fn)) + errors[fn] = err; + } + } + // Mark initCopy/autoCopy functions calling functions marked with - // FLAG_ERRONEOUS_INITCOPY/FLAG_ERRONEOUS_AUTOCOPY with the same + // FLAG_ERRONEOUS_COPY with the same // flag. This situation can come up with the compiler-generated // tuple copy functions. bool changed; do { changed = false; forv_Vec(FnSymbol, fn, gFnSymbols) { - if (fn->hasFlag(FLAG_ERRONEOUS_INITCOPY)) { + if (fn->hasFlag(FLAG_ERRONEOUS_COPY)) { for_SymbolSymExprs(se, fn) { if (FnSymbol* callInFn = se->getFunction()) { - if (callInFn->hasFlag(FLAG_INIT_COPY_FN) && - !callInFn->hasFlag(FLAG_ERRONEOUS_INITCOPY)) { - callInFn->addFlag(FLAG_ERRONEOUS_INITCOPY); + bool inCopyIsh = callInFn->hasFlag(FLAG_INIT_COPY_FN) || + callInFn->hasFlag(FLAG_AUTO_COPY_FN) || + callInFn->hasFlag(FLAG_UNALIAS_FN); + if (inCopyIsh && !callInFn->hasFlag(FLAG_ERRONEOUS_COPY)) { + callInFn->addFlag(FLAG_ERRONEOUS_COPY); changed = true; - } - } - } - } - if (fn->hasFlag(FLAG_ERRONEOUS_AUTOCOPY)) { - for_SymbolSymExprs(se, fn) { - if (FnSymbol* callInFn = se->getFunction()) { - if (callInFn->hasFlag(FLAG_AUTO_COPY_FN) && - !callInFn->hasFlag(FLAG_ERRONEOUS_AUTOCOPY)) { - callInFn->addFlag(FLAG_ERRONEOUS_AUTOCOPY); - changed = true; + // propagate error if present + if (errors.count(fn) != 0) + errors[callInFn] = errors[fn]; } } } @@ -1121,33 +1126,34 @@ static void checkForErroneousInitCopies() { } while(changed); forv_Vec(FnSymbol, fn, gFnSymbols) { - if (fn->hasFlag(FLAG_ERRONEOUS_INITCOPY)) { + if (fn->hasFlag(FLAG_ERRONEOUS_COPY)) { // Error on each call site for_SymbolSymExprs(se, fn) { if (FnSymbol* callInFn = se->getFunction()) { - if (!callInFn->hasFlag(FLAG_INIT_COPY_FN)) { - USR_FATAL_CONT(se, - "copy-initialization invoked for a type " - "that does not have a copy initializer"); - } else { - // Should have been propagated above - INT_ASSERT(callInFn->hasFlag(FLAG_ERRONEOUS_INITCOPY)); - } - } - } - } + bool inCopyIsh = callInFn->hasFlag(FLAG_INIT_COPY_FN) || + callInFn->hasFlag(FLAG_AUTO_COPY_FN) || + callInFn->hasFlag(FLAG_UNALIAS_FN); + if (inCopyIsh == false) { - if (fn->hasFlag(FLAG_ERRONEOUS_AUTOCOPY)) { - // Error on each call site - for_SymbolSymExprs(se, fn) { - if (FnSymbol* callInFn = se->getFunction()) { - if (!callInFn->hasFlag(FLAG_AUTO_COPY_FN)) { - USR_FATAL_CONT(se, - "implicit copy-initialization invoked for a type " - "that does not allow it"); + if (callInFn->hasFlag(FLAG_INIT_COPY_FN)) { + USR_FATAL_CONT(se, "invalid copy-initialization"); + } else { + USR_FATAL_CONT(se, "invalid implicit copy-initialization"); + } + + if (errors.count(fn) != 0) + USR_FATAL_CONT(se, "%s", errors[fn]); + + Type* t = fn->getFormal(1)->getValType(); + astlocT typePoint = t->astloc; + if (t->symbol->userInstantiationPointLoc.filename != NULL) + typePoint = t->symbol->userInstantiationPointLoc; + + USR_PRINT(typePoint, + "%s does not have a valid init=", toString(t)); } else { // Should have been propagated above - INT_ASSERT(callInFn->hasFlag(FLAG_ERRONEOUS_AUTOCOPY)); + INT_ASSERT(callInFn->hasFlag(FLAG_ERRONEOUS_COPY)); } } } diff --git a/compiler/resolution/functionResolution.cpp b/compiler/resolution/functionResolution.cpp index 9f765b8cd10f..4d310cc61b14 100644 --- a/compiler/resolution/functionResolution.cpp +++ b/compiler/resolution/functionResolution.cpp @@ -150,13 +150,14 @@ typedef enum { static int inTryResolve; static std::vector tryResolveStates; static std::vector tryResolveFunctions; -typedef std::map > try_resolve_map_t; +typedef std::map > try_resolve_map_t; try_resolve_map_t tryResolveErrors; //# //# Static Function Declarations //# -static FnSymbol* resolveUninsertedCall(Type* type, CallExpr* call, bool errorOnFailure=true); +static FnSymbol* resolveUninsertedCall(Type* type, CallExpr* call, + bool errorOnFailure); static bool hasUserAssign(Type* type); static void resolveOther(); static bool fits_in_int(int width, Immediate* imm); @@ -298,7 +299,7 @@ void makeRefType(Type* type) { } CallExpr* call = new CallExpr(dtRef->symbol, type->symbol); - resolveUninsertedCall(type, call); + resolveUninsertedCall(type, call, false); type->refType = toAggregateType(call->typeInfo()); type->refType->getField(1)->type = type; @@ -394,6 +395,24 @@ FnSymbol* getUnalias(Type* t) { return unaliasMap.get(t); } +const char* getErroneousCopyError(FnSymbol* fn) { + try_resolve_map_t::iterator it; + it = tryResolveErrors.find(fn); + if (it != tryResolveErrors.end()) { + const char* err = it->second.second; + return err; + } + + return NULL; +} + +void markCopyErroneous(FnSymbol* fn, const char* err) { + fn->addFlag(FLAG_ERRONEOUS_COPY); + if (err != NULL) + tryResolveErrors[fn] = std::make_pair(fn,err); +} + + /************************************* | ************************************** * * * * @@ -2191,8 +2210,10 @@ bool signatureMatch(FnSymbol* fn, FnSymbol* gn) { * * ************************************** | *************************************/ -static FnSymbol* resolveUninsertedCall(BlockStmt* insert, CallExpr* call, bool errorOnFailure); -static FnSymbol* resolveUninsertedCall(Expr* insert, CallExpr* call, bool errorOnFailure); +static FnSymbol* resolveUninsertedCall(BlockStmt* insert, CallExpr* call, + bool errorOnFailure); +static FnSymbol* resolveUninsertedCall(Expr* insert, CallExpr* call, + bool errorOnFailure); static Expr* getInsertPointForTypeFunction(Type* type) { AggregateType* at = toAggregateType(type); @@ -2218,7 +2239,8 @@ static Expr* getInsertPointForTypeFunction(Type* type) { return retval; } -static FnSymbol* resolveUninsertedCall(Type* type, CallExpr* call, bool errorOnFailure) { +static FnSymbol* resolveUninsertedCall(Type* type, CallExpr* call, + bool errorOnFailure) { FnSymbol* retval = NULL; Expr* where = getInsertPointForTypeFunction(type); @@ -2230,37 +2252,44 @@ static FnSymbol* resolveUninsertedCall(Type* type, CallExpr* call, bool errorOnF return retval; } -static FnSymbol* resolveUninsertedCall(BlockStmt* insert, CallExpr* call, bool - errorOnFailure) { +static FnSymbol* resolveUninsertedCall(BlockStmt* insert, CallExpr* call, + bool errorOnFailure) { + FnSymbol* ret = NULL; BlockStmt* block = new BlockStmt(call, BLOCK_SCOPELESS); insert->insertAtHead(block); // Tail? - if (errorOnFailure) + if (errorOnFailure) { resolveCall(call); - else - tryResolveCall(call); + ret = call->resolvedFunction(); + } else { + ret = tryResolveCall(call, false); + } call->remove(); block->remove(); - return call->resolvedFunction(); + return ret; } -static FnSymbol* resolveUninsertedCall(Expr* insert, CallExpr* call, bool errorOnFailure) { +static FnSymbol* resolveUninsertedCall(Expr* insert, CallExpr* call, + bool errorOnFailure) { + FnSymbol* ret = NULL; BlockStmt* block = new BlockStmt(call, BLOCK_SCOPELESS); insert->insertBefore(block); - if (errorOnFailure) + if (errorOnFailure) { resolveCall(call); - else - tryResolveCall(call); + ret = call->resolvedFunction(); + } else { + ret = tryResolveCall(call, false); + } call->remove(); block->remove(); - return call->resolvedFunction(); + return ret; } static void checkForInfiniteRecord(AggregateType* at, std::set& nestedRecords) { @@ -3019,10 +3048,21 @@ FnSymbol* resolveNormalCall(CallExpr* call, check_state_t checkState) { tryResolveFunctions.pop_back(); } } + check_state_t state = tryResolveStates.back(); tryResolveStates.pop_back(); inTryResolve--; + // Also check for errors in tryResolveErrors + // in case this is the 2nd time we attempted to resolve it. + if (FnSymbol* fn = call->resolvedFunction()) { + try_resolve_map_t::iterator it; + it = tryResolveErrors.find(fn); + if (it != tryResolveErrors.end()) { + state = CHECK_FAILED; + } + } + if (state == CHECK_FAILED) retval = NULL; } @@ -3210,7 +3250,8 @@ static FnSymbol* resolveNormalCall(CallInfo& info, check_state_t checkState) { // If no candidates were found and it's a method, try forwarding if (candidates.n == 0 && info.call->numActuals() >= 1 && - info.call->get(1)->typeInfo() == dtMethodToken) { + info.call->get(1)->typeInfo() == dtMethodToken && + isUnresolvedSymExpr(info.call->baseExpr)) { Type* receiverType = canonicalDecoratedClassType(info.call->get(2)->getValType()); if (typeUsesForwarding(receiverType)) { FnSymbol* fn = resolveForwardedCall(info, checkState); @@ -3568,7 +3609,8 @@ static void reissueMsgs(FnSymbol* resolvedFn, } } -void resolveNormalCallCompilerWarningStuff(CallExpr* call, FnSymbol* resolvedFn) { +void resolveNormalCallCompilerWarningStuff(CallExpr* call, + FnSymbol* resolvedFn) { reissueMsgs(resolvedFn, innerCompilerErrorMap, outerCompilerErrorMap, true); reissueMsgs(resolvedFn, innerCompilerWarningMap, outerCompilerWarningMap, false); @@ -3580,8 +3622,8 @@ void resolveNormalCallCompilerWarningStuff(CallExpr* call, FnSymbol* resolvedFn) tryResolveErrors[fn] = it->second; } else { BaseAST* from = it->second.first; - std::string& err = it->second.second; - USR_FATAL_CONT(from, "%s", err.c_str()); + const char* err = it->second.second; + USR_FATAL_CONT(from, "%s", err); USR_PRINT(call, "in function called here"); } } @@ -5792,7 +5834,8 @@ static void resolveInitField(CallExpr* call) { (fs->defPoint->exprType == NULL && fs->defPoint->init == NULL) || (fs->defPoint->init == NULL && fs->defPoint->exprType != NULL && ct->fieldIsGeneric(fs, ignoredHasDefault))) { - AggregateType* instantiate = ct->getInstantiation(srcSym, index); + Expr* insnPt = call->getFunction()->instantiationPoint(); + AggregateType* instantiate = ct->getInstantiation(srcSym, index, insnPt); if (instantiate != ct) { // TODO: make this set of operations a helper function I can call INT_ASSERT(parentFn->_this); @@ -6116,8 +6159,23 @@ static void resolveInitVar(CallExpr* call) { call->insertAtTail(initCopy); call->primitive = primitives[PRIM_MOVE]; - resolveExpr(initCopy); - resolveMove(call); + if (initCopyIter == false) { + // If there is an error in that initCopy call, + // just mark it for later (rather than raising the error now) + // since the initCopy might be removed later in compilation. + inTryResolve++; + tryResolveStates.push_back(CHECK_CALLABLE_ONLY); + + resolveExpr(initCopy); + resolveMove(call); + + tryResolveStates.pop_back(); + inTryResolve--; + } else { + // give errors for init-copy on iterators now + resolveExpr(initCopy); + resolveMove(call); + } } else if (isRecord(targetType->getValType())) { AggregateType* at = toAggregateType(targetType->getValType()); @@ -6195,50 +6253,87 @@ static void resolveInitVar(CallExpr* call) { * * ************************************** | *************************************/ -FnSymbol* findCopyInitFn(AggregateType* at) { - VarSymbol* tmpAt = newTemp(at); +static FnSymbol* fixInstantiationPointAndTryResolveBody(AggregateType* at, + CallExpr* call); - FnSymbol* ret = NULL; +FnSymbol* findCopyInitFn(AggregateType* at, const char*& err) { + VarSymbol* tmpAt = newTemp(at); CallExpr* call = NULL; call = new CallExpr(astrInitEquals, gMethodToken, tmpAt, tmpAt); - ret = resolveUninsertedCall(at, call, false); - - // ret's instantiationPoint points to the dummy BlockStmt created by + FnSymbol* foundFn = resolveUninsertedCall(at, call, false); + // foundFn's instantiationPoint points to the dummy BlockStmt created by // resolveUninsertedCall, so it needs to be updated. - if (ret != NULL) { - Expr* point = NULL; - if (BlockStmt* stmt = at->symbol->instantiationPoint) { - point = stmt; - } - ret->setInstantiationPoint(point); - } + FnSymbol* resolvedFn = fixInstantiationPointAndTryResolveBody(at, call); + FnSymbol* ret = NULL; + + if (foundFn != NULL && resolvedFn == NULL) + if (tryResolveErrors.count(foundFn) != 0) + err = tryResolveErrors[foundFn].second; + + if (foundFn != NULL) + ret = resolvedFn; return ret; } FnSymbol* findAssignFn(AggregateType* at) { VarSymbol* tmpAt = newTemp(at); - FnSymbol* ret = NULL; CallExpr* call = new CallExpr("=", tmpAt, tmpAt); - ret = resolveUninsertedCall(at, call, false); + + FnSymbol* foundFn = resolveUninsertedCall(at, call, false); + FnSymbol* resolvedFn = fixInstantiationPointAndTryResolveBody(at, call); + FnSymbol* ret = NULL; + + if (foundFn != NULL) + ret = resolvedFn; return ret; } FnSymbol* findZeroArgInitFn(AggregateType* at) { VarSymbol* tmpAt = newTemp(at); - FnSymbol* ret = NULL; CallExpr* call = new CallExpr(astrInit, gMethodToken, tmpAt); - ret = resolveUninsertedCall(at, call, false); + FnSymbol* foundFn = resolveUninsertedCall(at, call, false); + FnSymbol* resolvedFn = fixInstantiationPointAndTryResolveBody(at, call); + FnSymbol* ret = NULL; + + if (foundFn != NULL) + ret = resolvedFn; return ret; } +static FnSymbol* fixInstantiationPointAndTryResolveBody(AggregateType* at, + CallExpr* call) { + + if (FnSymbol* fn = call->resolvedFunction()) { + fn->setInstantiationPoint(at->symbol->instantiationPoint); + + inTryResolve++; + tryResolveStates.push_back(CHECK_BODY_RESOLVES); + tryResolveFunctions.push_back(fn); + + resolveFunction(fn); + + check_state_t state = tryResolveStates.back(); + + tryResolveFunctions.pop_back(); + tryResolveStates.pop_back(); + inTryResolve--; + + // return the function if no compilerError was encountered + if (state != CHECK_FAILED) + return fn; + } + + return NULL; +} + /************************************* | ************************************** * * * * @@ -7997,6 +8092,20 @@ static void resolveExprMaybeIssueError(CallExpr* call) { tryResolveErrors[fn] = std::make_pair(from,str); } + + // Make any errors in initCopy / autoCopy mark the function + // with the flag for later errors (during callDestructors) + for (int i = callStack.n-1; i >= 0; i--) { + CallExpr* frame = callStack.v[i]; + FnSymbol* fn = frame->getFunction(); + bool inCopyIsh = fn->hasFlag(FLAG_INIT_COPY_FN) || + fn->hasFlag(FLAG_AUTO_COPY_FN) || + fn->hasFlag(FLAG_UNALIAS_FN); + if (inCopyIsh) { + fn->addFlag(FLAG_ERRONEOUS_COPY); + tryResolveErrors[fn] = std::make_pair(from,str); + } + } } } else { @@ -8336,7 +8445,7 @@ static void resolveSupportForModuleDeinits() { VarSymbol* fnPtrDum = newTemp("fnPtr", dtCFnPtr); CallExpr* addModule = new CallExpr("chpl_addModule", modNameDum, fnPtrDum); - resolveUninsertedCall(chpl_gen_main->body, addModule, /*err on fail*/ true); + resolveUninsertedCall(chpl_gen_main->body, addModule, true); gAddModuleFn = addModule->resolvedFunction(); @@ -8407,7 +8516,7 @@ static void insertRuntimeTypeTemps() { VarSymbol* tmp = newTemp("_runtime_type_tmp_", at); at->symbol->defPoint->insertBefore(new DefExpr(tmp)); CallExpr* call = new CallExpr("chpl__convertValueToRuntimeType", tmp); - FnSymbol* fn = resolveUninsertedCall(at, call); + FnSymbol* fn = resolveUninsertedCall(at, call, true); resolveFunction(fn); valueToRuntimeTypeMap.put(at, fn); tmp->defPoint->remove(); @@ -8693,15 +8802,31 @@ static const char* autoCopyFnForType(AggregateType* at) { static FnSymbol* autoMemoryFunction(AggregateType* at, const char* fnName) { VarSymbol* tmp = newTemp(at); CallExpr* call = new CallExpr(fnName, tmp); - FnSymbol* retval = NULL; chpl_gen_main->insertAtHead(new DefExpr(tmp)); - retval = resolveUninsertedCall(at, call); + FnSymbol* foundFn = resolveUninsertedCall(at, call, false); + FnSymbol* resolvedFn = fixInstantiationPointAndTryResolveBody(at, call); + FnSymbol* retval = NULL; + + if (foundFn != NULL) + retval = resolvedFn; + + if (retval == NULL) { + if (FnSymbol* fn = call->resolvedFunction()) { + // if it's an initCopy e.g. we should have already marked it as erroneous + if (fn->hasFlag(FLAG_INIT_COPY_FN) || + fn->hasFlag(FLAG_AUTO_COPY_FN) || + fn->hasFlag(FLAG_UNALIAS_FN)) + INT_ASSERT(fn->hasFlag(FLAG_ERRONEOUS_COPY)); - resolveFunction(retval); + // Return the resolved function, for storing in the map + retval = fn; + } + } - INT_ASSERT(retval->hasFlag(FLAG_PROMOTION_WRAPPER) == false); + if (retval != NULL) + INT_ASSERT(retval->hasFlag(FLAG_PROMOTION_WRAPPER) == false); tmp->defPoint->remove(); diff --git a/compiler/resolution/generics.cpp b/compiler/resolution/generics.cpp index e35bda1301c0..5c942ce5f9c1 100644 --- a/compiler/resolution/generics.cpp +++ b/compiler/resolution/generics.cpp @@ -482,7 +482,9 @@ static bool fixupDefaultInitCopy(FnSymbol* fn, // it up completely... instantiateBody(newFn); - if (FnSymbol* initFn = findCopyInitFn(ct)) { + const char* err = NULL; + + if (FnSymbol* initFn = findCopyInitFn(ct, err)) { Symbol* thisTmp = newTemp(ct); DefExpr* def = new DefExpr(thisTmp); CallExpr* initCall = NULL; @@ -541,7 +543,7 @@ static bool fixupDefaultInitCopy(FnSymbol* fn, } else { // No copy-initializer could be found - newFn->addFlag(FLAG_ERRONEOUS_INITCOPY); + markCopyErroneous(newFn, err); } retval = true; diff --git a/compiler/resolution/preFold.cpp b/compiler/resolution/preFold.cpp index 60aefc19df81..0d339661e2e9 100644 --- a/compiler/resolution/preFold.cpp +++ b/compiler/resolution/preFold.cpp @@ -266,7 +266,113 @@ static bool recordContainsNonNilable(Type* t) { return false; } +static bool isNonNilableOwned(Type* t) { + return isManagedPtrType(t) && + getManagedPtrManagerType(t) == dtOwned && + isNonNilableClassType(t); +} + +static void setRecordCopyableFlags(AggregateType* at) { + TypeSymbol* ts = at->symbol; + if (!ts->hasFlag(FLAG_TYPE_INIT_EQUAL_FROM_CONST) && + !ts->hasFlag(FLAG_TYPE_INIT_EQUAL_FROM_REF) && + !ts->hasFlag(FLAG_TYPE_INIT_EQUAL_MISSING)) { + + if (isNonNilableOwned(at)) { + ts->addFlag(FLAG_TYPE_INIT_EQUAL_MISSING); + } else { + // Try resolving a test init= to set the flags + const char* err = NULL; + FnSymbol* initEq = findCopyInitFn(at, err); + if (initEq == NULL) { + ts->addFlag(FLAG_TYPE_INIT_EQUAL_MISSING); + } else if (initEq->hasFlag(FLAG_COMPILER_GENERATED)) { + if (recordContainsNonNilableOwned(at)) + ts->addFlag(FLAG_TYPE_INIT_EQUAL_MISSING); + else if (recordContainsOwned(at)) + ts->addFlag(FLAG_TYPE_INIT_EQUAL_FROM_REF); + else + ts->addFlag(FLAG_TYPE_INIT_EQUAL_FROM_CONST); + } else { + // formals are mt, this, other + ArgSymbol* other = initEq->getFormal(3); + IntentTag intent = concreteIntentForArg(other); + if (intent == INTENT_IN || + intent == INTENT_CONST_IN || + intent == INTENT_CONST_REF) { + ts->addFlag(FLAG_TYPE_INIT_EQUAL_FROM_CONST); + } else { + // this case includes INTENT_REF_MAYBE_CONST + ts->addFlag(FLAG_TYPE_INIT_EQUAL_FROM_REF); + } + } + } + } +} + +static void setRecordAssignableFlags(AggregateType* at) { + TypeSymbol* ts = at->symbol; + if (!ts->hasFlag(FLAG_TYPE_ASSIGN_FROM_CONST) && + !ts->hasFlag(FLAG_TYPE_ASSIGN_FROM_REF) && + !ts->hasFlag(FLAG_TYPE_ASSIGN_MISSING)) { + + if (isNonNilableOwned(at)) { + ts->addFlag(FLAG_TYPE_ASSIGN_MISSING); + } else { + // Try resolving a test = to set the flags + FnSymbol* assign = findAssignFn(at); + if (assign == NULL) { + ts->addFlag(FLAG_TYPE_ASSIGN_MISSING); + } else if (assign->hasFlag(FLAG_COMPILER_GENERATED)) { + if (recordContainsNonNilableOwned(at)) + ts->addFlag(FLAG_TYPE_ASSIGN_MISSING); + else if (recordContainsOwned(at)) + ts->addFlag(FLAG_TYPE_ASSIGN_FROM_REF); + else + ts->addFlag(FLAG_TYPE_ASSIGN_FROM_CONST); + } else { + // formals are lhs, rhs + ArgSymbol* rhs = assign->getFormal(2); + IntentTag intent = concreteIntentForArg(rhs); + if (intent == INTENT_IN || + intent == INTENT_CONST_IN || + intent == INTENT_CONST_REF) { + ts->addFlag(FLAG_TYPE_ASSIGN_FROM_CONST); + } else { + // this case includes INTENT_REF_MAYBE_CONST + ts->addFlag(FLAG_TYPE_ASSIGN_FROM_REF); + } + } + } + } +} +static void setRecordDefaultValueFlags(AggregateType* at) { + TypeSymbol* ts = at->symbol; + + if (!ts->hasFlag(FLAG_TYPE_DEFAULT_VALUE) && + !ts->hasFlag(FLAG_TYPE_NO_DEFAULT_VALUE)) { + + if (isNonNilableClassType(at)) { + ts->addFlag(FLAG_TYPE_NO_DEFAULT_VALUE); + } else if (isNilableClassType(at)) { + ts->addFlag(FLAG_TYPE_DEFAULT_VALUE); + } else { + // Try resolving a test init() to set the flags + FnSymbol* initZero = findZeroArgInitFn(at); + if (initZero == NULL) { + ts->addFlag(FLAG_TYPE_NO_DEFAULT_VALUE); + } else if (initZero->hasFlag(FLAG_COMPILER_GENERATED)) { + if (recordContainsNonNilable(at)) + ts->addFlag(FLAG_TYPE_NO_DEFAULT_VALUE); + else + ts->addFlag(FLAG_TYPE_DEFAULT_VALUE); + } else { + ts->addFlag(FLAG_TYPE_DEFAULT_VALUE); + } + } + } +} static Expr* preFoldPrimOp(CallExpr* call) { Expr* retval = NULL; @@ -857,136 +963,90 @@ static Expr* preFoldPrimOp(CallExpr* call) { break; } case PRIM_IS_COPYABLE: - case PRIM_IS_CONST_COPYABLE: - case PRIM_IS_ASSIGNABLE: - case PRIM_IS_CONST_ASSIGNABLE: - case PRIM_HAS_DEFAULT_VALUE: { + case PRIM_IS_CONST_COPYABLE: { Type* t = call->get(1)->typeInfo(); bool val = true; if (isRecord(t)) { AggregateType* at = toAggregateType(t); + TypeSymbol* ts = at->symbol; - bool isNonNilableOwned = isManagedPtrType(t) && - getManagedPtrManagerType(t) == dtOwned && - isNonNilableClassType(t); + setRecordCopyableFlags(at); - TypeSymbol* ts = t->symbol; - if (!ts->hasFlag(FLAG_TYPE_ASSIGN_FROM_CONST) && - !ts->hasFlag(FLAG_TYPE_ASSIGN_FROM_REF) && - !ts->hasFlag(FLAG_TYPE_ASSIGN_MISSING)) { + if (call->isPrimitive(PRIM_IS_COPYABLE)) + val = ts->hasFlag(FLAG_TYPE_INIT_EQUAL_FROM_CONST) || + ts->hasFlag(FLAG_TYPE_INIT_EQUAL_FROM_REF); + else if (call->isPrimitive(PRIM_IS_CONST_COPYABLE)) + val = ts->hasFlag(FLAG_TYPE_INIT_EQUAL_FROM_CONST); + else + INT_FATAL("not handled"); - if (isNonNilableOwned) { - ts->addFlag(FLAG_TYPE_ASSIGN_MISSING); - } else { - // Try resolving a test = to set the flags - FnSymbol* assign = findAssignFn(at); - if (assign == NULL) { - ts->addFlag(FLAG_TYPE_ASSIGN_MISSING); - } else if (assign->hasFlag(FLAG_COMPILER_GENERATED)) { - if (recordContainsNonNilableOwned(t)) - ts->addFlag(FLAG_TYPE_ASSIGN_MISSING); - else if (recordContainsOwned(t)) - ts->addFlag(FLAG_TYPE_ASSIGN_FROM_REF); - else - ts->addFlag(FLAG_TYPE_ASSIGN_FROM_CONST); - } else { - // formals are lhs, rhs - ArgSymbol* rhs = assign->getFormal(2); - IntentTag intent = concreteIntentForArg(rhs); - if (intent == INTENT_IN || - intent == INTENT_CONST_IN || - intent == INTENT_CONST_REF) { - ts->addFlag(FLAG_TYPE_ASSIGN_FROM_CONST); - } else { - // this case includes INTENT_REF_MAYBE_CONST - ts->addFlag(FLAG_TYPE_ASSIGN_FROM_REF); - } - } - } - } - if (!ts->hasFlag(FLAG_TYPE_INIT_EQUAL_FROM_CONST) && - !ts->hasFlag(FLAG_TYPE_INIT_EQUAL_FROM_REF) && - !ts->hasFlag(FLAG_TYPE_INIT_EQUAL_MISSING)) { + } else { + // Non-record types are always copyable from const + val = true; + } - if (isNonNilableOwned) { - ts->addFlag(FLAG_TYPE_INIT_EQUAL_MISSING); - } else { - // Try resolving a test init= to set the flags - FnSymbol* initEq = findCopyInitFn(at); - if (initEq == NULL) { - ts->addFlag(FLAG_TYPE_INIT_EQUAL_MISSING); - } else if (initEq->hasFlag(FLAG_COMPILER_GENERATED)) { - if (recordContainsNonNilableOwned(t)) - ts->addFlag(FLAG_TYPE_INIT_EQUAL_MISSING); - else if (recordContainsOwned(t)) - ts->addFlag(FLAG_TYPE_INIT_EQUAL_FROM_REF); - else - ts->addFlag(FLAG_TYPE_INIT_EQUAL_FROM_CONST); - } else { - // formals are mt, this, other - ArgSymbol* other = initEq->getFormal(3); - IntentTag intent = concreteIntentForArg(other); - if (intent == INTENT_IN || - intent == INTENT_CONST_IN || - intent == INTENT_CONST_REF) { - ts->addFlag(FLAG_TYPE_INIT_EQUAL_FROM_CONST); - } else { - // this case includes INTENT_REF_MAYBE_CONST - ts->addFlag(FLAG_TYPE_INIT_EQUAL_FROM_REF); - } - } - } - } + if (val) + retval = new SymExpr(gTrue); + else + retval = new SymExpr(gFalse); - if (!ts->hasFlag(FLAG_TYPE_DEFAULT_VALUE) && - !ts->hasFlag(FLAG_TYPE_NO_DEFAULT_VALUE)) { + call->replace(retval); - if (isNonNilableClassType(t)) { - ts->addFlag(FLAG_TYPE_NO_DEFAULT_VALUE); - } else if (isNilableClassType(t)) { - ts->addFlag(FLAG_TYPE_DEFAULT_VALUE); - } else { - // Try resolving a test init() to set the flags - FnSymbol* initZero = findZeroArgInitFn(at); - if (initZero == NULL) { - ts->addFlag(FLAG_TYPE_NO_DEFAULT_VALUE); - } else if (initZero->hasFlag(FLAG_COMPILER_GENERATED)) { - if (recordContainsNonNilable(t)) - ts->addFlag(FLAG_TYPE_NO_DEFAULT_VALUE); - else - ts->addFlag(FLAG_TYPE_DEFAULT_VALUE); - } else { - ts->addFlag(FLAG_TYPE_DEFAULT_VALUE); - } - } - } + break; + } + case PRIM_IS_ASSIGNABLE: + case PRIM_IS_CONST_ASSIGNABLE: { + Type* t = call->get(1)->typeInfo(); + bool val = true; + if (isRecord(t)) { + AggregateType* at = toAggregateType(t); + TypeSymbol* ts = at->symbol; - if (call->isPrimitive(PRIM_IS_COPYABLE)) - val = ts->hasFlag(FLAG_TYPE_INIT_EQUAL_FROM_CONST) || - ts->hasFlag(FLAG_TYPE_INIT_EQUAL_FROM_REF); - else if (call->isPrimitive(PRIM_IS_CONST_COPYABLE)) - val = ts->hasFlag(FLAG_TYPE_INIT_EQUAL_FROM_CONST); - else if (call->isPrimitive(PRIM_IS_ASSIGNABLE)) + setRecordAssignableFlags(at); + + if (call->isPrimitive(PRIM_IS_ASSIGNABLE)) val = ts->hasFlag(FLAG_TYPE_ASSIGN_FROM_CONST) || ts->hasFlag(FLAG_TYPE_ASSIGN_FROM_REF); else if (call->isPrimitive(PRIM_IS_CONST_ASSIGNABLE)) val = ts->hasFlag(FLAG_TYPE_ASSIGN_FROM_CONST); - else if (call->isPrimitive(PRIM_HAS_DEFAULT_VALUE)) - val = ts->hasFlag(FLAG_TYPE_DEFAULT_VALUE); else INT_FATAL("not handled"); } else { - // Non-record types are always copyable/assignable from const - // and almost always default initializeable + // Non-record types are always assignable from const + val = true; + } + + if (val) + retval = new SymExpr(gTrue); + else + retval = new SymExpr(gFalse); + + call->replace(retval); + + break; + } + case PRIM_HAS_DEFAULT_VALUE: { + Type* t = call->get(1)->typeInfo(); + + bool val = true; + if (isRecord(t)) { + AggregateType* at = toAggregateType(t); + TypeSymbol* ts = at->symbol; + + setRecordDefaultValueFlags(at); + if (call->isPrimitive(PRIM_HAS_DEFAULT_VALUE)) - val = !isNonNilableClassType(t); + val = ts->hasFlag(FLAG_TYPE_DEFAULT_VALUE); else - val = true; - } + INT_FATAL("not handled"); + } else { + // non-nilable class types have no default value + val = !isNonNilableClassType(t); + } if (val) retval = new SymExpr(gTrue); diff --git a/compiler/resolution/tuples.cpp b/compiler/resolution/tuples.cpp index 32e6a008d3dc..7cbb845bfc24 100644 --- a/compiler/resolution/tuples.cpp +++ b/compiler/resolution/tuples.cpp @@ -373,6 +373,7 @@ TupleInfo getTupleInfo(std::vector& args, // Create the TypeSymbol TypeSymbol* newTypeSymbol = new TypeSymbol(name.c_str(), newType); newTypeSymbol->cname = astr(cname.c_str()); + newTypeSymbol->instantiationPoint = instantiationPoint; // Set appropriate flags on the new TypeSymbol newTypeSymbol->addFlag(FLAG_ALLOW_REF); diff --git a/compiler/util/Makefile.share b/compiler/util/Makefile.share index 0ceba1b5c7ec..12849d6b20a6 100644 --- a/compiler/util/Makefile.share +++ b/compiler/util/Makefile.share @@ -16,6 +16,7 @@ # limitations under the License. UTIL_SRCS = \ + astlocs.cpp \ exprAnalysis.cpp \ files.cpp \ misc.cpp \ diff --git a/compiler/util/astlocs.cpp b/compiler/util/astlocs.cpp new file mode 100644 index 000000000000..64a0d0246c28 --- /dev/null +++ b/compiler/util/astlocs.cpp @@ -0,0 +1,185 @@ +/* + * Copyright 2004-2020 Hewlett Packard Enterprise Development LP + * Other additional copyright holders may be indicated within. + * + * The entirety of this work is licensed under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "astlocs.h" + +#include "stringutil.h" +#include "expr.h" +#include "stmt.h" + +#include + +astlocT currentAstLoc(0,NULL); + +/************************************* | ************************************** +* * +* Definitions for astlocMarker * +* * +************************************** | *************************************/ + +// constructor, invoked upon SET_LINENO +astlocMarker::astlocMarker(astlocT newAstLoc) + : previousAstLoc(currentAstLoc) +{ + //previousAstLoc = currentAstLoc; + currentAstLoc = newAstLoc; +} + +// constructor, for special occasions +astlocMarker::astlocMarker(int lineno, const char* filename) + : previousAstLoc(currentAstLoc) +{ + currentAstLoc.lineno = lineno; + currentAstLoc.filename = astr(filename); +} + +// destructor, invoked upon leaving SET_LINENO's scope +astlocMarker::~astlocMarker() { + currentAstLoc = previousAstLoc; +} + +/************************************* | ************************************** +* * +* Functions for finding AST locations * +* * +************************************** | *************************************/ + + +// find an AST location that is: +// not in an inlined function or a task function in an inlined function +// in non-user modules +// (assuming preserveInlinedLineNumbers==false) +// to use for line number reporting. +Expr* findLocationIgnoringInternalInlining(Expr* cur) { + + while (true) { + if (cur == NULL || cur->parentSymbol == NULL) + return cur; + + FnSymbol* curFn = cur->getFunction(); + // If we didn't find a function, or it's not in tree, give up + if (curFn == NULL || curFn->inTree() == false) + return cur; + + // If it's already in user code, use that, because + // the line number is probably better + if (curFn->getModule()->modTag == MOD_USER) + return cur; + + bool inlined = curFn->hasFlag(FLAG_INLINED_FN); + + if (inlined == false || preserveInlinedLineNumbers) + return cur; + + // Look for a call to that function + for_SymbolSymExprs(se, curFn) { + CallExpr* call = toCallExpr(se->parentExpr); + if (se == call->baseExpr) { + // Switch to considering that call point + cur = call; + break; + } + } + } + + return cur; // never reached +} + +bool printsUserLocation(const BaseAST* astIn) { + BaseAST* ast = const_cast(astIn); + + if (Expr* expr = toExpr(ast)) { + Expr* foundExpr = findLocationIgnoringInternalInlining(expr); + if (foundExpr != NULL) + ast = foundExpr; + } + + ModuleSymbol* mod = NULL; + if (ast) + mod = ast->getModule(); + + return (ast && mod && mod->modTag == MOD_USER); +} + +static SymExpr* findMentionOfSymbol(BlockStmt* block, Symbol* sym) { + std::vector symExprs; + collectSymExprsFor(block, sym, symExprs); + for_vector(SymExpr, se, symExprs) { + if (se->symbol() == sym) { + return se; + } + } + return NULL; +} + +astlocT getUserInstantiationPoint(const BaseAST* ast) { + + // TODO - call this function for instantiating AggregateType and FnSymbol + + BaseAST* cur = const_cast(ast); + + while (true) { + + // Stop if this is already a user location + if (printsUserLocation(cur)) break; + + BaseAST* last = cur; + + if (DefExpr* d = toDefExpr(cur)) { + // Continue with the defined symbol + if (d->sym != NULL) + cur = d->sym; + } else if (Expr* e = toExpr(cur)) { + // Continue with the expression's parent function + FnSymbol* parentFn = e->getFunction(); + if (parentFn != NULL) + cur = parentFn; + } else if (FnSymbol* fn = toFnSymbol(cur)) { + // Find the first call to the function within the instantiation point, + // so that we can have a better error message line number. + BlockStmt* instantiationPoint = fn->instantiationPoint(); + + if (instantiationPoint != NULL) { + SymExpr* mention = findMentionOfSymbol(instantiationPoint, fn); + if (mention != NULL) + cur = mention; + else + cur = instantiationPoint; + } + } else if (TypeSymbol* ts = toTypeSymbol(cur)) { + // Find the first use of the TypeSymbol at the type's instantation point + // so we can have a better error message line number. + BlockStmt* instantiationPoint = ts->instantiationPoint; + + if (instantiationPoint != NULL) { + SymExpr* mention = findMentionOfSymbol(instantiationPoint, ts); + if (mention != NULL) + cur = mention; + else + cur = instantiationPoint; + } + } + + // Stop if we've made no progress + if (last == cur) break; + } + + INT_ASSERT(cur != NULL); + return cur->astloc; +} diff --git a/compiler/util/misc.cpp b/compiler/util/misc.cpp index 4d06c0c31a7d..acbf5b27bd68 100644 --- a/compiler/util/misc.cpp +++ b/compiler/util/misc.cpp @@ -219,59 +219,6 @@ static void print_user_internal_error() { print_error("chpl version %s", version); } - -// find an AST location that is: -// not in an inlined function or a task function in an inlined function -// in non-user modules -// (assuming preserveInlinedLineNumbers==false) -// to use for line number reporting. -static Expr* findLocationIgnoringInternalInlining(Expr* cur) { - - while (true) { - if (cur == NULL || cur->parentSymbol == NULL) - return cur; - - FnSymbol* curFn = cur->getFunction(); - // If we didn't find a function, or it's not in tree, give up - if (curFn == NULL || curFn->inTree() == false) - return cur; - - // If it's already in user code, use that, because - // the line number is probably better - if (curFn->getModule()->modTag == MOD_USER) - return cur; - - bool inlined = curFn->hasFlag(FLAG_INLINED_FN); - - if (inlined == false || preserveInlinedLineNumbers) - return cur; - - // Look for a call to that function - for_SymbolSymExprs(se, curFn) { - CallExpr* call = toCallExpr(se->parentExpr); - if (se == call->baseExpr) { - // Switch to considering that call point - cur = call; - break; - } - } - } - - return cur; // never reached -} - -bool printsUserLocation(const BaseAST* astIn) { - BaseAST* ast = const_cast(astIn); - - if (Expr* expr = toExpr(ast)) { - Expr* foundExpr = findLocationIgnoringInternalInlining(expr); - if (foundExpr != NULL) - ast = foundExpr; - } - - return (ast && ast->getModule()->modTag == MOD_USER); -} - // find a caller (direct or not) that is not in a task function, // for line number reporting static FnSymbol* findNonTaskCaller(FnSymbol* fn) { @@ -353,7 +300,7 @@ static void printInstantiationNoteForLastError() { err_fn = NULL; } -static bool printErrorHeader(BaseAST* ast) { +static bool printErrorHeader(BaseAST* ast, astlocT astloc) { if (Expr* expr = toExpr(ast)) { Expr* use = findLocationIgnoringInternalInlining(expr); @@ -430,6 +377,10 @@ static bool printErrorHeader(BaseAST* ast) { have_ast_line = true; filename = cleanFilename(ast); linenum = ast->linenum(); + } else if ( astloc.filename != NULL) { + have_ast_line = true; + filename = cleanFilename(astloc.filename); + linenum = astloc.lineno; } else { have_ast_line = false; if ( !err_print && currentAstLoc.filename && currentAstLoc.lineno > 0 ) { @@ -579,69 +530,54 @@ void printCallStackCalls() { printf("\n"); } +/************************************* | ************************************** +* * +* * +* * +************************************** | *************************************/ -void handleError(const char* fmt, ...) { - fflush(stdout); - fflush(stderr); +static void vhandleError(const BaseAST* ast, + astlocT astloc, + const char* fmt, + va_list args); - if (err_ignore) { - return; - } +void handleError(const char *fmt, ...) { + astlocT astloc(0, NULL); - bool guess = printErrorHeader(NULL); + va_list args; - // - // Only print out the arguments if this is a user error or we're - // in developer mode. - // - if (err_user || developer) { - va_list args; - va_start(args, fmt); - vprint_error(fmt, args); - va_end(args); - } + va_start(args, fmt); - printErrorFooter(guess); - print_error("\n"); + vhandleError(NULL, astloc, fmt, args); - printCallStackOnError(); + va_end(args); +} - if (!err_user && !developer) { - return; - } +void handleError(const BaseAST* ast, const char *fmt, ...) { + astlocT astloc(0, NULL); - if (exit_immediately) { - if (ignore_errors_for_pass) { - exit_end_of_pass = true; - } else if (!ignore_errors && !(ignore_user_errors && err_user)) { - printInstantiationNoteForLastError(); - clean_exit(1); - } - } -} + va_list args; + va_start(args, fmt); -/************************************* | ************************************** -* * -* * -* * -************************************** | *************************************/ + vhandleError(ast, astloc, fmt, args); -static void vhandleError(const BaseAST* ast, - const char* fmt, - va_list args); + va_end(args); +} -void handleError(const BaseAST* ast, const char *fmt, ...) { +void handleError(astlocT astloc, const char *fmt, ...) { va_list args; va_start(args, fmt); - vhandleError(ast, fmt, args); + vhandleError(NULL, astloc, fmt, args); va_end(args); } + static void vhandleError(const BaseAST* ast, + astlocT astloc, const char* fmt, va_list args) { if (err_ignore) { @@ -650,8 +586,12 @@ static void vhandleError(const BaseAST* ast, bool guess = false; - guess = printErrorHeader(const_cast(ast)); + guess = printErrorHeader(const_cast(ast), astloc); + // + // Only print out the arguments if this is a user error or we're + // in developer mode. + // if (err_user || developer) { vprint_error(fmt, args); } diff --git a/compiler/util/stringutil.cpp b/compiler/util/stringutil.cpp index c040bce3e921..f5868fd2c2d9 100644 --- a/compiler/util/stringutil.cpp +++ b/compiler/util/stringutil.cpp @@ -173,9 +173,8 @@ void deleteStrings() { } \ if (strcmp(str+startPos, checkStr) != 0) { \ if (userSupplied) { \ - VarSymbol* lineTemp = createASTforLineNumber(filename, \ - line); \ - USR_FATAL(lineTemp, "Integer literal overflow: %s is too" \ + astlocT astloc(line, filename); \ + USR_FATAL(astloc, "Integer literal overflow: %s is too" \ " big for type " #type, str); \ } else { \ INT_FATAL("Integer literal overflow: %s is too " \ @@ -212,8 +211,8 @@ uint64_t binStr2uint64(const char* str, bool userSupplied, } if (strlen(str+startPos) > 64) { if (userSupplied) { - VarSymbol* lineTemp = createASTforLineNumber(filename, line); - USR_FATAL(lineTemp, "Integer literal overflow: '%s' is too big " + astlocT astloc(line, filename); + USR_FATAL(astloc, "Integer literal overflow: '%s' is too big " "for type uint64", str); } else { INT_FATAL("Integer literal overflow: '%s' is too big " @@ -254,8 +253,8 @@ uint64_t octStr2uint64(const char* str, bool userSupplied, if (len-startPos > 22 || (len-startPos == 22 && str[startPos] != '1')) { if (userSupplied) { - VarSymbol* lineTemp = createASTforLineNumber(filename, line); - USR_FATAL(lineTemp, "Integer literal overflow: '%s' is too big " + astlocT astloc(line, filename); + USR_FATAL(astloc, "Integer literal overflow: '%s' is too big " "for type uint64", str); } else { INT_FATAL("Integer literal overflow: '%s' is too big " @@ -289,8 +288,8 @@ uint64_t hexStr2uint64(const char* str, bool userSupplied, if (strlen(str+startPos) > 16) { if (userSupplied) { - VarSymbol* lineTemp = createASTforLineNumber(filename, line); - USR_FATAL(lineTemp, "Integer literal overflow: '%s' is too big " + astlocT astloc(line, filename); + USR_FATAL(astloc, "Integer literal overflow: '%s' is too big " "for type uint64", str); } else { INT_FATAL("Integer literal overflow: '%s' is too big " diff --git a/modules/internal/ChapelArray.chpl b/modules/internal/ChapelArray.chpl index fbaf394deaeb..ac27633751dd 100644 --- a/modules/internal/ChapelArray.chpl +++ b/modules/internal/ChapelArray.chpl @@ -4016,14 +4016,25 @@ module ChapelArray { } pragma "init copy fn" - proc chpl__initCopy(const ref a: []) { + proc chpl__initCopy(a: []) { var b : [a._dom] a.eltType; + if !isConstAssignableType(a.eltType) { + if !isAssignableType(a.eltType) { + compilerError("Cannot copy array with element type that cannot be assigned"); + } else { + // Make sure this function uses ref argument intent for a + // TODO: use a where clause instead to make this less strange + // provided that does not significantly slow compiles + ref r = a; + } + } + chpl__uncheckedArrayTransfer(b, a); return b; } - pragma "auto copy fn" proc chpl__autoCopy(const ref x: []) { + pragma "auto copy fn" proc chpl__autoCopy(x: []) { pragma "no copy" var b = chpl__initCopy(x); return b; } diff --git a/modules/internal/ChapelBase.chpl b/modules/internal/ChapelBase.chpl index 2132e981e330..23198f9d7b66 100644 --- a/modules/internal/ChapelBase.chpl +++ b/modules/internal/ChapelBase.chpl @@ -1605,8 +1605,9 @@ module ChapelBase { } pragma "init copy fn" - inline proc chpl__initCopy(type t) { + inline proc chpl__initCopy(type t) type { compilerError("illegal assignment of type to value"); + return t; } pragma "compiler generated" diff --git a/test/arrays/ferguson/array-of-owned-error1.chpl b/test/arrays/ferguson/array-of-owned-error1.chpl new file mode 100644 index 000000000000..46774a9eda79 --- /dev/null +++ b/test/arrays/ferguson/array-of-owned-error1.chpl @@ -0,0 +1,12 @@ +class C { var x: int; } + +proc id(arg) { + return arg; +} + +proc main () { + var tmp:[1..2] owned C? = for i in 1..2 do new owned C(i); + var a:[1..2] owned C = tmp:owned C; + id(a); + writeln(a); +} diff --git a/test/arrays/ferguson/array-of-owned-error1.good b/test/arrays/ferguson/array-of-owned-error1.good new file mode 100644 index 000000000000..659971550b9d --- /dev/null +++ b/test/arrays/ferguson/array-of-owned-error1.good @@ -0,0 +1,4 @@ +array-of-owned-error1.chpl:3: In function 'id': +array-of-owned-error1.chpl:4: error: invalid implicit copy-initialization +array-of-owned-error1.chpl:4: error: Cannot copy array with element type that cannot be assigned +array-of-owned-error1.chpl:9: note: [domain(1,int(64),false)] owned C does not have a valid init= diff --git a/test/arrays/ferguson/array-of-owned-error2.chpl b/test/arrays/ferguson/array-of-owned-error2.chpl new file mode 100644 index 000000000000..6cc5f30ff741 --- /dev/null +++ b/test/arrays/ferguson/array-of-owned-error2.chpl @@ -0,0 +1,9 @@ +class C { var x: int; } + +proc main () { + var tmp:[1..2] owned C? = for i in 1..2 do new owned C(i); + var a:[1..2] owned C = tmp:owned C; + var b = a; + writeln(a); + writeln(b); +} diff --git a/test/arrays/ferguson/array-of-owned-error2.good b/test/arrays/ferguson/array-of-owned-error2.good new file mode 100644 index 000000000000..2871f4022098 --- /dev/null +++ b/test/arrays/ferguson/array-of-owned-error2.good @@ -0,0 +1,4 @@ +array-of-owned-error2.chpl:3: In function 'main': +array-of-owned-error2.chpl:6: error: invalid implicit copy-initialization +array-of-owned-error2.chpl:6: error: Cannot copy array with element type that cannot be assigned +array-of-owned-error2.chpl:5: note: [domain(1,int(64),false)] owned C does not have a valid init= diff --git a/test/arrays/ferguson/array-of-owned-no-error.chpl b/test/arrays/ferguson/array-of-owned-no-error.chpl new file mode 100644 index 000000000000..e59c8940dee3 --- /dev/null +++ b/test/arrays/ferguson/array-of-owned-no-error.chpl @@ -0,0 +1,8 @@ +class C { var x: int; } + +proc main () { + var tmp:[1..2] owned C? = for i in 1..2 do new owned C(i); + var a:[1..2] owned C = tmp:owned C; + writeln(a.eltType:string); + writeln(a); +} diff --git a/test/arrays/ferguson/array-of-owned-no-error.good b/test/arrays/ferguson/array-of-owned-no-error.good new file mode 100644 index 000000000000..d79473df9b8e --- /dev/null +++ b/test/arrays/ferguson/array-of-owned-no-error.good @@ -0,0 +1,2 @@ +owned C +{x = 1} {x = 2} diff --git a/test/arrays/ferguson/array-of-owned-return.bad b/test/arrays/ferguson/array-of-owned-return.bad new file mode 100644 index 000000000000..d16f5beaf870 --- /dev/null +++ b/test/arrays/ferguson/array-of-owned-return.bad @@ -0,0 +1,4 @@ +array-of-owned-return.chpl:9: In function 'main': +array-of-owned-return.chpl:10: error: invalid implicit copy-initialization +array-of-owned-return.chpl:10: error: Cannot copy array with element type that cannot be assigned +array-of-owned-return.chpl:5: note: [domain(1,int(64),false)] owned C does not have a valid init= diff --git a/test/arrays/ferguson/array-of-owned-return.chpl b/test/arrays/ferguson/array-of-owned-return.chpl new file mode 100644 index 000000000000..0498c813f572 --- /dev/null +++ b/test/arrays/ferguson/array-of-owned-return.chpl @@ -0,0 +1,12 @@ +class C { var x: int; } + +proc makeArrayOwned() { + var A: [1..3] owned C? = for i in 1..3 do new owned C?(i); + var B: [1..3] owned C = A:owned C; + return B; +} + +proc main() { + var arr = makeArrayOwned(); + writeln(arr); +} diff --git a/test/arrays/ferguson/array-of-owned-return.future b/test/arrays/ferguson/array-of-owned-return.future new file mode 100644 index 000000000000..353fcea8b63f --- /dev/null +++ b/test/arrays/ferguson/array-of-owned-return.future @@ -0,0 +1,2 @@ +bug: should be able to return array of owned +#14896 diff --git a/test/arrays/ferguson/array-of-owned-return.good b/test/arrays/ferguson/array-of-owned-return.good new file mode 100644 index 000000000000..cd3ca1e64a8d --- /dev/null +++ b/test/arrays/ferguson/array-of-owned-return.good @@ -0,0 +1 @@ +{x = 1} {x = 2} {x = 3} diff --git a/test/classes/delete-free/owned/make-owned-in-for-expr.chpl b/test/classes/delete-free/owned/make-owned-in-for-expr.chpl index a14e9da7982b..7e93b8bd11c5 100644 --- a/test/classes/delete-free/owned/make-owned-in-for-expr.chpl +++ b/test/classes/delete-free/owned/make-owned-in-for-expr.chpl @@ -1,5 +1,5 @@ class C { var x:int; } -var arr = for i in 1..3 do new owned C(i); +var arr = for i in 1..3 do new owned C?(i); writeln(arr); diff --git a/test/classes/initializers/constAndParamInHeader.chpl b/test/classes/initializers/constAndParamInHeader.chpl deleted file mode 100644 index b9f563922219..000000000000 --- a/test/classes/initializers/constAndParamInHeader.chpl +++ /dev/null @@ -1,25 +0,0 @@ - - -class Foo { - var value : int; - const SOME_CONST : uint = 100; - proc init(val = SOME_CONST) { - value = val; - } -} - -class Bar { - var value : int; - param SOME_CONST : uint = 100; - proc init(val = SOME_CONST) { - value = val; - } -} - -var objA = new shared Foo(); -var objB = new shared Bar(); - -writeln(objA.value); -writeln(objB.value); - - diff --git a/test/classes/initializers/constAndParamInHeader.good b/test/classes/initializers/constAndParamInHeader.good deleted file mode 100644 index e2daf16e3723..000000000000 --- a/test/classes/initializers/constAndParamInHeader.good +++ /dev/null @@ -1,4 +0,0 @@ -constAndParamInHeader.chpl:6: In initializer: -constAndParamInHeader.chpl:6: error: invalid access of class member in initializer argument list -constAndParamInHeader.chpl:14: In initializer: -constAndParamInHeader.chpl:14: error: invalid access of class member in initializer argument list diff --git a/test/classes/initializers/constInHeader.chpl b/test/classes/initializers/constInHeader.chpl new file mode 100644 index 000000000000..7ece8fdb5387 --- /dev/null +++ b/test/classes/initializers/constInHeader.chpl @@ -0,0 +1,11 @@ +class Foo { + var value : int; + const SOME_CONST : uint = 100; + proc init(val = SOME_CONST) { + value = val; + } +} + +var objA = new shared Foo(); + +writeln(objA.value); diff --git a/test/classes/initializers/constInHeader.good b/test/classes/initializers/constInHeader.good new file mode 100644 index 000000000000..37f3fc50ddfd --- /dev/null +++ b/test/classes/initializers/constInHeader.good @@ -0,0 +1,2 @@ +constInHeader.chpl:4: In initializer: +constInHeader.chpl:4: error: invalid access of class member in initializer argument list diff --git a/test/classes/initializers/paramInHeader1.chpl b/test/classes/initializers/paramInHeader1.chpl new file mode 100644 index 000000000000..0e43c91532c2 --- /dev/null +++ b/test/classes/initializers/paramInHeader1.chpl @@ -0,0 +1,11 @@ +class Bar { + var value : int; + param SOME_CONST : uint = 100; + proc init(val = SOME_CONST) { + value = val; + } +} + +var objB = new shared Bar(); + +writeln(objB.value); diff --git a/test/classes/initializers/paramInHeader1.good b/test/classes/initializers/paramInHeader1.good new file mode 100644 index 000000000000..6b6a4c12aec8 --- /dev/null +++ b/test/classes/initializers/paramInHeader1.good @@ -0,0 +1,2 @@ +paramInHeader1.chpl:4: In initializer: +paramInHeader1.chpl:4: error: invalid access of class member in initializer argument list diff --git a/test/classes/initializers/paramInHeader2.chpl b/test/classes/initializers/paramInHeader2.chpl new file mode 100644 index 000000000000..c25025da4e1a --- /dev/null +++ b/test/classes/initializers/paramInHeader2.chpl @@ -0,0 +1,11 @@ +class Bar { + var value : int; + param SOME_CONST : uint = 100; + proc init(val = this.type.SOME_CONST) { + value = val; + } +} + +var objB = new shared Bar(); + +writeln(objB.value); diff --git a/test/classes/initializers/paramInHeader2.good b/test/classes/initializers/paramInHeader2.good new file mode 100644 index 000000000000..c44b5e099be4 --- /dev/null +++ b/test/classes/initializers/paramInHeader2.good @@ -0,0 +1,2 @@ +paramInHeader2.chpl:4: In initializer: +paramInHeader2.chpl:4: error: invalid access of class member in initializer argument list diff --git a/test/release/examples/benchmarks/shootout/chameneosredux.chpl b/test/release/examples/benchmarks/shootout/chameneosredux.chpl index 07934afc2a8d..3f3188870b8d 100644 --- a/test/release/examples/benchmarks/shootout/chameneosredux.chpl +++ b/test/release/examples/benchmarks/shootout/chameneosredux.chpl @@ -64,7 +64,7 @@ record Population { // an array of chameneos objects representing the population // var chameneos = [i in 1..size] - new owned Chameneos(i, if size == 10 then colors10[i] + new owned Chameneos?(i, if size == 10 then colors10[i] else ((i-1)%3): Color); // @@ -72,7 +72,7 @@ record Population { // proc printColors() { for c in chameneos do - write(" ", c.color); + write(" ", c!.color); writeln(); } @@ -84,7 +84,7 @@ record Population { const place = new MeetingPlace(numMeetings); coforall c in chameneos do // create a task per chameneos - c.haveMeetings(place, chameneos); + c!.haveMeetings(place, chameneos); } // @@ -95,11 +95,11 @@ record Population { // proc printNotes() { for c in chameneos { - write(c.meetings); - spellInt(c.meetingsWithSelf); + write(c!.meetings); + spellInt(c!.meetingsWithSelf); } - spellInt(+ reduce chameneos.meetings); + spellInt(+ reduce chameneos!.meetings); writeln(); } } @@ -149,7 +149,7 @@ class Chameneos { if firstToArrive then waitForMeetingToEnd(); else - meetWith(chameneos[peerID]); + meetWith(chameneos[peerID]!); } } } while (meetingsLeft); diff --git a/test/studies/shootout/chameneos-redux/bradc/chameneosredux-blc.chpl b/test/studies/shootout/chameneos-redux/bradc/chameneosredux-blc.chpl index 4759fb5318e8..d213609dbc83 100644 --- a/test/studies/shootout/chameneos-redux/bradc/chameneosredux-blc.chpl +++ b/test/studies/shootout/chameneos-redux/bradc/chameneosredux-blc.chpl @@ -53,7 +53,7 @@ record Population { // construct the population in terms of an array of colors passed in // proc init(colors: [] Color) { - chameneos = [i in colors.domain] new owned Chameneos(i, colors[i]); + chameneos = [i in colors.domain] new owned Chameneos?(i, colors[i]); } // @@ -64,7 +64,7 @@ record Population { const place = new MeetingPlace(numMeetings); coforall c in chameneos do // create a task per chameneos - c.haveMeetings(place, chameneos); + c!.haveMeetings(place, chameneos); } // @@ -74,15 +74,15 @@ record Population { // proc print() { for c in chameneos do - write(" ", c.initialColor); + write(" ", c!.initialColor); writeln(); for c in chameneos { - write(c.meetings); - spellInt(c.meetingsWithSelf); + write(c!.meetings); + spellInt(c!.meetingsWithSelf); } - spellInt(+ reduce chameneos.meetings); + spellInt(+ reduce chameneos!.meetings); writeln(); } } @@ -133,7 +133,7 @@ class Chameneos { if firstToArrive then waitForMeetingToEnd(); else - meetWith(chameneos[peerID]); + meetWith(chameneos[peerID]!); } } } while (meetingsLeft); diff --git a/test/studies/shootout/chameneos-redux/hannah/chameneos-redux-cas.chpl b/test/studies/shootout/chameneos-redux/hannah/chameneos-redux-cas.chpl index 3dd478c7f1b2..ec3ccdca7010 100644 --- a/test/studies/shootout/chameneos-redux/hannah/chameneos-redux-cas.chpl +++ b/test/studies/shootout/chameneos-redux/hannah/chameneos-redux-cas.chpl @@ -86,7 +86,7 @@ class Chameneos { with another Chameneos. If it does, it will get the other's color and use this color and its own to compute the color both will have after the meeting has ended, setting both of their colors to this value. */ - proc start(population : [] owned Chameneos, meetingPlace: MeetingPlace) { + proc start(population : [] owned Chameneos?, meetingPlace: MeetingPlace) { var stateTemp : uint(32); var peer_idx : uint(32); var xchg : uint(32); @@ -111,7 +111,7 @@ class Chameneos { is_same = 1; halt("halt: chameneos met with self"); } - const peer = population[peer_idx:int(32)].borrow(); + const peer = population[peer_idx:int(32)]!; newColor = getComplement(color, peer.color); peer.color = newColor; peer.meetings += 1; @@ -155,7 +155,7 @@ proc populate (size : int(32)) { Color.yellow, Color.blue, Color.red, Color.yellow, Color.red, Color.blue); const D : domain(1, int(32)) = {1..size}; - var population : [D] owned Chameneos; + var population : [D] owned Chameneos?; if (size == 10) { for i in D { @@ -174,26 +174,26 @@ proc populate (size : int(32)) { met another Chameneos, spells out the number of times it met with itself, then spells out the total number of times all the Chameneos met another Chameneos. */ -proc run(population : [] owned Chameneos, meetingPlace : MeetingPlace) { +proc run(population : [] owned Chameneos?, meetingPlace : MeetingPlace) { for i in population { - write(" ", i.color); + write(" ", i!.color); } writeln(); coforall i in population { - i.start(population, meetingPlace); + i!.start(population, meetingPlace); } meetingPlace.reset(); } -proc runQuiet(population : [] owned Chameneos, meetingPlace : MeetingPlace) { +proc runQuiet(population : [] owned Chameneos?, meetingPlace : MeetingPlace) { coforall i in population { - i.start(population, meetingPlace); + i!.start(population, meetingPlace); } meetingPlace.reset(); - const totalMeetings = + reduce population.meetings; - const totalMeetingsWithSelf = + reduce population.meetingsWithSelf; + const totalMeetings = + reduce population!.meetings; + const totalMeetingsWithSelf = + reduce population!.meetingsWithSelf; if (totalMeetings == numMeetings*2) { writeln("total meetings PASS"); } else { @@ -209,12 +209,12 @@ proc runQuiet(population : [] owned Chameneos, meetingPlace : MeetingPlace) { writeln(); } -proc printInfo(population : [] owned Chameneos) { +proc printInfo(population : [] owned Chameneos?) { for i in population { - write(i.meetings); - spellInt(i.meetingsWithSelf); + write(i!.meetings); + spellInt(i!.meetingsWithSelf); } - const totalMeetings = + reduce population.meetings; + const totalMeetings = + reduce population!.meetings; spellInt(totalMeetings); writeln(); } diff --git a/test/studies/shootout/chameneos-redux/lydia/chameneos-redux-lydia.chpl b/test/studies/shootout/chameneos-redux/lydia/chameneos-redux-lydia.chpl index ce48711664a8..262a53cfa233 100644 --- a/test/studies/shootout/chameneos-redux/lydia/chameneos-redux-lydia.chpl +++ b/test/studies/shootout/chameneos-redux/lydia/chameneos-redux-lydia.chpl @@ -86,7 +86,7 @@ class Chameneos { with another Chameneos. If it does, it will get the other's color and use this color and its own to compute the color both will have after the meeting has ended, setting both of their colors to this value. */ - proc start(population : [] owned Chameneos, meetingPlace: MeetingPlace) { + proc start(population : [] owned Chameneos?, meetingPlace: MeetingPlace) { var stateTemp, peer_idx, xchg : int; stateTemp = meetingPlace.state.read(memoryOrder.acquire); @@ -119,13 +119,13 @@ class Chameneos { /* Given the id of its peer, finds and updates the data of its peer and itself */ - proc runMeeting (population : [] owned Chameneos, peer_idx) { + proc runMeeting (population : [] owned Chameneos?, peer_idx) { var newColor : Color; var is_same : int; if (id == peer_idx) { is_same = 1; } - const peer = population[peer_idx:int(32)].borrow(); + const peer = population[peer_idx:int(32)]!; newColor = getComplement(color, peer.color); peer.color = newColor; peer.meetings += 1; @@ -178,7 +178,7 @@ proc populate (size) { Color.yellow, Color.blue, Color.red, Color.yellow, Color.red, Color.blue); const D : domain(1, int) = {1..size}; - var population : [D] owned Chameneos; + var population : [D] owned Chameneos?; if (size == 10) { for i in D { @@ -197,25 +197,25 @@ proc populate (size) { met another Chameneos, spells out the number of times it met with itself, then spells out the total number of times all the Chameneos met another Chameneos. */ -proc run(population : [] owned Chameneos, meetingPlace : MeetingPlace) { +proc run(population : [] owned Chameneos?, meetingPlace : MeetingPlace) { for i in population { - write(" ", i.color); + write(" ", i!.color); } writeln(); coforall i in population { - i.start(population, meetingPlace); + i!.start(population, meetingPlace); } meetingPlace.reset(); } -proc printInfo(population : [] owned Chameneos) { +proc printInfo(population : [] owned Chameneos?) { for i in population { - write(i.meetings); - spellInt(i.meetingsWithSelf); + write(i!.meetings); + spellInt(i!.meetingsWithSelf); } - const totalMeetings = + reduce population.meetings; + const totalMeetings = + reduce population!.meetings; spellInt(totalMeetings); writeln(); } diff --git a/test/studies/shootout/chameneos-redux/lydia/chameneos-redux-waitFor.chpl b/test/studies/shootout/chameneos-redux/lydia/chameneos-redux-waitFor.chpl index 65de79da400f..14b63e3e87fd 100644 --- a/test/studies/shootout/chameneos-redux/lydia/chameneos-redux-waitFor.chpl +++ b/test/studies/shootout/chameneos-redux/lydia/chameneos-redux-waitFor.chpl @@ -82,7 +82,7 @@ class Chameneos { with another Chameneos. If it does, it will get the other's color and use this color and its own to compute the color both will have after the meeting has ended, setting both of their colors to this value. */ - proc start(population : [] owned Chameneos, meetingPlace: MeetingPlace) { + proc start(population : [] owned Chameneos?, meetingPlace: MeetingPlace) { var stateTemp, peer_idx, xchg : int; stateTemp = meetingPlace.state.read(memoryOrder.acquire); @@ -116,13 +116,13 @@ class Chameneos { /* Given the id of its peer, finds and updates the data of its peer and itself */ - proc runMeeting (population : [] owned Chameneos, peer_idx) { + proc runMeeting (population : [] owned Chameneos?, peer_idx) { var is_same : int; var newColor : Color; if (id == peer_idx) { is_same = 1; } - const peer = population[peer_idx:int(32)].borrow(); + const peer = population[peer_idx:int(32)]!; newColor = getComplement(color, peer.color); peer.color = newColor; peer.meetings += 1; @@ -155,7 +155,7 @@ proc populate (size : int) { Color.yellow, Color.blue, Color.red, Color.yellow, Color.red, Color.blue); const D : domain(1, int) = {1..size}; - var population : [D] owned Chameneos; + var population : [D] owned Chameneos?; if (size == 10) { for i in D { @@ -174,25 +174,25 @@ proc populate (size : int) { met another Chameneos, spells out the number of times it met with itself, then spells out the total number of times all the Chameneos met another Chameneos. */ -proc run(population : [] owned Chameneos, meetingPlace : MeetingPlace) { +proc run(population : [] owned Chameneos?, meetingPlace : MeetingPlace) { for i in population { - write(" ", i.color); + write(" ", i!.color); } writeln(); coforall i in population { - i.start(population, meetingPlace); + i!.start(population, meetingPlace); } meetingPlace.reset(); } -proc printInfo(population : [] owned Chameneos) { +proc printInfo(population : [] owned Chameneos?) { for i in population { - write(i.meetings); - spellInt(i.meetingsWithSelf); + write(i!.meetings); + spellInt(i!.meetingsWithSelf); } - const totalMeetings = + reduce population.meetings; + const totalMeetings = + reduce population!.meetings; spellInt(totalMeetings); writeln(); } diff --git a/test/studies/shootout/chameneos-redux/lydia/chameneos-redux-yield.chpl b/test/studies/shootout/chameneos-redux/lydia/chameneos-redux-yield.chpl index 7a4784838d7f..1d5b1d844905 100644 --- a/test/studies/shootout/chameneos-redux/lydia/chameneos-redux-yield.chpl +++ b/test/studies/shootout/chameneos-redux/lydia/chameneos-redux-yield.chpl @@ -84,7 +84,7 @@ class Chameneos { with another Chameneos. If it does, it will get the other's color and use this color and its own to compute the color both will have after the meeting has ended, setting both of their colors to this value. */ - proc start(population : [] owned Chameneos, meetingPlace: MeetingPlace) { + proc start(population : [] owned Chameneos?, meetingPlace: MeetingPlace) { var stateTemp, peer_idx, xchg: int; stateTemp = meetingPlace.state.read(memoryOrder.acquire); @@ -120,13 +120,13 @@ class Chameneos { /* Given the id of its peer, finds and updates the data of its peer and itself */ - proc runMeeting (population : [] owned Chameneos, peer_idx) { + proc runMeeting (population : [] owned Chameneos?, peer_idx) { var is_same : int; var newColor : Color; if (id == peer_idx) { is_same = 1; } - const peer = population[peer_idx:int(32)].borrow(); + const peer = population[peer_idx:int(32)]!; newColor = getComplement(color, peer.color); peer.color = newColor; peer.meetings += 1; @@ -164,7 +164,7 @@ proc populate (size : int) { for i in D do let ithColor = if size == 10 then colorsDefault10(i) else ((i-1) % numColors):Color - in new owned Chameneos(i, ithColor); + in new owned Chameneos?(i, ithColor); return population; } @@ -174,25 +174,25 @@ proc populate (size : int) { met another Chameneos, spells out the number of times it met with itself, then spells out the total number of times all the Chameneos met another Chameneos. */ -proc run(population : [] owned Chameneos, meetingPlace : MeetingPlace) { +proc run(population : [] owned Chameneos?, meetingPlace : MeetingPlace) { for i in population { - write(" ", i.color); + write(" ", i!.color); } writeln(); coforall i in population { - i.start(population, meetingPlace); + i!.start(population, meetingPlace); } meetingPlace.reset(); } -proc printInfo(population : [] owned Chameneos) { +proc printInfo(population : [] owned Chameneos?) { for i in population { - write(i.meetings); - spellInt(i.meetingsWithSelf); + write(i!.meetings); + spellInt(i!.meetingsWithSelf); } - const totalMeetings = + reduce (population.meetings); + const totalMeetings = + reduce (population!.meetings); spellInt(totalMeetings); writeln(); } diff --git a/test/types/records/ferguson/missing-functions/no-assign-error.chpl b/test/types/records/ferguson/missing-functions/no-assign-error.chpl new file mode 100644 index 000000000000..fa71d5c8fbc1 --- /dev/null +++ b/test/types/records/ferguson/missing-functions/no-assign-error.chpl @@ -0,0 +1,16 @@ +record R { + var x: int; +} + +// record opts out of being copyable +proc =(ref lhs: R, const ref rhs: R) { + compilerError("You can't assign an R"); +} + +proc main() { + var r = new R(1); + writeln(r); + + var r2 = new R(2); + r2 = r; +} diff --git a/test/types/records/ferguson/missing-functions/no-assign-error.good b/test/types/records/ferguson/missing-functions/no-assign-error.good new file mode 100644 index 000000000000..d79ab825b055 --- /dev/null +++ b/test/types/records/ferguson/missing-functions/no-assign-error.good @@ -0,0 +1,2 @@ +no-assign-error.chpl:10: In function 'main': +no-assign-error.chpl:15: error: You can't assign an R diff --git a/test/types/records/ferguson/missing-functions/no-assign-not-called.chpl b/test/types/records/ferguson/missing-functions/no-assign-not-called.chpl new file mode 100644 index 000000000000..de997f659d3f --- /dev/null +++ b/test/types/records/ferguson/missing-functions/no-assign-not-called.chpl @@ -0,0 +1,20 @@ +record R { + var x: int; +} + +// record opts out of being copyable +proc =(ref lhs: R, const ref rhs: R) { + compilerError("You can't assign an R"); +} + +proc main() { + var r = new R(1); + writeln(r); + + { + use Types; + + assert(isAssignable(R) == false); + assert(isConstAssignable(R) == false); + } +} diff --git a/test/types/records/ferguson/missing-functions/no-assign-not-called.good b/test/types/records/ferguson/missing-functions/no-assign-not-called.good new file mode 100644 index 000000000000..895683db0941 --- /dev/null +++ b/test/types/records/ferguson/missing-functions/no-assign-not-called.good @@ -0,0 +1 @@ +(x = 1) diff --git a/test/types/records/ferguson/missing-functions/no-copy-init-error.chpl b/test/types/records/ferguson/missing-functions/no-copy-init-error.chpl new file mode 100644 index 000000000000..7bc6191b1a4f --- /dev/null +++ b/test/types/records/ferguson/missing-functions/no-copy-init-error.chpl @@ -0,0 +1,19 @@ +record R { + var x: int; +} + +// record opts out of being copyable +proc R.init=(other: R) { + compilerError("You can't copy an R"); +} + +// program tries to copy-init R +proc ret(arg) { return arg; } + +proc main() { + var r = new R(1); + writeln(r); + + var bub = ret(r); + writeln(r); +} diff --git a/test/types/records/ferguson/missing-functions/no-copy-init-error.good b/test/types/records/ferguson/missing-functions/no-copy-init-error.good new file mode 100644 index 000000000000..818698c2f5f0 --- /dev/null +++ b/test/types/records/ferguson/missing-functions/no-copy-init-error.good @@ -0,0 +1,4 @@ +no-copy-init-error.chpl:11: In function 'ret': +no-copy-init-error.chpl:11: error: invalid implicit copy-initialization +no-copy-init-error.chpl:11: error: You can't copy an R +no-copy-init-error.chpl:1: note: R does not have a valid init= diff --git a/test/types/records/ferguson/missing-functions/no-copy-init-not-called.chpl b/test/types/records/ferguson/missing-functions/no-copy-init-not-called.chpl new file mode 100644 index 000000000000..74c878762016 --- /dev/null +++ b/test/types/records/ferguson/missing-functions/no-copy-init-not-called.chpl @@ -0,0 +1,21 @@ +record R { + var x: int; +} + +// record opts out of being copyable +proc R.init=(other: R) { + compilerError("You can't copy an R"); +} + +// program does not try to copy initialize R +proc main() { + var r = new R(1); + writeln(r); + + { + use Types; + + assert(isCopyable(R) == false); + assert(isConstCopyable(R) == false); + } +} diff --git a/test/types/records/ferguson/missing-functions/no-copy-init-not-called.good b/test/types/records/ferguson/missing-functions/no-copy-init-not-called.good new file mode 100644 index 000000000000..895683db0941 --- /dev/null +++ b/test/types/records/ferguson/missing-functions/no-copy-init-not-called.good @@ -0,0 +1 @@ +(x = 1) diff --git a/test/types/records/split-init/for-expr-owned-bug.bad b/test/types/records/split-init/for-expr-owned-bug.bad index a40390b1cb04..cdf0fde126fa 100644 --- a/test/types/records/split-init/for-expr-owned-bug.bad +++ b/test/types/records/split-init/for-expr-owned-bug.bad @@ -1 +1,4 @@ -$CHPL_HOME/modules/internal/ChapelArray.chpl:4193: error: non-lvalue actual is passed to a 'ref' formal of chpl__initCopy() +$CHPL_HOME/modules/internal/ChapelArray.chpl:4215: error: non-lvalue actual is passed to a 'ref' formal of chpl__initCopy() +for-expr-owned-bug.chpl:6: In function 'main': +for-expr-owned-bug.chpl:7: error: Cannot copy array with element type that cannot be assigned +$CHPL_HOME/modules/internal/ChapelArray.chpl:4299: note: in function called here