Index: libcfa/src/collections/array.hfa
===================================================================
--- libcfa/src/collections/array.hfa	(revision 1abcec9b6adf2ed1d9f833ccc39e3e9f529c3d52)
+++ libcfa/src/collections/array.hfa	(revision 5b21636bba032ec700286a9e66bc6fae49054e5f)
@@ -1,3 +1,4 @@
 #pragma once
+
 
 
@@ -175,46 +176,4 @@
 }
 
-// RAII for copying interacts poorly with CFA RAII's default (quasi-mandate) to take
-// 'src' parameters by value; note doc/proposals/autogen.md suggests changing this pattern.
-// If Timmed offers a by-value copy-ctor, then
-//  - the array copy-ctor will make lots of memcpys of them
-//  - the array assignment operator will make temporaries (and call their RAII, implying
-//    the above memcpys) of them
-//  - the array assignment operator will scale poorly to higher dimensionality due to #226
-// Furthermore, enabling this experimental flag breaks some multidimensional behaviour,
-// as illustraed by a test failure in array-md-sbscr-cases, which has not been investigated.
-// These limitations kick in upon calling the copy operation, not upon including the definitions.
-// Design choice here:
-//  - FIX ME: deal with `const &` on params
-#ifdef EXPERIMENTAL_ARRAY_ALLOW_COPY_RAII
-	// call copy ctor on elements
-	//   array(float, 5) y;     // given
-	//   array(float, 5) x = y; // <- do
-	forall( [N], S & | sized(S), Timmed *, Tbase & | { void ?{}( Timmed &, Timmed & ); } )
-	static inline void ?{}( arpk( N, S, Timmed, Tbase ) & this, arpk( N, S, Timmed, Tbase ) & src ) {
-		?{}( this, delay_init );
-		for (i; N) ?{}( (Timmed &)this.strides[i], src[i] );
-	}
-	forall( [N], S & | sized(S), Timmed *, Tbase & | { void ?{}( Timmed &, const Timmed & ); } )
-	static inline void ?{}( arpk( N, S, Timmed, Tbase ) & this, arpk( N, S, Timmed, Tbase ) & src  ) {
-		?{}( this, delay_init );
-		for (i; N) ?{}( (Timmed &)this.strides[i], src[i] );
-	}
-
-	// call assignment on elements
-	//   array(float, 5) x, y;  // given
-	//   x = y;                 // <- do
-	forall( [N], S & | sized(S), Timmed & | is_value(Timmed), Tbase & )
-	static inline void ?=?( arpk( N, S, Timmed, Tbase ) & this, arpk( N, S, Timmed, Tbase ) & src ) {
-		for (i; N) this[i] = src[i];
-		return src;
-	}
-	forall( [N], S & | sized(S), Timmed &, Tbase & | { void ?=?( Timmed &, const Timmed & ); } )
-	static inline void ?=?( arpk( N, S, Timmed, Tbase ) & this, arpk( N, S, Timmed, Tbase ) & src  ) {
-		for (i; N) this[i] = src[i];
-		return src;
-	}
-#endif // EXPERIMENTAL_ARRAY_ALLOW_COPY_RAII
-
 
 //
