diff options
101 files changed, 1343 insertions, 1435 deletions
diff --git a/OTP_VERSION b/OTP_VERSION index b7aba827ff..1fbc0d2431 100644 --- a/OTP_VERSION +++ b/OTP_VERSION @@ -1 +1 @@ -17.1-rc0 +18.0-rc0 diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot Binary files differindex b57eef57ab..30736899c1 100644 --- a/bootstrap/bin/start.boot +++ b/bootstrap/bin/start.boot diff --git a/bootstrap/bin/start_clean.boot b/bootstrap/bin/start_clean.boot Binary files differindex b57eef57ab..30736899c1 100644 --- a/bootstrap/bin/start_clean.boot +++ b/bootstrap/bin/start_clean.boot diff --git a/bootstrap/lib/compiler/ebin/sys_pre_expand.beam b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam Binary files differindex c0c537f623..a1539bb35b 100644 --- a/bootstrap/lib/compiler/ebin/sys_pre_expand.beam +++ b/bootstrap/lib/compiler/ebin/sys_pre_expand.beam diff --git a/bootstrap/lib/kernel/ebin/application.beam b/bootstrap/lib/kernel/ebin/application.beam Binary files differindex 6946d17545..ba3a5ef81d 100644 --- a/bootstrap/lib/kernel/ebin/application.beam +++ b/bootstrap/lib/kernel/ebin/application.beam diff --git a/bootstrap/lib/kernel/ebin/application_controller.beam b/bootstrap/lib/kernel/ebin/application_controller.beam Binary files differindex 659f29e35e..2a4525c27f 100644 --- a/bootstrap/lib/kernel/ebin/application_controller.beam +++ b/bootstrap/lib/kernel/ebin/application_controller.beam diff --git a/bootstrap/lib/stdlib/ebin/erl_internal.beam b/bootstrap/lib/stdlib/ebin/erl_internal.beam Binary files differindex b851ca484e..9926b318c6 100644 --- a/bootstrap/lib/stdlib/ebin/erl_internal.beam +++ b/bootstrap/lib/stdlib/ebin/erl_internal.beam diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam Binary files differindex 9481dc6ef5..ffd3978820 100644 --- a/bootstrap/lib/stdlib/ebin/erl_lint.beam +++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam diff --git a/bootstrap/lib/stdlib/ebin/erl_parse.beam b/bootstrap/lib/stdlib/ebin/erl_parse.beam Binary files differindex 3d36ee85b7..b2c8aabd4a 100644 --- a/bootstrap/lib/stdlib/ebin/erl_parse.beam +++ b/bootstrap/lib/stdlib/ebin/erl_parse.beam diff --git a/bootstrap/lib/stdlib/ebin/erl_pp.beam b/bootstrap/lib/stdlib/ebin/erl_pp.beam Binary files differindex ff196f3a45..24f3b3bd80 100644 --- a/bootstrap/lib/stdlib/ebin/erl_pp.beam +++ b/bootstrap/lib/stdlib/ebin/erl_pp.beam diff --git a/bootstrap/lib/stdlib/ebin/gen_event.beam b/bootstrap/lib/stdlib/ebin/gen_event.beam Binary files differindex d45e508d22..f914bfccee 100644 --- a/bootstrap/lib/stdlib/ebin/gen_event.beam +++ b/bootstrap/lib/stdlib/ebin/gen_event.beam diff --git a/bootstrap/lib/stdlib/ebin/gen_fsm.beam b/bootstrap/lib/stdlib/ebin/gen_fsm.beam Binary files differindex bf5bbb7839..6f1eeea221 100644 --- a/bootstrap/lib/stdlib/ebin/gen_fsm.beam +++ b/bootstrap/lib/stdlib/ebin/gen_fsm.beam diff --git a/bootstrap/lib/stdlib/ebin/gen_server.beam b/bootstrap/lib/stdlib/ebin/gen_server.beam Binary files differindex fe95ca0826..dbbf8963e4 100644 --- a/bootstrap/lib/stdlib/ebin/gen_server.beam +++ b/bootstrap/lib/stdlib/ebin/gen_server.beam diff --git a/bootstrap/lib/stdlib/ebin/pg.beam b/bootstrap/lib/stdlib/ebin/pg.beam Binary files differdeleted file mode 100644 index 31f30461d8..0000000000 --- a/bootstrap/lib/stdlib/ebin/pg.beam +++ /dev/null diff --git a/bootstrap/lib/stdlib/ebin/stdlib.app b/bootstrap/lib/stdlib/ebin/stdlib.app index 1d255fb2c8..8b349c8122 100644 --- a/bootstrap/lib/stdlib/ebin/stdlib.app +++ b/bootstrap/lib/stdlib/ebin/stdlib.app @@ -77,7 +77,6 @@  	     orddict,  	     ordsets,  	     otp_internal, -	     pg,  	     pool,  	     proc_lib,  	     proplists, diff --git a/bootstrap/lib/stdlib/ebin/supervisor.beam b/bootstrap/lib/stdlib/ebin/supervisor.beam Binary files differindex 40d9b28a18..c2f7d78ec2 100644 --- a/bootstrap/lib/stdlib/ebin/supervisor.beam +++ b/bootstrap/lib/stdlib/ebin/supervisor.beam diff --git a/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam b/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam Binary files differindex 54d7385738..d4b8cab555 100644 --- a/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam +++ b/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index d28e519ae1..dc930f2d38 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -278,7 +278,6 @@ atom http httph https http_response http_request http_header http_eoh http_error  atom id  atom if_clause  atom ignore -atom imports  atom in  atom in_exiting  atom inactive @@ -333,6 +332,7 @@ atom max  atom maximum  atom max_tables max_processes  atom mbuf_size +atom md5  atom memory  atom memory_internal  atom memory_types diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 1026e5f649..9b251a6ad1 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -4993,14 +4993,14 @@ get_map_elements_fail:  	  * ... remainder of original BEAM code  	  */  	 ASSERT(I[-5] == (Uint) OpCode(i_func_info_IaaI)); -	 c_p->hipe.ncallee = (void(*)(void)) I[-4]; +	 c_p->hipe.u.ncallee = (void(*)(void)) I[-4];  	 cmd = HIPE_MODE_SWITCH_CMD_CALL | (I[-1] << 8);  	 ++hipe_trap_count;  	 goto L_hipe_mode_switch;       }       OpCase(hipe_trap_call_closure): {         ASSERT(I[-5] == (Uint) OpCode(i_func_info_IaaI)); -       c_p->hipe.ncallee = (void(*)(void)) I[-4]; +       c_p->hipe.u.ncallee = (void(*)(void)) I[-4];         cmd = HIPE_MODE_SWITCH_CMD_CALL_CLOSURE | (I[-1] << 8);         ++hipe_trap_count;         goto L_hipe_mode_switch; @@ -5034,7 +5034,10 @@ get_map_elements_fail:         case HIPE_MODE_SWITCH_RES_RETURN:  	 ASSERT(is_value(reg[0]));  	 MoveReturn(reg[0], r(0)); -       case HIPE_MODE_SWITCH_RES_CALL: +       case HIPE_MODE_SWITCH_RES_CALL_EXPORTED: +	 c_p->i = c_p->hipe.u.callee_exp->addressv[erts_active_code_ix()]; +	 /*fall through*/ +       case HIPE_MODE_SWITCH_RES_CALL_BEAM:  	 SET_I(c_p->i);  	 r(0) = reg[0];  	 Dispatch(); diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index e96177cfd9..a4e72a130a 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -245,7 +245,7 @@ typedef struct {  /*   * This structure contains all information about the module being loaded.   */   - +#define MD5_SIZE 16  typedef struct LoaderState {      /*       * The current logical file within the binary. @@ -292,7 +292,7 @@ typedef struct LoaderState {      StringPatch* string_patches; /* Linked list of position into string table to patch. */      BeamInstr catches;		/* Linked list of catch_yf instructions. */      unsigned loaded_size;	/* Final size of code when loaded. */ -    byte mod_md5[16];		/* MD5 for module code. */ +    byte mod_md5[MD5_SIZE];	/* MD5 for module code. */      int may_load_nif;           /* true if NIFs may later be loaded for this module */        int on_load;		/* Index in the code for the on_load function  				 * (or 0 if there is no on_load function) @@ -528,6 +528,7 @@ static Eterm exported_from_module(Process* p, Eterm mod);  static Eterm functions_in_module(Process* p, Eterm mod);  static Eterm attributes_for_module(Process* p, Eterm mod);  static Eterm compilation_info_for_module(Process* p, Eterm mod); +static Eterm md5_of_module(Process* p, Eterm mod);  static Eterm native_addresses(Process* p, Eterm mod);  int patch_funentries(Eterm Patchlist);  int patch(Eterm Addresses, Uint fe); @@ -648,6 +649,7 @@ erts_prepare_loading(Binary* magic, Process *c_p, Eterm group_leader,      stp->code[MI_COMPILE_PTR] = 0;      stp->code[MI_COMPILE_SIZE] = 0;      stp->code[MI_COMPILE_SIZE_ON_HEAP] = 0; +    stp->code[MI_MD5_PTR] = 0;      /*       * Read the atom table. @@ -4038,7 +4040,7 @@ freeze_code(LoaderState* stp)      }      size = (stp->ci * sizeof(BeamInstr)) +  	(stp->total_literal_size * sizeof(Eterm)) + -	strtab_size + attr_size + compile_size + line_size; +	strtab_size + attr_size + compile_size + MD5_SIZE + line_size;      /*       * Move the code to its final location. @@ -4247,11 +4249,20 @@ freeze_code(LoaderState* stp)  	code[MI_COMPILE_SIZE_ON_HEAP] = decoded_size;      }      CHKBLK(ERTS_ALC_T_CODE,code); +    { +	byte* md5_sum = str_table + strtab_size + attr_size + compile_size; +	CHKBLK(ERTS_ALC_T_CODE,code); +	sys_memcpy(md5_sum, stp->mod_md5, MD5_SIZE); +	CHKBLK(ERTS_ALC_T_CODE,code); +	code[MI_MD5_PTR] = (BeamInstr) md5_sum; +	CHKBLK(ERTS_ALC_T_CODE,code); +    } +    CHKBLK(ERTS_ALC_T_CODE,code);      /*       * Make sure that we have not overflowed the allocated code space.       */ -    ASSERT(str_table + strtab_size + attr_size + compile_size == +    ASSERT(str_table + strtab_size + attr_size + compile_size + MD5_SIZE ==  	   ((byte *) code) + size);      /* @@ -5103,10 +5114,11 @@ erts_module_info_0(Process* p, Eterm module)      hp += 3; \      list = CONS(hp, tup, list) +    BUILD_INFO(am_md5);      BUILD_INFO(am_compile);      BUILD_INFO(am_attributes); -    BUILD_INFO(am_imports);      BUILD_INFO(am_exports); +    BUILD_INFO(am_module);  #undef BUILD_INFO      return list;  } @@ -5116,8 +5128,8 @@ erts_module_info_1(Process* p, Eterm module, Eterm what)  {      if (what == am_module) {  	return module; -    } else if (what == am_imports) { -	return NIL; +    } else if (what == am_md5) { +	return md5_of_module(p, module);      } else if (what == am_exports) {  	return exported_from_module(p, module);      } else if (what == am_functions) { @@ -5306,7 +5318,7 @@ attributes_for_module(Process* p, /* Process whose heap to use. */      Eterm result = NIL;      Eterm* end; -    if (is_not_atom(mod) || (is_not_list(result) && is_not_nil(result))) { +    if (is_not_atom(mod)) {  	return THE_NON_VALUE;      } @@ -5345,7 +5357,7 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */      Eterm result = NIL;      Eterm* end; -    if (is_not_atom(mod) || (is_not_list(result) && is_not_nil(result))) { +    if (is_not_atom(mod)) {  	return THE_NON_VALUE;      } @@ -5368,6 +5380,33 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */  }  /* + * Returns the MD5 checksum for a module + * + * Returns a tagged term, or 0 on error. + */ + +Eterm +md5_of_module(Process* p, /* Process whose heap to use. */ +              Eterm mod) /* Tagged atom for module. */ +{ +    Module* modp; +    BeamInstr* code; +    Eterm res = NIL; + +    if (is_not_atom(mod)) { +	return THE_NON_VALUE; +    } + +    modp = erts_get_module(mod, erts_active_code_ix()); +    if (modp == NULL) { +	return THE_NON_VALUE; +    } +    code = modp->curr.code; +    res = new_binary(p, (byte *) code[MI_MD5_PTR], MD5_SIZE); +    return res; +} + +/*   * Build a single {M,F,A,Loction} item to be part of   * a stack trace.   */ @@ -5543,7 +5582,7 @@ code_module_md5_1(BIF_ALIST_1)  	res = am_undefined;  	goto done;      } -    res = new_binary(p, stp->mod_md5, sizeof(stp->mod_md5)); +    res = new_binary(p, stp->mod_md5, MD5_SIZE);   done:      erts_free_aligned_binary_bytes(temp_alloc); @@ -5939,6 +5978,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)      code[MI_LITERALS_END] = 0;      code[MI_LITERALS_OFF_HEAP] = 0;      code[MI_ON_LOAD_FUNCTION_PTR] = 0; +    code[MI_MD5_PTR] = 0;      ci = MI_FUNCTIONS + n + 1;      /* diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h index bd22b0c4de..0e3ca0bdb0 100644 --- a/erts/emulator/beam/beam_load.h +++ b/erts/emulator/beam/beam_load.h @@ -91,7 +91,6 @@ extern Uint erts_total_code_size;  #define MI_LITERALS_END		8  #define MI_LITERALS_OFF_HEAP	9 -  /*   * Pointer to the on_load function (or NULL if none).   */ @@ -103,6 +102,11 @@ extern Uint erts_total_code_size;  #define MI_LINE_TABLE 11  /* + * Pointer to the module MD5 sum (16 bytes) + */ +#define MI_MD5_PTR 12 + +/*   * Start of function pointer table.  This table contains pointers to   * all functions in the module plus an additional pointer just beyond   * the end of the last function. @@ -111,7 +115,7 @@ extern Uint erts_total_code_size;   * this table.   */ -#define MI_FUNCTIONS         12 +#define MI_FUNCTIONS         13  /*   * Layout of the line table. diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index d18760dc43..1a0c7a9fc9 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -1,7 +1,7 @@  /*   * %CopyrightBegin%   * - * Copyright Ericsson AB 2005-2013. All Rights Reserved. + * Copyright Ericsson AB 2005-2014. All Rights Reserved.   *   * The contents of this file are subject to the Erlang Public License,   * Version 1.1, (the "License"); you may not use this file except in @@ -25,6 +25,7 @@  #include "sys.h"  #include "big.h"  #include "erl_map.h" +#include "erl_binary.h"  #define PRINT_CHAR(CNT, FN, ARG, C)					\  do {									\ @@ -138,6 +139,25 @@ is_printable_string(Eterm list, Eterm* base)      return 0;  } +static int is_printable_ascii(byte* bytep, Uint bytesize, Uint bitoffs) +{ +    if (!bitoffs) { +	while (bytesize--) { +	    if (*bytep < ' ' || *bytep >= 127) +		return 0; +	    bytep++; +	} +    } else { +	while (bytesize--) { +	    byte octet = (bytep[0] << bitoffs) | (bytep[1] >> (8-bitoffs)); +	    if (octet < ' ' || octet >= 127) +		return 0; +	    bytep++; +	} +    } +    return 1; +} +  /* print a atom doing what quoting is necessary */  static int print_atom_name(fmtfn_t fn, void* arg, Eterm atom, long *dcount)  { @@ -446,13 +466,65 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount,  		PRINT_STRING(res, fn, arg, "#MatchState");  	    }  	    else { -		ProcBin* pb = (ProcBin *) binary_val(wobj); -		if (pb->size == 1) -		    PRINT_STRING(res, fn, arg, "<<1 byte>>"); -		else { +		byte* bytep; +		Uint bytesize = binary_size_rel(obj,obj_base); +		Uint bitoffs; +		Uint bitsize; +		byte octet; +		ERTS_GET_BINARY_BYTES_REL(obj, bytep, bitoffs, bitsize, obj_base); + +		if (bitsize || !bytesize +		    || !is_printable_ascii(bytep, bytesize, bitoffs)) { +		    int is_first = 1;  		    PRINT_STRING(res, fn, arg, "<<"); -		    PRINT_UWORD(res, fn, arg, 'u', 0, 1, (ErlPfUWord) pb->size); -		    PRINT_STRING(res, fn, arg, " bytes>>"); +		    while (bytesize) { +			if (is_first) +			    is_first = 0; +			else +			    PRINT_CHAR(res, fn, arg, ','); +			if (bitoffs) +			    octet = (bytep[0] << bitoffs) | (bytep[1] >> (8-bitoffs)); +			else +			    octet = bytep[0]; +			PRINT_UWORD(res, fn, arg, 'u', 0, 1, octet); +			++bytep; +			--bytesize; +		    } +		    if (bitsize) { +			Uint bits = bitoffs + bitsize; +			octet = bytep[0]; +			if (bits < 8) +			    octet >>= 8 - bits; +			else if (bits > 8) { +			    bits -= 8;  /* bits in last byte */ +			    octet <<= bits; +			    octet |= bytep[1] >> (8 - bits); +			} +			octet &= (1 << bitsize) - 1; +			if (is_first) +			    is_first = 0; +			else +			    PRINT_CHAR(res, fn, arg, ','); +			PRINT_UWORD(res, fn, arg, 'u', 0, 1, octet); +			PRINT_CHAR(res, fn, arg, ':'); +			PRINT_UWORD(res, fn, arg, 'u', 0, 1, bitsize); +		    } +		    PRINT_STRING(res, fn, arg, ">>"); +		} +		else { +		    PRINT_STRING(res, fn, arg, "<<\""); +		    while (bytesize) { +			if (bitoffs) +			    octet = (bytep[0] << bitoffs) | (bytep[1] >> (8-bitoffs)); +			else +			    octet = bytep[0]; +			if (octet == '"') +			    PRINT_CHAR(res, fn, arg, '\\'); +			PRINT_CHAR(res, fn, arg, octet); +			++bytep; +			--bytesize; +		    } +		    PRINT_STRING(res, fn, arg, "\">>");  		}  	    }  	    break; diff --git a/erts/emulator/hipe/hipe_amd64.c b/erts/emulator/hipe/hipe_amd64.c index b5dff06987..16c597e7b4 100644 --- a/erts/emulator/hipe/hipe_amd64.c +++ b/erts/emulator/hipe/hipe_amd64.c @@ -224,18 +224,19 @@ void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process *      return alloc_code(nrbytes);  } -/* called from hipe_bif0.c:hipe_bifs_make_native_stub_2() -   and hipe_bif0.c:hipe_make_stub() */ -void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) + +/* Make stub for native code calling exported beam function. +*/ +void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity)  {      /*       * This creates a native code stub with the following contents:       * -     * movq $Address, P_BEAM_IP(%ebp)  %% Actually two movl +     * movq $Address, P_CALLEE_EXP(%ebp)  %% Actually two movl       * movb $Arity, P_ARITY(%ebp)       * jmp callemu       * -     * The stub has variable size, depending on whether the P_BEAM_IP +     * The stub has variable size, depending on whether the P_CALLEE_EXP       * and P_ARITY offsets fit in 8-bit signed displacements or not.       * The rel32 offset in the final jmp depends on its actual location,       * which also depends on the size of the previous instructions. @@ -248,49 +249,49 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity)      codeSize =	/* 23, 26, 29, or 32 bytes */        23 +	/* 23 when all offsets are 8-bit */ -      (P_BEAM_IP >= 128 ? 3 : 0) + -      ((P_BEAM_IP + 4) >= 128 ? 3 : 0) + +      (P_CALLEE_EXP >= 128 ? 3 : 0) + +      ((P_CALLEE_EXP + 4) >= 128 ? 3 : 0) +        (P_ARITY >= 128 ? 3 : 0);      codep = code = alloc_code(codeSize); -    /* movl $beamAddress, P_BEAM_IP(%ebp); 3 or 6 bytes, plus 4 */ +    /* movl $callee_exp, P_CALLEE_EXP(%ebp); 3 or 6 bytes, plus 4 */      codep[0] = 0xc7; -#if P_BEAM_IP >= 128 +#if P_CALLEE_EXP >= 128      codep[1] = 0x85;	/* disp32[EBP] */ -    codep[2] =  P_BEAM_IP        & 0xFF; -    codep[3] = (P_BEAM_IP >>  8) & 0xFF; -    codep[4] = (P_BEAM_IP >> 16) & 0xFF; -    codep[5] = (P_BEAM_IP >> 24) & 0xFF; +    codep[2] =  P_CALLEE_EXP        & 0xFF; +    codep[3] = (P_CALLEE_EXP >>  8) & 0xFF; +    codep[4] = (P_CALLEE_EXP >> 16) & 0xFF; +    codep[5] = (P_CALLEE_EXP >> 24) & 0xFF;      codep += 6;  #else      codep[1] = 0x45;	/* disp8[EBP] */ -    codep[2] = P_BEAM_IP; +    codep[2] = P_CALLEE_EXP;      codep += 3;  #endif -    codep[0] = ((unsigned long)beamAddress      ) & 0xFF; -    codep[1] = ((unsigned long)beamAddress >>  8) & 0xFF; -    codep[2] = ((unsigned long)beamAddress >> 16) & 0xFF; -    codep[3] = ((unsigned long)beamAddress >> 24) & 0xFF; +    codep[0] = ((unsigned long)callee_exp      ) & 0xFF; +    codep[1] = ((unsigned long)callee_exp >>  8) & 0xFF; +    codep[2] = ((unsigned long)callee_exp >> 16) & 0xFF; +    codep[3] = ((unsigned long)callee_exp >> 24) & 0xFF;      codep += 4; -    /* movl (shl 32 $beamAddress), P_BEAM_IP+4(%ebp); 3 or 6 bytes, plus 4 */ +    /* movl (shl 32 $callee_exp), P_CALLEE_EXP+4(%ebp); 3 or 6 bytes, plus 4 */      codep[0] = 0xc7; -#if P_BEAM_IP+4 >= 128 +#if P_CALLEE_EXP+4 >= 128      codep[1] = 0x85;	/* disp32[EBP] */ -    codep[2] =  (P_BEAM_IP+4)        & 0xFF; -    codep[3] = ((P_BEAM_IP+4) >>  8) & 0xFF; -    codep[4] = ((P_BEAM_IP+4) >> 16) & 0xFF; -    codep[5] = ((P_BEAM_IP+4) >> 24) & 0xFF; +    codep[2] =  (P_CALLEE_EXP+4)        & 0xFF; +    codep[3] = ((P_CALLEE_EXP+4) >>  8) & 0xFF; +    codep[4] = ((P_CALLEE_EXP+4) >> 16) & 0xFF; +    codep[5] = ((P_CALLEE_EXP+4) >> 24) & 0xFF;      codep += 6;  #else      codep[1] = 0x45;	/* disp8[EBP] */ -    codep[2] =  (P_BEAM_IP+4); +    codep[2] =  (P_CALLEE_EXP+4);      codep += 3;  #endif -    codep[0] = ((unsigned long)beamAddress >> 32) & 0xFF; -    codep[1] = ((unsigned long)beamAddress >> 40) & 0xFF; -    codep[2] = ((unsigned long)beamAddress >> 48) & 0xFF; -    codep[3] = ((unsigned long)beamAddress >> 56) & 0xFF; +    codep[0] = ((unsigned long)callee_exp >> 32) & 0xFF; +    codep[1] = ((unsigned long)callee_exp >> 40) & 0xFF; +    codep[2] = ((unsigned long)callee_exp >> 48) & 0xFF; +    codep[3] = ((unsigned long)callee_exp >> 56) & 0xFF;      codep += 4;      /* movb $beamArity, P_ARITY(%ebp); 3 or 6 bytes */ diff --git a/erts/emulator/hipe/hipe_amd64_glue.S b/erts/emulator/hipe/hipe_amd64_glue.S index 8816906870..bebe0a8fd1 100644 --- a/erts/emulator/hipe/hipe_amd64_glue.S +++ b/erts/emulator/hipe/hipe_amd64_glue.S @@ -109,7 +109,7 @@ ASYM(nbif_return):   * stub (hipe_x86_loader.erl) which should look as follows:   *   * stub for f/N: - *	movq	$<f's BEAM code address>, P_BEAM_IP(P) + *	movq	$<f's export entry address>, P_CALLEE_EXP(P)   *	movb	$<N>, P_ARITY(P)   *	jmp	nbif_callemu   * @@ -119,7 +119,7 @@ ASYM(nbif_return):  	GLOBAL(ASYM(nbif_callemu))  ASYM(nbif_callemu):  	STORE_ARG_REGS -	movl	$HIPE_MODE_SWITCH_RES_CALL, %eax +	movl	$HIPE_MODE_SWITCH_RES_CALL_EXPORTED, %eax  	jmp	.suspend_exit  /* diff --git a/erts/emulator/hipe/hipe_arm.c b/erts/emulator/hipe/hipe_arm.c index 3db3ffe9b1..165eb543c8 100644 --- a/erts/emulator/hipe/hipe_arm.c +++ b/erts/emulator/hipe/hipe_arm.c @@ -260,9 +260,9 @@ int hipe_patch_insn(void *address, Uint32 value, Eterm type)      return 0;  } -/* called from hipe_bif0.c:hipe_bifs_make_native_stub_2() -   and hipe_bif0.c:hipe_make_stub() */ -void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) +/* Make stub for native code calling exported beam function +*/ +void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity)  {      unsigned int *code;      unsigned int *tramp_callemu; @@ -272,9 +272,9 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity)       * Native code calls BEAM via a stub looking as follows:       *       * mov r0, #beamArity -     * ldr r8, [pc,#0] // beamAddress +     * ldr r8, [pc,#0] // callee_exp       * b nbif_callemu -     * .long beamAddress +     * .long callee_exp       *       * I'm using r0 and r8 since they aren't used for       * parameter passing in native code. The branch to @@ -292,12 +292,12 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity)      /* mov r0, #beamArity */      code[0] = 0xE3A00000 | (beamArity & 0xFF); -    /* ldr r8, [pc,#0] // beamAddress */ +    /* ldr r8, [pc,#0] // callee_exp */      code[1] = 0xE59F8000;      /* b nbif_callemu */      code[2] = 0xEA000000 | (callemu_offset & 0x00FFFFFF); -    /* .long beamAddress */ -    code[3] = (unsigned int)beamAddress; +    /* .long callee_exp */ +    code[3] = (unsigned int)callee_exp;      hipe_flush_icache_range(code, 4*sizeof(int)); diff --git a/erts/emulator/hipe/hipe_arm_glue.S b/erts/emulator/hipe/hipe_arm_glue.S index 2e2b8604a6..e58e112ca7 100644 --- a/erts/emulator/hipe/hipe_arm_glue.S +++ b/erts/emulator/hipe/hipe_arm_glue.S @@ -135,7 +135,7 @@ hipe_arm_throw_to_native:   * which should look as follows:   *   * stub for f/N: - *	<set r8 to f's BEAM code address> + *	<set r8 to f's export entry address>   *	<set r0 to N>   *	b nbif_callemu   * @@ -143,10 +143,10 @@ hipe_arm_throw_to_native:   */  	.global	nbif_callemu  nbif_callemu: -	str	r8, [P, #P_BEAM_IP] +	str	r8, [P, #P_CALLEE_EXP]  	str	r0, [P, #P_ARITY]  	STORE_ARG_REGS -	mov	r0, #HIPE_MODE_SWITCH_RES_CALL +	mov	r0, #HIPE_MODE_SWITCH_RES_CALL_EXPORTED  	b	.suspend_exit  /* diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index 2497d51df1..327546bfd0 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -89,25 +89,6 @@ static Eterm address_to_term(const void *address, Process *p)  /*   * BIFs for reading and writing memory. Used internally by HiPE.   */ -#if 0 /* XXX: unused */ -BIF_RETTYPE hipe_bifs_read_u8_1(BIF_ALIST_1) -{ -    unsigned char *address = term_to_address(BIF_ARG_1); -    if (!address) -	BIF_ERROR(BIF_P, BADARG); -    BIF_RET(make_small(*address)); -} -#endif - -#if 0 /* XXX: unused */ -BIF_RETTYPE hipe_bifs_read_u32_1(BIF_ALIST_1) -{ -    Uint32 *address = term_to_address(BIF_ARG_1); -    if (!address || !hipe_word32_address_ok(address)) -	BIF_ERROR(BIF_P, BADARG); -    BIF_RET(Uint_to_term(*address, BIF_P)); -} -#endif  BIF_RETTYPE hipe_bifs_write_u8_2(BIF_ALIST_2)  { @@ -120,22 +101,6 @@ BIF_RETTYPE hipe_bifs_write_u8_2(BIF_ALIST_2)      BIF_RET(NIL);  } -#if 0 /* XXX: unused */ -BIF_RETTYPE hipe_bifs_write_s32_2(BIF_ALIST_2) -{ -    Sint32 *address; -    Sint value; - -    address = term_to_address(BIF_ARG_1); -    if (!address || !hipe_word32_address_ok(address)) -	BIF_ERROR(BIF_P, BADARG); -    if (!term_to_Sint32(BIF_ARG_2, &value)) -	BIF_ERROR(BIF_P, BADARG); -    *address = value; -    BIF_RET(NIL); -} -#endif -  BIF_RETTYPE hipe_bifs_write_u32_2(BIF_ALIST_2)  {      Uint32 *address; @@ -639,33 +604,6 @@ BIF_RETTYPE hipe_bifs_fun_to_address_1(BIF_ALIST_1)      BIF_RET(address_to_term(pc, BIF_P));  } -static void *hipe_get_emu_address(Eterm m, Eterm f, unsigned int arity, int is_remote) -{ -    void *address = NULL; -    if (!is_remote) -	address = hipe_find_emu_address(m, f, arity); -    if (!address) { -	/* if not found, stub it via the export entry */ -	/* no lock needed around erts_export_get_or_make_stub() */ -	Export *export_entry = erts_export_get_or_make_stub(m, f, arity); -	address = export_entry->addressv[erts_active_code_ix()]; -    } -    return address; -} - -#if 0 /* XXX: unused */ -BIF_RETTYPE hipe_bifs_get_emu_address_1(BIF_ALIST_1) -{ -    struct mfa mfa; -    void *address; - -    if (!term_to_mfa(BIF_ARG_1, &mfa)) -	BIF_ERROR(BIF_P, BADARG); -    address = hipe_get_emu_address(mfa.mod, mfa.fun, mfa.ari); -    BIF_RET(address_to_term(address, BIF_P)); -} -#endif -  BIF_RETTYPE hipe_bifs_set_native_address_3(BIF_ALIST_3)  {      Eterm *pc; @@ -713,33 +651,6 @@ BIF_RETTYPE hipe_bifs_set_native_address_3(BIF_ALIST_3)      BIF_RET(am_false);  } -#if 0 /* XXX: unused */ -/* - * hipe_bifs_address_to_fun(Address) - *    - Address is the address of the start of a emu function's code - *    - returns {Module, Function, Arity} - */ -BIF_RETTYPE hipe_bifs_address_to_fun_1(BIF_ALIST_1) -{ -    Eterm *pc; -    Eterm *funcinfo; -    Eterm *hp; - -    pc = term_to_address(BIF_ARG_1); -    if (!pc) -	BIF_ERROR(BIF_P, BADARG); -    funcinfo = find_function_from_pc(pc); -    if (!funcinfo) -	BIF_RET(am_false); -    hp = HAlloc(BIF_P, 4); -    hp[0] = make_arityval(3); -    hp[1] = funcinfo[0]; -    hp[2] = funcinfo[1]; -    hp[3] = make_small(funcinfo[2]); -    BIF_RET(make_tuple(hp)); -} -#endif -  BIF_RETTYPE hipe_bifs_enter_sdesc_1(BIF_ALIST_1)  {      struct sdesc *sdesc; @@ -948,37 +859,6 @@ BIF_RETTYPE hipe_bifs_primop_address_1(BIF_ALIST_1)      BIF_RET(address_to_term(primop->address, BIF_P));  } -#if 0 /* XXX: unused */ -/* - * hipe_bifs_gbif_address(F,A) -> address or false - */ -#define GBIF_LIST(ATOM,ARY,CFUN) extern Eterm gbif_##CFUN(void); -#include "hipe_gbif_list.h" -#undef GBIF_LIST - -BIF_RETTYPE hipe_bifs_gbif_address_2(BIF_ALIST_2) -{ -    Uint arity; -    void *address; - -    if (is_not_atom(BIF_ARG_1) || is_not_small(BIF_ARG_2)) -	BIF_RET(am_false);	/* error or false, does it matter? */ -    arity = signed_val(BIF_ARG_2); -    /* XXX: replace with a hash table later */ -    do { /* trick to let us use 'break' instead of 'goto' */ -#define GBIF_LIST(ATOM,ARY,CFUN) if (BIF_ARG_1 == ATOM && arity == ARY) { address = CFUN; break; } -#include "hipe_gbif_list.h" -#undef GBIF_LIST -	printf("\r\n%s: guard BIF ", __FUNCTION__); -	fflush(stdout); -	erts_printf("%T", BIF_ARG_1); -	printf("/%lu isn't listed in hipe_gbif_list.h\r\n", arity); -	BIF_RET(am_false); -    } while (0); -    BIF_RET(address_to_term(address, BIF_P)); -} -#endif -  BIF_RETTYPE hipe_bifs_atom_to_word_1(BIF_ALIST_1)  {      if (is_not_atom(BIF_ARG_1)) @@ -1028,77 +908,6 @@ void hipe_emulate_fpe(Process* p)  }  #endif -#if 0 /* XXX: unused */ -/* - * At least parts of this should be inlined in native code. - * The rest could be made a primop used by both the emulator and - * native code... - */ -BIF_RETTYPE hipe_bifs_make_fun_3(BIF_ALIST_3) -{ -    Eterm free_vars; -    Eterm mod; -    Eterm *tp; -    Uint index; -    Uint uniq; -    Uint num_free; -    Eterm tmp_var; -    Uint *tmp_ptr; -    unsigned needed; -    ErlFunThing *funp; -    Eterm *hp; -    int i; - -    if (is_not_list(BIF_ARG_1) && is_not_nil(BIF_ARG_1)) -	BIF_ERROR(BIF_P, BADARG); -    free_vars = BIF_ARG_1; - -    if (is_not_atom(BIF_ARG_2)) -	BIF_ERROR(BIF_P, BADARG); -    mod = BIF_ARG_2; - -    if (is_not_tuple(BIF_ARG_3) || -	(arityval(*tuple_val(BIF_ARG_3)) != 3)) -	BIF_ERROR(BIF_P, BADARG); -    tp = tuple_val(BIF_ARG_3); - -    if (term_to_Uint(tp[1], &index) == 0) -	BIF_ERROR(BIF_P, BADARG); -    if (term_to_Uint(tp[2], &uniq) == 0) -	BIF_ERROR(BIF_P, BADARG); -    if (term_to_Uint(tp[3], &num_free) == 0) -	BIF_ERROR(BIF_P, BADARG); - -    needed = ERL_FUN_SIZE + num_free; -    funp = (ErlFunThing *) HAlloc(BIF_P, needed); -    hp = funp->env; - -    funp->thing_word = HEADER_FUN; - -    /* Need a ErlFunEntry *fe -     * fe->refc++; -     * funp->fe = fe; -     */ - -    funp->num_free = num_free; -    funp->creator = BIF_P->id; -    for (i = 0; i < num_free; i++) { -	if (is_nil(free_vars)) -	    BIF_ERROR(BIF_P, BADARG); -	tmp_ptr = list_val(free_vars); -	tmp_var = CAR(tmp_ptr); -	free_vars = CDR(tmp_ptr); -	*hp++ = tmp_var; -    } -    if (is_not_nil(free_vars)) -	BIF_ERROR(BIF_P, BADARG); - -    funp->next = MSO(BIF_P).funs; -    MSO(BIF_P).funs = funp; - -    BIF_RET(make_fun(funp)); -} -#endif  /*   * args: Module, {Uniq, Index, BeamAddress} @@ -1163,22 +972,6 @@ BIF_RETTYPE hipe_bifs_set_native_address_in_fe_2(BIF_ALIST_2)      BIF_RET(am_true);  } -#if 0 /* XXX: unused */ -BIF_RETTYPE hipe_bifs_make_native_stub_2(BIF_ALIST_2) -{ -    void *beamAddress; -    Uint beamArity; -    void *stubAddress; - -    if ((beamAddress = term_to_address(BIF_ARG_1)) == 0 || -	is_not_small(BIF_ARG_2) || -	(beamArity = unsigned_val(BIF_ARG_2)) >= 256) -	BIF_ERROR(BIF_P, BADARG); -    stubAddress = hipe_make_native_stub(beamAddress, beamArity); -    BIF_RET(address_to_term(stubAddress, BIF_P)); -} -#endif -  /*   * MFA info hash table:   * - maps MFA to native code entry point @@ -1323,16 +1116,6 @@ static inline struct hipe_mfa_info *hipe_mfa_info_table_get_locked(Eterm m, Eter      return NULL;  } -#if 0 /* XXX: unused */ -void *hipe_mfa_find_na(Eterm m, Eterm f, unsigned int arity) -{ -    const struct hipe_mfa_info *p; - -    p = hipe_mfa_info_table_get(m, f, arity); -    return p ? p->address : NULL; -} -#endif -  static struct hipe_mfa_info *hipe_mfa_info_table_put_locked(Eterm m, Eterm f, unsigned int arity)  {      unsigned long h; @@ -1490,18 +1273,13 @@ void hipe_mfa_save_orig_beam_op(Eterm mod, Eterm fun, unsigned int ari, Eterm *p  static void *hipe_make_stub(Eterm m, Eterm f, unsigned int arity, int is_remote)  { -    void *BEAMAddress; +    Export *export_entry;      void *StubAddress; -#if 0 -    if (is_not_atom(m) || is_not_atom(f) || arity > 255) -	return NULL; -#endif -    BEAMAddress = hipe_get_emu_address(m, f, arity, is_remote); -    StubAddress = hipe_make_native_stub(BEAMAddress, arity); -#if 0 -    hipe_mfa_set_na(m, f, arity, StubAddress); -#endif +    ASSERT(is_remote); + +    export_entry = erts_export_get_or_make_stub(m, f, arity); +    StubAddress = hipe_make_native_stub(export_entry, arity);      return StubAddress;  } diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c index 32694a8f97..7f82252308 100644 --- a/erts/emulator/hipe/hipe_debug.c +++ b/erts/emulator/hipe/hipe_debug.c @@ -231,7 +231,7 @@ void hipe_print_pcb(Process *p)      U("nsp        ", hipe.nsp);      U("nstack     ", hipe.nstack);      U("nstend     ", hipe.nstend); -    U("ncallee    ", hipe.ncallee); +    U("ncallee    ", hipe.u.ncallee);      hipe_arch_print_pcb(&p->hipe);  #endif	/* HIPE */  #undef U diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c index 0e287908b1..ed355ce264 100644 --- a/erts/emulator/hipe/hipe_mkliterals.c +++ b/erts/emulator/hipe/hipe_mkliterals.c @@ -498,8 +498,8 @@ static const struct rts_param rts_params[] = {      { 38, "P_ARG4", 1, offsetof(struct process, def_arg_reg[4]) },      { 39, "P_ARG5", 1, offsetof(struct process, def_arg_reg[5]) },      { 40, "P_NSP", 1, offsetof(struct process, hipe.nsp) }, -    { 41, "P_NCALLEE", 1, offsetof(struct process, hipe.ncallee) }, -    { 42, "P_CLOSURE", 1, offsetof(struct process, hipe.closure) }, +    { 41, "P_NCALLEE", 1, offsetof(struct process, hipe.u.ncallee) }, +    { 42, "P_CLOSURE", 1, offsetof(struct process, hipe.u.closure) },      { 43, "P_NSP_LIMIT", 1, offsetof(struct process, hipe.nstack) },      { 44, "P_CSP",  #if defined(__i386__) || defined(__x86_64__) @@ -524,6 +524,7 @@ static const struct rts_param rts_params[] = {      },      { 49, "P_MSG_FIRST", 1, offsetof(struct process, msg.first) },      { 50, "P_MSG_SAVE", 1, offsetof(struct process, msg.save) }, +    { 51, "P_CALLEE_EXP", 1, offsetof(struct process, hipe.u.callee_exp) },  };  #define NR_PARAMS	ARRAY_SIZE(rts_params) diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index 4ddc2790b1..4dbba9da61 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -257,14 +257,14 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])  	  /* BEAM calls a native code function */  	  unsigned arity = cmd >> 8; -	  /* p->hipe.ncallee set in beam_emu */ +	  /* p->hipe.u.ncallee set in beam_emu */  	  if (p->cp == hipe_beam_pc_return) {  	    /* Native called BEAM, which now tailcalls native. */  	    hipe_pop_beam_trap_frame(p);  	    result = hipe_tailcall_to_native(p, arity, reg);  	    break;  	  } -	  DPRINTF("calling %#lx/%u", (long)p->hipe.ncallee, arity); +	  DPRINTF("calling %#lx/%u", (long)p->hipe.u.ncallee, arity);  	  result = hipe_call_to_native(p, arity, reg);  	  break;        } @@ -282,18 +282,18 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])  	  arity -= funp->num_free;	/* arity == #formals */  	  reg[arity] = fun;  	  ++arity;	/* correct for having added the closure */ -	  /* HIPE_ASSERT(p->hipe.ncallee == (void(*)(void))funp->native_address); */ +	  /* HIPE_ASSERT(p->hipe.u.ncallee == (void(*)(void))funp->native_address); */  	  /* just like a normal call from now on */ -	  /* p->hipe.ncallee set in beam_emu */ +	  /* p->hipe.u.ncallee set in beam_emu */  	  if (p->cp == hipe_beam_pc_return) {  	      /* Native called BEAM, which now tailcalls native. */  	      hipe_pop_beam_trap_frame(p);  	      result = hipe_tailcall_to_native(p, arity, reg);  	      break;  	  } -	  DPRINTF("calling %#lx/%u", (long)p->hipe.ncallee, arity); +	  DPRINTF("calling %#lx/%u", (long)p->hipe.u.ncallee, arity);  	  result = hipe_call_to_native(p, arity, reg);  	  break;        } @@ -396,13 +396,13 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])  	  if (is_recursive)  	      hipe_push_beam_trap_frame(p, reg, p->arity); -	  result = HIPE_MODE_SWITCH_RES_CALL; +	  result = HIPE_MODE_SWITCH_RES_CALL_BEAM;  	  break;        } -      case HIPE_MODE_SWITCH_RES_CALL: { +      case HIPE_MODE_SWITCH_RES_CALL_EXPORTED: {  	  /* Native code calls or tailcalls BEAM.  	   * -	   * p->i is the callee's BEAM code +	   * p->hipe.u.callee_exp is the callee's export entry  	   * p->arity is the callee's arity  	   * p->def_arg_reg[] contains the register parameters  	   * p->hipe.nsp[] contains the stacked parameters @@ -422,15 +422,15 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])  	   * F(A1, ..., AN, FV1, ..., FVM, Closure)  	   *  (Where Ai is argument i and FVj is free variable j)  	   * -	   * p->hipe.closure contains the closure +	   * p->hipe.u.closure contains the closure  	   * p->def_arg_reg[] contains the register parameters  	   * p->hipe.nsp[] contains the stacked parameters  	   */  	  ErlFunThing *closure;  	  unsigned num_free, arity, i, is_recursive; -	  HIPE_ASSERT(is_fun(p->hipe.closure)); -	  closure = (ErlFunThing*)fun_val(p->hipe.closure); +	  HIPE_ASSERT(is_fun(p->hipe.u.closure)); +	  closure = (ErlFunThing*)fun_val(p->hipe.u.closure);  	  num_free = closure->num_free;  	  arity = closure->fe->arity; @@ -460,10 +460,10 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])  	      p->i = closure->fe->address;  	      /* Change result code to the faster plain CALL type. */ -	      result = HIPE_MODE_SWITCH_RES_CALL; +	      result = HIPE_MODE_SWITCH_RES_CALL_BEAM;  	  }  	  /* Append the closure as the last parameter. Don't increment arity. */ -	  reg[arity] = p->hipe.closure; +	  reg[arity] = p->hipe.u.closure;  	  if (is_recursive) {  	      /* BEAM called native, which now calls BEAM. @@ -541,7 +541,7 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])  	      }  	  }  	  HIPE_CHECK_PCB(p); -	  result = HIPE_MODE_SWITCH_RES_CALL; +	  result = HIPE_MODE_SWITCH_RES_CALL_BEAM;  	  p->def_arg_reg[3] = result;  	  return p;        } @@ -569,7 +569,7 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])  	  address = hipe_get_remote_na(mfa[0], mfa[1], arity);  	  if (!address)  		  goto do_apply_fail; -	  p->hipe.ncallee = (void(*)(void)) address; +	  p->hipe.u.ncallee = (void(*)(void)) address;  	  result = hipe_tailcall_to_native(p, arity, reg);  	  goto do_return_from_native;        do_apply_fail: diff --git a/erts/emulator/hipe/hipe_mode_switch.h b/erts/emulator/hipe/hipe_mode_switch.h index 06721e3c04..6ec5da1ae9 100644 --- a/erts/emulator/hipe/hipe_mode_switch.h +++ b/erts/emulator/hipe/hipe_mode_switch.h @@ -31,7 +31,7 @@  /* result codes for beam_emu <- hipe_mode_switch() return */  #define HIPE_MODE_SWITCH_RES_RETURN		4 -#define HIPE_MODE_SWITCH_RES_CALL		5 +#define HIPE_MODE_SWITCH_RES_CALL_EXPORTED	5  #define HIPE_MODE_SWITCH_RES_THROW		6  /* additional result codes for hipe_mode_switch() <- native return */ @@ -45,6 +45,8 @@  #define HIPE_MODE_SWITCH_RES_APPLY		13 /* mode_switch <- native */ +#define HIPE_MODE_SWITCH_RES_CALL_BEAM	        14 +  #ifndef ASM  #include "error.h" diff --git a/erts/emulator/hipe/hipe_ppc.c b/erts/emulator/hipe/hipe_ppc.c index 2d8fd61e1e..4dc26cdbc8 100644 --- a/erts/emulator/hipe/hipe_ppc.c +++ b/erts/emulator/hipe/hipe_ppc.c @@ -285,7 +285,7 @@ int hipe_patch_insn(void *address, Uint64 value, Eterm type)      }  } -void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) +void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity)  {      unsigned int *code; @@ -294,16 +294,16 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity)      code = alloc_stub(7); -    /* addis r12,0,beamAddress@highest */ -    code[0] = 0x3d800000 | (((unsigned long)beamAddress >> 48) & 0xffff); -    /* ori r12,r12,beamAddress@higher */ -    code[1] = 0x618c0000 | (((unsigned long)beamAddress >> 32) & 0xffff); +    /* addis r12,0,callee_exp@highest */ +    code[0] = 0x3d800000 | (((unsigned long)callee_exp >> 48) & 0xffff); +    /* ori r12,r12,callee_exp@higher */ +    code[1] = 0x618c0000 | (((unsigned long)callee_exp >> 32) & 0xffff);      /* sldi r12,r12,32 (rldicr r12,r12,32,31) */      code[2] = 0x798c07c6; -    /* oris r12,r12,beamAddress@h */ -    code[3] = 0x658c0000 | (((unsigned long)beamAddress >> 16) & 0xffff); -    /* ori r12,r12,beamAddress@l */ -    code[4] = 0x618c0000 | ((unsigned long)beamAddress & 0xffff); +    /* oris r12,r12,callee_exp@h */ +    code[3] = 0x658c0000 | (((unsigned long)callee_exp >> 16) & 0xffff); +    /* ori r12,r12,callee_exp@l */ +    code[4] = 0x618c0000 | ((unsigned long)callee_exp & 0xffff);      /* addi r0,0,beamArity */      code[5] = 0x38000000 | (beamArity & 0x7FFF);      /* ba nbif_callemu */ @@ -355,18 +355,16 @@ int hipe_patch_insn(void *address, Uint32 value, Eterm type)      return 0;  } -/* called from hipe_bif0.c:hipe_bifs_make_native_stub_2() -   and hipe_bif0.c:hipe_make_stub() */ -void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) +void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity)  {      unsigned int *code;      /*       * Native code calls BEAM via a stub looking as follows:       * -     * addi r12,0,beamAddress@l +     * addi r12,0,callee_exp@l       * addi r0,0,beamArity -     * addis r12,r12,beamAddress@ha +     * addis r12,r12,callee_exp@ha       * ba nbif_callemu       *       * I'm using r0 and r12 since the standard SVR4 ABI allows @@ -384,12 +382,12 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity)      code = alloc_stub(4); -    /* addi r12,0,beamAddress@l */ -    code[0] = 0x39800000 | ((unsigned long)beamAddress & 0xFFFF); +    /* addi r12,0,callee_exp@l */ +    code[0] = 0x39800000 | ((unsigned long)callee_exp & 0xFFFF);      /* addi r0,0,beamArity */      code[1] = 0x38000000 | (beamArity & 0x7FFF); -    /* addis r12,r12,beamAddress@ha */ -    code[2] = 0x3D8C0000 | at_ha((unsigned long)beamAddress); +    /* addis r12,r12,callee_exp@ha */ +    code[2] = 0x3D8C0000 | at_ha((unsigned long)callee_exp);      /* ba nbif_callemu */      code[3] = 0x48000002 | (unsigned long)&nbif_callemu; diff --git a/erts/emulator/hipe/hipe_ppc_glue.S b/erts/emulator/hipe/hipe_ppc_glue.S index 6f0217c738..0c337a14df 100644 --- a/erts/emulator/hipe/hipe_ppc_glue.S +++ b/erts/emulator/hipe/hipe_ppc_glue.S @@ -296,7 +296,7 @@ CSYM(hipe_ppc_throw_to_native):   * which should look as follows:   *   * stub for f/N: - *	<set r12 to f's BEAM code address> + *	<set r12 to f's export entry address>   *	<set r0 to N>   *	b nbif_callemu   * @@ -312,10 +312,10 @@ CSYM(hipe_ppc_throw_to_native):   */  	GLOBAL(ASYM(nbif_callemu))  ASYM(nbif_callemu): -	STORE	r12, P_BEAM_IP(P) +	STORE	r12, P_CALLEE_EXP(P)  	STORE	r0, P_ARITY(P)  	STORE_ARG_REGS -	li	r3, HIPE_MODE_SWITCH_RES_CALL +	li	r3, HIPE_MODE_SWITCH_RES_CALL_EXPORTED  	b	.suspend_exit  /* diff --git a/erts/emulator/hipe/hipe_process.h b/erts/emulator/hipe/hipe_process.h index 4ee99d78a2..86655ad42c 100644 --- a/erts/emulator/hipe/hipe_process.h +++ b/erts/emulator/hipe/hipe_process.h @@ -23,14 +23,17 @@  #define HIPE_PROCESS_H  #include "erl_alloc.h" +#include "export.h"  struct hipe_process_state {      Eterm *nsp;			/* Native stack pointer. */      Eterm *nstack;		/* Native stack block start. */      Eterm *nstend;		/* Native stack block end (start+size). */ -    /* XXX: ncallee and closure could share space in a union */ -    void (*ncallee)(void);	/* Native code callee (label) to invoke. */ -    Eterm closure;		/* Used to pass a closure from native code. */ +    union { +	void (*ncallee)(void);	/* Native code callee (label) to invoke. */ +	Eterm closure;		/* Used to pass a closure from native code. */ +	Export* callee_exp;     /* Used to pass export entry from native code */ +    }u;      Eterm *nstgraylim;		/* Gray/white stack boundary. */      Eterm *nstblacklim;		/* Black/gray stack boundary. Must exist if  				   graylim exists. Ignored if no graylim. */ diff --git a/erts/emulator/hipe/hipe_risc_stack.c b/erts/emulator/hipe/hipe_risc_stack.c index 1183856c7e..bea3a0fecd 100644 --- a/erts/emulator/hipe/hipe_risc_stack.c +++ b/erts/emulator/hipe/hipe_risc_stack.c @@ -226,7 +226,7 @@ void (*hipe_handle_stack_trap(Process *p))(void)   * The native stack MUST contain a stack frame as it appears on   * entry to a function (actuals, caller's frame, caller's return address).   * p->hipe.narity MUST contain the arity (number of actuals). - * On exit, p->hipe.ncallee is set to the handler's PC and p->hipe.nsp + * On exit, p->hipe.u.ncallee is set to the handler's PC and p->hipe.nsp   * is set to its SP (low address of its stack frame).   */  void hipe_find_handler(Process *p) @@ -254,7 +254,7 @@ void hipe_find_handler(Process *p)  	if ((exnra = sdesc_exnra(sdesc)) != 0 &&  	    (p->catches >= 0 ||  	     exnra == (unsigned long)&nbif_fail)) { -	    p->hipe.ncallee = (void(*)(void)) exnra; +	    p->hipe.u.ncallee = (void(*)(void)) exnra;  	    p->hipe.nsp = nsp;  	    p->hipe.narity = 0;  	    /* update the gray/white boundary if we threw past it */ diff --git a/erts/emulator/hipe/hipe_sparc.c b/erts/emulator/hipe/hipe_sparc.c index 49d4da7bab..2052aa8498 100644 --- a/erts/emulator/hipe/hipe_sparc.c +++ b/erts/emulator/hipe/hipe_sparc.c @@ -204,9 +204,7 @@ void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process *      return alloc_code(nrbytes);  } -/* called from hipe_bif0.c:hipe_bifs_make_native_stub_2() -   and hipe_bif0.c:hipe_make_stub() */ -void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) +void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity)  {      unsigned int *code;      unsigned int callEmuOffset; @@ -215,11 +213,11 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity)      code = alloc_code(5*sizeof(int));      /* sethi %hi(Address), %i4 */ -    code[0] = 0x39000000 | (((unsigned int)beamAddress >> 10) & 0x3FFFFF); +    code[0] = 0x39000000 | (((unsigned int)callee_exp >> 10) & 0x3FFFFF);      /* or %g0, %o7, %i3 ! mov %o7, %i3 */      code[1] = 0xB610000F;      /* or %i4, %lo(Address), %i4 */ -    code[2] = 0xB8172000 | ((unsigned int)beamAddress & 0x3FF); +    code[2] = 0xB8172000 | ((unsigned int)callee_exp & 0x3FF);      /* call callemu */      callEmuOffset = (char*)nbif_callemu - (char*)&code[3];      code[3] = (1 << 30) | ((callEmuOffset >> 2) & 0x3FFFFFFF); diff --git a/erts/emulator/hipe/hipe_sparc_glue.S b/erts/emulator/hipe/hipe_sparc_glue.S index 44bdf1bc7e..ab40a48ee7 100644 --- a/erts/emulator/hipe/hipe_sparc_glue.S +++ b/erts/emulator/hipe/hipe_sparc_glue.S @@ -155,9 +155,9 @@ hipe_sparc_throw_to_native:   * which should look as follows:   *   * stub for f/N: - *	sethi %hi(f's BEAM code address), TEMP_ARG0 + *	sethi %hi(f's export entry address), TEMP_ARG0   *	mov RA, TEMP_RA		! because the call below clobbers RA (%o7) - *	or TEMP_ARG0, %lo(f's BEAM code address), TEMP_ARG0 + *	or TEMP_ARG0, %lo(f's export entry address), TEMP_ARG0   *	call nbif_callemu	! clobbers RA!   *	mov N, TEMP_ARG1	! delay slot: TEMP_ARG1 := ARITY   * @@ -165,12 +165,12 @@ hipe_sparc_throw_to_native:   */  	.global nbif_callemu  nbif_callemu: -	st	TEMP_ARG0, [P+P_BEAM_IP] +	st	TEMP_ARG0, [P+P_CALLEE_EXP]  	st	TEMP_ARG1, [P+P_ARITY]  	st	TEMP_RA, [P+P_NRA]  	STORE_ARG_REGS  	ba	.flush_exit -	mov	HIPE_MODE_SWITCH_RES_CALL, %o0 +	mov	HIPE_MODE_SWITCH_RES_CALL_EXPORTED, %o0  /*   * nbif_apply diff --git a/erts/emulator/hipe/hipe_x86.c b/erts/emulator/hipe/hipe_x86.c index 327c74e9aa..314f6b597c 100644 --- a/erts/emulator/hipe/hipe_x86.c +++ b/erts/emulator/hipe/hipe_x86.c @@ -182,18 +182,16 @@ void *hipe_alloc_code(Uint nrbytes, Eterm callees, Eterm *trampolines, Process *      return alloc_code(nrbytes);  } -/* called from hipe_bif0.c:hipe_bifs_make_native_stub_2() -   and hipe_bif0.c:hipe_make_stub() */ -void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity) +void *hipe_make_native_stub(void *callee_exp, unsigned int beamArity)  {      /*       * This creates a native code stub with the following contents:       * -     * movl $Address, P_BEAM_IP(%ebp) +     * movl $Address, P_CALLEE_EXP(%ebp)       * movb $Arity, P_ARITY(%ebp)       * jmp callemu       * -     * The stub has variable size, depending on whether the P_BEAM_IP +     * The stub has variable size, depending on whether the P_CALLEE_EXP       * and P_ARITY offsets fit in 8-bit signed displacements or not.       * The rel32 offset in the final jmp depends on its actual location,       * which also depends on the size of the previous instructions. @@ -206,28 +204,28 @@ void *hipe_make_native_stub(void *beamAddress, unsigned int beamArity)      codeSize =	/* 16, 19, or 22 bytes */  	16 +	/* 16 when both offsets are 8-bit */ -	(P_BEAM_IP >= 128 ? 3 : 0) + +	(P_CALLEE_EXP >= 128 ? 3 : 0) +  	(P_ARITY >= 128 ? 3 : 0);      codep = code = alloc_code(codeSize); -    /* movl $beamAddress, P_BEAM_IP(%ebp); 3 or 6 bytes, plus 4 */ +    /* movl $beamAddress, P_CALLEE_EXP(%ebp); 3 or 6 bytes, plus 4 */      codep[0] = 0xc7; -#if P_BEAM_IP >= 128 +#if P_CALLEE_EXP >= 128      codep[1] = 0x85;	/* disp32[EBP] */ -    codep[2] = P_BEAM_IP & 0xFF; -    codep[3] = (P_BEAM_IP >> 8) & 0xFF; -    codep[4] = (P_BEAM_IP >> 16) & 0xFF; -    codep[5] = (P_BEAM_IP >> 24) & 0xFF; +    codep[2] = P_CALLEE_EXP & 0xFF; +    codep[3] = (P_CALLEE_EXP >> 8) & 0xFF; +    codep[4] = (P_CALLEE_EXP >> 16) & 0xFF; +    codep[5] = (P_CALLEE_EXP >> 24) & 0xFF;      codep += 6;  #else      codep[1] = 0x45;	/* disp8[EBP] */ -    codep[2] = P_BEAM_IP; +    codep[2] = P_CALLEE_EXP;      codep += 3;  #endif -    codep[0] = ((unsigned int)beamAddress) & 0xFF; -    codep[1] = ((unsigned int)beamAddress >> 8) & 0xFF; -    codep[2] = ((unsigned int)beamAddress >> 16) & 0xFF; -    codep[3] = ((unsigned int)beamAddress >> 24) & 0xFF; +    codep[0] = ((unsigned int)callee_exp) & 0xFF; +    codep[1] = ((unsigned int)callee_exp >> 8) & 0xFF; +    codep[2] = ((unsigned int)callee_exp >> 16) & 0xFF; +    codep[3] = ((unsigned int)callee_exp >> 24) & 0xFF;      codep += 4;      /* movb $beamArity, P_ARITY(%ebp); 3 or 6 bytes */ diff --git a/erts/emulator/hipe/hipe_x86_glue.S b/erts/emulator/hipe/hipe_x86_glue.S index 88b86f4de7..638780156a 100644 --- a/erts/emulator/hipe/hipe_x86_glue.S +++ b/erts/emulator/hipe/hipe_x86_glue.S @@ -104,7 +104,7 @@ ASYM(nbif_return):   * stub (hipe_x86_loader.erl) which should look as follows:   *   * stub for f/N: - *	movl	$<f's BEAM code address>, P_BEAM_IP(P) + *	movl	$<f's export entry address>, P_CALLEE_EXP(P)   *	movb	$<N>, P_ARITY(P)   *	jmp	nbif_callemu   * @@ -114,7 +114,7 @@ ASYM(nbif_return):  	GLOBAL(ASYM(nbif_callemu))  ASYM(nbif_callemu):  	STORE_ARG_REGS -	movl	$HIPE_MODE_SWITCH_RES_CALL, %eax +	movl	$HIPE_MODE_SWITCH_RES_CALL_EXPORTED, %eax  	jmp	.suspend_exit  /* diff --git a/erts/emulator/hipe/hipe_x86_stack.c b/erts/emulator/hipe/hipe_x86_stack.c index 9ad3fa9d31..7f1c2f7d41 100644 --- a/erts/emulator/hipe/hipe_x86_stack.c +++ b/erts/emulator/hipe/hipe_x86_stack.c @@ -209,7 +209,7 @@ void (*hipe_handle_stack_trap(Process *p))(void)   * The native stack MUST contain a stack frame as it appears on   * entry to a function (return address, actuals, caller's frame).   * p->hipe.narity MUST contain the arity (number of actuals). - * On exit, p->hipe.ncallee is set to the handler's PC and p->hipe.nsp + * On exit, p->hipe.u.ncallee is set to the handler's PC and p->hipe.nsp   * is set to its SP (low address of its stack frame).   */  void hipe_find_handler(Process *p) @@ -240,7 +240,7 @@ void hipe_find_handler(Process *p)  	if ((exnra = sdesc_exnra(sdesc)) != 0 &&  	    (p->catches >= 0 ||  	     exnra == (unsigned long)nbif_fail)) { -	    p->hipe.ncallee = (void(*)(void)) exnra; +	    p->hipe.u.ncallee = (void(*)(void)) exnra;  	    p->hipe.nsp = nsp;  	    p->hipe.narity = 0;  	    /* update the gray/white boundary if we threw past it */ diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index 0ded6b274e..ae44c8424f 100755 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -1392,39 +1392,46 @@ int parse_command(wchar_t* cmd){      return i;  } -static BOOL need_quotes(wchar_t *str) -{ -    int in_quote = 0; -    int backslashed = 0; -    int naked_space = 0; -    while (*str != L'\0') { -	switch (*str) { -	case L'\\' : -	    backslashed = !backslashed; -	    break; -	case L'"': -	    if (backslashed) { -		backslashed=0; -	    } else { -		in_quote = !in_quote; -	    } -	    break; -	case L' ': -	    backslashed = 0; -	    if (!(backslashed || in_quote)) { -		naked_space++; -	    } -	    break; -	default: -	    backslashed = 0; +/* + * Translating of command line arguments to correct format. In the examples + * below the '' are not part of the actual string.  + * 'io:format("hello").' -> 'io:format(\"hello\").' + * 'io:format("is anybody in there?").' -> '"io:format(\"is anybody in there?\")."' + * 'Just nod if you can hear me.' -> '"Just nod if you can hear me."' + * 'Is there ""anyone at home?' -> '"Is there \"\"anyone at home?"' + * 'Relax."' -> 'Relax.\"' + * + * If new == NULL we just calculate the length. + * + * The reason for having to quote all of the is becasue CreateProcessW removes + * one level of escaping since it takes a single long command line rather + * than the argument chunks that unix uses. + */ +static int escape_and_quote(wchar_t *str, wchar_t *new, BOOL *quoted) { +    int i, j = 0; +    if (new == NULL) +        *quoted = FALSE; +    else if (*quoted) +        new[j++] = L'"'; +    for ( i = 0; str[i] != L'\0'; i++,j++) { +        if (str[i] == L' ' && new == NULL && *quoted == FALSE) { +	    *quoted = TRUE; +	    j++; +	} +	/* check if we have to escape quotes */ +	if (str[i] == L'"') { +	    if (new) new[j] = L'\\'; +	    j++;  	} -	++str; +	if (new) new[j] = str[i];      } -    return (naked_space > 0); +    if (*quoted) { +        if (new) new[j] = L'"'; +	j++; +    } +    return j;  } -	     -	      /*   *---------------------------------------------------------------------- @@ -1585,31 +1592,24 @@ create_child_process  	    wcscpy(appname, execPath);  	}  	if (argv == NULL) {  -	    BOOL orig_need_q = need_quotes(execPath); +	    BOOL orig_need_q;  	    wchar_t *ptr; -	    int ocl = wcslen(execPath); +	    int ocl = escape_and_quote(execPath, NULL, &orig_need_q);  	    if (run_cmd) {  		newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, -						    (ocl + ((orig_need_q) ? 3 : 1) -						     + 11)*sizeof(wchar_t)); +						    (ocl + 1 + 11)*sizeof(wchar_t));  		memcpy(newcmdline,L"cmd.exe /c ",11*sizeof(wchar_t));  		ptr = newcmdline + 11;  	    } else {  		newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, -						    (ocl + ((orig_need_q) ? 3 : 1))*sizeof(wchar_t)); +						    (ocl + 1)*sizeof(wchar_t));  		ptr = (wchar_t *) newcmdline;  	    } -	    if (orig_need_q) { -		*ptr++ = L'"'; -	    } -	    memcpy(ptr,execPath,ocl*sizeof(wchar_t)); -	    ptr += ocl; -	    if (orig_need_q) { -		*ptr++ = L'"'; -	    } -	    *ptr = L'\0'; +	    ptr += escape_and_quote(execPath, ptr, &orig_need_q); +	    ptr[0] = L'\0';  	} else { -	    int sum = 1; /* '\0' */ +	    int sum = 0; +	    BOOL *qte = NULL;  	    wchar_t **ar = argv;  	    wchar_t *n;  	    wchar_t *save_arg0 = NULL; @@ -1620,11 +1620,13 @@ create_child_process  	    if (run_cmd) {  		sum += 11; /* cmd.exe /c */  	    } + +	    while (*ar != NULL)  ar++; +	    qte = erts_alloc(ERTS_ALC_T_TMP, (ar - argv)*sizeof(BOOL)); + +	    ar = argv;  	    while (*ar != NULL) { -		sum += wcslen(*ar); -		if (need_quotes(*ar)) { -		    sum += 2; /* quotes */ -		} +		sum += escape_and_quote(*ar,NULL,qte+(ar - argv));  		sum++; /* space */  		++ar;  	    } @@ -1636,26 +1638,18 @@ create_child_process  		n += 11;  	    }  	    while (*ar != NULL) { -		int q = need_quotes(*ar); -		sum = wcslen(*ar); -		if (q) { -		    *n++ = L'"'; -		} -		memcpy(n,*ar,sum*sizeof(wchar_t)); -		n += sum; -		if (q) { -		    *n++ = L'"'; -		} +		n += escape_and_quote(*ar,n,qte+(ar - argv));  		*n++ = L' ';  		++ar;  	    } -	    *(n-1) = L'\0'; +	    *(n-1) = L'\0'; /* overwrite last space with '\0' */  	    if (save_arg0 != NULL) {  		argv[0] = save_arg0;  	    } +	    erts_free(ERTS_ALC_T_TMP, qte);  	}	     -	DEBUGF(("Creating child process: %s, createFlags = %d\n", newcmdline, createFlags)); +	DEBUGF((stderr,"Creating child process: %S, createFlags = %d\n", newcmdline, createFlags));  	ok = CreateProcessW((wchar_t *) appname,  			    (wchar_t *) newcmdline,  			    NULL, diff --git a/erts/emulator/test/module_info_SUITE.erl b/erts/emulator/test/module_info_SUITE.erl index 8a63d9fe3e..f3986f0c4f 100644 --- a/erts/emulator/test/module_info_SUITE.erl +++ b/erts/emulator/test/module_info_SUITE.erl @@ -24,7 +24,7 @@  -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,   	 init_per_group/2,end_per_group/2,  	 init_per_testcase/2,end_per_testcase/2, -	 exports/1,functions/1,native/1]). +	 exports/1,functions/1,native/1,info/1]).  %%-compile(native). @@ -52,8 +52,8 @@ end_per_group(_GroupName, Config) ->      Config. -modules() ->  -    [exports, functions, native]. +modules() -> +    [exports, functions, native, info].  init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->      Dog = ?t:timetrap(?t:minutes(3)), @@ -122,6 +122,22 @@ native_proj({Name,Arity,Addr}) ->  native_filter(Set) ->      sofs:no_elements(Set) =/= 1. +%% Test that the module info of this module is correct. Use +%% erlang:get_module_info(?MODULE) to avoid compiler optimization tricks. +info(Config) when is_list(Config) -> +    Info = erlang:get_module_info(?MODULE), +    All = all_exported(), +    {ok,{?MODULE,MD5}} = beam_lib:md5(code:which(?MODULE)), +    {module, ?MODULE} = lists:keyfind(module, 1, Info), +    {md5, MD5} = lists:keyfind(md5, 1, Info), +    {exports, Exports} = lists:keyfind(exports, 1, Info), +    All = lists:sort(Exports), +    {attributes, Attrs} = lists:keyfind(attributes, 1, Info), +    {vsn,_} = lists:keyfind(vsn, 1, Attrs), +    {compile, Compile} = lists:keyfind(compile, 1, Info), +    {options,_} = lists:keyfind(options, 1, Compile), +    ok. +  %% Helper functions (local).  add_arity(L) -> diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index e01b2f253b..738d60b8a4 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -1405,6 +1405,12 @@ spawn_executable(Config) when is_list(Config) ->  	run_echo_args(SpaceDir,[ExactFile2,"hello world","dlrow olleh"]),      [ExactFile2,"hello world","dlrow olleh"] =  	run_echo_args(SpaceDir,[binary, ExactFile2,"hello world","dlrow olleh"]), + +    [ExactFile2,"hello \"world\"","\"dlrow\" olleh"] = +	run_echo_args(SpaceDir,[binary, ExactFile2,"hello \"world\"","\"dlrow\" olleh"]), +    [ExactFile2,"hello \"world\"","\"dlrow\" olleh"] = +	run_echo_args(SpaceDir,[binary, ExactFile2,"hello \"world\"","\"dlrow\" olleh"]), +      [ExactFile2] = run_echo_args(SpaceDir,[default]),      [ExactFile2,"hello world","dlrow olleh"] =   	run_echo_args(SpaceDir,[switch_order,ExactFile2,"hello world", diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 1508eed9ee..dd3ea87c90 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1664,7 +1664,7 @@ element(_N, _Tuple) ->  %% Not documented  -spec erlang:get_module_info(Module, Item) -> ModuleInfo when        Module :: atom(), -      Item :: module | imports | exports | functions | attributes | compile | native_addresses, +      Item :: module | imports | exports | functions | attributes | compile | native_addresses | md5,        ModuleInfo :: atom() | [] | [{atom(), arity()}] | [{atom(), term()}] | [{atom(), arity(), integer()}].  get_module_info(_Module, _Item) ->      erlang:nif_error(undefined). diff --git a/erts/test/erlc_SUITE_data/src/start_ok.script b/erts/test/erlc_SUITE_data/src/start_ok.script index 4cd89f0439..7ef97dc3f3 100644 --- a/erts/test/erlc_SUITE_data/src/start_ok.script +++ b/erts/test/erlc_SUITE_data/src/start_ok.script @@ -52,7 +52,6 @@  	     shell_default,  	     timer,  	     gen_fsm, -	     pg,  	     unix,  	     dict,  	     pool, @@ -156,7 +155,6 @@                                          {timer,1},                                          {gen_fsm,1},                                          {io_lib_pretty,1}, -                                        {pg,1},                                          {slave,1},                                          {unix,1},                                          {dict,1}, diff --git a/erts/vsn.mk b/erts/vsn.mk index fff334c89f..ab98bd4a17 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@  # %CopyrightEnd%  #  -VSN = 6.0.2 +VSN = 7.0  # Port number 4365 in 4.2  # Port number 4366 in 4.3 diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl index 761ae8409c..6410b73941 100644 --- a/lib/compiler/src/sys_pre_expand.erl +++ b/lib/compiler/src/sys_pre_expand.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 1996-2012. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -33,12 +33,15 @@  -include("../include/erl_bits.hrl"). +-type fa() :: {atom(), arity()}. +  -record(expand, {module=[],                     %Module name                   exports=[],                    %Exports                   imports=[],                    %Imports                   compile=[],                    %Compile flags                   attributes=[],                 %Attributes                   callbacks=[],                  %Callbacks +                 optional_callbacks=[] :: [fa()],  %Optional callbacks                   defined,			%Defined functions (gb_set)                   vcount=0,                      %Variable counter                   func=[],                       %Current function @@ -99,7 +102,21 @@ define_functions(Forms, #expand{defined=Predef}=St) ->  module_attrs(#expand{attributes=Attributes}=St) ->      Attrs = [{attribute,Line,Name,Val} || {Name,Line,Val} <- Attributes],      Callbacks = [Callback || {_,_,callback,_}=Callback <- Attrs], -    {Attrs,St#expand{callbacks=Callbacks}}. +    OptionalCallbacks = get_optional_callbacks(Attrs), +    {Attrs,St#expand{callbacks=Callbacks, +                     optional_callbacks=OptionalCallbacks}}. + +get_optional_callbacks(Attrs) -> +    L = [O || +            {attribute, _, optional_callbacks, O} <- Attrs, +            is_fa_list(O)], +    lists:append(L). + +is_fa_list([{FuncName, Arity}|L]) +  when is_atom(FuncName), is_integer(Arity), Arity >= 0 -> +    is_fa_list(L); +is_fa_list([]) -> true; +is_fa_list(_) -> false.  module_predef_funcs(St) ->      {Mpf1,St1}=module_predef_func_beh_info(St), @@ -108,19 +125,24 @@ module_predef_funcs(St) ->  module_predef_func_beh_info(#expand{callbacks=[]}=St) ->      {[], St}; -module_predef_func_beh_info(#expand{callbacks=Callbacks,defined=Defined, +module_predef_func_beh_info(#expand{callbacks=Callbacks, +                                    optional_callbacks=OptionalCallbacks, +                                    defined=Defined,  				    exports=Exports}=St) ->      PreDef=[{behaviour_info,1}],      PreExp=PreDef, -    {[gen_beh_info(Callbacks)], +    {[gen_beh_info(Callbacks, OptionalCallbacks)],       St#expand{defined=gb_sets:union(gb_sets:from_list(PreDef), Defined),  	       exports=union(from_list(PreExp), Exports)}}. -gen_beh_info(Callbacks) -> +gen_beh_info(Callbacks, OptionalCallbacks) ->      List = make_list(Callbacks), +    OptionalList = make_optional_list(OptionalCallbacks),      {function,0,behaviour_info,1,       [{clause,0,[{atom,0,callbacks}],[], -       [List]}]}. +       [List]}, +      {clause,0,[{atom,0,optional_callbacks}],[], +       [OptionalList]}]}.  make_list([]) -> {nil,0};  make_list([{_,_,_,[{{Name,Arity},_}]}|Rest]) -> @@ -130,6 +152,14 @@ make_list([{_,_,_,[{{Name,Arity},_}]}|Rest]) ->         {integer,0,Arity}]},       make_list(Rest)}. +make_optional_list([]) -> {nil,0}; +make_optional_list([{Name,Arity}|Rest]) -> +    {cons,0, +     {tuple,0, +      [{atom,0,Name}, +       {integer,0,Arity}]}, +     make_optional_list(Rest)}. +  module_predef_funcs_mod_info(St) ->      PreDef = [{module_info,0},{module_info,1}],      PreExp = PreDef, diff --git a/lib/dialyzer/src/dialyzer_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl index 1d458b49fc..bbedd3201e 100644 --- a/lib/dialyzer/src/dialyzer_behaviours.erl +++ b/lib/dialyzer/src/dialyzer_behaviours.erl @@ -102,14 +102,18 @@ check_all_callbacks(Module, Behaviour, [Cb|Rest],  		    #state{plt = Plt, codeserver = Codeserver,  			   records = Records} = State, Acc) ->    {{Behaviour, Function, Arity}, -   {{_BehFile, _BehLine}, Callback}} = Cb, +   {{_BehFile, _BehLine}, Callback, Xtra}} = Cb,    CbMFA = {Module, Function, Arity},    CbReturnType = dialyzer_contracts:get_contract_return(Callback),    CbArgTypes = dialyzer_contracts:get_contract_args(Callback),    Acc0 = Acc,    Acc1 =       case dialyzer_plt:lookup(Plt, CbMFA) of -      'none' -> [{callback_missing, [Behaviour, Function, Arity]}|Acc0]; +      'none' -> +        case lists:member(optional_callback, Xtra) of +          true -> Acc0; +          false -> [{callback_missing, [Behaviour, Function, Arity]}|Acc0] +        end;        {'value', RetArgTypes} ->  	Acc00 = Acc0,  	{ReturnType, ArgTypes} = RetArgTypes, @@ -137,7 +141,7 @@ check_all_callbacks(Module, Behaviour, [Cb|Rest],    Acc2 =      case dialyzer_codeserver:lookup_mfa_contract(CbMFA, Codeserver) of        'error' -> Acc1; -      {ok, {{File, Line}, Contract}} -> +      {ok, {{File, Line}, Contract, _Xtra}} ->  	Acc10 = Acc1,  	SpecReturnType0 = dialyzer_contracts:get_contract_return(Contract),  	SpecArgTypes0 = dialyzer_contracts:get_contract_args(Contract), diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl index aab3d6add6..593e71f30b 100644 --- a/lib/dialyzer/src/dialyzer_codeserver.erl +++ b/lib/dialyzer/src/dialyzer_codeserver.erl @@ -278,10 +278,10 @@ lookup_mod_contracts(Mod, #codeserver{contracts = ContDict})    case ets_dict_find(Mod, ContDict) of      error -> dict:new();      {ok, Keys} -> -      dict:from_list([get_contract_pair(Key, ContDict)|| Key <- Keys]) +      dict:from_list([get_file_contract(Key, ContDict)|| Key <- Keys])    end. -get_contract_pair(Key, ContDict) -> +get_file_contract(Key, ContDict) ->    {Key, ets:lookup_element(ContDict, Key, 2)}.  -spec lookup_mfa_contract(mfa(), codeserver()) -> diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl index 1d2dfc7b2d..3aa5a1779c 100644 --- a/lib/dialyzer/src/dialyzer_contracts.erl +++ b/lib/dialyzer/src/dialyzer_contracts.erl @@ -45,7 +45,7 @@  %% Types used in other parts of the system below  %%----------------------------------------------------------------------- --type file_contract() :: {file_line(), #contract{}}. +-type file_contract() :: {file_line(), #contract{}, Extra :: [_]}.  -type plt_contracts() :: [{mfa(), #contract{}}]. % actually, an orddict() @@ -148,10 +148,10 @@ process_contract_remote_types(CodeServer) ->    ExpTypes = dialyzer_codeserver:get_exported_types(CodeServer),    RecordDict = dialyzer_codeserver:get_records(CodeServer),    ContractFun = -    fun({_M, _F, _A}, {File, #tmp_contract{contract_funs = CFuns, forms = Forms}}) -> +    fun({_M, _F, _A}, {File, #tmp_contract{contract_funs = CFuns, forms = Forms}, Xtra}) ->  	NewCs = [CFun(ExpTypes, RecordDict) || CFun <- CFuns],  	Args = general_domain(NewCs), -	{File, #contract{contracts = NewCs, args = Args, forms = Forms}} +	{File, #contract{contracts = NewCs, args = Args, forms = Forms}, Xtra}      end,    ModuleFun =      fun(_ModuleName, ContractDict) -> @@ -177,7 +177,7 @@ check_contracts(Contracts, Callgraph, FunTypes, FindOpaques) ->  	case dialyzer_callgraph:lookup_name(Label, Callgraph) of  	  {ok, {M,F,A} = MFA} ->  	    case orddict:find(MFA, Contracts) of -	      {ok, {_FileLine, Contract}} -> +	      {ok, {_FileLine, Contract, _Xtra}} ->                  Opaques = FindOpaques(M),  		case check_contract(Contract, Type, Opaques) of  		  ok -> @@ -364,7 +364,7 @@ contracts_without_fun(Contracts, AllFuns0, Callgraph) ->    [warn_spec_missing_fun(MFA, Contracts) || MFA <- ErrorContractMFAs].  warn_spec_missing_fun({M, F, A} = MFA, Contracts) -> -  {FileLine, _Contract} = dict:fetch(MFA, Contracts), +  {FileLine, _Contract, _Xtra} = dict:fetch(MFA, Contracts),    {?WARN_CONTRACT_SYNTAX, FileLine, {spec_missing_fun, [M, F, A]}}.  %% This treats the "when" constraints. It will be extended, we hope. @@ -388,14 +388,16 @@ insert_constraints([], Dict) -> Dict.  -type types() :: erl_types:type_table(). --spec store_tmp_contract(mfa(), file_line(), [_], contracts(), types()) -> +-type spec_data() :: {TypeSpec :: [_], Xtra:: [_]}. + +-spec store_tmp_contract(mfa(), file_line(), spec_data(), contracts(), types()) ->          contracts(). -store_tmp_contract(MFA, FileLine, TypeSpec, SpecDict, RecordsDict) -> +store_tmp_contract(MFA, FileLine, {TypeSpec, Xtra}, SpecDict, RecordsDict) ->    %% io:format("contract from form: ~p\n", [TypeSpec]),    TmpContract = contract_from_form(TypeSpec, RecordsDict, FileLine),    %% io:format("contract: ~p\n", [TmpContract]), -  dict:store(MFA, {FileLine, TmpContract}, SpecDict). +  dict:store(MFA, {FileLine, TmpContract, Xtra}, SpecDict).  contract_from_form(Forms, RecDict, FileLine) ->    {CFuns, Forms1} = contract_from_form(Forms, RecDict, FileLine, [], []), @@ -599,7 +601,7 @@ get_invalid_contract_warnings_modules([Mod|Mods], CodeServer, Plt, FindOpaques,  get_invalid_contract_warnings_modules([], _CodeServer, _Plt, _FindOpaques, Acc) ->    Acc. -get_invalid_contract_warnings_funs([{MFA, {FileLine, Contract}}|Left], +get_invalid_contract_warnings_funs([{MFA, {FileLine, Contract, _Xtra}}|Left],  				   Plt, RecDict, FindOpaques, Acc) ->    case dialyzer_plt:lookup(Plt, MFA) of      none -> diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index e0873b17f8..bb5604cd5f 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -2770,8 +2770,7 @@ filter_match_fail([]) ->  %%% ===========================================================================  state__new(Callgraph, Tree, Plt, Module, Records) -> -  Opaques = erl_types:module_builtin_opaques(Module) ++ -    erl_types:t_opaque_from_records(Records), +  Opaques = erl_types:t_opaque_from_records(Records),    TreeMap = build_tree_map(Tree),    Funs = dict:fetch_keys(TreeMap),    FunTab = init_fun_tab(Funs, dict:new(), TreeMap, Callgraph, Plt), diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl index 63798f44b1..7c970daf41 100644 --- a/lib/dialyzer/src/dialyzer_plt.erl +++ b/lib/dialyzer/src/dialyzer_plt.erl @@ -158,9 +158,7 @@ lookup_contract(#mini_plt{contracts = ETSContracts},    ets_table_lookup(ETSContracts, MFA).  -spec lookup_callbacks(plt(), module()) -> -	 'none' | {'value', [{mfa(), {{Filename::string(), -				       Line::pos_integer()}, -				      #contract{}}}]}. +	 'none' | {'value', [{mfa(), dialyzer_contracts:file_contract()}]}.  lookup_callbacks(#mini_plt{callbacks = ETSCallbacks}, Mod) when is_atom(Mod) ->    ets_table_lookup(ETSCallbacks, Mod). @@ -618,9 +616,7 @@ table_insert_list(Plt, [{Key, Val}|Left]) ->  table_insert_list(Plt, []) ->    Plt. -table_insert(Plt, Key, {_Ret, _Arg} = Obj) -> -  dict:store(Key, Obj, Plt); -table_insert(Plt, Key, #contract{} = C) -> +table_insert(Plt, Key, {_File, #contract{}, _Xtra} = C) ->    dict:store(Key, C, Plt).  table_lookup(Plt, Obj) -> diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index ef9b00e203..6dc4285194 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -201,7 +201,7 @@ postprocess_dataflow_warns([{?WARN_CONTRACT_RANGE, {CallF, CallL}, Msg}|Rest],  			   Codeserver, WAcc, Acc) ->    {contract_range, [Contract, M, F, A, ArgStrings, CRet]} = Msg,    case dialyzer_codeserver:lookup_mfa_contract({M,F,A}, Codeserver) of -    {ok, {{ContrF, _ContrL} = FileLine, _C}} -> +    {ok, {{ContrF, _ContrL} = FileLine, _C, _X}} ->        case CallF =:= ContrF of  	true ->  	  NewMsg = {contract_range, [Contract, M, F, ArgStrings, CallL, CRet]}, @@ -401,7 +401,7 @@ decorate_succ_typings(Contracts, Callgraph, FunTypes, FindOpaques) ->            case dialyzer_callgraph:lookup_name(Label, Callgraph) of              {ok, MFA} ->                case orddict:find(MFA, Contracts) of -                {ok, {_FileLine, Contract}} -> +                {ok, {_FileLine, Contract, _Xtra}} ->                    Args = dialyzer_contracts:get_contract_args(Contract),                    Ret = dialyzer_contracts:get_contract_return(Contract),                    C = erl_types:t_fun(Args, Ret), @@ -422,10 +422,7 @@ lookup_and_find_opaques_fun(Codeserver) ->    end.  find_opaques_fun(Records) -> -  fun(Module) -> -      erl_types:module_builtin_opaques(Module) ++ -      erl_types:t_opaque_from_records(Records) -  end. +  fun(_Module) -> erl_types:t_opaque_from_records(Records) end.  get_fun_types_from_plt(FunList, Callgraph, Plt) ->    get_fun_types_from_plt(FunList, Callgraph, Plt, dict:new()). diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl index 21183e3459..54f8d1d673 100644 --- a/lib/dialyzer/src/dialyzer_utils.erl +++ b/lib/dialyzer/src/dialyzer_utils.erl @@ -322,8 +322,26 @@ merge_records(NewRecords, OldRecords) ->          {'ok', spec_dict(), callback_dict()} | {'error', string()}.  get_spec_info(ModName, AbstractCode, RecordsDict) -> +  OptionalCallbacks0 = get_optional_callbacks(AbstractCode, ModName), +  OptionalCallbacks = gb_sets:from_list(OptionalCallbacks0),    get_spec_info(AbstractCode, dict:new(), dict:new(), -		RecordsDict, ModName, "nofile"). +		RecordsDict, ModName, OptionalCallbacks, "nofile"). + +get_optional_callbacks(Abs, ModName) -> +  [{ModName, F, A} || {F, A} <- get_optional_callbacks(Abs)]. + +get_optional_callbacks(Abs) -> +    L = [O || +            {attribute, _, optional_callbacks, O} <- Abs, +            is_fa_list(O)], +    lists:append(L). + +is_fa_list([{FuncName, Arity}|L]) +  when is_atom(FuncName), is_integer(Arity), Arity >= 0 -> +    is_fa_list(L); +is_fa_list([]) -> true; +is_fa_list(_) -> false. +  %% TypeSpec is a list of conditional contracts for a function.  %% Each contract is of the form {[Argument], Range, [Constraint]} where @@ -332,13 +350,14 @@ get_spec_info(ModName, AbstractCode, RecordsDict) ->  %%    are erl_types:erl_type()  get_spec_info([{attribute, Ln, Contract, {Id, TypeSpec}}|Left], -	      SpecDict, CallbackDict, RecordsDict, ModName, File) +	      SpecDict, CallbackDict, RecordsDict, ModName, OptCb, File)    when ((Contract =:= 'spec') or (Contract =:= 'callback')),         is_list(TypeSpec) ->    MFA = case Id of  	  {_, _, _} = T -> T;  	  {F, A} -> {ModName, F, A}  	end, +  Xtra = [optional_callback || gb_sets:is_member(MFA, OptCb)],    ActiveDict =      case Contract of        spec     -> SpecDict; @@ -346,8 +365,9 @@ get_spec_info([{attribute, Ln, Contract, {Id, TypeSpec}}|Left],      end,    try dict:find(MFA, ActiveDict) of      error -> +      SpecData = {TypeSpec, Xtra},        NewActiveDict = -	dialyzer_contracts:store_tmp_contract(MFA, {File, Ln}, TypeSpec, +	dialyzer_contracts:store_tmp_contract(MFA, {File, Ln}, SpecData,  					      ActiveDict, RecordsDict),        {NewSpecDict, NewCallbackDict} =  	case Contract of @@ -355,8 +375,8 @@ get_spec_info([{attribute, Ln, Contract, {Id, TypeSpec}}|Left],  	  callback -> {SpecDict, NewActiveDict}  	end,        get_spec_info(Left, NewSpecDict, NewCallbackDict, -		    RecordsDict, ModName,File); -    {ok, {{OtherFile, L},_C}} -> +		    RecordsDict, ModName, OptCb, File); +    {ok, {{OtherFile, L}, _D}} ->        {Mod, Fun, Arity} = MFA,        Msg = flat_format("  Contract/callback for function ~w:~w/~w "  			"already defined in ~s:~w\n", @@ -368,13 +388,15 @@ get_spec_info([{attribute, Ln, Contract, {Id, TypeSpec}}|Left],  			  [Ln, Error])}    end;  get_spec_info([{attribute, _, file, {IncludeFile, _}}|Left], -	      SpecDict, CallbackDict, RecordsDict, ModName, _File) -> +	      SpecDict, CallbackDict, RecordsDict, ModName, OptCb, _File) ->    get_spec_info(Left, SpecDict, CallbackDict, -		RecordsDict, ModName, IncludeFile); +		RecordsDict, ModName, OptCb, IncludeFile);  get_spec_info([_Other|Left], SpecDict, CallbackDict, -	      RecordsDict, ModName, File) -> -  get_spec_info(Left, SpecDict, CallbackDict, RecordsDict, ModName, File); -get_spec_info([], SpecDict, CallbackDict, _RecordsDict, _ModName, _File) -> +	      RecordsDict, ModName, OptCb, File) -> +  get_spec_info(Left, SpecDict, CallbackDict, +                RecordsDict, ModName, OptCb, File); +get_spec_info([], SpecDict, CallbackDict, +              _RecordsDict, _ModName, _OptCb, _File) ->    {ok, SpecDict, CallbackDict}.  %% ============================================================================ diff --git a/lib/dialyzer/test/small_SUITE_data/results/behaviour_info b/lib/dialyzer/test/small_SUITE_data/results/behaviour_info new file mode 100644 index 0000000000..2da4d26acb --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/behaviour_info @@ -0,0 +1,2 @@ + +with_bad_format_status.erl:12: The inferred type for the 1st argument of format_status/2 ('bad_arg') is not a supertype of 'normal' | 'terminate', which is expected type for this argument in the callback of the gen_server behaviour diff --git a/lib/dialyzer/test/small_SUITE_data/src/behaviour_info/with_bad_format_status.erl b/lib/dialyzer/test/small_SUITE_data/src/behaviour_info/with_bad_format_status.erl new file mode 100644 index 0000000000..24591e08fa --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/behaviour_info/with_bad_format_status.erl @@ -0,0 +1,12 @@ +-module(with_bad_format_status). + +-behaviour(gen_server). +-export([handle_call/3,handle_cast/2,handle_info/2, +         code_change/3, init/1, terminate/2, format_status/2]). +handle_call(_, _, S) -> {noreply, S}. +handle_cast(_, S) -> {noreply, S}. +handle_info(_, S) -> {noreply, S}. +code_change(_, _, _) -> {error, not_implemented}. +init(_) -> {ok, state}. +terminate(_, _) -> ok. +format_status(bad_arg, _) -> ok. % optional callback diff --git a/lib/dialyzer/test/small_SUITE_data/src/behaviour_info/with_format_status.erl b/lib/dialyzer/test/small_SUITE_data/src/behaviour_info/with_format_status.erl new file mode 100644 index 0000000000..a56ff63d1d --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/behaviour_info/with_format_status.erl @@ -0,0 +1,13 @@ +-module(with_format_status). + +-behaviour(gen_server). +-export([handle_call/3,handle_cast/2,handle_info/2, +         code_change/3, init/1, terminate/2, format_status/2]). +-export([handle_call/3,handle_cast/2,handle_info/2]). +handle_call(_, _, S) -> {noreply, S}. +handle_cast(_, S) -> {noreply, S}. +handle_info(_, S) -> {noreply, S}. +code_change(_, _, _) -> {error, not_implemented}. +init(_) -> {ok, state}. +terminate(_, _) -> ok. +format_status(normal, _) -> ok. % optional callback diff --git a/lib/dialyzer/test/small_SUITE_data/src/predef2.erl b/lib/dialyzer/test/small_SUITE_data/src/predef2.erl deleted file mode 100644 index b1d941a49a..0000000000 --- a/lib/dialyzer/test/small_SUITE_data/src/predef2.erl +++ /dev/null @@ -1,56 +0,0 @@ --module(predef2). - --export([array/1, dict/1, digraph/1, digraph2/1, gb_set/1, gb_tree/1, -         queue/1, set/1, tid/0, tid2/0]). - --export_type([array/0, digraph/0, gb_set/0]). - --spec array(array()) -> array:array(). - -array(A) -> -    array:relax(A). - --spec dict(dict()) -> dict:dict(). - -dict(D) -> -    dict:store(1, a, D). - --spec digraph(digraph()) -> [digraph:edge()]. - -digraph(G) -> -    digraph:edges(G). - --spec digraph2(digraph:graph()) -> [digraph:edge()]. - -digraph2(G) -> -    digraph:edges(G). - --spec gb_set(gb_set()) -> gb_sets:set(). - -gb_set(S) -> -    gb_sets:balance(S). - --spec gb_tree(gb_tree()) -> gb_trees:tree(). - -gb_tree(S) -> -    gb_trees:balance(S). - --spec queue(queue()) -> queue:queue(). - -queue(Q) -> -    queue:reverse(Q). - --spec set(set()) -> sets:set(). - -set(S) -> -    sets:union([S]). - --spec tid() -> tid(). - -tid() -> -    ets:new(tid, []). - --spec tid2() -> ets:tid(). - -tid2() -> -    ets:new(tid, []). diff --git a/lib/edoc/src/edoc_data.erl b/lib/edoc/src/edoc_data.erl index f88ba05f4b..eceb5cb1bd 100644 --- a/lib/edoc/src/edoc_data.erl +++ b/lib/edoc/src/edoc_data.erl @@ -173,21 +173,34 @@ callbacks(Es, Module, Env, Opts) ->  	lists:keymember(callback, 1, Module#module.attributes)      of  	true -> -	    try (Module#module.name):behaviour_info(callbacks) of -		Fs -> -		    Fs1 = [{F,A} || {F,A} <- Fs, is_atom(F), is_integer(A)], -		    if Fs1 =:= [] -> -			    []; -		       true -> -			    [{callbacks, -			      [callback(F, Env, Opts) || F <- Fs1]}] -		    end -	    catch -		_:_ -> [] -	    end; +            M = Module#module.name, +            Fs = get_callback_functions(M, callbacks), +            Os1 = get_callback_functions(M, optional_callbacks), +            Fs1 = [FA || FA <- Fs, not lists:member(FA, Os1)], +            Req = if Fs1 =:= [] -> +                          []; +                     true -> +                          [{callbacks, +                            [callback(FA, Env, Opts) || FA <- Fs1]}] +                  end, +            Opt = if Os1 =:= [] -> +                          []; +                     true -> +                          [{optional_callbacks, +                            [callback(FA, Env, Opts) || FA <- Os1]}] +                  end, +            Req ++ Opt;  	false -> []      end. +get_callback_functions(M, Callbacks) -> +    try +        [FA || {F, A} = FA <- M:behaviour_info(Callbacks), +               is_atom(F), is_integer(A), A >= 0] +    catch +        _:_ -> [] +    end. +  %% <!ELEMENT callback EMPTY>  %% <!ATTLIST callback  %%   name CDATA #REQUIRED diff --git a/lib/edoc/src/edoc_layout.erl b/lib/edoc/src/edoc_layout.erl index e164ff060f..87018af25a 100644 --- a/lib/edoc/src/edoc_layout.erl +++ b/lib/edoc/src/edoc_layout.erl @@ -701,6 +701,8 @@ deprecated(Es, S) ->      end.  behaviours(Es, Name) -> +    CBs = get_content(callbacks, Es), +    OCBs = get_content(optional_callbacks, Es),      (case get_elem(behaviour, Es) of  	 [] -> [];  	 Es1 -> @@ -709,13 +711,24 @@ behaviours(Es, Name) ->  	      ?NL]       end       ++ -     case get_content(callbacks, Es) of -	 [] -> []; -	 Es1 -> +     if CBs =:= [], OCBs =:= [] -> +             []; +	 true -> +             Req = if CBs =:= [] -> +                       []; +                       true -> +                           [br, " Required callback functions: "] +                           ++ seq(fun callback/1, CBs, ["."]) +                   end, +             Opt = if OCBs =:= [] -> +                       []; +                       true -> +                           [br, " Optional callback functions: "] +                           ++ seq(fun callback/1, OCBs, ["."]) +                   end,  	     [{p, ([{b, ["This module defines the ", {tt, [Name]}, -			 " behaviour."]}, -		    br, " Required callback functions: "] -		   ++ seq(fun callback/1, Es1, ["."]))}, +			 " behaviour."]}] +                   ++ Req ++ Opt)},  	      ?NL]       end). diff --git a/lib/edoc/src/edoc_specs.erl b/lib/edoc/src/edoc_specs.erl index 211a354c74..3bf81c6503 100644 --- a/lib/edoc/src/edoc_specs.erl +++ b/lib/edoc/src/edoc_specs.erl @@ -362,7 +362,7 @@ d2e({type,_,map,any}) ->      #t_map{ types = []};  d2e({type,_,map,Es}) ->      #t_map{ types = d2e(Es) }; -d2e({type,_,map_field_assoc,K,V}) -> +d2e({type,_,map_field_assoc,[K,V]}) ->      #t_map_field{ k_type = d2e(K), v_type=d2e(V) };  d2e({type,_,map_field_exact,K,V}) ->      #t_map_field{ k_type = d2e(K), v_type=d2e(V) }; @@ -388,6 +388,9 @@ d2e({record_field,L,_Name}=F) ->  d2e({type,_,Name,Types0}) ->      Types = d2e(Types0),      typevar_anno(#t_type{name = #t_name{name = Name}, args = Types}, Types); +d2e({user_type,_,Name,Types0}) -> +    Types = d2e(Types0), +    typevar_anno(#t_type{name = #t_name{name = Name}, args = Types}, Types);  d2e({var,_,'_'}) ->      #t_type{name = #t_name{name = ?TOP_TYPE}};  d2e({var,_,TypeName}) -> diff --git a/lib/edoc/src/edoc_tags.erl b/lib/edoc/src/edoc_tags.erl index 264a533a52..82a1b72d84 100644 --- a/lib/edoc/src/edoc_tags.erl +++ b/lib/edoc/src/edoc_tags.erl @@ -329,10 +329,7 @@ parse_typedef(Data, Line, _Env, Where) ->      NAs = length(As),      case edoc_types:is_predefined(T, NAs) of  	true -> -            case -                edoc_types:is_new_predefined(T, NAs) -                orelse edoc_types:is_predefined_otp_type(T, NAs) -            of +            case edoc_types:is_new_predefined(T, NAs) of                  false ->                      throw_error(Line, {"redefining built-in type '~w'.",                                         [T]}); @@ -499,7 +496,6 @@ check_used_type(#t_name{name = N, module = Mod}=Name, Args, P, LocalTypes) ->          Mod =/= []          orelse lists:member(TypeName, ets:lookup(DT, Name))          orelse edoc_types:is_predefined(N, NArgs) -        orelse edoc_types:is_predefined_otp_type(N, NArgs)          orelse lists:member(TypeName, LocalTypes)      of          true -> diff --git a/lib/edoc/src/edoc_types.erl b/lib/edoc/src/edoc_types.erl index d4e00d3ecd..2f21eb24b6 100644 --- a/lib/edoc/src/edoc_types.erl +++ b/lib/edoc/src/edoc_types.erl @@ -25,7 +25,7 @@  -module(edoc_types). --export([is_predefined/2, is_new_predefined/2, is_predefined_otp_type/2, +-export([is_predefined/2, is_new_predefined/2,           to_ref/1, to_xml/2, to_label/1, arg_names/1, set_arg_names/2,           arg_descs/1, range_desc/1]). @@ -34,67 +34,13 @@  -include("edoc_types.hrl").  -include_lib("xmerl/include/xmerl.hrl"). - -is_predefined(any, 0) -> true; -is_predefined(atom, 0) -> true; -is_predefined(binary, 0) -> true; -is_predefined(bool, 0) -> true;    % kept for backwards compatibility -is_predefined(char, 0) -> true;  is_predefined(cons, 2) -> true;  is_predefined(deep_string, 0) -> true; -is_predefined(float, 0) -> true; -is_predefined(function, 0) -> true; -is_predefined(integer, 0) -> true; -is_predefined(list, 0) -> true; -is_predefined(list, 1) -> true; -is_predefined(nil, 0) -> true; -is_predefined(none, 0) -> true; -is_predefined(no_return, 0) -> true; -is_predefined(number, 0) -> true; -is_predefined(pid, 0) -> true; -is_predefined(port, 0) -> true; -is_predefined(reference, 0) -> true; -is_predefined(string, 0) -> true; -is_predefined(term, 0) -> true; -is_predefined(tuple, 0) -> true; -is_predefined(F, A) -> is_new_predefined(F, A). +is_predefined(F, A) -> erl_internal:is_type(F, A). -%% Should eventually be coalesced with is_predefined/2. -is_new_predefined(arity, 0) -> true; -is_new_predefined(bitstring, 0) -> true; -is_new_predefined(boolean, 0) -> true; -is_new_predefined(byte, 0) -> true; -is_new_predefined(iodata, 0) -> true; -is_new_predefined(iolist, 0) -> true;  is_new_predefined(map, 0) -> true; -is_new_predefined(maybe_improper_list, 0) -> true; -is_new_predefined(maybe_improper_list, 2) -> true; -is_new_predefined(mfa, 0) -> true; -is_new_predefined(module, 0) -> true; -is_new_predefined(neg_integer, 0) -> true; -is_new_predefined(node, 0) -> true; -is_new_predefined(non_neg_integer, 0) -> true; -is_new_predefined(nonempty_improper_list, 2) -> true; -is_new_predefined(nonempty_list, 0) -> true; -is_new_predefined(nonempty_list, 1) -> true; -is_new_predefined(nonempty_maybe_improper_list, 0) -> true; -is_new_predefined(nonempty_maybe_improper_list, 2) -> true; -is_new_predefined(nonempty_string, 0) -> true; -is_new_predefined(pos_integer, 0) -> true; -is_new_predefined(timeout, 0) -> true;  is_new_predefined(_, _) -> false. -%% The following types will be removed later, but they are currently -%% kind of built-in. -is_predefined_otp_type(array, 0) -> true; -is_predefined_otp_type(dict, 0) -> true; -is_predefined_otp_type(digraph, 0) -> true; -is_predefined_otp_type(gb_set, 0) -> true; -is_predefined_otp_type(gb_tree, 0) -> true; -is_predefined_otp_type(queue, 0) -> true; -is_predefined_otp_type(set, 0) -> true; -is_predefined_otp_type(_, _) -> false. -  to_ref(#t_typedef{name = N}) ->      to_ref(N);  to_ref(#t_def{name = N}) -> @@ -129,8 +75,7 @@ to_xml(#t_type{name = N, args = As}, Env) ->      Predef = case N of  		 #t_name{module = [], name = T} ->                       NArgs = length(As), -		     (is_predefined(T, NArgs) -                      orelse is_predefined_otp_type(T, NArgs)); +		     is_predefined(T, NArgs);  		 _ ->  		     false  	     end, diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index 67661130a5..ba55cb0aba 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -40,7 +40,6 @@  	 any_none_or_unit/1,  	 lookup_record/3,  	 max/2, -	 module_builtin_opaques/1,  	 min/2,  	 number_max/1, number_max/2,  	 number_min/1, number_min/2, @@ -188,7 +187,6 @@  	 t_subtract_list/2,  	 t_sup/1,  	 t_sup/2, -	 t_tid/0,  	 t_timeout/0,  	 t_to_string/1,  	 t_to_string/2, @@ -464,16 +462,6 @@ has_opaque_subtype(T) ->  t_opaque_structure(?opaque(Elements)) ->    t_sup([Struct || #opaque{struct = Struct} <- ordsets:to_list(Elements)]). --spec t_opaque_modules(erl_type()) -> [module()]. - -t_opaque_modules(?opaque(Elements)) -> -  case ordsets:size(Elements) of -    1 -> -      [#opaque{mod = Mod}] = set_to_list(Elements), -      [Mod]; -    _ -> throw({error, "Unexpected multiple opaque types"}) -  end. -  -spec t_contains_opaque(erl_type()) -> boolean().  t_contains_opaque(Type) -> @@ -787,11 +775,6 @@ t_struct_from_opaque(Type, _Opaques) -> Type.  list_struct_from_opaque(Types, Opaques) ->    [t_struct_from_opaque(Type, Opaques) || Type <- Types]. --spec module_builtin_opaques(module()) -> [erl_type()]. - -module_builtin_opaques(Module) -> -  [O || O <- all_opaque_builtins(), lists:member(Module, t_opaque_modules(O))]. -  %%-----------------------------------------------------------------------------  %% Remote types: these types are used for preprocessing;  %% they should never reach the analysis stage. @@ -1972,82 +1955,6 @@ t_parameterized_module() ->  t_timeout() ->    t_sup(t_non_neg_integer(), t_atom('infinity')). -%%----------------------------------------------------------------------------- -%% Some built-in opaque types -%% - --spec t_array() -> erl_type(). - -t_array() -> -  t_opaque(array, array, [t_any()], -	   t_tuple([t_atom('array'), -		    t_sup([t_atom('undefined'), t_non_neg_integer()]), -                    t_sup([t_atom('undefined'), t_non_neg_integer()]), -		    t_any(), -                    t_any()])). - --spec t_dict() -> erl_type(). - -t_dict() -> -  t_opaque(dict, dict, [t_any(), t_any()], -	   t_tuple([t_atom('dict'), -		    t_sup([t_atom('undefined'), t_non_neg_integer()]), -		    t_sup([t_atom('undefined'), t_non_neg_integer()]), -		    t_sup([t_atom('undefined'), t_non_neg_integer()]), -		    t_sup([t_atom('undefined'), t_non_neg_integer()]), -		    t_sup([t_atom('undefined'), t_non_neg_integer()]), -		    t_sup([t_atom('undefined'), t_non_neg_integer()]), -                    t_sup([t_atom('undefined'), t_tuple()]), -                    t_sup([t_atom('undefined'), t_tuple()])])). - --spec t_digraph() -> erl_type(). - -t_digraph() -> -  t_opaque(digraph, digraph, [], -	   t_tuple([t_atom('digraph'), -		    t_sup(t_atom(), t_tid()), -		    t_sup(t_atom(), t_tid()), -		    t_sup(t_atom(), t_tid()), -		    t_boolean()])). - --spec t_gb_set() -> erl_type(). - -t_gb_set() -> -  t_opaque(gb_sets, gb_set, [], -	   t_tuple([t_non_neg_integer(), t_sup(t_atom('nil'), t_tuple(3))])). - --spec t_gb_tree() -> erl_type(). - -t_gb_tree() -> -  t_opaque(gb_trees, gb_tree, [], -	   t_tuple([t_non_neg_integer(), t_sup(t_atom('nil'), t_tuple(4))])). - --spec t_queue() -> erl_type(). - -t_queue() -> -  t_opaque(queue, queue, [t_any()], t_tuple([t_list(), t_list()])). - --spec t_set() -> erl_type(). - -t_set() -> -  t_opaque(sets, set, [t_any()], -	   t_tuple([t_atom('set'), t_non_neg_integer(), t_non_neg_integer(), -		    t_pos_integer(), t_non_neg_integer(), t_non_neg_integer(), -		    t_non_neg_integer(), -                    t_sup([t_atom('undefined'), t_tuple()]), -                    t_sup([t_atom('undefined'), t_tuple()])])). - --spec t_tid() -> erl_type(). - -t_tid() -> -  t_opaque(ets, tid, [], t_integer()). - --spec all_opaque_builtins() -> [erl_type(),...]. - -all_opaque_builtins() -> -  [t_array(), t_dict(), t_digraph(), t_gb_set(), -   t_gb_tree(), t_queue(), t_set(), t_tid()]. -  %%------------------------------------  %% ?none is allowed in products. A product of size 1 is not a product. @@ -3302,8 +3209,8 @@ is_opaque_type2(#opaque{mod = Mod1, name = Name1, args = Args1}, Opaques) ->  is_type_name(Mod, Name, Args1, Mod, Name, Args2) ->    length(Args1) =:= length(Args2); -is_type_name(Mod1, Name1, Args1, Mod2, Name2, Args2) -> -  is_same_type_name2(Mod1, Name1, Args1, Mod2, Name2, Args2). +is_type_name(_Mod1, _Name1, _Args1, _Mod2, _Name2, _Args2) -> +  false.  %% Two functions since t_unify is not symmetric.  unify_tuple_set_and_tuple1(?tuple_set([{Arity, List}]), @@ -4141,15 +4048,7 @@ opaque_name(Mod, Name, Extra) ->    flat_format("~s(~s)", [S, Extra]).  mod_name(Mod, Name) -> -  case is_obsolete_opaque_builtin(Mod, Name) of -    true  -> flat_format("~w", [Name]); -    false -> flat_format("~w:~w", [Mod, Name]) -  end. - -is_obsolete_opaque_builtin(digraph, digraph) -> true; -is_obsolete_opaque_builtin(gb_sets, gb_set) -> true; -is_obsolete_opaque_builtin(gb_trees, gb_tree) -> true; -is_obsolete_opaque_builtin(_, _) -> false. +  flat_format("~w:~w", [Mod, Name]).  %%=============================================================================  %%  @@ -4214,8 +4113,6 @@ t_from_form({type, _L, any, []}, _TypeNames, _RecDict, _VarDict) ->    {t_any(), []};  t_from_form({type, _L, arity, []}, _TypeNames, _RecDict, _VarDict) ->    {t_arity(), []}; -t_from_form({type, _L, array, []}, TypeNames, RecDict, VarDict) -> -  builtin_type(array, t_array(), TypeNames, RecDict, VarDict);  t_from_form({type, _L, atom, []}, _TypeNames, _RecDict, _VarDict) ->    {t_atom(), []};  t_from_form({type, _L, binary, []}, _TypeNames, _RecDict, _VarDict) -> @@ -4237,10 +4134,6 @@ t_from_form({type, _L, byte, []}, _TypeNames, _RecDict, _VarDict) ->    {t_byte(), []};  t_from_form({type, _L, char, []}, _TypeNames, _RecDict, _VarDict) ->    {t_char(), []}; -t_from_form({type, _L, dict, []}, TypeNames, RecDict, VarDict) -> -  builtin_type(dict, t_dict(), TypeNames, RecDict, VarDict); -t_from_form({type, _L, digraph, []}, TypeNames, RecDict, VarDict) -> -  builtin_type(digraph, t_digraph(), TypeNames, RecDict, VarDict);  t_from_form({type, _L, float, []}, _TypeNames, _RecDict, _VarDict) ->    {t_float(), []};  t_from_form({type, _L, function, []}, _TypeNames, _RecDict, _VarDict) -> @@ -4256,10 +4149,6 @@ t_from_form({type, _L, 'fun', [{type, _, product, Domain}, Range]},    {L, R1} = list_from_form(Domain, TypeNames, RecDict, VarDict),    {T, R2} = t_from_form(Range, TypeNames, RecDict, VarDict),    {t_fun(L, T), R1 ++ R2}; -t_from_form({type, _L, gb_set, []}, TypeNames, RecDict, VarDict) -> -  builtin_type(gb_set, t_gb_set(), TypeNames, RecDict, VarDict); -t_from_form({type, _L, gb_tree, []}, TypeNames, RecDict, VarDict) -> -  builtin_type(gb_tree, t_gb_tree(), TypeNames, RecDict, VarDict);  t_from_form({type, _L, identifier, []}, _TypeNames, _RecDict, _VarDict) ->    {t_identifier(), []};  t_from_form({type, _L, integer, []}, _TypeNames, _RecDict, _VarDict) -> @@ -4332,8 +4221,6 @@ t_from_form({type, _L, maybe_improper_list, [Content, Termination]},  t_from_form({type, _L, product, Elements}, TypeNames, RecDict, VarDict) ->    {L, R} = list_from_form(Elements, TypeNames, RecDict, VarDict),    {t_product(L), R}; -t_from_form({type, _L, queue, []}, TypeNames, RecDict, VarDict) -> -  builtin_type(queue, t_queue(), TypeNames, RecDict, VarDict);  t_from_form({type, _L, range, [From, To]} = Type,  	    _TypeNames, _RecDict, _VarDict) ->    case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of @@ -4345,14 +4232,10 @@ t_from_form({type, _L, record, [Name|Fields]}, TypeNames, RecDict, VarDict) ->    record_from_form(Name, Fields, TypeNames, RecDict, VarDict);  t_from_form({type, _L, reference, []}, _TypeNames, _RecDict, _VarDict) ->    {t_reference(), []}; -t_from_form({type, _L, set, []}, TypeNames, RecDict, VarDict) -> -  builtin_type(set, t_set(), TypeNames, RecDict, VarDict);  t_from_form({type, _L, string, []}, _TypeNames, _RecDict, _VarDict) ->    {t_string(), []};  t_from_form({type, _L, term, []}, _TypeNames, _RecDict, _VarDict) ->    {t_any(), []}; -t_from_form({type, _L, tid, []}, TypeNames, RecDict, VarDict) -> -  builtin_type(tid, t_tid(), TypeNames, RecDict, VarDict);  t_from_form({type, _L, timeout, []}, _TypeNames, _RecDict, _VarDict) ->    {t_timeout(), []};  t_from_form({type, _L, tuple, any}, _TypeNames, _RecDict, _VarDict) -> @@ -4363,7 +4246,10 @@ t_from_form({type, _L, tuple, Args}, TypeNames, RecDict, VarDict) ->  t_from_form({type, _L, union, Args}, TypeNames, RecDict, VarDict) ->    {L, R} = list_from_form(Args, TypeNames, RecDict, VarDict),    {t_sup(L), R}; +t_from_form({user_type, _L, Name, Args}, TypeNames, RecDict, VarDict) -> +  type_from_form(Name, Args, TypeNames, RecDict, VarDict);  t_from_form({type, _L, Name, Args}, TypeNames, RecDict, VarDict) -> +  %% Compatibility: modules compiled before Erlang/OTP 18.0.    type_from_form(Name, Args, TypeNames, RecDict, VarDict);  t_from_form({opaque, _L, Name, {Mod, Args, Rep}}, _TypeNames,              _RecDict, _VarDict) -> @@ -4607,9 +4493,12 @@ t_form_to_string({type, _L, Name, []} = T) ->    try t_to_string(t_from_form(T))    catch throw:{error, _} -> atom_to_string(Name) ++ "()"    end; -t_form_to_string({type, _L, Name, List}) ->  +t_form_to_string({user_type, _L, Name, List}) ->    flat_format("~w(~s)", -              [Name, string:join(t_form_to_string_list(List), ",")]). +              [Name, string:join(t_form_to_string_list(List), ",")]); +t_form_to_string({type, L, Name, List}) -> +  %% Compatibility: modules compiled before Erlang/OTP 18.0. +  t_form_to_string({user_type, L, Name, List}).  t_form_to_string_list(List) ->    t_form_to_string_list(List, []). @@ -4722,27 +4611,14 @@ do_opaque(Type, _Opaques, Pred) ->  is_same_type_name(ModNameArgs, ModNameArgs) -> true;  is_same_type_name({Mod, Name, Args1}, {Mod, Name, Args2}) ->    all_any(Args1) orelse all_any(Args2); -is_same_type_name({Mod1, Name1, Args1}, {Mod2, Name2, Args2}) -> -  is_same_type_name2(Mod1, Name1, Args1, Mod2, Name2, Args2). +is_same_type_name(_ModNameArgs1, _ModNameArgs2) -> +  false.  all_any([]) -> true;  all_any([T|L]) ->    t_is_any(T) andalso all_any(L);  all_any(_) -> false. -%% Compatibility. In Erlang/OTP 17 the pre-defined opaque types -%% digraph() and so on can be used, but there are also new types such -%% as digraph:graph() with the exact same meaning. In Erlang/OTP R18.0 -%% all but the last clause can be removed. - -is_same_type_name2(digraph,  digraph, [], digraph,  graph, [])   -> true; -is_same_type_name2(digraph,  graph, [],   digraph,  digraph, []) -> true; -is_same_type_name2(gb_sets,  gb_set, [],  gb_sets,  set, [_]) -> true; -is_same_type_name2(gb_sets,  set, [_], gb_sets,  gb_set, [])  -> true; -is_same_type_name2(gb_trees, gb_tree, [], gb_trees, tree, [_, _]) -> true; -is_same_type_name2(gb_trees, tree, [_, _], gb_trees, gb_tree, []) -> true; -is_same_type_name2(_, _, _, _, _, _) -> false. -  map_keys(?map(Pairs)) ->    [K || {K, _} <- Pairs]. diff --git a/lib/kernel/doc/src/pg2.xml b/lib/kernel/doc/src/pg2.xml index 5eb63c1ef6..35cf85470a 100644 --- a/lib/kernel/doc/src/pg2.xml +++ b/lib/kernel/doc/src/pg2.xml @@ -34,11 +34,8 @@    <module>pg2</module>    <modulesummary>Distributed Named Process Groups</modulesummary>    <description> -    <p>This module implements process groups. The groups in this -      module differ from the groups in the module <c>pg</c> in several -      ways. In <c>pg</c>, each message is sent to all members in the -      group. In this module, each message may be sent to one, some, or -      all members. +    <p>This module implements process groups. Each message may be sent +      to one, some, or all members of the group.        </p>      <p>A group of processes can be accessed by a common name. For        example, if there is a group named <c>foobar</c>, there can be a @@ -160,8 +157,7 @@    <section>      <title>See Also</title> -    <p><seealso marker="kernel_app">kernel(6)</seealso>,  -       <seealso marker="stdlib:pg">pg(3)</seealso></p> +    <p><seealso marker="kernel_app">kernel(6)</seealso></p>    </section>  </erlref> diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index f8f4cc1ec2..1bae762bed 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -17,9 +17,7 @@  %% %CopyrightEnd%  {"%VSN%",   %% Up from - max one major revision back - [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 -  {<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R16 + [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-17   %% Down to - max one major revision back - [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 -  {<<"2\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R16 + [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}]  % OTP-17  }. diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl index e9567c82cb..4700dc4bab 100644 --- a/lib/observer/test/crashdump_viewer_SUITE.erl +++ b/lib/observer/test/crashdump_viewer_SUITE.erl @@ -101,7 +101,7 @@ end_per_group(_GroupName, Config) ->  init_per_suite(Config) when is_list(Config) ->      delete_saved(Config),      DataDir = ?config(data_dir,Config), -    Rels = [R || R <- [r15b,r16b], ?t:is_release_available(R)] ++ [current], +    Rels = [R || R <- [r16b,'17'], ?t:is_release_available(R)] ++ [current],      io:format("Creating crash dumps for the following releases: ~p", [Rels]),      AllDumps = create_dumps(DataDir,Rels),      [{dumps,AllDumps}|Config]. @@ -557,12 +557,6 @@ dump_with_strange_module_name(DataDir,Rel,DumpName) ->      CD.  dump(Node,DataDir,Rel,DumpName) -> -    case Rel of -	_ when Rel<r15b, Rel=/=current -> -	    rpc:call(Node,os,putenv,["ERL_CRASH_DUMP_SECONDS","600"]); -	_ -> -	    ok -    end,      rpc:call(Node,erlang,halt,[DumpName]),      Crashdump0 = filename:join(filename:dirname(code:which(?t)),  			       "erl_crash_dump.n1"), @@ -617,42 +611,21 @@ dos_dump(DataDir,Rel,Dump) ->  rel_opt(Rel) ->      case Rel of -	r9b -> [{erl,[{release,"r9b_patched"}]}]; -	r9c -> [{erl,[{release,"r9c_patched"}]}]; -	r10b -> [{erl,[{release,"r10b_patched"}]}]; -	r11b -> [{erl,[{release,"r11b_patched"}]}]; -	r12b -> [{erl,[{release,"r12b_patched"}]}]; -	r13b -> [{erl,[{release,"r13b_patched"}]}]; -	r14b -> [{erl,[{release,"r14b_latest"}]}]; %naming convention changed -	r15b -> [{erl,[{release,"r15b_latest"}]}];  	r16b -> [{erl,[{release,"r16b_latest"}]}]; +	'17' -> [{erl,[{release,"17_latest"}]}];  	current -> []      end.  dump_prefix(Rel) ->      case Rel of -	r9b -> "r9b_dump."; -	r9c -> "r9c_dump."; -	r10b -> "r10b_dump."; -	r11b -> "r11b_dump."; -	r12b -> "r12b_dump."; -	r13b -> "r13b_dump."; -	r14b -> "r14b_dump."; -	r15b -> "r15b_dump.";  	r16b -> "r16b_dump."; -	current -> "r17b_dump." +	'17' -> "r17_dump."; +	current -> "r18_dump."      end.  compat_rel(Rel) ->      case Rel of -	r9b -> "+R9 "; -	r9c -> "+R9 "; -	r10b -> "+R10 "; -	r11b -> "+R11 "; -	r12b -> "+R12 "; -	r13b -> "+R13 "; -	r14b -> "+R14 "; -	r15b -> "+R15 ";  	r16b -> "+R16 "; +	'17' -> "+R17 ";  	current -> ""      end. diff --git a/lib/sasl/src/sasl.appup.src b/lib/sasl/src/sasl.appup.src index e789853eea..af04d007ac 100644 --- a/lib/sasl/src/sasl.appup.src +++ b/lib/sasl/src/sasl.appup.src @@ -17,9 +17,7 @@  %% %CopyrightEnd%  {"%VSN%",   %% Up from - max one major revision back - [{<<"2\\.4(\\.[0-9]+)*">>,[restart_new_emulator]},         %% R17 -  {<<"2\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}],        %% R16 + [{<<"2\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-17   %% Down to - max one major revision back - [{<<"2\\.4(\\.[0-9]+)*">>,[restart_new_emulator]},         %% R17 -  {<<"2\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}]         %% R16 + [{<<"2\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}]  % OTP-17  }. diff --git a/lib/sasl/test/test_lib.hrl b/lib/sasl/test/test_lib.hrl index c8a4e92f24..b16c4ac34c 100644 --- a/lib/sasl/test/test_lib.hrl +++ b/lib/sasl/test/test_lib.hrl @@ -1,3 +1,3 @@  -define(ertsvsn,"4.4"). --define(kernelvsn,"2.16.4"). --define(stdlibvsn,"1.19.4"). +-define(kernelvsn,"3.0"). +-define(stdlibvsn,"2.0"). diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile index ff77c3eea0..f5d8b2072a 100644 --- a/lib/stdlib/doc/src/Makefile +++ b/lib/stdlib/doc/src/Makefile @@ -76,7 +76,6 @@ XML_REF3_FILES = \  	ms_transform.xml \  	orddict.xml \  	ordsets.xml \ -	pg.xml \  	pool.xml \  	proc_lib.xml \  	proplists.xml \ diff --git a/lib/stdlib/doc/src/binary.xml b/lib/stdlib/doc/src/binary.xml index 2410f1f9b8..0fde763bfb 100644 --- a/lib/stdlib/doc/src/binary.xml +++ b/lib/stdlib/doc/src/binary.xml @@ -5,7 +5,7 @@    <header>      <copyright>        <year>2009</year> -      <year>2013</year> +      <year>2014</year>        <holder>Ericsson AB, All Rights Reserved</holder>      </copyright>      <legalnotice> @@ -450,7 +450,7 @@ store(Binary, GBSet) ->        </code>        <p>In this example, we chose to copy the binary content before -      inserting it in the <c>gb_set()</c> if it references a binary more than +      inserting it in the <c>gb_sets:set()</c> if it references a binary more than        twice the size of the data we're going to keep. Of course        different rules for when copying will apply to different        programs.</p> diff --git a/lib/stdlib/doc/src/pg.xml b/lib/stdlib/doc/src/pg.xml deleted file mode 100644 index a3b69884b6..0000000000 --- a/lib/stdlib/doc/src/pg.xml +++ /dev/null @@ -1,114 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE erlref SYSTEM "erlref.dtd"> - -<erlref> -  <header> -    <copyright> -      <year>1996</year> -      <year>2014</year> -      <holder>Ericsson AB, All Rights Reserved</holder> -    </copyright> -    <legalnotice> -  The contents of this file are subject to the Erlang Public License, -  Version 1.1, (the "License"); you may not use this file except in -  compliance with the License. You should have received a copy of the -  Erlang Public License along with this software. If not, it can be -  retrieved online at http://www.erlang.org/. - -  Software distributed under the License is distributed on an "AS IS" -  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -  the License for the specific language governing rights and limitations -  under the License. - -  The Initial Developer of the Original Code is Ericsson AB. -    </legalnotice> - -    <title>pg</title> -    <prepared></prepared> -    <docno></docno> -    <date></date> -    <rev></rev> -  </header> -  <module>pg</module> -  <modulesummary>Distributed, Named Process Groups</modulesummary> -  <description> -    <warning> -      <p>This module is deprecated and will be removed in Erlang/OTP 18.</p> -    </warning> -    <p>This (experimental) module implements process groups. A process -      group is a group of processes that can be accessed by a common -      name. For example, a group named <c>foobar</c> can include a set -      of processes as members of this group and they can be located on -      different nodes.</p> -    <p>When messages are sent to the named group, all members of -      the group receive the message. The messages are serialized. If -      the process <c>P1</c> sends the message <c>M1</c> to the group, -      and process <c>P2</c> simultaneously sends message <c>M2</c>, then -      all members of the group receive the two messages in the same -      order. If members of a group terminate, they are automatically  -      removed from the group.</p> -    <p>This module is not complete. The module is inspired by the ISIS -      system and the causal order protocol of the ISIS system should  -      also be implemented. At the moment, all messages are serialized -      by sending them through a group master process.</p> -  </description> -  <funcs> -    <func> -      <name name="create" arity="1"/> -      <fsummary>Create an empty group</fsummary> -      <desc> -        <p>Creates an empty group named <c><anno>PgName</anno></c> on the current -          node.</p> -      </desc> -    </func> -    <func> -      <name name="create" arity="2"/> -      <fsummary>Create an empty group on another node</fsummary> -      <desc> -        <p>Creates an empty group named <c><anno>PgName</anno></c> on the node -          <c><anno>Node</anno></c>.</p> -      </desc> -    </func> -    <func> -      <name name="join" arity="2"/> -      <fsummary>Join a pid to a process group</fsummary> -      <desc> -        <p>Joins the pid <c><anno>Pid</anno></c> to the process group -          <c><anno>PgName</anno></c>. -          Returns a list of all old members of the group.</p> -      </desc> -    </func> -    <func> -      <name name="send" arity="2"/> -      <fsummary>Send a message to all members of a process group</fsummary> -      <desc> -        <p>Sends the tuple <c>{pg_message, From, PgName, Msg}</c> to -          all members of the process group <c><anno>PgName</anno></c>.</p> -        <p>Failure: <c>{badarg, {<anno>PgName</anno>, <anno>Msg</anno>}}</c> -          if <c><anno>PgName</anno></c> is -          not a process group (a globally registered name).</p> -      </desc> -    </func> -    <func> -      <name name="esend" arity="2"/> -      <fsummary>Send a message to all members of a process group, except ourselves</fsummary> -      <desc> -        <p>Sends the tuple <c>{pg_message, From, PgName, Msg}</c> to -          all members of the process group <c><anno>PgName</anno></c>, except -          ourselves.</p> -        <p>Failure: <c>{badarg, {<anno>PgName</anno>, <anno>Msg</anno>}}</c> -          if <c><anno>PgName</anno></c> is -          not a process group (a globally registered name).</p> -      </desc> -    </func> -    <func> -      <name name="members" arity="1"/> -      <fsummary>Return a list of all members of a process group</fsummary> -      <desc> -        <p>Returns a list of all members of the process group -          <c>PgName</c>.</p> -      </desc> -    </func> -  </funcs> -</erlref> - diff --git a/lib/stdlib/doc/src/ref_man.xml b/lib/stdlib/doc/src/ref_man.xml index 6c35578bdf..ea4009dc3e 100644 --- a/lib/stdlib/doc/src/ref_man.xml +++ b/lib/stdlib/doc/src/ref_man.xml @@ -73,7 +73,6 @@    <xi:include href="ms_transform.xml"/>    <xi:include href="orddict.xml"/>    <xi:include href="ordsets.xml"/> -  <xi:include href="pg.xml"/>    <xi:include href="pool.xml"/>    <xi:include href="proc_lib.xml"/>    <xi:include href="proplists.xml"/> diff --git a/lib/stdlib/doc/src/specs.xml b/lib/stdlib/doc/src/specs.xml index 60a04ed5e7..fd77b52da6 100644 --- a/lib/stdlib/doc/src/specs.xml +++ b/lib/stdlib/doc/src/specs.xml @@ -39,7 +39,6 @@    <xi:include href="../specs/specs_ms_transform.xml"/>    <xi:include href="../specs/specs_orddict.xml"/>    <xi:include href="../specs/specs_ordsets.xml"/> -  <xi:include href="../specs/specs_pg.xml"/>    <xi:include href="../specs/specs_pool.xml"/>    <xi:include href="../specs/specs_proc_lib.xml"/>    <xi:include href="../specs/specs_proplists.xml"/> diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile index 9ab2cd4134..1b3744b6fb 100644 --- a/lib/stdlib/src/Makefile +++ b/lib/stdlib/src/Makefile @@ -97,7 +97,6 @@ MODULES= \  	otp_internal \  	orddict \  	ordsets \ -	pg \  	re \  	pool \  	proc_lib \ diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index edfb097de0..e523b6b476 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 1998-2013. All Rights Reserved. +%% Copyright Ericsson AB 1998-2014. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -51,6 +51,8 @@  	 type_test/2,new_type_test/2,old_type_test/2,old_bif/2]).  -export([arith_op/2,bool_op/2,comp_op/2,list_op/2,send_op/2,op_type/2]). +-export([is_type/2]). +  %%---------------------------------------------------------------------------  %%  Erlang builtin functions allowed in guards. @@ -530,3 +532,53 @@ old_bif(unlink, 1) -> true;  old_bif(unregister, 1) -> true;  old_bif(whereis, 1) -> true;  old_bif(Name, A) when is_atom(Name), is_integer(A) -> false. + +-spec is_type(Name, NumberOfTypeVariables) -> boolean() when +      Name :: atom(), +      NumberOfTypeVariables :: non_neg_integer(). +%% Returns true if Name/NumberOfTypeVariables is a predefined type. + +is_type(any, 0) -> true; +is_type(arity, 0) -> true; +is_type(atom, 0) -> true; +is_type(binary, 0) -> true; +is_type(bitstring, 0) -> true; +is_type(bool, 0) -> true; +is_type(boolean, 0) -> true; +is_type(byte, 0) -> true; +is_type(char, 0) -> true; +is_type(float, 0) -> true; +is_type(function, 0) -> true; +is_type(identifier, 0) -> true; +is_type(integer, 0) -> true; +is_type(iodata, 0) -> true; +is_type(iolist, 0) -> true; +is_type(list, 0) -> true; +is_type(list, 1) -> true; +is_type(map, 0) -> true; +is_type(maybe_improper_list, 0) -> true; +is_type(maybe_improper_list, 2) -> true; +is_type(mfa, 0) -> true; +is_type(module, 0) -> true; +is_type(neg_integer, 0) -> true; +is_type(nil, 0) -> true; +is_type(no_return, 0) -> true; +is_type(node, 0) -> true; +is_type(non_neg_integer, 0) -> true; +is_type(none, 0) -> true; +is_type(nonempty_improper_list, 2) -> true; +is_type(nonempty_list, 0) -> true; +is_type(nonempty_list, 1) -> true; +is_type(nonempty_maybe_improper_list, 0) -> true; +is_type(nonempty_maybe_improper_list, 2) -> true; +is_type(nonempty_string, 0) -> true; +is_type(number, 0) -> true; +is_type(pid, 0) -> true; +is_type(port, 0) -> true; +is_type(pos_integer, 0) -> true; +is_type(reference, 0) -> true; +is_type(string, 0) -> true; +is_type(term, 0) -> true; +is_type(timeout, 0) -> true; +is_type(tuple, 0) -> true; +is_type(_, _) -> false. diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 39cc03cf7a..f34c3b5c7b 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -130,6 +130,8 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->                     :: dict:dict(mfa(), line()),                 callbacks = dict:new()           %Callback types                     :: dict:dict(mfa(), line()), +               optional_callbacks = dict:new()  %Optional callbacks +                   :: dict:dict(mfa(), line()),                 types = dict:new()               %Type definitions                     :: dict:dict(ta(), #typeinfo{}),                 exp_types=gb_sets:empty()        %Exported types @@ -313,13 +315,20 @@ format_error({undefined_behaviour,Behaviour}) ->      io_lib:format("behaviour ~w undefined", [Behaviour]);  format_error({undefined_behaviour_callbacks,Behaviour}) ->      io_lib:format("behaviour ~w callback functions are undefined", -                  [Behaviour]); +		  [Behaviour]);  format_error({ill_defined_behaviour_callbacks,Behaviour}) ->      io_lib:format("behaviour ~w callback functions erroneously defined",  		  [Behaviour]); +format_error({ill_defined_optional_callbacks,Behaviour}) -> +    io_lib:format("behaviour ~w optional callback functions erroneously defined", +		  [Behaviour]);  format_error({behaviour_info, {_M,F,A}}) ->      io_lib:format("cannot define callback attibute for ~w/~w when "                    "behaviour_info is defined",[F,A]); +format_error({redefine_optional_callback, {F, A}}) -> +    io_lib:format("optional callback ~w/~w duplicated", [F, A]); +format_error({undefined_callback, {_M, F, A}}) -> +    io_lib:format("callback ~w/~w is undefined", [F, A]);  %% --- types and specs ---  format_error({singleton_typevar, Name}) ->      io_lib:format("type variable ~w is only used once (is unbound)", [Name]); @@ -331,14 +340,10 @@ format_error({undefined_type, {TypeName, Arity}}) ->      io_lib:format("type ~w~s undefined", [TypeName, gen_type_paren(Arity)]);  format_error({unused_type, {TypeName, Arity}}) ->      io_lib:format("type ~w~s is unused", [TypeName, gen_type_paren(Arity)]); -%% format_error({new_builtin_type, {TypeName, Arity}}) -> -%%     io_lib:format("type ~w~s is a new builtin type; " -%% 		  "its (re)definition is allowed only until the next release", -%% 		  [TypeName, gen_type_paren(Arity)]); -format_error({new_var_arity_type, TypeName}) -> -    io_lib:format("type ~w is a new builtin type; " +format_error({new_builtin_type, {TypeName, Arity}}) -> +    io_lib:format("type ~w~s is a new builtin type; "  		  "its (re)definition is allowed only until the next release", -		  [TypeName]); +		  [TypeName, gen_type_paren(Arity)]);  format_error({builtin_type, {TypeName, Arity}}) ->      io_lib:format("type ~w~s is a builtin type; it cannot be redefined",  		  [TypeName, gen_type_paren(Arity)]); @@ -352,10 +357,14 @@ format_error({type_syntax, Constr}) ->      io_lib:format("bad ~w type", [Constr]);  format_error({redefine_spec, {M, F, A}}) ->      io_lib:format("spec for ~w:~w/~w already defined", [M, F, A]); -format_error({redefine_callback, {M, F, A}}) -> -    io_lib:format("callback ~w:~w/~w already defined", [M, F, A]); -format_error({spec_fun_undefined, {M, F, A}}) -> -    io_lib:format("spec for undefined function ~w:~w/~w", [M, F, A]); +format_error({redefine_spec, {F, A}}) -> +    io_lib:format("spec for ~w/~w already defined", [F, A]); +format_error({redefine_callback, {F, A}}) -> +    io_lib:format("callback ~w/~w already defined", [F, A]); +format_error({bad_callback, {M, F, A}}) -> +    io_lib:format("explicit module not allowed for callback ~w:~w/~w ", [M, F, A]); +format_error({spec_fun_undefined, {F, A}}) -> +    io_lib:format("spec for undefined function ~w/~w", [F, A]);  format_error({missing_spec, {F,A}}) ->      io_lib:format("missing specification for function ~w/~w", [F, A]);  format_error(spec_wrong_arity) -> @@ -727,6 +736,8 @@ attribute_state({attribute,L,spec,{Fun,Types}}, St) ->      spec_decl(L, Fun, Types, St);  attribute_state({attribute,L,callback,{Fun,Types}}, St) ->      callback_decl(L, Fun, Types, St); +attribute_state({attribute,L,optional_callbacks,Es}, St) -> +    optional_callbacks(L, Es, St);  attribute_state({attribute,L,on_load,Val}, St) ->      on_load(L, Val, St);  attribute_state({attribute,_L,_Other,_Val}, St) -> % Ignore others @@ -834,57 +845,73 @@ check_behaviour(St0) ->  %%  Check behaviours for existence and defined functions.  behaviour_check(Bs, St0) -> -    {AllBfs,St1} = all_behaviour_callbacks(Bs, [], St0), -    St = behaviour_missing_callbacks(AllBfs, St1), +    {AllBfs0, St1} = all_behaviour_callbacks(Bs, [], St0), +    St = behaviour_missing_callbacks(AllBfs0, St1), +    Exports = exports(St0), +    F = fun(Bfs, OBfs) -> +                [B || B <- Bfs, +                      not lists:member(B, OBfs) +                      orelse gb_sets:is_member(B, Exports)] +        end, +    %% After fixing missing callbacks new warnings may be emitted. +    AllBfs = [{Item,F(Bfs0, OBfs0)} || {Item,Bfs0,OBfs0} <- AllBfs0],      behaviour_conflicting(AllBfs, St).  all_behaviour_callbacks([{Line,B}|Bs], Acc, St0) -> -    {Bfs0,St} = behaviour_callbacks(Line, B, St0), -    all_behaviour_callbacks(Bs, [{{Line,B},Bfs0}|Acc], St); +    {Bfs0,OBfs0,St} = behaviour_callbacks(Line, B, St0), +    all_behaviour_callbacks(Bs, [{{Line,B},Bfs0,OBfs0}|Acc], St);  all_behaviour_callbacks([], Acc, St) -> {reverse(Acc),St}.  behaviour_callbacks(Line, B, St0) ->      try B:behaviour_info(callbacks) of -        Funcs when is_list(Funcs) -> -            All = all(fun({FuncName, Arity}) -> -                              is_atom(FuncName) andalso is_integer(Arity); -			 ({FuncName, Arity, Spec}) -> -			      is_atom(FuncName) andalso is_integer(Arity) -				  andalso is_list(Spec); -                         (_Other) -> -                              false -                      end, -                      Funcs), -	    MaybeRemoveSpec = fun({_F,_A}=FA) -> FA; -				 ({F,A,_S}) -> {F,A}; -				 (Other) -> Other -			      end, -            if -                All =:= true -> -                    {[MaybeRemoveSpec(F) || F <- Funcs], St0}; +        undefined -> +            St1 = add_warning(Line, {undefined_behaviour_callbacks, B}, St0), +            {[], [], St1}; +        Funcs -> +            case is_fa_list(Funcs) of                  true -> +                    try B:behaviour_info(optional_callbacks) of +                        undefined -> +                            {Funcs, [], St0}; +                        OptFuncs -> +                            %% OptFuncs should always be OK thanks to +                            %% sys_pre_expand. +                            case is_fa_list(OptFuncs) of +                                true -> +                                    {Funcs, OptFuncs, St0}; +                                false -> +                                    W = {ill_defined_optional_callbacks, B}, +                                    St1 = add_warning(Line, W, St0), +                                    {Funcs, [], St1} +                            end +                    catch +                        _:_ -> +                            {Funcs, [], St0} +                    end; +                false ->                      St1 = add_warning(Line, -                                      {ill_defined_behaviour_callbacks,B}, +                                      {ill_defined_behaviour_callbacks, B},                                        St0), -                    {[], St1} -            end; -        undefined -> -            St1 = add_warning(Line, {undefined_behaviour_callbacks,B}, St0), -            {[], St1}; -        _Other -> -            St1 = add_warning(Line, {ill_defined_behaviour_callbacks,B}, St0), -            {[], St1} +                    {[], [], St1} +            end      catch          _:_ -> -            St1 = add_warning(Line, {undefined_behaviour,B}, St0), -            {[], St1} +            St1 = add_warning(Line, {undefined_behaviour, B}, St0), +            {[], [], St1}      end. -behaviour_missing_callbacks([{{Line,B},Bfs}|T], St0) -> +behaviour_missing_callbacks([{{Line,B},Bfs0,OBfs}|T], St0) -> +    Bfs = ordsets:subtract(ordsets:from_list(Bfs0), ordsets:from_list(OBfs)),      Exports = gb_sets:to_list(exports(St0)), -    Missing = ordsets:subtract(ordsets:from_list(Bfs), Exports), +    Missing = ordsets:subtract(Bfs, Exports),      St = foldl(fun (F, S0) -> -		       add_warning(Line, {undefined_behaviour_func,F,B}, S0) +                       case is_fa(F) of +                           true -> +                               M = {undefined_behaviour_func,F,B}, +                               add_warning(Line, M, S0); +                           false -> +                               S0 % ill_defined_behaviour_callbacks +                       end  	       end, St0, Missing),      behaviour_missing_callbacks(T, St);  behaviour_missing_callbacks([], St) -> St. @@ -1046,10 +1073,9 @@ check_undefined_types(#lint{usage=Usage,types=Def}=St0) ->      Used = Usage#usage.used_types,      UTAs = dict:fetch_keys(Used),      Undef = [{TA,dict:fetch(TA, Used)} || -		{T,_}=TA <- UTAs, +		TA <- UTAs,  		not dict:is_key(TA, Def), -		not is_default_type(TA), -                not is_newly_introduced_var_arity_type(T)], +		not is_default_type(TA)],      foldl(fun ({TA,L}, St) ->  		  add_error(L, {undefined_type,TA}, St)  	  end, St0, Undef). @@ -1127,19 +1153,29 @@ check_unused_records(Forms, St0) ->      end.  check_callback_information(#lint{callbacks = Callbacks, -				 defined = Defined} = State) -> -    case gb_sets:is_member({behaviour_info,1}, Defined) of -	false -> State; +                                 optional_callbacks = OptionalCbs, +				 defined = Defined} = St0) -> +    OptFun = fun({MFA, Line}, St) -> +                     case dict:is_key(MFA, Callbacks) of +                         true -> +                             St; +                         false -> +                             add_error(Line, {undefined_callback, MFA}, St) +                     end +             end, +    St1 = lists:foldl(OptFun, St0, dict:to_list(OptionalCbs)), +    case gb_sets:is_member({behaviour_info, 1}, Defined) of +	false -> St1;  	true ->  	    case dict:size(Callbacks) of -		0 -> State; +		0 -> St1;  		_ ->  		    CallbacksList = dict:to_list(Callbacks),  		    FoldL = -			fun({Fa,Line},St) -> +			fun({Fa, Line}, St) ->  				add_error(Line, {behaviour_info, Fa}, St)  			end, -		    lists:foldl(FoldL, State, CallbacksList) +		    lists:foldl(FoldL, St1, CallbacksList)  	    end      end. @@ -2615,30 +2651,21 @@ type_def(Attr, Line, TypeName, ProtoType, Args, St0) ->          true ->              case is_obsolete_builtin_type(TypePair) of                  true -> StoreType(St0); -                false -> add_error(Line, {builtin_type, TypePair}, St0) -%%                     case is_newly_introduced_builtin_type(TypePair) of -%%                         %% allow some types just for bootstrapping -%%                         true -> -%%                             Warn = {new_builtin_type, TypePair}, -%%                             St1 = add_warning(Line, Warn, St0), -%%                             StoreType(St1); -%%                         false -> -%%                             add_error(Line, {builtin_type, TypePair}, St0) -%%                     end +                false -> +                     case is_newly_introduced_builtin_type(TypePair) of +                         %% allow some types just for bootstrapping +                         true -> +                             Warn = {new_builtin_type, TypePair}, +                             St1 = add_warning(Line, Warn, St0), +                             StoreType(St1); +                         false -> +                             add_error(Line, {builtin_type, TypePair}, St0) +                     end              end;          false -> -            case -                dict:is_key(TypePair, TypeDefs) orelse -                is_var_arity_type(TypeName) -            of +            case dict:is_key(TypePair, TypeDefs) of                  true -> -                    case is_newly_introduced_var_arity_type(TypeName) of -                        true -> -                            Warn = {new_var_arity_type, TypeName}, -                            add_warning(Line, Warn, St0); -                        false -> -                            add_error(Line, {redefine_type, TypePair}, St0) -                    end; +                    add_error(Line, {redefine_type, TypePair}, St0);                  false ->                      St1 = case                                Attr =:= opaque andalso @@ -2675,7 +2702,7 @@ check_type({paren_type, _L, [Type]}, SeenVars, St) ->  check_type({remote_type, L, [{atom, _, Mod}, {atom, _, Name}, Args]},  	   SeenVars, #lint{module=CurrentMod} = St) ->      case Mod =:= CurrentMod of -	true -> check_type({type, L, Name, Args}, SeenVars, St); +	true -> check_type({user_type, L, Name, Args}, SeenVars, St);  	false ->  	    lists:foldl(fun(T, {AccSeenVars, AccSt}) ->  				check_type(T, AccSeenVars, AccSt) @@ -2709,12 +2736,15 @@ check_type({type, L, range, [From, To]}, SeenVars, St) ->  	    _ -> add_error(L, {type_syntax, range}, St)  	end,      {SeenVars, St1}; -check_type({type, _L, map, any}, SeenVars, St) -> {SeenVars, St}; +check_type({type, L, map, any}, SeenVars, St) -> +    %% To get usage right while map/0 is a newly_introduced_builtin_type. +    St1 = used_type({map, 0}, L, St), +    {SeenVars, St1};  check_type({type, _L, map, Pairs}, SeenVars, St) ->      lists:foldl(fun(Pair, {AccSeenVars, AccSt}) ->  			check_type(Pair, AccSeenVars, AccSt)  		end, {SeenVars, St}, Pairs); -check_type({type, _L, map_field_assoc, Dom, Range}, SeenVars, St) -> +check_type({type, _L, map_field_assoc, [Dom, Range]}, SeenVars, St) ->      check_type({type, -1, product, [Dom, Range]}, SeenVars, St);  check_type({type, _L, tuple, any}, SeenVars, St) -> {SeenVars, St};  check_type({type, _L, any}, SeenVars, St) -> {SeenVars, St}; @@ -2733,41 +2763,39 @@ check_type({type, L, record, [Name|Fields]}, SeenVars, St) ->  	    check_record_types(L, Atom, Fields, SeenVars, St1);  	_ -> {SeenVars, add_error(L, {type_syntax, record}, St)}      end; -check_type({type, _L, product, Args}, SeenVars, St) -> +check_type({type, _L, Tag, Args}, SeenVars, St) when Tag =:= product; +                                                     Tag =:= union; +                                                     Tag =:= tuple ->      lists:foldl(fun(T, {AccSeenVars, AccSt}) ->  			check_type(T, AccSeenVars, AccSt)  		end, {SeenVars, St}, Args);  check_type({type, La, TypeName, Args}, SeenVars, St) -> -    #lint{usage=Usage, module = Module, types=Types} = St, +    #lint{module = Module, types=Types} = St,      Arity = length(Args),      TypePair = {TypeName, Arity}, -    St1 = case is_var_arity_type(TypeName) of -	      true -> St; -	      false -> -                  Obsolete = (is_warn_enabled(deprecated_type, St) -                              andalso obsolete_builtin_type(TypePair)), -                  IsObsolete = -                      case Obsolete of -                          {deprecated, Repl, _} when element(1, Repl) =/= Module -> -                              case dict:find(TypePair, Types) of -                                  {ok, _} -> false; -                                  error -> true -                              end; -                          _ -> false -                      end, -                  case IsObsolete of -                      true -> +    Obsolete = (is_warn_enabled(deprecated_type, St) +                andalso obsolete_builtin_type(TypePair)), +    St1 = case Obsolete of +              {deprecated, Repl, _} when element(1, Repl) =/= Module -> +                  case dict:find(TypePair, Types) of +                      {ok, _} -> +                          used_type(TypePair, La, St); +                      error ->                            {deprecated, Replacement, Rel} = Obsolete,                            Tag = deprecated_builtin_type,                            W = {Tag, TypePair, Replacement, Rel}, -                          add_warning(La, W, St); -                      false -> -                          OldUsed = Usage#usage.used_types, -                          UsedTypes = dict:store(TypePair, La, OldUsed), -                          St#lint{usage=Usage#usage{used_types=UsedTypes}} -                  end -	  end, +                          add_warning(La, W, St) +                end; +            _ -> St +        end,      check_type({type, -1, product, Args}, SeenVars, St1); +check_type({user_type, L, TypeName, Args}, SeenVars, St) -> +    Arity = length(Args), +    TypePair = {TypeName, Arity}, +    St1 = used_type(TypePair, L, St), +    lists:foldl(fun(T, {AccSeenVars, AccSt}) -> +			check_type(T, AccSeenVars, AccSt) +		end, {SeenVars, St1}, Args);  check_type(I, SeenVars, St) ->      case erl_eval:partial_eval(I) of          {integer,_ILn,_Integer} -> {SeenVars, St}; @@ -2809,95 +2837,24 @@ check_record_types([{type, _, field_type, [{atom, AL, FName}, Type]}|Left],  check_record_types([], _Name, _DefFields, SeenVars, St, _SeenFields) ->      {SeenVars, St}. -is_var_arity_type(tuple) -> true; -is_var_arity_type(map) -> true; -is_var_arity_type(product) -> true; -is_var_arity_type(union) -> true; -is_var_arity_type(record) -> true; -is_var_arity_type(_) -> false. - -is_default_type({any, 0}) -> true; -is_default_type({arity, 0}) -> true; -is_default_type({array, 0}) -> true; -is_default_type({atom, 0}) -> true; -is_default_type({atom, 1}) -> true; -is_default_type({binary, 0}) -> true; -is_default_type({binary, 2}) -> true; -is_default_type({bitstring, 0}) -> true; -is_default_type({bool, 0}) -> true; -is_default_type({boolean, 0}) -> true; -is_default_type({byte, 0}) -> true; -is_default_type({char, 0}) -> true; -is_default_type({dict, 0}) -> true; -is_default_type({digraph, 0}) -> true; -is_default_type({float, 0}) -> true; -is_default_type({'fun', 0}) -> true; -is_default_type({'fun', 2}) -> true; -is_default_type({function, 0}) -> true; -is_default_type({gb_set, 0}) -> true; -is_default_type({gb_tree, 0}) -> true; -is_default_type({identifier, 0}) -> true; -is_default_type({integer, 0}) -> true; -is_default_type({integer, 1}) -> true; -is_default_type({iodata, 0}) -> true; -is_default_type({iolist, 0}) -> true; -is_default_type({list, 0}) -> true; -is_default_type({list, 1}) -> true; -is_default_type({maybe_improper_list, 0}) -> true; -is_default_type({maybe_improper_list, 2}) -> true; -is_default_type({mfa, 0}) -> true; -is_default_type({module, 0}) -> true; -is_default_type({neg_integer, 0}) -> true; -is_default_type({nil, 0}) -> true; -is_default_type({no_return, 0}) -> true; -is_default_type({node, 0}) -> true; -is_default_type({non_neg_integer, 0}) -> true; -is_default_type({none, 0}) -> true; -is_default_type({nonempty_list, 0}) -> true; -is_default_type({nonempty_list, 1}) -> true; -is_default_type({nonempty_improper_list, 2}) -> true; -is_default_type({nonempty_maybe_improper_list, 0}) -> true; -is_default_type({nonempty_maybe_improper_list, 2}) -> true; -is_default_type({nonempty_string, 0}) -> true; -is_default_type({number, 0}) -> true; -is_default_type({pid, 0}) -> true; -is_default_type({port, 0}) -> true; -is_default_type({pos_integer, 0}) -> true; -is_default_type({queue, 0}) -> true; -is_default_type({range, 2}) -> true; -is_default_type({reference, 0}) -> true; -is_default_type({set, 0}) -> true; -is_default_type({string, 0}) -> true; -is_default_type({term, 0}) -> true; -is_default_type({timeout, 0}) -> true; -is_default_type({var, 1}) -> true; -is_default_type(_) -> false. - -is_newly_introduced_var_arity_type(map) -> true; -is_newly_introduced_var_arity_type(_) -> false. - -%% is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false. +used_type(TypePair, L, St) -> +    Usage = St#lint.usage, +    OldUsed = Usage#usage.used_types, +    UsedTypes = dict:store(TypePair, L, OldUsed), +    St#lint{usage=Usage#usage{used_types=UsedTypes}}. + +is_default_type({Name, NumberOfTypeVariables}) -> +    erl_internal:is_type(Name, NumberOfTypeVariables). + +is_newly_introduced_builtin_type({map, 0}) -> true; +is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false.  is_obsolete_builtin_type(TypePair) ->      obsolete_builtin_type(TypePair) =/= no. -%% Obsolete in OTP 17.0. -obsolete_builtin_type({array, 0}) -> -    {deprecated, {array, array, 1}, "OTP 18.0"}; -obsolete_builtin_type({dict, 0}) -> -    {deprecated, {dict, dict, 2}, "OTP 18.0"}; -obsolete_builtin_type({digraph, 0}) -> -    {deprecated, {digraph, graph}, "OTP 18.0"}; -obsolete_builtin_type({gb_set, 0}) -> -    {deprecated, {gb_sets, set, 1}, "OTP 18.0"}; -obsolete_builtin_type({gb_tree, 0}) -> -    {deprecated, {gb_trees, tree, 2}, "OTP 18.0"}; -obsolete_builtin_type({queue, 0}) -> -    {deprecated, {queue, queue, 1}, "OTP 18.0"}; -obsolete_builtin_type({set, 0}) -> -    {deprecated, {sets, set, 1}, "OTP 18.0"}; -obsolete_builtin_type({tid, 0}) -> -    {deprecated, {ets, tid}, "OTP 18.0"}; +%% To keep Dialyzer silent... +obsolete_builtin_type({1, 255}) -> +    {deprecated, {2, 255}, ""};  obsolete_builtin_type({Name, A}) when is_atom(Name), is_integer(A) -> no.  %% spec_decl(Line, Fun, Types, State) -> State. @@ -2909,7 +2866,7 @@ spec_decl(Line, MFA0, TypeSpecs, St0 = #lint{specs = Specs, module = Mod}) ->  	  end,      St1 = St0#lint{specs = dict:store(MFA, Line, Specs)},      case dict:is_key(MFA, Specs) of -	true -> add_error(Line, {redefine_spec, MFA}, St1); +	true -> add_error(Line, {redefine_spec, MFA0}, St1);  	false -> check_specs(TypeSpecs, Arity, St1)      end. @@ -2917,16 +2874,50 @@ spec_decl(Line, MFA0, TypeSpecs, St0 = #lint{specs = Specs, module = Mod}) ->  callback_decl(Line, MFA0, TypeSpecs,  	      St0 = #lint{callbacks = Callbacks, module = Mod}) -> -    MFA = case MFA0 of -	      {F, Arity} -> {Mod, F, Arity}; -	      {_M, _F, Arity} -> MFA0 -	  end, -    St1 = St0#lint{callbacks = dict:store(MFA, Line, Callbacks)}, -    case dict:is_key(MFA, Callbacks) of -	true -> add_error(Line, {redefine_callback, MFA}, St1); -	false -> check_specs(TypeSpecs, Arity, St1) +    case MFA0 of +        {_M, _F, _A} -> add_error(Line, {bad_callback, MFA0}, St0); +        {F, Arity} -> +            MFA = {Mod, F, Arity}, +            St1 = St0#lint{callbacks = dict:store(MFA, Line, Callbacks)}, +            case dict:is_key(MFA, Callbacks) of +                true -> add_error(Line, {redefine_callback, MFA0}, St1); +                false -> check_specs(TypeSpecs, Arity, St1) +            end      end. +%% optional_callbacks(Line, FAs, State) -> State. + +optional_callbacks(Line, Term, St0) -> +    try true = is_fa_list(Term), Term of +        FAs -> +            optional_cbs(Line, FAs, St0) +    catch +        _:_ -> +            St0 % ignore others +    end. + +optional_cbs(_Line, [], St) -> +    St; +optional_cbs(Line, [{F,A}|FAs], St0) -> +    #lint{optional_callbacks = OptionalCbs, module = Mod} = St0, +    MFA = {Mod, F, A}, +    St1 = St0#lint{optional_callbacks = dict:store(MFA, Line, OptionalCbs)}, +    St2 = case dict:is_key(MFA, OptionalCbs) of +              true -> +                  add_error(Line, {redefine_optional_callback, {F,A}}, St1); +              false -> +                  St1 +          end, +    optional_cbs(Line, FAs, St2). + +is_fa_list([E|L]) -> is_fa(E) andalso is_fa_list(L); +is_fa_list([]) -> true; +is_fa_list(_) -> false. + +is_fa({FuncName, Arity}) +  when is_atom(FuncName), is_integer(Arity), Arity >= 0 -> true; +is_fa(_) -> false. +  check_specs([FunType|Left], Arity, St0) ->      {FunType1, CTypes} =  	case FunType of @@ -2950,10 +2941,11 @@ check_specs([], _Arity, St) ->      St.  check_specs_without_function(#lint{module=Mod,defined=Funcs,specs=Specs}=St) -> -    Fun = fun({M, F, A} = MFA, Line, AccSt) when M =:= Mod -> -		  case gb_sets:is_element({F, A}, Funcs) of +    Fun = fun({M, F, A}, Line, AccSt) when M =:= Mod -> +                  FA = {F, A}, +		  case gb_sets:is_element(FA, Funcs) of  		      true -> AccSt; -		      false -> add_error(Line, {spec_fun_undefined, MFA}, AccSt) +		      false -> add_error(Line, {spec_fun_undefined, FA}, AccSt)  		  end;  	     ({_M, _F, _A}, _Line, AccSt) -> AccSt  	  end, diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index e1ae3b7aea..a626d98ee4 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -146,8 +146,7 @@ type -> '(' top_type ')'                  : {paren_type, ?line('$2'), ['$2']}.  type -> var                               : '$1'.  type -> atom                              : '$1'.  type -> atom '(' ')'                      : build_gen_type('$1'). -type -> atom '(' top_types ')'            : {type, ?line('$1'), -                                             normalise('$1'), '$3'}. +type -> atom '(' top_types ')'            : build_type('$1', '$3').  type -> atom ':' atom '(' ')'             : {remote_type, ?line('$1'),                                               ['$1', '$3', []]}.  type -> atom ':' atom '(' top_types ')'   : {remote_type, ?line('$1'), @@ -181,7 +180,7 @@ fun_type -> '(' top_types ')' '->' top_type  map_pair_types -> map_pair_type                    : ['$1'].  map_pair_types -> map_pair_type ',' map_pair_types : ['$1'|'$3']. -map_pair_type  -> top_type '=>' top_type           : {type, ?line('$2'), map_field_assoc,'$1','$3'}. +map_pair_type  -> top_type '=>' top_type           : {type, ?line('$2'), map_field_assoc,['$1','$3']}.  field_types -> field_type                 : ['$1'].  field_types -> field_type ',' field_types : ['$1'|'$3']. @@ -665,6 +664,8 @@ find_arity_from_specs([Spec|_]) ->      {type, _, 'fun', [{type, _, product, Args},_]} = Fun,      length(Args). +build_def({var, L, '_'}, _Types) -> +    ret_err(L, "bad type variable");  build_def(LHS, Types) ->      IsSubType = {atom, ?line(LHS), is_subtype},      {type, ?line(LHS), constraint, [IsSubType, [LHS, Types]]}. @@ -684,7 +685,8 @@ build_gen_type({atom, La, tuple}) ->  build_gen_type({atom, La, map}) ->      {type, La, map, any};  build_gen_type({atom, La, Name}) -> -    {type, La, Name, []}. +    Tag = type_tag(Name, 0), +    {Tag, La, Name, []}.  build_bin_type([{var, _, '_'}|Left], Int) ->      build_bin_type(Left, Int); @@ -693,6 +695,16 @@ build_bin_type([], Int) ->  build_bin_type([{var, La, _}|_], _) ->      ret_err(La, "Bad binary type"). +build_type({atom, L, Name}, Types) -> +    Tag = type_tag(Name, length(Types)), +    {Tag, L, Name, Types}. + +type_tag(TypeName, NumberOfTypeVariables) -> +    case erl_internal:is_type(TypeName, NumberOfTypeVariables) of +        true -> type; +        false -> user_type +    end. +  %% build_attribute(AttrName, AttrValue) ->  %%	{attribute,Line,module,Module}  %%	{attribute,Line,export,Exports} diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 82bc2c1460..10842d21ab 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -232,13 +232,21 @@ lattribute(import, Name, _Opts, _State) when is_list(Name) ->      attr("import", [{var,0,pname(Name)}]);  lattribute(import, {From,Falist}, _Opts, _State) ->      attr("import",[{var,0,pname(From)},falist(Falist)]); +lattribute(optional_callbacks, Falist, Opts, _State) -> +    ArgL = try falist(Falist) +           catch _:_ -> abstract(Falist, Opts) +           end, +    call({var,0,"-optional_callbacks"}, [ArgL], 0, options(none));  lattribute(file, {Name,Line}, _Opts, State) ->      attr("file", [{var,0,(State#pp.string_fun)(Name)},{integer,0,Line}]);  lattribute(record, {Name,Is}, Opts, _State) ->      Nl = leaf(format("-record(~w,", [Name])),      [{first,Nl,record_fields(Is, Opts)},$)]; -lattribute(Name, Arg, #options{encoding = Encoding}, _State) -> -    attr(write(Name), [erl_parse:abstract(Arg, [{encoding,Encoding}])]). +lattribute(Name, Arg, Options, _State) -> +    attr(write(Name), [abstract(Arg, Options)]). + +abstract(Arg, #options{encoding = Encoding}) -> +    erl_parse:abstract(Arg, [{encoding,Encoding}]).  typeattr(Tag, {TypeName,Type,Args}, _Opts) ->      {first,leaf("-"++atom_to_list(Tag)++" "), @@ -277,6 +285,9 @@ ltype({type,_,'fun',[{type,_,any},_]}=FunType) ->  ltype({type,_Line,'fun',[{type,_,product,_},_]}=FunType) ->      [fun_type(['fun',$(], FunType),$)];  ltype({type,Line,T,Ts}) -> +    %% Compatibility. Before 18.0. +    simple_type({atom,Line,T}, Ts); +ltype({user_type,Line,T,Ts}) ->      simple_type({atom,Line,T}, Ts);  ltype({remote_type,Line,[M,F,Ts]}) ->      simple_type({remote,Line,M,F}, Ts); @@ -299,7 +310,7 @@ map_type(Fs) ->  map_pair_types(Fs) ->      tuple_type(Fs, fun map_pair_type/1). -map_pair_type({type,_Line,map_field_assoc,Ktype,Vtype}) -> +map_pair_type({type,_Line,map_field_assoc,[Ktype,Vtype]}) ->      {seq,[],[]," =>",[ltype(Ktype),ltype(Vtype)]}.  record_type(Name, Fields) -> diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl index d39dd89d3a..bb0ff46268 100644 --- a/lib/stdlib/src/gen_event.erl +++ b/lib/stdlib/src/gen_event.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -101,6 +101,14 @@  -callback code_change(OldVsn :: (term() | {down, term()}),                        State :: term(), Extra :: term()) ->      {ok, NewState :: term()}. +-callback format_status(Opt, StatusData) -> Status when +      Opt :: 'normal' | 'terminate', +      StatusData :: [PDict | State], +      PDict :: [{Key :: term(), Value :: term()}], +      State :: term(), +      Status :: term(). + +-optional_callbacks([format_status/2]).  %%--------------------------------------------------------------------------- diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl index e914f7d0b2..56137cde13 100644 --- a/lib/stdlib/src/gen_fsm.erl +++ b/lib/stdlib/src/gen_fsm.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -160,6 +160,14 @@  -callback code_change(OldVsn :: term() | {down, term()}, StateName :: atom(),  		      StateData :: term(), Extra :: term()) ->      {ok, NextStateName :: atom(), NewStateData :: term()}. +-callback format_status(Opt, StatusData) -> Status when +      Opt :: 'normal' | 'terminate', +      StatusData :: [PDict | State], +      PDict :: [{Key :: term(), Value :: term()}], +      State :: term(), +      Status :: term(). + +-optional_callbacks([format_status/2]).  %%% ---------------------------------------------------  %%% Starts a generic state machine. diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl index 202a931fae..22acdf10b7 100644 --- a/lib/stdlib/src/gen_server.erl +++ b/lib/stdlib/src/gen_server.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -137,6 +137,15 @@  -callback code_change(OldVsn :: (term() | {down, term()}), State :: term(),                        Extra :: term()) ->      {ok, NewState :: term()} | {error, Reason :: term()}. +-callback format_status(Opt, StatusData) -> Status when +      Opt :: 'normal' | 'terminate', +      StatusData :: [PDict | State], +      PDict :: [{Key :: term(), Value :: term()}], +      State :: term(), +      Status :: term(). + +-optional_callbacks([format_status/2]). +  %%%  -----------------------------------------------------------------  %%% Starts a generic server. diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl index c0ee8799c8..662a0aca74 100644 --- a/lib/stdlib/src/otp_internal.erl +++ b/lib/stdlib/src/otp_internal.erl @@ -577,8 +577,6 @@ obsolete_1(asn1rt, utf8_binary_to_list, 1) ->      {deprecated,{unicode,characters_to_list,1}};  obsolete_1(asn1rt, utf8_list_to_binary, 1) ->      {deprecated,{unicode,characters_to_binary,1}}; -obsolete_1(pg, _, _) -> -    {deprecated,"deprecated; will be removed in OTP 18"};  obsolete_1(_, _, _) ->      no. diff --git a/lib/stdlib/src/pg.erl b/lib/stdlib/src/pg.erl deleted file mode 100644 index a41fd329c2..0000000000 --- a/lib/stdlib/src/pg.erl +++ /dev/null @@ -1,187 +0,0 @@ -%% -%% %CopyrightBegin% -%%  -%% Copyright Ericsson AB 1996-2014. All Rights Reserved. -%%  -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%%  -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%%  -%% %CopyrightEnd% -%% --module(pg). --deprecated(module). - -%% pg provides a process group facility. Messages  -%% can be multicasted to all members in the group - --export([create/1, -	 create/2, -	 standby/2, -	 join/2, -	 send/2, -	 esend/2, -	 members/1, -	 name_to_pid/1, -	 master/1]). - - -%% Create a brand new empty process group with the master residing  -%% at the local node - --spec create(PgName) -> 'ok' | {'error', Reason} when -      PgName :: term(), -      Reason :: 'already_created' | term(). - -create(PgName) ->  -    catch begin check(PgName), -    Pid = spawn(pg,master,[PgName]), -    global:register_name(PgName,Pid), -    ok end. - -%% Create a brand new empty process group with the master  -%% residing at Node - --spec create(PgName, Node) -> 'ok' | {'error', Reason} when -      PgName :: term(), -      Node :: node(), -      Reason :: 'already_created' | term(). - -create(PgName, Node) -> -    catch begin check(PgName), -    Pid = spawn(Node,pg,master,[PgName]), -    global:register_name(PgName,Pid), -    ok end. - -%% Have a process on Node that will act as a standby for the process -%% group manager. So if the node where the manager runs fails, the -%% process group will continue to function. - --spec standby(term(), node()) -> 'ok'. - -standby(_PgName, _Node) -> -    ok. - -%% Tell process group PgName that Pid is a new member of the group -%% synchronously return a list of all old members in the group - --spec join(PgName, Pid) -> Members when -      PgName :: term(), -      Pid :: pid(), -      Members :: [pid()]. - -join(PgName, Pid) when is_atom(PgName) ->  -    global:send(PgName, {join,self(),Pid}), -    receive -	{_P,{members,Members}} -> -	    Members -    end. - -%% Multi cast Mess to all members in the group - --spec send(PgName, Msg) -> 'ok' when -      PgName :: term(), -      Msg :: term(). - -send(PgName, Mess) when is_atom(PgName) -> -    global:send(PgName, {send, self(), Mess}), -    ok; -send(Pg, Mess) when is_pid(Pg) -> -    Pg ! {send,self(),Mess}, -    ok. - -%% multi cast a message to all members in the group but ourselves -%% If we are a member - --spec esend(PgName, Msg) -> 'ok' when -      PgName :: term(), -      Msg :: term(). - -esend(PgName, Mess) when is_atom(PgName) -> -    global:send(PgName, {esend,self(),Mess}), -    ok; -esend(Pg, Mess) when is_pid(Pg) -> -    Pg ! {esend,self(),Mess}, -    ok. - -%% Return the members of the group - --spec members(PgName) -> Members when -      PgName :: term(), -      Members :: [pid()]. - -members(PgName) when is_atom(PgName) -> -    global:send(PgName, {self() ,members}), -    receive -	{_P,{members,Members}} -> -	    Members -    end; -members(Pg) when is_pid(Pg) -> -    Pg ! {self,members}, -    receive -	{_P,{members,Members}} -> -	    Members -    end. - --spec name_to_pid(atom()) -> pid() | 'undefined'. - -name_to_pid(PgName) when is_atom(PgName) -> -    global:whereis_name(PgName). - --spec master(term()) -> no_return(). - -master(PgName) -> -    process_flag(trap_exit, true), -    master_loop(PgName, []). - -master_loop(PgName,Members) -> -    receive -	{send,From,Message} -> -	    send_all(Members,{pg_message,From,PgName,Message}), -	    master_loop(PgName,Members); -	{esend,From,Message} -> -	    send_all(lists:delete(From,Members), -		     {pg_message,From,PgName,Message}), -	    master_loop(PgName,Members); -	{join,From,Pid} -> -	    link(Pid), -	    send_all(Members,{new_member,PgName,Pid}), -	    From ! {self(),{members,Members}}, -	    master_loop(PgName,[Pid|Members]); -	{From,members} -> -	    From ! {self(),{members,Members}}, -	    master_loop(PgName,Members); -	{'EXIT',From,_} -> -	    L = -		case lists:member(From,Members) of -		    true -> -			NewMembers = lists:delete(From,Members), -			send_all(NewMembers, {crashed_member,PgName,From}), -			NewMembers; -		    false -> -			Members -		end, -	    master_loop(PgName,L)   -    end. - -send_all([], _) -> ok; -send_all([P|Ps], M) -> -    P ! M, -    send_all(Ps, M). - -%% Check if the process group already exists - -check(PgName) -> -    case global:whereis_name(PgName) of -        Pid when is_pid(Pid) ->  -            throw({error,already_created}); -        undefined -> -	    ok -    end. diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src index d388410de0..42a787dacf 100644 --- a/lib/stdlib/src/stdlib.app.src +++ b/lib/stdlib/src/stdlib.app.src @@ -77,7 +77,6 @@  	     orddict,  	     ordsets,  	     otp_internal, -	     pg,  	     pool,  	     proc_lib,  	     proplists, diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src index 22eefb2514..b584cbb47c 100644 --- a/lib/stdlib/src/stdlib.appup.src +++ b/lib/stdlib/src/stdlib.appup.src @@ -17,9 +17,7 @@  %% %CopyrightEnd%  {"%VSN%",   %% Up from - max one major revision back - [{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 -  {<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R16 + [{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-17   %% Down to - max one major revision back - [{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 -  {<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R16 + [{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}]  % OTP-17  }. diff --git a/lib/stdlib/test/erl_internal_SUITE.erl b/lib/stdlib/test/erl_internal_SUITE.erl index b6b3c004ea..197a7a33eb 100644 --- a/lib/stdlib/test/erl_internal_SUITE.erl +++ b/lib/stdlib/test/erl_internal_SUITE.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %%  -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2014. All Rights Reserved.  %%   %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -51,7 +51,7 @@ end_per_group(_GroupName, Config) ->  -define(default_timeout, ?t:minutes(2)).  init_per_testcase(_Case, Config) -> -    ?line Dog = test_server:timetrap(?default_timeout), +    Dog = test_server:timetrap(?default_timeout),      [{watchdog, Dog}|Config].  end_per_testcase(_Case, Config) -> @@ -63,27 +63,50 @@ behav(suite) -> [];  behav(doc) ->      ["Check that the behaviour callbacks are correctly defined"];  behav(_) -> -    ?line check_behav_list([{start,2}, {stop,1}],  -			   application:behaviour_info(callbacks)), -    ?line check_behav_list([{init,1}, {handle_call,3}, {handle_cast,2}, -			    {handle_info,2}, {terminate,2}, {code_change,3}], -			   gen_server:behaviour_info(callbacks)), -    ?line check_behav_list([{init,1}, {handle_event,3}, {handle_sync_event,4}, -			    {handle_info,3}, {terminate,3}, {code_change,4}], -			   gen_fsm:behaviour_info(callbacks)), -    ?line check_behav_list([{init,1}, {handle_event,2}, {handle_call,2}, -			    {handle_info,2}, {terminate,2}, {code_change,3}], -			   gen_event:behaviour_info(callbacks)), -    ?line check_behav_list( [{init,1}, {terminate,2}],  -			   supervisor_bridge:behaviour_info(callbacks)), -    ?line check_behav_list([{init,1}], -			   supervisor:behaviour_info(callbacks)), -    ok. +    Modules = [application, gen_server, gen_fsm, gen_event, +               supervisor_bridge, supervisor], +    lists:foreach(fun check_behav/1, Modules). + +check_behav(Module) -> +    Callbacks = callbacks(Module), +    Optional = optional_callbacks(Module), +    check_behav_list(Callbacks, Module:behaviour_info(callbacks)), +    check_behav_list(Optional, Module:behaviour_info(optional_callbacks)).  check_behav_list([], []) -> ok;  check_behav_list([L | L1], L2) -> -    ?line true = lists:member(L, L2), -    ?line L3 = lists:delete(L, L2), +    true = lists:member(L, L2), +    L3 = lists:delete(L, L2),      check_behav_list(L1, L3). - +callbacks(application) -> +    [{start,2}, {stop,1}]; +callbacks(gen_server) -> +    [{init,1}, {handle_call,3}, {handle_cast,2}, +     {handle_info,2}, {terminate,2}, {code_change,3}, +     {format_status,2}]; +callbacks(gen_fsm) -> +    [{init,1}, {handle_event,3}, {handle_sync_event,4}, +     {handle_info,3}, {terminate,3}, {code_change,4}, +     {format_status,2}]; +callbacks(gen_event) -> +    [{init,1}, {handle_event,2}, {handle_call,2}, +     {handle_info,2}, {terminate,2}, {code_change,3}, +     {format_status,2}]; +callbacks(supervisor_bridge) -> +    [{init,1}, {terminate,2}]; +callbacks(supervisor) -> +    [{init,1}]. + +optional_callbacks(application) -> +    []; +optional_callbacks(gen_server) -> +    [{format_status,2}]; +optional_callbacks(gen_fsm) -> +    [{format_status,2}]; +optional_callbacks(gen_event) -> +    [{format_status,2}]; +optional_callbacks(supervisor_bridge) -> +    []; +optional_callbacks(supervisor) -> +    []. diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index ea61b2082b..ca91d94213 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -55,7 +55,7 @@            otp_11772/1, otp_11771/1, otp_11872/1,            export_all/1,  	  bif_clash/1, -	  behaviour_basic/1, behaviour_multiple/1, +	  behaviour_basic/1, behaviour_multiple/1, otp_11861/1,  	  otp_7550/1,  	  otp_8051/1,  	  format_warn/1, @@ -63,7 +63,7 @@  	  too_many_arguments/1,  	  basic_errors/1,bin_syntax_errors/1,            predef/1, -          maps/1,maps_type/1 +          maps/1,maps_type/1,otp_11851/1          ]).  % Default timetrap timeout (set in init_per_testcase). @@ -89,10 +89,10 @@ all() ->       otp_5362, otp_5371, otp_7227, otp_5494, otp_5644,       otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, otp_11254,       otp_11772, otp_11771, otp_11872, export_all, -     bif_clash, behaviour_basic, behaviour_multiple, +     bif_clash, behaviour_basic, behaviour_multiple, otp_11861,       otp_7550, otp_8051, format_warn, {group, on_load},       too_many_arguments, basic_errors, bin_syntax_errors, predef, -     maps, maps_type]. +     maps, maps_type, otp_11851].  groups() ->       [{unused_vars_warn, [], @@ -2648,8 +2648,9 @@ otp_11872(Config) when is_list(Config) ->              t() ->                  1.           ">>, -    {error,[{6,erl_lint,{undefined_type,{product,0}}}], -     [{8,erl_lint,{new_var_arity_type,map}}]} = +    {error,[{6,erl_lint,{undefined_type,{product,0}}}, +            {8,erl_lint,{undefined_type,{dict,0}}}], +           [{8,erl_lint,{new_builtin_type,{map,0}}}]} =          run_test2(Config, Ts, []),      ok. @@ -3080,6 +3081,193 @@ behaviour_multiple(Config) when is_list(Config) ->      ?line [] = run(Config, Ts),      ok. +otp_11861(doc) -> +    "OTP-11861. behaviour_info() and -callback."; +otp_11861(suite) -> []; +otp_11861(Conf) when is_list(Conf) -> +    CallbackFiles = [callback1, callback2, callback3, +                     bad_behaviour1, bad_behaviour2], +    lists:foreach(fun(M) -> +                          F = filename:join(?datadir, M), +                          Opts = [{outdir,?privdir}, return], +                          {ok, M, []} = compile:file(F, Opts) +                  end, CallbackFiles), +    CodePath = code:get_path(), +    true = code:add_path(?privdir), +    Ts = [{otp_11861_1, +           <<" +              -export([b1/1]). +              -behaviour(callback1). +              -behaviour(callback2). + +              -spec b1(atom()) -> integer(). +              b1(A) when is_atom(A)-> +                  3. +             ">>, +           [], +           %% b2/1 is optional in both modules +           {warnings,[{4,erl_lint, +                       {conflicting_behaviours,{b1,1},callback2,3,callback1}}]}}, +          {otp_11861_2, +           <<" +              -export([b2/1]). +              -behaviour(callback1). +              -behaviour(callback2). + +              -spec b2(integer()) -> atom(). +              b2(I) when is_integer(I)-> +                  a. +             ">>, +           [], +           %% b2/1 is optional in callback2, but not in callback1 +           {warnings,[{3,erl_lint,{undefined_behaviour_func,{b1,1},callback1}}, +                      {4,erl_lint, +                       {conflicting_behaviours,{b2,1},callback2,3,callback1}}]}}, +          {otp_11861_3, +           <<" +              -callback b(_) -> atom(). +              -optional_callbacks({b1,1}). % non-existing and ignored +             ">>, +           [], +           []}, +          {otp_11861_4, +           <<" +              -callback b(_) -> atom(). +              -optional_callbacks([{b1,1}]). % non-existing +             ">>, +           [], +           %% No behaviour-info(), but callback. +           {errors,[{3,erl_lint,{undefined_callback,{lint_test,b1,1}}}],[]}}, +          {otp_11861_5, +           <<" +              -optional_callbacks([{b1,1}]). % non-existing +             ">>, +           [], +           %% No behaviour-info() and no callback: warning anyway +           {errors,[{2,erl_lint,{undefined_callback,{lint_test,b1,1}}}],[]}}, +          {otp_11861_6, +           <<" +              -optional_callbacks([b1/1]). % non-existing +              behaviour_info(callbacks) -> [{b1,1}]. +             ">>, +           [], +           %% behaviour-info() and no callback: warning anyway +           {errors,[{2,erl_lint,{undefined_callback,{lint_test,b1,1}}}],[]}}, +          {otp_11861_7, +           <<" +              -optional_callbacks([b1/1]). % non-existing +              -callback b(_) -> atom(). +              behaviour_info(callbacks) -> [{b1,1}]. +             ">>, +           [], +           %% behaviour-info() callback: warning +           {errors,[{2,erl_lint,{undefined_callback,{lint_test,b1,1}}}, +                    {3,erl_lint,{behaviour_info,{lint_test,b,1}}}], +            []}}, +          {otp_11861_8, +           <<" +              -callback b(_) -> atom(). +              -optional_callbacks([b/1, {b, 1}]). +             ">>, +           [], +           {errors,[{3,erl_lint,{redefine_optional_callback,{b,1}}}],[]}}, +          {otp_11861_9, +           <<" +              -behaviour(gen_server). +              -export([handle_call/3,handle_cast/2,handle_info/2, +                       code_change/3, init/1, terminate/2]). +              handle_call(_, _, _) -> ok. +              handle_cast(_, _) -> ok. +              handle_info(_, _) -> ok. +              code_change(_, _, _) -> ok. +              init(_) -> ok. +              terminate(_, _) -> ok. +             ">>, +           [], +           []}, +          {otp_11861_9, +           <<" +              -behaviour(gen_server). +              -export([handle_call/3,handle_cast/2,handle_info/2, +                       code_change/3, init/1, terminate/2, format_status/2]). +              handle_call(_, _, _) -> ok. +              handle_cast(_, _) -> ok. +              handle_info(_, _) -> ok. +              code_change(_, _, _) -> ok. +              init(_) -> ok. +              terminate(_, _) -> ok. +              format_status(_, _) -> ok. % optional callback +             ">>, +           [], +           %% Nothing... +           []}, +          {otp_11861_10, +           <<" +              -optional_callbacks([{b1,1,bad}]). % badly formed and ignored +              behaviour_info(callbacks) -> [{b1,1}]. +             ">>, +           [], +           []}, +          {otp_11861_11, +           <<" +              -behaviour(bad_behaviour1). +             ">>, +           [], +           {warnings,[{2,erl_lint, +                       {ill_defined_behaviour_callbacks,bad_behaviour1}}]}}, +          {otp_11861_12, +           <<" +              -behaviour(non_existing_behaviour). +             ">>, +           [], +           {warnings,[{2,erl_lint, +                       {undefined_behaviour,non_existing_behaviour}}]}}, +          {otp_11861_13, +           <<" +              -behaviour(bad_behaviour_none). +             ">>, +           [], +           {warnings,[{2,erl_lint,{undefined_behaviour,bad_behaviour_none}}]}}, +          {otp_11861_14, +           <<" +              -callback b(_) -> atom(). +             ">>, +           [], +           []}, +          {otp_11861_15, +           <<" +              -optional_callbacks([{b1,1,bad}]). % badly formed +              -callback b(_) -> atom(). +             ">>, +           [], +           []}, +          {otp_11861_16, +           <<" +              -callback b(_) -> atom(). +              -callback b(_) -> atom(). +             ">>, +           [], +           {errors,[{3,erl_lint,{redefine_callback,{b,1}}}],[]}}, +          {otp_11861_17, +           <<" +              -behaviour(bad_behaviour2). +             ">>, +           [], +           {warnings,[{2,erl_lint,{undefined_behaviour_callbacks, +                                   bad_behaviour2}}]}}, +          {otp_11861_18, +           <<" +              -export([f1/1]). +              -behaviour(callback3). +              f1(_) -> ok. +             ">>, +           [], +           []} +	 ], +    ?line [] = run(Conf, Ts), +    true = code:set_path(CodePath), +    ok. +  otp_7550(doc) ->      "Test that the new utf8/utf16/utf32 types do not allow size or unit specifiers.";  otp_7550(Config) when is_list(Config) -> @@ -3145,8 +3333,8 @@ format_warn(Config) when is_list(Config) ->      ok.  format_level(Level, Count, Config) -> -    ?line W = get_compilation_warnings(Config, "format", -                                       [{warn_format, Level}]), +    ?line W = get_compilation_result(Config, "format", +                                     [{warn_format, Level}]),      %% Pick out the 'format' warnings.      ?line FW = lists:filter(fun({_Line, erl_lint, {format_error, _}}) -> true;                                 (_) -> false @@ -3330,42 +3518,22 @@ bin_syntax_errors(Config) ->      ok.  predef(doc) -> -    "OTP-10342: Predefined types: array(), digraph(), and so on"; +    "OTP-10342: No longer predefined types: array(), digraph(), and so on";  predef(suite) -> [];  predef(Config) when is_list(Config) -> -    W = get_compilation_warnings(Config, "predef", []), +    W = get_compilation_result(Config, "predef", []),      [] = W, -    W2 = get_compilation_warnings(Config, "predef2", []), -    Tag = deprecated_builtin_type, -    [{7,erl_lint,{Tag,{array,0},{array,array,1},"OTP 18.0"}}, -     {12,erl_lint,{Tag,{dict,0},{dict,dict,2},"OTP 18.0"}}, -     {17,erl_lint,{Tag,{digraph,0},{digraph,graph},"OTP 18.0"}}, -     {27,erl_lint,{Tag,{gb_set,0},{gb_sets,set,1},"OTP 18.0"}}, -     {32,erl_lint,{Tag,{gb_tree,0},{gb_trees,tree,2},"OTP 18.0"}}, -     {37,erl_lint,{Tag,{queue,0},{queue,queue,1},"OTP 18.0"}}, -     {42,erl_lint,{Tag,{set,0},{sets,set,1},"OTP 18.0"}}, -     {47,erl_lint,{Tag,{tid,0},{ets,tid},"OTP 18.0"}}] = W2, -    Ts = [{otp_10342_1, -           <<"-compile(nowarn_deprecated_type). - -              -spec t(dict()) -> non_neg_integer(). - -              t(D) -> -                  erlang:phash2(D, 3000). -             ">>, -           {[nowarn_unused_function]}, -           []}, -         {otp_10342_2, -           <<"-spec t(dict()) -> non_neg_integer(). - -              t(D) -> -                  erlang:phash2(D, 3000). -             ">>, -           {[nowarn_unused_function]}, -           {warnings,[{1,erl_lint, -                       {deprecated_builtin_type,{dict,0},{dict,dict,2}, -                        "OTP 18.0"}}]}}], -    [] = run(Config, Ts), +    %% dict(), digraph() and so on were removed in Erlang/OTP 18.0. +    E2 = get_compilation_result(Config, "predef2", []), +    Tag = undefined_type, +    {[{7,erl_lint,{Tag,{array,0}}}, +      {12,erl_lint,{Tag,{dict,0}}}, +      {17,erl_lint,{Tag,{digraph,0}}}, +      {27,erl_lint,{Tag,{gb_set,0}}}, +      {32,erl_lint,{Tag,{gb_tree,0}}}, +      {37,erl_lint,{Tag,{queue,0}}}, +      {42,erl_lint,{Tag,{set,0}}}, +      {47,erl_lint,{Tag,{tid,0}}}],[]} = E2,      ok.  maps(Config) -> @@ -3470,7 +3638,94 @@ maps_type(Config) when is_list(Config) ->  	    t(M) -> M.  	 ">>,  	 [], -	 {warnings,[{3,erl_lint,{new_var_arity_type,map}}]}}], +	 {warnings,[{3,erl_lint,{new_builtin_type,{map,0}}}]}}], +    [] = run(Config, Ts), +    ok. + +otp_11851(doc) -> +    "OTP-11851: More atoms can be used as type names + bug fixes."; +otp_11851(Config) when is_list(Config) -> +    Ts = [ +	{otp_11851_1, +	 <<"-export([t/0]). +            -type range(A, B) :: A | B. + +            -type union(A) :: A. + +            -type product() :: integer(). + +            -type tuple(A) :: A. + +            -type map(A) :: A. + +            -type record() :: a | b. + +            -type integer(A) :: A. + +            -type atom(A) :: A. + +            -type binary(A, B) :: A | B. + +            -type 'fun'() :: integer(). + +            -type 'fun'(X) :: X. + +            -type 'fun'(X, Y) :: X | Y. + +            -type all() :: range(atom(), integer()) | union(pid()) | product() +                         | tuple(reference()) | map(function()) | record() +                         | integer(atom()) | atom(integer()) +                         | binary(pid(), tuple()) | 'fun'(port()) +                         | 'fun'() | 'fun'(<<>>, 'none'). + +            -spec t() -> all(). + +            t() -> +                a. +	">>, +	[], +	[]}, +	{otp_11851_2, +	 <<"-export([a/1, b/1, t/0]). + +            -callback b(_) -> integer(). + +            -callback ?MODULE:a(_) -> integer(). + +            a(_) -> 3. + +            b(_) -> a. + +            t()-> a. +	">>, +	[], +	{errors,[{5,erl_lint,{bad_callback,{lint_test,a,1}}}],[]}}, +	{otp_11851_3, +	 <<"-export([a/1]). + +            -spec a(_A) -> boolean() when +                  _ :: atom(), +                  _A :: integer(). + +            a(_) -> true. +	">>, +	[], +	{errors,[{4,erl_parse,"bad type variable"}],[]}}, +	{otp_11851_4, +	 <<" +            -spec a(_) -> ok. +            -spec a(_) -> ok. + +            -spec ?MODULE:a(_) -> ok. +            -spec ?MODULE:a(_) -> ok. +	">>, +	[], +         {errors,[{3,erl_lint,{redefine_spec,{a,1}}}, +                  {5,erl_lint,{redefine_spec,{lint_test,a,1}}}, +                  {6,erl_lint,{redefine_spec,{lint_test,a,1}}}, +                  {6,erl_lint,{spec_fun_undefined,{a,1}}}], +          []}} +          ],      [] = run(Config, Ts),      ok. @@ -3487,9 +3742,9 @@ run(Config, Tests) ->          end,      lists:foldl(F, [], Tests). -%% Compiles a test file and returns the list of warnings. +%% Compiles a test file and returns the list of warnings/errors. -get_compilation_warnings(Conf, Filename, Warnings) -> +get_compilation_result(Conf, Filename, Warnings) ->      ?line DataDir = ?datadir,      ?line File = filename:join(DataDir, Filename),      {ok,Bin} = file:read_file(File++".erl"), @@ -3498,6 +3753,7 @@ get_compilation_warnings(Conf, Filename, Warnings) ->      Test = lists:nthtail(Start+Length, FileS),      case run_test(Conf, Test, Warnings) of          {warnings, Ws} -> Ws; +        {errors,Es,Ws} -> {Es,Ws};          [] -> []      end. diff --git a/lib/stdlib/test/erl_lint_SUITE_data/bad_behaviour1.erl b/lib/stdlib/test/erl_lint_SUITE_data/bad_behaviour1.erl new file mode 100644 index 0000000000..230f4b4519 --- /dev/null +++ b/lib/stdlib/test/erl_lint_SUITE_data/bad_behaviour1.erl @@ -0,0 +1,6 @@ +-module(bad_behaviour1). + +-export([behaviour_info/1]). + +behaviour_info(callbacks) -> +    [{a,1,bad}]. diff --git a/lib/stdlib/test/erl_lint_SUITE_data/bad_behaviour2.erl b/lib/stdlib/test/erl_lint_SUITE_data/bad_behaviour2.erl new file mode 100644 index 0000000000..bb755ce18b --- /dev/null +++ b/lib/stdlib/test/erl_lint_SUITE_data/bad_behaviour2.erl @@ -0,0 +1,6 @@ +-module(bad_behaviour2). + +-export([behaviour_info/1]). + +behaviour_info(callbacks) -> +    undefined. diff --git a/lib/stdlib/test/erl_lint_SUITE_data/callback1.erl b/lib/stdlib/test/erl_lint_SUITE_data/callback1.erl new file mode 100644 index 0000000000..3cc5b51879 --- /dev/null +++ b/lib/stdlib/test/erl_lint_SUITE_data/callback1.erl @@ -0,0 +1,6 @@ +-module(callback1). + +-callback b1(I :: integer()) -> atom(). +-callback b2(A :: atom()) -> integer(). + +-optional_callbacks([{b2,1}]). diff --git a/lib/stdlib/test/erl_lint_SUITE_data/callback2.erl b/lib/stdlib/test/erl_lint_SUITE_data/callback2.erl new file mode 100644 index 0000000000..211cf9f115 --- /dev/null +++ b/lib/stdlib/test/erl_lint_SUITE_data/callback2.erl @@ -0,0 +1,6 @@ +-module(callback2). + +-callback b1(I :: integer()) -> atom(). +-callback b2(A :: atom()) -> integer(). + +-optional_callbacks([b1/1, b2/1]). diff --git a/lib/stdlib/test/erl_lint_SUITE_data/callback3.erl b/lib/stdlib/test/erl_lint_SUITE_data/callback3.erl new file mode 100644 index 0000000000..97b3ecb860 --- /dev/null +++ b/lib/stdlib/test/erl_lint_SUITE_data/callback3.erl @@ -0,0 +1,8 @@ +-module(callback3). + +-export([behaviour_info/1]). + +behaviour_info(callbacks) -> +    [{f1, 1}]; +behaviour_info(_) -> +    undefined. diff --git a/lib/stdlib/test/erl_lint_SUITE_data/predef.erl b/lib/stdlib/test/erl_lint_SUITE_data/predef.erl index ee9073aa67..3cb7bf40f1 100644 --- a/lib/stdlib/test/erl_lint_SUITE_data/predef.erl +++ b/lib/stdlib/test/erl_lint_SUITE_data/predef.erl @@ -5,8 +5,8 @@  -export_type([array/0, digraph/0, gb_set/0]). -%% Before Erlang/OTP 17.0 local re-definitions of pre-defined opaque -%% types were ignored but did not generate any warning. +%% Since Erlang/OTP 18.0 array() and so on are no longer pre-defined, +%% so there is nothing special about them at all.  -opaque array() :: atom().  -opaque digraph() :: atom().  -opaque gb_set() :: atom(). diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index babf3a49eb..12817943d0 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 2006-2013. All Rights Reserved. +%% Copyright Ericsson AB 2006-2014. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -50,7 +50,7 @@  	  otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1,  	  otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1, -          otp_10302/1, otp_10820/1, otp_11100/1]). +          otp_10302/1, otp_10820/1, otp_11100/1, otp_11861/1]).  %% Internal export.  -export([ehook/6]). @@ -83,7 +83,7 @@ groups() ->       {tickets, [],        [otp_6321, otp_6911, otp_6914, otp_8150, otp_8238,         otp_8473, otp_8522, otp_8567, otp_8664, otp_9147, -       otp_10302, otp_10820, otp_11100]}]. +       otp_10302, otp_10820, otp_11100, otp_11861]}].  init_per_suite(Config) ->      Config. @@ -874,6 +874,7 @@ type_examples() ->       {ex3,<<"-type paren() :: (ann2()). ">>},       {ex4,<<"-type t1() :: atom(). ">>},       {ex5,<<"-type t2() :: [t1()]. ">>}, +     {ex56,<<"-type integer(A) :: A. ">>},       {ex6,<<"-type t3(Atom) :: integer(Atom). ">>},       {ex7,<<"-type '\\'t::4'() :: t3('\\'foobar'). ">>},       {ex8,<<"-type t5() :: {t1(), t3(foo)}. ">>}, @@ -1204,8 +1205,18 @@ otp_11100(Config) when is_list(Config) ->               []}}),      ok. +otp_11861(doc) -> +    "OTP-11861. behaviour_info() and -callback."; +otp_11861(suite) -> []; +otp_11861(Config) when is_list(Config) -> +    "-optional_callbacks([bar/0]).\n" = +        pf({attribute,3,optional_callbacks,[{bar,0}]}), +    "-optional_callbacks([{bar,1,bad}]).\n" = +        pf({attribute,4,optional_callbacks,[{bar,1,bad}]}), +    ok. +  pf(Form) -> -    lists:flatten(erl_pp:form(Form,none)). +    lists:flatten(erl_pp:form(Form, none)).  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl index 46a5ca48df..7f1e7dda31 100644 --- a/lib/syntax_tools/src/erl_syntax.erl +++ b/lib/syntax_tools/src/erl_syntax.erl @@ -3320,6 +3320,11 @@ attribute_arguments(Node) ->  		    [set_pos(  		       list(unfold_function_names(Data, Pos)),  		       Pos)]; +		optional_callbacks -> +                    D = try list(unfold_function_names(Data, Pos)) +                        catch _:_ -> abstract(Data) +                        end, +		    [set_pos(D, Pos)];  		import ->  		    {Module, Imports} = Data,  		    [set_pos(atom(Module), Pos), @@ -6129,6 +6134,13 @@ abstract_tail(H, T) ->  %% {@link char/1} function to explicitly create an abstract  %% character.)  %% +%% Note: `arity_qualifier' nodes are recognized. This is to follow The +%% Erlang Parser when it comes to wild attributes: both {F, A} and F/A +%% are recognized, which makes it possible to turn wild attributes +%% into recognized attributes without at the same time making it +%% impossible to compile files using the new syntax with the old +%% version of the Erlang Compiler. +%%  %% @see abstract/1  %% @see is_literal/1  %% @see char/1 @@ -6170,6 +6182,20 @@ concrete(Node) ->  					   {value, concrete(F), []}  				   end, [], true),  	    B; +        arity_qualifier -> +            A = erl_syntax:arity_qualifier_argument(Node), +            case erl_syntax:type(A) of +                integer -> +                    F = erl_syntax:arity_qualifier_body(Node), +                    case erl_syntax:type(F) of +                        atom -> +                            {F, A}; +                        _ -> +                            erlang:error({badarg, Node}) +                    end; +                _ -> +                    erlang:error({badarg, Node}) +            end;          _ ->  	    erlang:error({badarg, Node})      end. diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index 572bf24ca4..cbad05081e 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -405,7 +405,7 @@ get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, Records) ->    case dialyzer_codeserver:lookup_mfa_contract(MFA, CodeServer) of      error ->        {{F, A}, {Range, Arg}}; -    {ok, {_FileLine, Contract}} -> +    {ok, {_FileLine, Contract, _Xtra}} ->        Sig = erl_types:t_fun(Arg, Range),        case dialyzer_contracts:check_contract(Contract, Sig) of  	ok -> {{F, A}, {contract, Contract}}; diff --git a/system/doc/design_principles/distributed_applications.xml b/system/doc/design_principles/distributed_applications.xml index 2886f06b53..4d4ba3136e 100644 --- a/system/doc/design_principles/distributed_applications.xml +++ b/system/doc/design_principles/distributed_applications.xml @@ -43,7 +43,7 @@        addressing mechanism is required to ensure that it can be        addressed by other applications, regardless on which node it        currently executes. This issue is not addressed here, but the -      Kernel module <c>global</c> or STDLIB module <c>pg</c> can be +      Kernel modules <c>global</c> or <c>pg2</c> can be        used for this purpose.</p>    </section> diff --git a/system/doc/design_principles/spec_proc.xml b/system/doc/design_principles/spec_proc.xml index e4fb5fdca7..e849388a38 100644 --- a/system/doc/design_principles/spec_proc.xml +++ b/system/doc/design_principles/spec_proc.xml @@ -431,43 +431,79 @@ loop(...) ->    <section>      <title>User-Defined Behaviours</title> -    <p><marker id="behaviours"/>To implement a user-defined behaviour, write code similar to -      code for a special process but calling functions in a callback -      module for handling specific tasks.</p> -    <p>If it is desired that the compiler should warn for missing callback -    functions, as it does for the OTP behaviours, add <c>-callback</c> attributes in the -    behaviour module to describe the expected callbacks:</p> + +    <p><marker id="behaviours"/>To implement a user-defined behaviour, +      write code similar to code for a special process but calling +      functions in a callback module for handling specific tasks.</p> +    <p>If it is desired that the compiler should warn for missing +      callback functions, as it does for the OTP behaviours, add +      <c>-callback</c> attributes in the behaviour module to describe +      the expected callback functions:</p> +      <code type="none">  -callback Name1(Arg1_1, Arg1_2, ..., Arg1_N1) -> Res1.  -callback Name2(Arg2_1, Arg2_2, ..., Arg2_N2) -> Res2.  ...  -callback NameM(ArgM_1, ArgM_2, ..., ArgM_NM) -> ResM.</code> -    <p>where <c>NameX</c> are the names of the expected callbacks and -    <c>ArgX_Y</c>, <c>ResX</c> are types as they are described in Specifications -    for functions in <seealso marker="../reference_manual/typespec">Types and -    Function Specifications</seealso>. The whole syntax of <c>-spec</c> attribute is -    supported by <c>-callback</c> attribute.</p> -    <p>Alternatively you may directly implement and export the function:</p> + +    <p>where each <c>Name</c> is the name of a callback function and +      <c>Arg</c> and <c>Res</c> are types as described in +      Specifications for functions in <seealso +      marker="../reference_manual/typespec">Types and Function +      Specifications</seealso>. The whole syntax of the +      <c>-spec</c> attribute is supported by <c>-callback</c> +      attribute.</p> +    <p>Callback functions that are optional for the user of the +      behaviour to implement are specified by use of the +      <c>-optional_callbacks</c> attribute:</p> + +<code type="none"> +-optional_callbacks([OptName1/OptArity1, ..., OptNameK/OptArityK]).</code> + +    <p>where each <c>OptName/OptArity</c> specifies the name and arity +      of a callback function. Note that the <c>-optional_callbacks</c> +      attribute is to be used together with the <c>-callback</c> +      attribute; it cannot be combined with the +      <c>behaviour_info()</c> function described below.</p> +    <p>Tools that need to know about optional callback functions can +      call <c>Behaviour:behaviour_info(optional_callbacks)</c> to get +      a list of all optional callback functions.</p> + +    <note><p>We recommend using the <c>-callback</c> attribute rather +      than the <c>behaviour_info()</c> function. The reason is that +      the extra type information can be used by tools to produce +      documentation or find discrepancies.</p></note> + +    <p>As an alternative to the <c>-callback</c> and +      <c>-optional_callbacks</c> attributes you may directly implement +      and export <c>behaviour_info()</c>:</p> +      <code type="none">  behaviour_info(callbacks) ->      [{Name1, Arity1},...,{NameN, ArityN}].</code> -    <p>where each <c>{Name, Arity}</c> specifies the name and arity of a callback -    function. This function is otherwise automatically generated by the compiler -    using the <c>-callback</c> attributes.</p> + +    <p>where each <c>{Name, Arity}</c> specifies the name and arity of +      a callback function. This function is otherwise automatically +      generated by the compiler using the <c>-callback</c> +      attributes.</p>      <p>When the compiler encounters the module attribute -    <c>-behaviour(Behaviour).</c> in a module <c>Mod</c>, it will call -    <c>Behaviour:behaviour_info(callbacks)</c> and compare the result with the -    set of functions actually exported from <c>Mod</c>, and issue a warning if -    any callback function is missing.</p> +      <c>-behaviour(Behaviour).</c> in a module <c>Mod</c>, it will +      call <c>Behaviour:behaviour_info(callbacks)</c> and compare the +      result with the set of functions actually exported from +      <c>Mod</c>, and issue a warning if any callback function is +      missing.</p>      <p>Example:</p>      <code type="none">  %% User-defined behaviour module  -module(simple_server). --export([start_link/2,...]). +-export([start_link/2, init/3, ...]).  -callback init(State :: term()) -> 'ok'.  -callback handle_req(Req :: term(), State :: term()) -> {'ok', Reply :: term()}.  -callback terminate() -> 'ok'. +-callback format_state(State :: term()) -> term(). + +-optional_callbacks([format_state/1]).  %% Alternatively you may define:  %% diff --git a/system/doc/reference_manual/modules.xml b/system/doc/reference_manual/modules.xml index f0ec7ef165..5fc8b363f8 100644 --- a/system/doc/reference_manual/modules.xml +++ b/system/doc/reference_manual/modules.xml @@ -229,13 +229,9 @@ behaviour_info(callbacks) -> Callbacks.</pre>        <p>The <c>module_info/0</c> function in each module returns        a list of <c>{Key,Value}</c> tuples with information about        the module. Currently, the list contain tuples with the following -      <c>Key</c>s: <c>attributes</c>, <c>compile</c>,  -      <c>exports</c>, and <c>imports</c>. The order and number of tuples +      <c>Key</c>s: <c>module</c>, <c>attributes</c>, <c>compile</c>, +      <c>exports</c> and <c>md5</c>. The order and number of tuples        may change without prior notice.</p> - -      <warning><p>The <c>{imports,Value}</c> tuple may be removed in a future -      release because <c>Value</c> is always an empty list. -      Do not write code that depends on it being present.</p></warning>      </section>      <section> @@ -246,6 +242,11 @@ behaviour_info(callbacks) -> Callbacks.</pre>        <p>The following values are allowed for <c>Key</c>:</p>        <taglist> +        <tag><c>module</c></tag> +	  <item> +	  <p>Return an atom representing the module name.</p> +	  </item> +          <tag><c>attributes</c></tag>  	  <item>  	  <p>Return a list of <c>{AttributeName,ValueList}</c> tuples, @@ -267,10 +268,9 @@ behaviour_info(callbacks) -> Callbacks.</pre>  	  <seealso marker="stdlib:beam_lib#strip/1">beam_lib(3)</seealso>.</p>  	  </item> -        <tag><c>imports</c></tag> +        <tag><c>md5</c></tag>  	  <item> -	  <p>Always return an empty list. The <c>imports</c> key may not -	  be supported in future release.</p> +	  <p>Return a binary representing the MD5 checksum of the module.</p>  	  </item>          <tag><c>exports</c></tag> diff --git a/system/doc/tutorial/distribution.xml b/system/doc/tutorial/distribution.xml index 6a0ea759c4..ced8e4a545 100644 --- a/system/doc/tutorial/distribution.xml +++ b/system/doc/tutorial/distribution.xml @@ -58,7 +58,6 @@        <item>global_group - Grouping nodes to global name registration groups.</item>        <item>net_adm - Various net administration routines.</item>        <item>net_kernel - Networking kernel.</item> -      <item>pg - Distributed named process groups, experimental implementation.</item>        <item>pg2 - Distributed named process groups.</item>        <item>pool - Load distribution facility.</item>        <item>slave - Functions for starting and controlling slave nodes.</item>  | 
