public boolean containedBy(Type t, Type s) {
        switch (t.tag) {
        case UNDETVAR:
            if (s.tag == WILDCARD) {
                UndetVar undetvar = (UndetVar)t;
                WildcardType wt = (WildcardType)s;
                switch(wt.kind) {
                    case UNBOUND: //similar to ? extends Object
                    case EXTENDS: {
                        Type bound = upperBound(s);
                        // We should check the new upper bound against any of the
                        // undetvar's lower bounds.
                        for (Type t2 : undetvar.lobounds) {
                            if (!isSubtype(t2, bound))
                                return false;
                        }
                        undetvar.hibounds = undetvar.hibounds.prepend(bound);
                        break;
                    }
                    case SUPER: {
                        Type bound = lowerBound(s);
                        // We should check the new lower bound against any of the
                        // undetvar's lower bounds.
                        for (Type t2 : undetvar.hibounds) {
                            if (!isSubtype(bound, t2))
                                return false;
                        }
                        undetvar.lobounds = undetvar.lobounds.prepend(bound);
                        break;
                    }
                }
                return true;
            } else {
                return isSameType(t, s);
            }
        case ERROR:
            return true;
        default:
            return containsType(s, t);
        }
    }