Index: libcfa/src/collections/string.cfa
===================================================================
--- libcfa/src/collections/string.cfa	(revision 1abcec9b6adf2ed1d9f833ccc39e3e9f529c3d52)
+++ libcfa/src/collections/string.cfa	(revision 5b21636bba032ec700286a9e66bc6fae49054e5f)
@@ -244,9 +244,8 @@
 
 string ?()( string & s, ssize_t start, ssize_t len ) {
-	// if ( start < 0 ) start += len( s );
-	// if ( len < 0 ) { len = -len; start -= len - 1; }
-	// if ( start < 0 ) start = 0;
-	// if ( start > len( s ) ) start = len( s );
-	// if ( start + len > len( s ) ) len = len( s ) - start;
+	if ( start < 0 ) start += len( s );
+	if ( len < 0 ) { len = -len; start -= len - 1; }
+	if ( start < 0 || start >= len( s ) ) return (string){ "" };
+	if ( start + len > len( s ) ) len = len( s ) - start;
 	string ret = { *s.inner, start, len };
 	return ret`share;
Index: tests/collections/string-overwrite.cfa
===================================================================
--- tests/collections/string-overwrite.cfa	(revision 1abcec9b6adf2ed1d9f833ccc39e3e9f529c3d52)
+++ tests/collections/string-overwrite.cfa	(revision 5b21636bba032ec700286a9e66bc6fae49054e5f)
@@ -73,5 +73,4 @@
 */
 
-const char * OUT_DELIM = "------------------------------------------------------------------------";
 
 void showOneReplacement(string & s, int ms, int ml, int ws, int wl, const char* replaceWith) {
@@ -233,5 +232,5 @@
     };
     for ( i; sizeof(cases)/sizeof(cases[0]) ) {
-        sout | OUT_DELIM | cases[i].label;
+        sout | "------------------------------------------------------------------------" | cases[i].label;
         string replaceIn = alphabetTemplate;
         showOneReplacement( replaceIn, cases[i].ms, cases[i].ml, cases[i].ws, cases[i].wl, cases[i].replaceWith );
@@ -247,20 +246,4 @@
 
 
-    runReplaceCases();
-
-
-    // in `s(i, k) = sr`:
-    // - whlln, whole-string length = len(s)
-    // - sctln, selection length = k
-    // - rplln, replacement length = len(sr)
-
-    // Extra cases via syntax that appears more direct.
-    // Indirect, as drives the table:
-    //    string replace = src(strt, len)`share;
-    //    replace = "xyz";
-    // Direct, following:
-    //    src(strt, len) = "xyz";
-    sout | OUT_DELIM | "syn-direct";
-
     //          0         1         2
     //          01234567890123456789012345
@@ -268,88 +251,12 @@
 
     s(5,5) = "qqqqq";  // start=5, end=10, len=5
-    sout | s;
+
+    sout | s;
+
 
     s(5,0) = "-----";  // start=5, end=5, len=0
-    sout | s;
-
-
-    // Extra cases for at-/out-of-bound access
-    // Every selection starts and ends at a position between characters, inclusive of before-first and after-last
-    // Intended behaviour is
-    // - negative start means "back from whlln;" i.e., -1 is before-last
-    // - negative sctln menas "back from start;" i.e., -1 from middle selects 1 character before start
-    // - squish inward at subject string's first/last character
-    //   - start > whlln means same as start == whlln: after last character, with sctln >= 0 selecting zero characters
-    //   - start < -whlln means same as start == -whlln: before first character, with sctln <= 0 selecting zero characters
-    //   - (let xstart be the position in [0,whlln] equivalent to start, by the above, in the following...)
-    //   - xstart + sctln > whlln means same as sctln = whlln - xstart  (significant when sctln is positive)
-    //   - xstart + sctln < 0 means same as sctln = -xstart  (significant when sctln is negative)
-    // - start = 0 means
-    //   - before first, when sctln is zero or positive
-    //   - after last, when sctln is negative
-    // - start "outside the string" cases are:
-    //   - start = 0 can select forward from first or backward from last (as just stated); note the asymmetric "sctln = 0" handling
-    //   - (so, the following cases are available to specify front-v-back explicitly...)
-    //   - start >= whlln can select only backward from last, i.e. selects final epsilon when sctln >= 0
-    //   - start <= -whln can select only forward from first, i.e. selects initial epsilon when sctln <= 0
-    // most selections have two equivalent representations
-    sout | OUT_DELIM | "boundary";
-/*
-The test
-
-base = 01234
-
-
-(Start|Step) is (just over|right on|just under) the (min|mid|max) value
-|
-whlln (>|=|<) rplln
-|
-0 (=|<) (whlln|rplln)
-
-Every case needs concrete & independent
-whlln
-rpln
-start
-step, aka sctln
-
-Constraint space is
-sel[Start]
-*
-sel[Step]
-*
-lens1
-*
-lens2
-,
-excluding when the lens pair contradicts
-=
-972 concrete test cases (all combinations)
-
-Toward all pairs
-Each case achieves up to 6 further pairings.
-Best-case, achieved with 81 cases.  (162 permutations / 2 orderings of a pair)
-
-
-
-Start
-just over the min value
-just over the mid value
-just over the max value
-just over the min value
-just over the mid value
-just over the max value
-just over the min value
-just over the mid value
-just over the max value
-
-
-*/
-
-
-    // Extra cases for modifying substring of substring.
-    sout | OUT_DELIM | "compound";
-
-    s = "abcdefghijklmnopqrstuvwxyz";
-    string sx = s(5,5)`share;
-
+
+    sout | s;
+
+    runReplaceCases();
 }
